Repository: WerWolv/ImHex Branch: master Commit: eca7597acaaa Files: 1098 Total size: 21.0 MB Directory structure: gitextract_ps364jvn/ ├── .clang-tidy ├── .dockerignore ├── .gdbinit ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yaml │ │ └── feature_request.yaml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── codecov.yml │ ├── scripts/ │ │ └── delete-artifact.sh │ └── workflows/ │ ├── analysis.yml │ ├── build.yml │ ├── dl-cache.yml │ ├── nightly_release.yml │ ├── release.yml │ ├── stale_issues.yml │ └── tests.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CMakePresets.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── INSTALL.md ├── LICENSE ├── PLUGINS.md ├── PRIVACY.md ├── README.md ├── SECURITY.md ├── VERSION ├── changelog.md ├── cmake/ │ ├── build_helpers.cmake │ ├── cmake_uninstall.cmake.in │ ├── ide_helpers.cmake │ ├── modules/ │ │ ├── FindBacktrace.cmake │ │ ├── FindCapstone.cmake │ │ ├── FindCoreClrEmbed.cmake │ │ ├── FindGLFW.cmake │ │ ├── FindLZ4.cmake │ │ ├── FindMagic.cmake │ │ ├── FindPackageHandleStandardArgs.cmake │ │ ├── FindPackageMessage.cmake │ │ ├── FindYara.cmake │ │ ├── FindZSTD.cmake │ │ ├── Findlibssh2.cmake │ │ ├── FindmbedTLS.cmake │ │ ├── ImHexPlugin.cmake │ │ └── PostprocessBundle.cmake │ └── sdk/ │ ├── CMakeLists.txt │ └── template/ │ ├── CMakeLists.txt │ └── source/ │ └── example_plugin.cpp ├── dist/ │ ├── AppImage/ │ │ ├── AppImageBuilder.yml │ │ └── Dockerfile │ ├── Arch/ │ │ ├── Dockerfile │ │ └── PKGBUILD │ ├── DEBIAN/ │ │ └── control.in │ ├── ImHex-9999.ebuild │ ├── cli/ │ │ ├── imhex.bat │ │ └── imhex.sh │ ├── compiling/ │ │ ├── docker.md │ │ ├── linux.md │ │ ├── macos.md │ │ ├── macos_nogpu.md │ │ └── windows.md │ ├── cppcheck.supp │ ├── flake.nix │ ├── flatpak/ │ │ └── net.werwolv.ImHex.yaml │ ├── fonts/ │ │ ├── move_private_use_area.py │ │ └── ttf_to_header_file.py │ ├── gen_release_notes.py │ ├── get_deps_archlinux.sh │ ├── get_deps_debian.sh │ ├── get_deps_fedora.sh │ ├── get_deps_msys2.sh │ ├── get_deps_tumbleweed.sh │ ├── imhex.desktop │ ├── imhex.mime.xml │ ├── langtool.py │ ├── macOS/ │ │ ├── 0001-glfw-SW.patch │ │ ├── Brewfile │ │ ├── arm64.Dockerfile │ │ ├── arm64.crosscompile.Dockerfile │ │ └── osx_10_15/ │ │ └── x64-osx.cmake │ ├── msys2/ │ │ └── PKGBUILD │ ├── net.werwolv.ImHex.metainfo.xml │ ├── rpm/ │ │ └── imhex.spec │ ├── snap/ │ │ └── snapcraft.yaml │ └── web/ │ ├── Dockerfile │ ├── Host.Dockerfile │ ├── compose.yml │ ├── plugin-bundle.cpp.in │ ├── serve.py │ └── source/ │ ├── enable-threads.js │ ├── index.html │ ├── manifest.json │ ├── robots.txt │ ├── sitemap.xml │ ├── style.css │ └── wasm-config.js ├── lib/ │ ├── libimhex/ │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── include/ │ │ │ ├── hex/ │ │ │ │ ├── api/ │ │ │ │ │ ├── achievement_manager.hpp │ │ │ │ │ ├── content_registry/ │ │ │ │ │ │ ├── background_services.hpp │ │ │ │ │ │ ├── command_palette.hpp │ │ │ │ │ │ ├── communication_interface.hpp │ │ │ │ │ │ ├── data_formatter.hpp │ │ │ │ │ │ ├── data_information.hpp │ │ │ │ │ │ ├── data_inspector.hpp │ │ │ │ │ │ ├── data_processor.hpp │ │ │ │ │ │ ├── diffing.hpp │ │ │ │ │ │ ├── disassemblers.hpp │ │ │ │ │ │ ├── experiments.hpp │ │ │ │ │ │ ├── file_type_handler.hpp │ │ │ │ │ │ ├── hashes.hpp │ │ │ │ │ │ ├── hex_editor.hpp │ │ │ │ │ │ ├── pattern_language.hpp │ │ │ │ │ │ ├── provider.hpp │ │ │ │ │ │ ├── reports.hpp │ │ │ │ │ │ ├── settings.hpp │ │ │ │ │ │ ├── tools.hpp │ │ │ │ │ │ ├── user_interface.hpp │ │ │ │ │ │ └── views.hpp │ │ │ │ │ ├── event_manager.hpp │ │ │ │ │ ├── events/ │ │ │ │ │ │ ├── events_gui.hpp │ │ │ │ │ │ ├── events_interaction.hpp │ │ │ │ │ │ ├── events_lifecycle.hpp │ │ │ │ │ │ ├── events_provider.hpp │ │ │ │ │ │ ├── requests_gui.hpp │ │ │ │ │ │ ├── requests_interaction.hpp │ │ │ │ │ │ ├── requests_lifecycle.hpp │ │ │ │ │ │ └── requests_provider.hpp │ │ │ │ │ ├── imhex_api/ │ │ │ │ │ │ ├── bookmarks.hpp │ │ │ │ │ │ ├── fonts.hpp │ │ │ │ │ │ ├── hex_editor.hpp │ │ │ │ │ │ ├── messaging.hpp │ │ │ │ │ │ ├── provider.hpp │ │ │ │ │ │ └── system.hpp │ │ │ │ │ ├── layout_manager.hpp │ │ │ │ │ ├── localization_manager.hpp │ │ │ │ │ ├── plugin_manager.hpp │ │ │ │ │ ├── project_file_manager.hpp │ │ │ │ │ ├── shortcut_manager.hpp │ │ │ │ │ ├── task_manager.hpp │ │ │ │ │ ├── theme_manager.hpp │ │ │ │ │ ├── tutorial_manager.hpp │ │ │ │ │ └── workspace_manager.hpp │ │ │ │ ├── api_urls.hpp │ │ │ │ ├── data_processor/ │ │ │ │ │ ├── attribute.hpp │ │ │ │ │ ├── link.hpp │ │ │ │ │ └── node.hpp │ │ │ │ ├── helpers/ │ │ │ │ │ ├── auto_reset.hpp │ │ │ │ │ ├── binary_pattern.hpp │ │ │ │ │ ├── concepts.hpp │ │ │ │ │ ├── crypto.hpp │ │ │ │ │ ├── debugging.hpp │ │ │ │ │ ├── default_paths.hpp │ │ │ │ │ ├── encoding_file.hpp │ │ │ │ │ ├── fmt.hpp │ │ │ │ │ ├── fs.hpp │ │ │ │ │ ├── http_requests.hpp │ │ │ │ │ ├── http_requests_emscripten.hpp │ │ │ │ │ ├── http_requests_native.hpp │ │ │ │ │ ├── keys.hpp │ │ │ │ │ ├── literals.hpp │ │ │ │ │ ├── logger.hpp │ │ │ │ │ ├── magic.hpp │ │ │ │ │ ├── menu_items.hpp │ │ │ │ │ ├── opengl.hpp │ │ │ │ │ ├── patches.hpp │ │ │ │ │ ├── scaling.hpp │ │ │ │ │ ├── semantic_version.hpp │ │ │ │ │ ├── tar.hpp │ │ │ │ │ ├── types.hpp │ │ │ │ │ ├── udp_server.hpp │ │ │ │ │ ├── utils.hpp │ │ │ │ │ ├── utils_linux.hpp │ │ │ │ │ └── utils_macos.hpp │ │ │ │ ├── mcp/ │ │ │ │ │ ├── client.hpp │ │ │ │ │ └── server.hpp │ │ │ │ ├── plugin.hpp │ │ │ │ ├── providers/ │ │ │ │ │ ├── buffered_reader.hpp │ │ │ │ │ ├── cached_provider.hpp │ │ │ │ │ ├── memory_provider.hpp │ │ │ │ │ ├── overlay.hpp │ │ │ │ │ ├── provider.hpp │ │ │ │ │ ├── provider_data.hpp │ │ │ │ │ └── undo_redo/ │ │ │ │ │ ├── operations/ │ │ │ │ │ │ ├── operation.hpp │ │ │ │ │ │ └── operation_group.hpp │ │ │ │ │ └── stack.hpp │ │ │ │ ├── subcommands/ │ │ │ │ │ └── subcommands.hpp │ │ │ │ ├── test/ │ │ │ │ │ ├── test_provider.hpp │ │ │ │ │ └── tests.hpp │ │ │ │ └── ui/ │ │ │ │ ├── banner.hpp │ │ │ │ ├── imgui_imhex_extensions.h │ │ │ │ ├── popup.hpp │ │ │ │ ├── toast.hpp │ │ │ │ ├── view.hpp │ │ │ │ └── widgets.hpp │ │ │ ├── hex.cppm │ │ │ └── hex.hpp │ │ └── source/ │ │ ├── api/ │ │ │ ├── achievement_manager.cpp │ │ │ ├── content_registry.cpp │ │ │ ├── event_manager.cpp │ │ │ ├── imhex_api.cpp │ │ │ ├── layout_manager.cpp │ │ │ ├── localization_manager.cpp │ │ │ ├── plugin_manager.cpp │ │ │ ├── project_file_manager.cpp │ │ │ ├── shortcut_manager.cpp │ │ │ ├── task_manager.cpp │ │ │ ├── theme_manager.cpp │ │ │ ├── tutorial_manager.cpp │ │ │ └── workspace_manager.cpp │ │ ├── data_processor/ │ │ │ ├── attribute.cpp │ │ │ ├── link.cpp │ │ │ └── node.cpp │ │ ├── helpers/ │ │ │ ├── binary_pattern.cpp │ │ │ ├── crypto.cpp │ │ │ ├── debugging.cpp │ │ │ ├── default_paths.cpp │ │ │ ├── encoding_file.cpp │ │ │ ├── fs.cpp │ │ │ ├── http_requests.cpp │ │ │ ├── http_requests_emscripten.cpp │ │ │ ├── http_requests_native.cpp │ │ │ ├── imgui_hooks.cpp │ │ │ ├── keys.cpp │ │ │ ├── logger.cpp │ │ │ ├── macos_menu.m │ │ │ ├── magic.cpp │ │ │ ├── opengl.cpp │ │ │ ├── patches.cpp │ │ │ ├── scaling.cpp │ │ │ ├── semantic_version.cpp │ │ │ ├── tar.cpp │ │ │ ├── udp_server.cpp │ │ │ ├── utils.cpp │ │ │ ├── utils_linux.cpp │ │ │ └── utils_macos.m │ │ ├── mcp/ │ │ │ ├── client.cpp │ │ │ └── server.cpp │ │ ├── providers/ │ │ │ ├── cached_provider.cpp │ │ │ ├── memory_provider.cpp │ │ │ ├── provider.cpp │ │ │ └── undo/ │ │ │ └── stack.cpp │ │ ├── subcommands/ │ │ │ └── subcommands.cpp │ │ ├── test/ │ │ │ └── tests.cpp │ │ └── ui/ │ │ ├── banner.cpp │ │ ├── imgui_imhex_extensions.cpp │ │ ├── popup.cpp │ │ ├── toast.cpp │ │ └── view.cpp │ ├── third_party/ │ │ ├── .clang-tidy │ │ ├── boost/ │ │ │ ├── CMakeLists.txt │ │ │ └── regex/ │ │ │ ├── CMakeLists.txt │ │ │ └── include/ │ │ │ └── boost/ │ │ │ ├── cregex.hpp │ │ │ ├── regex/ │ │ │ │ ├── concepts.hpp │ │ │ │ ├── config/ │ │ │ │ │ ├── borland.hpp │ │ │ │ │ └── cwchar.hpp │ │ │ │ ├── config.hpp │ │ │ │ ├── icu.hpp │ │ │ │ ├── mfc.hpp │ │ │ │ ├── pattern_except.hpp │ │ │ │ ├── pending/ │ │ │ │ │ ├── object_cache.hpp │ │ │ │ │ ├── static_mutex.hpp │ │ │ │ │ └── unicode_iterator.hpp │ │ │ │ ├── regex_traits.hpp │ │ │ │ ├── user.hpp │ │ │ │ ├── v4/ │ │ │ │ │ ├── basic_regex.hpp │ │ │ │ │ ├── basic_regex_creator.hpp │ │ │ │ │ ├── basic_regex_parser.hpp │ │ │ │ │ ├── c_regex_traits.hpp │ │ │ │ │ ├── char_regex_traits.hpp │ │ │ │ │ ├── cpp_regex_traits.hpp │ │ │ │ │ ├── cregex.hpp │ │ │ │ │ ├── error_type.hpp │ │ │ │ │ ├── icu.hpp │ │ │ │ │ ├── indexed_bit_flag.hpp │ │ │ │ │ ├── iterator_category.hpp │ │ │ │ │ ├── iterator_traits.hpp │ │ │ │ │ ├── match_flags.hpp │ │ │ │ │ ├── match_results.hpp │ │ │ │ │ ├── mem_block_cache.hpp │ │ │ │ │ ├── object_cache.hpp │ │ │ │ │ ├── pattern_except.hpp │ │ │ │ │ ├── perl_matcher.hpp │ │ │ │ │ ├── perl_matcher_common.hpp │ │ │ │ │ ├── perl_matcher_non_recursive.hpp │ │ │ │ │ ├── perl_matcher_recursive.hpp │ │ │ │ │ ├── primary_transform.hpp │ │ │ │ │ ├── protected_call.hpp │ │ │ │ │ ├── regbase.hpp │ │ │ │ │ ├── regex.hpp │ │ │ │ │ ├── regex_format.hpp │ │ │ │ │ ├── regex_fwd.hpp │ │ │ │ │ ├── regex_grep.hpp │ │ │ │ │ ├── regex_iterator.hpp │ │ │ │ │ ├── regex_match.hpp │ │ │ │ │ ├── regex_merge.hpp │ │ │ │ │ ├── regex_raw_buffer.hpp │ │ │ │ │ ├── regex_replace.hpp │ │ │ │ │ ├── regex_search.hpp │ │ │ │ │ ├── regex_split.hpp │ │ │ │ │ ├── regex_token_iterator.hpp │ │ │ │ │ ├── regex_traits.hpp │ │ │ │ │ ├── regex_traits_defaults.hpp │ │ │ │ │ ├── regex_workaround.hpp │ │ │ │ │ ├── states.hpp │ │ │ │ │ ├── sub_match.hpp │ │ │ │ │ ├── syntax_type.hpp │ │ │ │ │ ├── u32regex_iterator.hpp │ │ │ │ │ ├── u32regex_token_iterator.hpp │ │ │ │ │ ├── unicode_iterator.hpp │ │ │ │ │ └── w32_regex_traits.hpp │ │ │ │ └── v5/ │ │ │ │ ├── basic_regex.hpp │ │ │ │ ├── basic_regex_creator.hpp │ │ │ │ ├── basic_regex_parser.hpp │ │ │ │ ├── c_regex_traits.hpp │ │ │ │ ├── char_regex_traits.hpp │ │ │ │ ├── cpp_regex_traits.hpp │ │ │ │ ├── cregex.hpp │ │ │ │ ├── error_type.hpp │ │ │ │ ├── icu.hpp │ │ │ │ ├── iterator_category.hpp │ │ │ │ ├── iterator_traits.hpp │ │ │ │ ├── match_flags.hpp │ │ │ │ ├── match_results.hpp │ │ │ │ ├── mem_block_cache.hpp │ │ │ │ ├── object_cache.hpp │ │ │ │ ├── pattern_except.hpp │ │ │ │ ├── perl_matcher.hpp │ │ │ │ ├── perl_matcher_common.hpp │ │ │ │ ├── perl_matcher_non_recursive.hpp │ │ │ │ ├── primary_transform.hpp │ │ │ │ ├── regbase.hpp │ │ │ │ ├── regex.hpp │ │ │ │ ├── regex_format.hpp │ │ │ │ ├── regex_fwd.hpp │ │ │ │ ├── regex_grep.hpp │ │ │ │ ├── regex_iterator.hpp │ │ │ │ ├── regex_match.hpp │ │ │ │ ├── regex_merge.hpp │ │ │ │ ├── regex_raw_buffer.hpp │ │ │ │ ├── regex_replace.hpp │ │ │ │ ├── regex_search.hpp │ │ │ │ ├── regex_split.hpp │ │ │ │ ├── regex_token_iterator.hpp │ │ │ │ ├── regex_traits.hpp │ │ │ │ ├── regex_traits_defaults.hpp │ │ │ │ ├── regex_workaround.hpp │ │ │ │ ├── states.hpp │ │ │ │ ├── sub_match.hpp │ │ │ │ ├── syntax_type.hpp │ │ │ │ ├── u32regex_iterator.hpp │ │ │ │ ├── u32regex_token_iterator.hpp │ │ │ │ ├── unicode_iterator.hpp │ │ │ │ └── w32_regex_traits.hpp │ │ │ ├── regex.h │ │ │ ├── regex.hpp │ │ │ └── regex_fwd.hpp │ │ ├── imgui/ │ │ │ ├── CMakeLists.txt │ │ │ ├── backend/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── include/ │ │ │ │ │ ├── emscripten_browser_clipboard.h │ │ │ │ │ ├── imgui_impl_glfw.h │ │ │ │ │ ├── imgui_impl_opengl3.h │ │ │ │ │ ├── imgui_impl_opengl3_loader.h │ │ │ │ │ ├── opengl_support.h │ │ │ │ │ └── stb_image.h │ │ │ │ └── source/ │ │ │ │ ├── imgui_impl_glfw.cpp │ │ │ │ └── imgui_impl_opengl3.cpp │ │ │ ├── cimgui/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── include/ │ │ │ │ │ └── cimgui.h │ │ │ │ └── source/ │ │ │ │ └── cimgui.cpp │ │ │ ├── imgui/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── README.md │ │ │ │ ├── include/ │ │ │ │ │ ├── imconfig.h │ │ │ │ │ ├── imgui.h │ │ │ │ │ ├── imgui_internal.h │ │ │ │ │ ├── imstb_rectpack.h │ │ │ │ │ ├── imstb_textedit.h │ │ │ │ │ ├── imstb_truetype.h │ │ │ │ │ └── misc/ │ │ │ │ │ └── freetype/ │ │ │ │ │ └── imgui_freetype.h │ │ │ │ └── source/ │ │ │ │ ├── imgui.cpp │ │ │ │ ├── imgui_demo.cpp │ │ │ │ ├── imgui_draw.cpp │ │ │ │ ├── imgui_tables.cpp │ │ │ │ ├── imgui_widgets.cpp │ │ │ │ └── misc/ │ │ │ │ └── freetype/ │ │ │ │ └── imgui_freetype.cpp │ │ │ ├── imgui_test_engine/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── README.md │ │ │ │ ├── include/ │ │ │ │ │ ├── imgui_capture_tool.h │ │ │ │ │ ├── imgui_te_context.h │ │ │ │ │ ├── imgui_te_coroutine.h │ │ │ │ │ ├── imgui_te_engine.h │ │ │ │ │ ├── imgui_te_exporters.h │ │ │ │ │ ├── imgui_te_imconfig.h │ │ │ │ │ ├── imgui_te_internal.h │ │ │ │ │ ├── imgui_te_perftool.h │ │ │ │ │ ├── imgui_te_ui.h │ │ │ │ │ ├── imgui_te_utils.h │ │ │ │ │ └── thirdparty/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── Str/ │ │ │ │ │ │ ├── .editorconfig │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── Str.h │ │ │ │ │ │ └── Str.natvis │ │ │ │ │ └── stb/ │ │ │ │ │ └── imstb_image_write.h │ │ │ │ └── source/ │ │ │ │ ├── imgui_capture_tool.cpp │ │ │ │ ├── imgui_te_context.cpp │ │ │ │ ├── imgui_te_coroutine.cpp │ │ │ │ ├── imgui_te_engine.cpp │ │ │ │ ├── imgui_te_exporters.cpp │ │ │ │ ├── imgui_te_perftool.cpp │ │ │ │ ├── imgui_te_ui.cpp │ │ │ │ └── imgui_te_utils.cpp │ │ │ ├── imnodes/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── LICENSE.md │ │ │ │ ├── README.md │ │ │ │ ├── include/ │ │ │ │ │ ├── imnodes.h │ │ │ │ │ └── imnodes_internal.h │ │ │ │ └── source/ │ │ │ │ └── imnodes.cpp │ │ │ ├── implot/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── README.md │ │ │ │ ├── include/ │ │ │ │ │ ├── implot.h │ │ │ │ │ └── implot_internal.h │ │ │ │ └── source/ │ │ │ │ ├── implot.cpp │ │ │ │ ├── implot_demo.cpp │ │ │ │ └── implot_items.cpp │ │ │ └── implot3d/ │ │ │ ├── CMakeLists.txt │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── include/ │ │ │ │ ├── implot3d.h │ │ │ │ └── implot3d_internal.h │ │ │ └── source/ │ │ │ ├── implot3d.cpp │ │ │ ├── implot3d_demo.cpp │ │ │ ├── implot3d_items.cpp │ │ │ └── implot3d_meshes.cpp │ │ ├── llvm-demangle/ │ │ │ ├── CMakeLists.txt │ │ │ ├── LICENSE.TXT │ │ │ ├── include/ │ │ │ │ └── llvm/ │ │ │ │ └── Demangle/ │ │ │ │ ├── Demangle.h │ │ │ │ ├── DemangleConfig.h │ │ │ │ ├── ItaniumDemangle.h │ │ │ │ ├── ItaniumNodes.def │ │ │ │ ├── MicrosoftDemangle.h │ │ │ │ ├── MicrosoftDemangleNodes.h │ │ │ │ ├── README.txt │ │ │ │ ├── StringViewExtras.h │ │ │ │ └── Utility.h │ │ │ └── source/ │ │ │ ├── DLangDemangle.cpp │ │ │ ├── Demangle.cpp │ │ │ ├── ItaniumDemangle.cpp │ │ │ ├── MicrosoftDemangle.cpp │ │ │ ├── MicrosoftDemangleNodes.cpp │ │ │ └── RustDemangle.cpp │ │ ├── microtar/ │ │ │ ├── CMakeLists.txt │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── include/ │ │ │ │ └── microtar.h │ │ │ └── source/ │ │ │ └── microtar.c │ │ ├── miniaudio/ │ │ │ ├── CMakeLists.txt │ │ │ ├── LICENSE │ │ │ ├── include/ │ │ │ │ └── miniaudio.h │ │ │ └── source/ │ │ │ └── miniaudio.c │ │ ├── nlohmann_json/ │ │ │ ├── CMakeLists.txt │ │ │ ├── LICENSE.MIT │ │ │ ├── cmake/ │ │ │ │ ├── config.cmake.in │ │ │ │ ├── nlohmann_jsonConfigVersion.cmake.in │ │ │ │ └── pkg-config.pc.in │ │ │ ├── include/ │ │ │ │ └── nlohmann/ │ │ │ │ ├── adl_serializer.hpp │ │ │ │ ├── byte_container_with_subtype.hpp │ │ │ │ ├── detail/ │ │ │ │ │ ├── abi_macros.hpp │ │ │ │ │ ├── conversions/ │ │ │ │ │ │ ├── from_json.hpp │ │ │ │ │ │ ├── to_chars.hpp │ │ │ │ │ │ └── to_json.hpp │ │ │ │ │ ├── exceptions.hpp │ │ │ │ │ ├── hash.hpp │ │ │ │ │ ├── input/ │ │ │ │ │ │ ├── binary_reader.hpp │ │ │ │ │ │ ├── input_adapters.hpp │ │ │ │ │ │ ├── json_sax.hpp │ │ │ │ │ │ ├── lexer.hpp │ │ │ │ │ │ ├── parser.hpp │ │ │ │ │ │ └── position_t.hpp │ │ │ │ │ ├── iterators/ │ │ │ │ │ │ ├── internal_iterator.hpp │ │ │ │ │ │ ├── iter_impl.hpp │ │ │ │ │ │ ├── iteration_proxy.hpp │ │ │ │ │ │ ├── iterator_traits.hpp │ │ │ │ │ │ ├── json_reverse_iterator.hpp │ │ │ │ │ │ └── primitive_iterator.hpp │ │ │ │ │ ├── json_custom_base_class.hpp │ │ │ │ │ ├── json_pointer.hpp │ │ │ │ │ ├── json_ref.hpp │ │ │ │ │ ├── macro_scope.hpp │ │ │ │ │ ├── macro_unscope.hpp │ │ │ │ │ ├── meta/ │ │ │ │ │ │ ├── call_std/ │ │ │ │ │ │ │ ├── begin.hpp │ │ │ │ │ │ │ └── end.hpp │ │ │ │ │ │ ├── cpp_future.hpp │ │ │ │ │ │ ├── detected.hpp │ │ │ │ │ │ ├── identity_tag.hpp │ │ │ │ │ │ ├── is_sax.hpp │ │ │ │ │ │ ├── std_fs.hpp │ │ │ │ │ │ ├── type_traits.hpp │ │ │ │ │ │ └── void_t.hpp │ │ │ │ │ ├── output/ │ │ │ │ │ │ ├── binary_writer.hpp │ │ │ │ │ │ ├── output_adapters.hpp │ │ │ │ │ │ └── serializer.hpp │ │ │ │ │ ├── string_concat.hpp │ │ │ │ │ ├── string_escape.hpp │ │ │ │ │ ├── string_utils.hpp │ │ │ │ │ └── value_t.hpp │ │ │ │ ├── json.hpp │ │ │ │ ├── json_fwd.hpp │ │ │ │ ├── ordered_map.hpp │ │ │ │ └── thirdparty/ │ │ │ │ └── hedley/ │ │ │ │ ├── hedley.hpp │ │ │ │ └── hedley_undef.hpp │ │ │ ├── nlohmann_json.natvis │ │ │ └── single_include/ │ │ │ └── nlohmann/ │ │ │ ├── json.hpp │ │ │ └── json_fwd.hpp │ │ └── yara/ │ │ ├── CMakeLists.txt │ │ └── crypto.h │ └── trace/ │ ├── CMakeLists.txt │ ├── include/ │ │ └── hex/ │ │ └── trace/ │ │ ├── exceptions.hpp │ │ └── stacktrace.hpp │ └── source/ │ ├── exceptions.cpp │ ├── instr_entry.cpp │ └── stacktrace.cpp ├── main/ │ ├── CMakeLists.txt │ ├── forwarder/ │ │ ├── CMakeLists.txt │ │ └── source/ │ │ └── main.cpp │ ├── gui/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ ├── crash_handlers.hpp │ │ │ ├── init/ │ │ │ │ ├── run.hpp │ │ │ │ ├── splash_window.hpp │ │ │ │ └── tasks.hpp │ │ │ ├── messaging.hpp │ │ │ └── window.hpp │ │ ├── romfs/ │ │ │ └── splash_colors.json │ │ └── source/ │ │ ├── crash_handlers.cpp │ │ ├── init/ │ │ │ ├── run/ │ │ │ │ ├── cli.cpp │ │ │ │ ├── common.cpp │ │ │ │ ├── desktop.cpp │ │ │ │ └── web.cpp │ │ │ ├── splash_window.cpp │ │ │ └── tasks.cpp │ │ ├── main.cpp │ │ ├── messaging/ │ │ │ ├── common.cpp │ │ │ ├── linux.cpp │ │ │ ├── macos.cpp │ │ │ ├── web.cpp │ │ │ └── windows.cpp │ │ └── window/ │ │ ├── platform/ │ │ │ ├── linux.cpp │ │ │ ├── macos.cpp │ │ │ ├── web.cpp │ │ │ └── windows.cpp │ │ └── window.cpp │ └── updater/ │ ├── CMakeLists.txt │ └── source/ │ └── main.cpp ├── plugins/ │ ├── builtin/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ ├── content/ │ │ │ │ ├── command_line_interface.hpp │ │ │ │ ├── data_processor_nodes.hpp │ │ │ │ ├── differing_byte_searcher.hpp │ │ │ │ ├── export_formatters/ │ │ │ │ │ ├── export_formatter.hpp │ │ │ │ │ ├── export_formatter_csv.hpp │ │ │ │ │ ├── export_formatter_json.hpp │ │ │ │ │ └── export_formatter_tsv.hpp │ │ │ │ ├── global_actions.hpp │ │ │ │ ├── helpers/ │ │ │ │ │ ├── constants.hpp │ │ │ │ │ └── diagrams.hpp │ │ │ │ ├── popups/ │ │ │ │ │ ├── hex_editor/ │ │ │ │ │ │ ├── popup_hex_editor_base_address.hpp │ │ │ │ │ │ ├── popup_hex_editor_decoded_string.hpp │ │ │ │ │ │ ├── popup_hex_editor_fill.hpp │ │ │ │ │ │ ├── popup_hex_editor_find.hpp │ │ │ │ │ │ ├── popup_hex_editor_goto.hpp │ │ │ │ │ │ ├── popup_hex_editor_insert.hpp │ │ │ │ │ │ ├── popup_hex_editor_page_size.hpp │ │ │ │ │ │ ├── popup_hex_editor_paste_behaviour.hpp │ │ │ │ │ │ ├── popup_hex_editor_remove.hpp │ │ │ │ │ │ ├── popup_hex_editor_resize.hpp │ │ │ │ │ │ └── popup_hex_editor_select.hpp │ │ │ │ │ ├── popup_blocking_task.hpp │ │ │ │ │ ├── popup_crash_recovered.hpp │ │ │ │ │ ├── popup_docs_question.hpp │ │ │ │ │ ├── popup_tasks_waiting.hpp │ │ │ │ │ └── popup_unsaved_changes.hpp │ │ │ │ ├── providers/ │ │ │ │ │ ├── base64_provider.hpp │ │ │ │ │ ├── command_provider.hpp │ │ │ │ │ ├── disk_provider.hpp │ │ │ │ │ ├── file_provider.hpp │ │ │ │ │ ├── gdb_provider.hpp │ │ │ │ │ ├── intel_hex_provider.hpp │ │ │ │ │ ├── memory_file_provider.hpp │ │ │ │ │ ├── motorola_srec_provider.hpp │ │ │ │ │ ├── null_provider.hpp │ │ │ │ │ ├── process_memory_provider.hpp │ │ │ │ │ ├── udp_provider.hpp │ │ │ │ │ ├── undo_operations/ │ │ │ │ │ │ ├── operation_bookmark.hpp │ │ │ │ │ │ ├── operation_insert.hpp │ │ │ │ │ │ ├── operation_remove.hpp │ │ │ │ │ │ └── operation_write.hpp │ │ │ │ │ └── view_provider.hpp │ │ │ │ ├── recent.hpp │ │ │ │ ├── text_highlighting/ │ │ │ │ │ └── pattern_language.hpp │ │ │ │ ├── tools_entries.hpp │ │ │ │ └── views/ │ │ │ │ ├── fullscreen/ │ │ │ │ │ ├── view_fullscreen_file_info.hpp │ │ │ │ │ └── view_fullscreen_save_editor.hpp │ │ │ │ ├── view_about.hpp │ │ │ │ ├── view_achievements.hpp │ │ │ │ ├── view_bookmarks.hpp │ │ │ │ ├── view_command_palette.hpp │ │ │ │ ├── view_data_inspector.hpp │ │ │ │ ├── view_data_processor.hpp │ │ │ │ ├── view_find.hpp │ │ │ │ ├── view_hex_editor.hpp │ │ │ │ ├── view_highlight_rules.hpp │ │ │ │ ├── view_information.hpp │ │ │ │ ├── view_logs.hpp │ │ │ │ ├── view_patches.hpp │ │ │ │ ├── view_pattern_data.hpp │ │ │ │ ├── view_pattern_editor.hpp │ │ │ │ ├── view_provider_settings.hpp │ │ │ │ ├── view_settings.hpp │ │ │ │ ├── view_store.hpp │ │ │ │ ├── view_theme_manager.hpp │ │ │ │ ├── view_tools.hpp │ │ │ │ └── view_tutorials.hpp │ │ │ └── plugin_builtin.hpp │ │ ├── romfs/ │ │ │ ├── assets/ │ │ │ │ ├── common/ │ │ │ │ │ └── color_names.json │ │ │ │ └── screenshot_descriptions.json │ │ │ ├── auto_extract/ │ │ │ │ └── workspaces/ │ │ │ │ ├── default.hexws │ │ │ │ └── minimal.hexws │ │ │ ├── lang/ │ │ │ │ ├── de_DE.json │ │ │ │ ├── en_US.json │ │ │ │ ├── es_ES.json │ │ │ │ ├── fr_FR.json │ │ │ │ ├── hu_HU.json │ │ │ │ ├── it_IT.json │ │ │ │ ├── ja_JP.json │ │ │ │ ├── ko_KR.json │ │ │ │ ├── languages.json │ │ │ │ ├── pl_PL.json │ │ │ │ ├── pt_BR.json │ │ │ │ ├── ru_RU.json │ │ │ │ ├── uk_UA.json │ │ │ │ ├── zh_CN.json │ │ │ │ └── zh_TW.json │ │ │ ├── layouts/ │ │ │ │ ├── default.hexlyt │ │ │ │ └── minimal.hexlyt │ │ │ ├── licenses/ │ │ │ │ └── LICENSE │ │ │ ├── logo.ans │ │ │ ├── mcp/ │ │ │ │ └── tools/ │ │ │ │ ├── execute_pattern_code.json │ │ │ │ ├── get_pattern_console_content.json │ │ │ │ ├── get_patterns.json │ │ │ │ ├── list_open_data_sources.json │ │ │ │ ├── open_file.json │ │ │ │ ├── read_data.json │ │ │ │ └── select_data_source.json │ │ │ ├── shaders/ │ │ │ │ └── retro/ │ │ │ │ ├── fragment.glsl │ │ │ │ └── vertex.glsl │ │ │ ├── themes/ │ │ │ │ ├── classic.json │ │ │ │ ├── dark.json │ │ │ │ └── light.json │ │ │ └── tips.json │ │ ├── source/ │ │ │ ├── content/ │ │ │ │ ├── achievements.cpp │ │ │ │ ├── background_services.cpp │ │ │ │ ├── command_line_interface.cpp │ │ │ │ ├── command_palette_commands.cpp │ │ │ │ ├── communication_interface.cpp │ │ │ │ ├── data_formatters.cpp │ │ │ │ ├── data_information_sections.cpp │ │ │ │ ├── data_inspector.cpp │ │ │ │ ├── data_processor_nodes/ │ │ │ │ │ ├── basic_nodes.cpp │ │ │ │ │ ├── control_nodes.cpp │ │ │ │ │ ├── decode_nodes.cpp │ │ │ │ │ ├── logic_nodes.cpp │ │ │ │ │ ├── math_nodes.cpp │ │ │ │ │ ├── other_nodes.cpp │ │ │ │ │ └── visual_nodes.cpp │ │ │ │ ├── data_processor_nodes.cpp │ │ │ │ ├── data_visualizers.cpp │ │ │ │ ├── differing_byte_searcher.cpp │ │ │ │ ├── events.cpp │ │ │ │ ├── file_extraction.cpp │ │ │ │ ├── file_handlers.cpp │ │ │ │ ├── global_actions.cpp │ │ │ │ ├── helpers/ │ │ │ │ │ └── constants.cpp │ │ │ │ ├── init_tasks.cpp │ │ │ │ ├── main_menu_items.cpp │ │ │ │ ├── mcp_tools.cpp │ │ │ │ ├── minimap_visualizers.cpp │ │ │ │ ├── out_of_box_experience.cpp │ │ │ │ ├── pl_builtin_functions.cpp │ │ │ │ ├── pl_builtin_types.cpp │ │ │ │ ├── pl_pragmas.cpp │ │ │ │ ├── pl_visualizers/ │ │ │ │ │ ├── chunk_entropy.cpp │ │ │ │ │ └── hex_viewer.cpp │ │ │ │ ├── pl_visualizers.cpp │ │ │ │ ├── popups/ │ │ │ │ │ └── hex_editor/ │ │ │ │ │ ├── popup_hex_editor_base_address.cpp │ │ │ │ │ ├── popup_hex_editor_decoded_string.cpp │ │ │ │ │ ├── popup_hex_editor_fill.cpp │ │ │ │ │ ├── popup_hex_editor_find.cpp │ │ │ │ │ ├── popup_hex_editor_goto.cpp │ │ │ │ │ ├── popup_hex_editor_insert.cpp │ │ │ │ │ ├── popup_hex_editor_page_size.cpp │ │ │ │ │ ├── popup_hex_editor_paste_behaviour.cpp │ │ │ │ │ ├── popup_hex_editor_remove.cpp │ │ │ │ │ ├── popup_hex_editor_resize.cpp │ │ │ │ │ └── popup_hex_editor_select.cpp │ │ │ │ ├── project.cpp │ │ │ │ ├── providers/ │ │ │ │ │ ├── base64_provider.cpp │ │ │ │ │ ├── command_provider.cpp │ │ │ │ │ ├── disk_provider.cpp │ │ │ │ │ ├── file_provider.cpp │ │ │ │ │ ├── gdb_provider.cpp │ │ │ │ │ ├── intel_hex_provider.cpp │ │ │ │ │ ├── memory_file_provider.cpp │ │ │ │ │ ├── motorola_srec_provider.cpp │ │ │ │ │ ├── process_memory_provider.cpp │ │ │ │ │ ├── udp_provider.cpp │ │ │ │ │ └── view_provider.cpp │ │ │ │ ├── providers.cpp │ │ │ │ ├── recent.cpp │ │ │ │ ├── report_generators.cpp │ │ │ │ ├── settings_entries.cpp │ │ │ │ ├── text_highlighting/ │ │ │ │ │ └── pattern_language.cpp │ │ │ │ ├── themes.cpp │ │ │ │ ├── tools/ │ │ │ │ │ ├── ascii_table.cpp │ │ │ │ │ ├── base_converter.cpp │ │ │ │ │ ├── byte_swapper.cpp │ │ │ │ │ ├── color_picker.cpp │ │ │ │ │ ├── demangler.cpp │ │ │ │ │ ├── euclidean_alg.cpp │ │ │ │ │ ├── file_tool_combiner.cpp │ │ │ │ │ ├── file_tool_shredder.cpp │ │ │ │ │ ├── file_tool_splitter.cpp │ │ │ │ │ ├── file_uploader.cpp │ │ │ │ │ ├── graphing_calc.cpp │ │ │ │ │ ├── http_requests.cpp │ │ │ │ │ ├── ieee_decoder.cpp │ │ │ │ │ ├── math_eval.cpp │ │ │ │ │ ├── multiplication_decoder.cpp │ │ │ │ │ ├── perms_calc.cpp │ │ │ │ │ ├── regex_replacer.cpp │ │ │ │ │ ├── tcp_client_server.cpp │ │ │ │ │ └── wiki_explainer.cpp │ │ │ │ ├── tools_entries.cpp │ │ │ │ ├── tutorials/ │ │ │ │ │ ├── introduction.cpp │ │ │ │ │ └── tutorials.cpp │ │ │ │ ├── ui_items.cpp │ │ │ │ ├── views/ │ │ │ │ │ ├── fullscreen/ │ │ │ │ │ │ ├── view_fullscreen_file_info.cpp │ │ │ │ │ │ └── view_fullscreen_save_editor.cpp │ │ │ │ │ ├── view_about.cpp │ │ │ │ │ ├── view_achievements.cpp │ │ │ │ │ ├── view_bookmarks.cpp │ │ │ │ │ ├── view_command_palette.cpp │ │ │ │ │ ├── view_data_inspector.cpp │ │ │ │ │ ├── view_data_processor.cpp │ │ │ │ │ ├── view_find.cpp │ │ │ │ │ ├── view_hex_editor.cpp │ │ │ │ │ ├── view_highlight_rules.cpp │ │ │ │ │ ├── view_information.cpp │ │ │ │ │ ├── view_logs.cpp │ │ │ │ │ ├── view_patches.cpp │ │ │ │ │ ├── view_pattern_data.cpp │ │ │ │ │ ├── view_pattern_editor.cpp │ │ │ │ │ ├── view_provider_settings.cpp │ │ │ │ │ ├── view_settings.cpp │ │ │ │ │ ├── view_store.cpp │ │ │ │ │ ├── view_theme_manager.cpp │ │ │ │ │ ├── view_tools.cpp │ │ │ │ │ └── view_tutorials.cpp │ │ │ │ ├── views.cpp │ │ │ │ ├── welcome_screen.cpp │ │ │ │ ├── window_decoration.cpp │ │ │ │ └── workspaces.cpp │ │ │ └── plugin_builtin.cpp │ │ └── tests/ │ │ ├── CMakeLists.txt │ │ └── source/ │ │ └── main.cpp │ ├── decompress/ │ │ ├── CMakeLists.txt │ │ └── source/ │ │ ├── content/ │ │ │ └── pl_functions.cpp │ │ └── plugin_decompress.cpp │ ├── diffing/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── content/ │ │ │ └── views/ │ │ │ └── view_diff.hpp │ │ ├── romfs/ │ │ │ └── lang/ │ │ │ ├── de_DE.json │ │ │ ├── en_US.json │ │ │ ├── es_ES.json │ │ │ ├── fr_FR.json │ │ │ ├── hu_HU.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── languages.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_UA.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ └── source/ │ │ ├── content/ │ │ │ ├── diffing_algorithms.cpp │ │ │ └── views/ │ │ │ └── view_diff.cpp │ │ └── plugin_diffing.cpp │ ├── disassembler/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── content/ │ │ │ ├── helpers/ │ │ │ │ └── capstone.hpp │ │ │ └── views/ │ │ │ └── view_disassembler.hpp │ │ ├── romfs/ │ │ │ └── lang/ │ │ │ ├── de_DE.json │ │ │ ├── en_US.json │ │ │ ├── es_ES.json │ │ │ ├── fr_FR.json │ │ │ ├── hu_HU.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── languages.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_UA.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ └── source/ │ │ ├── content/ │ │ │ ├── disassemblers/ │ │ │ │ ├── capstone_architectures.cpp │ │ │ │ └── custom_architectures.cpp │ │ │ ├── pl_builtin_types.cpp │ │ │ ├── pl_visualizers/ │ │ │ │ └── disassembler.cpp │ │ │ └── views/ │ │ │ └── view_disassembler.cpp │ │ └── plugin_disassembler.cpp │ ├── fonts/ │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── include/ │ │ │ ├── font_settings.hpp │ │ │ └── fonts/ │ │ │ ├── blender_icons.hpp │ │ │ ├── fonts.hpp │ │ │ ├── tabler_icons.hpp │ │ │ └── vscode_icons.hpp │ │ ├── romfs/ │ │ │ ├── BLENDERICONS_LICENSE.txt │ │ │ ├── JETBRAINS_MONO_LICENSE.txt │ │ │ ├── TABLERICONS_LICENSE.txt │ │ │ ├── UNIFONT_LICENSE.txt │ │ │ ├── VSCODICONS_LICENSE.txt │ │ │ ├── fonts/ │ │ │ │ └── unifont.otf │ │ │ └── lang/ │ │ │ ├── de_DE.json │ │ │ ├── en_US.json │ │ │ ├── es_ES.json │ │ │ ├── fr_FR.json │ │ │ ├── hu_HU.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── languages.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_UA.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ └── source/ │ │ ├── font_loader.cpp │ │ ├── font_settings.cpp │ │ ├── fonts.cpp │ │ └── library_fonts.cpp │ ├── hashes/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── content/ │ │ │ └── views/ │ │ │ └── view_hashes.hpp │ │ ├── romfs/ │ │ │ └── lang/ │ │ │ ├── de_DE.json │ │ │ ├── en_US.json │ │ │ ├── es_ES.json │ │ │ ├── fr_FR.json │ │ │ ├── hu_HU.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── languages.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_UA.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ └── source/ │ │ ├── content/ │ │ │ ├── hashes.cpp │ │ │ └── views/ │ │ │ └── view_hashes.cpp │ │ └── plugin_hashes.cpp │ ├── remote/ │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── include/ │ │ │ └── content/ │ │ │ ├── helpers/ │ │ │ │ └── sftp_client.hpp │ │ │ └── providers/ │ │ │ └── ssh_provider.hpp │ │ ├── romfs/ │ │ │ └── lang/ │ │ │ ├── de_DE.json │ │ │ ├── en_US.json │ │ │ ├── es_ES.json │ │ │ ├── fr_FR.json │ │ │ ├── hu_HU.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── languages.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_UA.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ └── source/ │ │ ├── content/ │ │ │ ├── helpers/ │ │ │ │ └── sftp_client.cpp │ │ │ └── providers/ │ │ │ └── ssh_provider.cpp │ │ └── plugin_remote.cpp │ ├── script_loader/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── loaders/ │ │ │ ├── dotnet/ │ │ │ │ └── dotnet_loader.hpp │ │ │ └── loader.hpp │ │ ├── romfs/ │ │ │ └── lang/ │ │ │ ├── de_DE.json │ │ │ ├── en_US.json │ │ │ ├── es_ES.json │ │ │ ├── fr_FR.json │ │ │ ├── hu_HU.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── languages.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_UA.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ ├── source/ │ │ │ ├── loaders/ │ │ │ │ └── dotnet/ │ │ │ │ └── dotnet_loader.cpp │ │ │ └── plugin_script_loader.cpp │ │ ├── support/ │ │ │ ├── c/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── include/ │ │ │ │ │ └── script_api.hpp │ │ │ │ └── source/ │ │ │ │ └── script_api/ │ │ │ │ └── v1/ │ │ │ │ ├── bookmarks.cpp │ │ │ │ ├── logger.cpp │ │ │ │ ├── mem.cpp │ │ │ │ └── ui.cpp │ │ │ └── dotnet/ │ │ │ ├── AssemblyLoader/ │ │ │ │ ├── .gitignore │ │ │ │ ├── AssemblyLoader.csproj │ │ │ │ ├── Program.cs │ │ │ │ └── Properties/ │ │ │ │ └── PublishProfiles/ │ │ │ │ ├── FolderProfile.pubxml │ │ │ │ └── FolderProfile.pubxml.user │ │ │ └── CMakeLists.txt │ │ └── templates/ │ │ └── CSharp/ │ │ ├── .gitignore │ │ ├── ImHexLibrary/ │ │ │ ├── Bookmarks.cs │ │ │ ├── ImHexLibrary.csproj │ │ │ ├── Library.cs │ │ │ ├── Logger.cs │ │ │ ├── Memory.cs │ │ │ └── UI.cs │ │ ├── ImHexScript/ │ │ │ ├── ImHexScript.csproj │ │ │ └── Program.cs │ │ └── ImHexScript.sln │ ├── ui/ │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── include/ │ │ │ ├── banners/ │ │ │ │ ├── banner_button.hpp │ │ │ │ └── banner_icon.hpp │ │ │ ├── popups/ │ │ │ │ ├── popup_file_chooser.hpp │ │ │ │ ├── popup_notification.hpp │ │ │ │ ├── popup_question.hpp │ │ │ │ └── popup_text_input.hpp │ │ │ ├── toasts/ │ │ │ │ └── toast_notification.hpp │ │ │ └── ui/ │ │ │ ├── hex_editor.hpp │ │ │ ├── markdown.hpp │ │ │ ├── pattern_drawer.hpp │ │ │ ├── pattern_value_editor.hpp │ │ │ ├── text_editor.hpp │ │ │ ├── visualizer_drawer.hpp │ │ │ └── widgets.hpp │ │ ├── romfs/ │ │ │ └── lang/ │ │ │ ├── de_DE.json │ │ │ ├── en_US.json │ │ │ ├── es_ES.json │ │ │ ├── fr_FR.json │ │ │ ├── hu_HU.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── languages.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_UA.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ └── source/ │ │ ├── library_ui.cpp │ │ └── ui/ │ │ ├── hex_editor.cpp │ │ ├── markdown.cpp │ │ ├── menu_items.cpp │ │ ├── pattern_drawer.cpp │ │ ├── pattern_value_editor.cpp │ │ ├── text_editor/ │ │ │ ├── LICENSE.txt │ │ │ ├── editor.cpp │ │ │ ├── highlighter.cpp │ │ │ ├── navigate.cpp │ │ │ ├── render.cpp │ │ │ ├── support.cpp │ │ │ └── utf8.cpp │ │ ├── visualizer_drawer.cpp │ │ └── widgets.cpp │ ├── visualizers/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── content/ │ │ │ └── visualizer_helpers.hpp │ │ ├── romfs/ │ │ │ ├── lang/ │ │ │ │ ├── de_DE.json │ │ │ │ ├── en_US.json │ │ │ │ ├── es_ES.json │ │ │ │ ├── fr_FR.json │ │ │ │ ├── hu_HU.json │ │ │ │ ├── it_IT.json │ │ │ │ ├── ja_JP.json │ │ │ │ ├── ko_KR.json │ │ │ │ ├── languages.json │ │ │ │ ├── pl_PL.json │ │ │ │ ├── pt_BR.json │ │ │ │ ├── ru_RU.json │ │ │ │ ├── uk_UA.json │ │ │ │ ├── zh_CN.json │ │ │ │ └── zh_TW.json │ │ │ ├── licenses/ │ │ │ │ └── MAP_LICENSE │ │ │ └── shaders/ │ │ │ └── default/ │ │ │ ├── fragment.glsl │ │ │ ├── lightFragment.glsl │ │ │ ├── lightVertex.glsl │ │ │ ├── lineFragment.glsl │ │ │ ├── lineVertex.glsl │ │ │ └── vertex.glsl │ │ └── source/ │ │ ├── content/ │ │ │ ├── pl_inline_visualizers.cpp │ │ │ ├── pl_visualizers/ │ │ │ │ ├── 3d_model.cpp │ │ │ │ ├── coordinates.cpp │ │ │ │ ├── digital_signal.cpp │ │ │ │ ├── image.cpp │ │ │ │ ├── line_plot.cpp │ │ │ │ ├── scatter_plot.cpp │ │ │ │ ├── sound.cpp │ │ │ │ ├── table.cpp │ │ │ │ └── timestamp.cpp │ │ │ └── pl_visualizers.cpp │ │ └── plugin_visualizers.cpp │ ├── windows/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── views/ │ │ │ └── view_tty_console.hpp │ │ ├── romfs/ │ │ │ └── lang/ │ │ │ ├── de_DE.json │ │ │ ├── en_US.json │ │ │ ├── es_ES.json │ │ │ ├── fr_FR.json │ │ │ ├── hu_HU.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── languages.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_UA.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ └── source/ │ │ ├── content/ │ │ │ ├── settings_entries.cpp │ │ │ └── ui_items.cpp │ │ ├── plugin_windows.cpp │ │ └── views/ │ │ └── view_tty_console.cpp │ └── yara_rules/ │ ├── CMakeLists.txt │ ├── include/ │ │ └── content/ │ │ ├── views/ │ │ │ └── view_yara.hpp │ │ └── yara_rule.hpp │ ├── romfs/ │ │ └── lang/ │ │ ├── de_DE.json │ │ ├── en_US.json │ │ ├── es_ES.json │ │ ├── fr_FR.json │ │ ├── hu_HU.json │ │ ├── it_IT.json │ │ ├── ja_JP.json │ │ ├── ko_KR.json │ │ ├── languages.json │ │ ├── pl_PL.json │ │ ├── pt_BR.json │ │ ├── ru_RU.json │ │ ├── uk_UA.json │ │ ├── zh_CN.json │ │ └── zh_TW.json │ └── source/ │ ├── content/ │ │ ├── data_information_sections.cpp │ │ ├── views/ │ │ │ └── view_yara.cpp │ │ └── yara_rule.cpp │ └── plugin_yara.cpp ├── resources/ │ ├── dist/ │ │ ├── macos/ │ │ │ ├── AppIcon.icns │ │ │ ├── Entitlements.plist │ │ │ └── Info.plist.in │ │ └── windows/ │ │ ├── LICENSE.rtf │ │ ├── WIX.template.in │ │ ├── imhex.manifest │ │ └── wix/ │ │ ├── BrowseDlg.wxs │ │ ├── LicenseAgreementDlg.wxs │ │ └── WixUI_InstallDirImHex.wxs │ ├── projects/ │ │ ├── dmg_background.xcf │ │ ├── icon.xcf │ │ ├── imhex_logo.afdesign │ │ ├── ms_banner.xcf │ │ ├── readme_banners.xcf │ │ ├── splash_wasm.xcf │ │ ├── wix_banner.xcf │ │ └── wix_dialog.xcf │ └── resource.rc └── tests/ ├── CMakeLists.txt ├── algorithms/ │ ├── CMakeLists.txt │ └── source/ │ ├── crypto.cpp │ └── endian.cpp ├── check_langs.py ├── common/ │ ├── CMakeLists.txt │ └── source/ │ └── main.cpp ├── helpers/ │ ├── CMakeLists.txt │ └── source/ │ ├── common.cpp │ ├── file.cpp │ ├── net.cpp │ └── utils.cpp └── plugins/ ├── CMakeLists.txt └── source/ └── plugins.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-tidy ================================================ # All rules should have a comment associated # Directives that do not have any effect (e.g. disabling a rule that is not enabled) can be done to add an explanation comment. # Or at least an empty comment # to show they were put here explicitely, # and not as part of the historical CLion-generated rules # Note: `- -X` means disable X # CLI usage: go to the build directory and run: `run-clang-tidy -allow-no-checks -source-filter ".*/lib/.*" -fix -j` Checks: - -* - mpi-* - bugprone-* - -bugprone-signal-handler - -bugprone-narrowing-conversions - -bugprone-redundant-branch-condition - -bugprone-exception-escape - -bugprone-shared-ptr-array-mismatch - -bugprone-implicit-widening-of-multiplication-result - -bugprone-signed-char-misuse - -bugprone-unhandled-exception-at-new - -bugprone-infinite-loop - -bugprone-easily-swappable-parameters - -bugprone-float-loop-counter # - -bugprone-unchecked-string-to-number-conversion # Unfortunately no alternative - -bugprone-branch-clone # Mostly warns about one-line duplicates - cert-err52-cpp - cert-err60-cpp - cert-str34-c - cert-dcl21-cpp - cert-msc50-cpp - cert-msc51-cpp - cert-dcl58-cpp - cppcoreguidelines-avoid-const-or-ref-data-members - cppcoreguidelines-pro-type-member-init # We want to use default member initializers - cppcoreguidelines-slicing - cppcoreguidelines-interfaces-global-init - -cppcoreguidelines-pro-type-static-cast-downcast # dynamic_cast has a runtime overhead - -cppcoreguidelines-narrowing-conversions # - google-runtime-operator - google-explicit-constructor - -google-default-arguments # Provider and ViewProvider read() is a good example of why this is useful - hicpp-multiway-paths-covered - hicpp-exception-baseclass - misc-* - -misc-definitions-in-headers - -misc-unused-parameters - -misc-unused-alias-decls - -misc-use-anonymous-namespace - -misc-misleading-identifier - -misc-confusable-identifiers - -misc-misleading-bidirectional - -misc-static-assert - -misc-no-recursion - -misc-const-correctness - -misc-use-internal-linkage # False positives if header where function is defined is not included - -misc-include-cleaner # Allow indirect includes - -misc-non-private-member-variables-in-classes # - modernize-* - -modernize-use-trailing-return-type - -modernize-use-std-print # We want to use fmt::print instead - -modernize-use-integer-sign-comparison # Too much occurrences to change - openmp-use-default-none - performance-* - -performance-no-int-to-ptr - portability-* - -portability-restrict-system-includes - readability-* - -readability-redundant-preprocessor - -readability-named-parameter - -readability-function-size - -readability-use-anyofallof - -readability-identifier-length - -readability-magic-numbers - -readability-braces-around-statements - -readability-suspicious-call-argument - -readability-isolate-declaration - -readability-else-after-return - -readability-redundant-access-specifiers - -readability-function-cognitive-complexity - -readability-identifier-naming - -readability-qualified-auto - -readability-use-std-min-max # Less readable imo - -readability-math-missing-parentheses # Basic math - -readability-implicit-bool-conversion # Not much of a problem ? - -readability-convert-member-functions-to-static # - -readability-use-concise-preprocessor-directives # We do not use #ifdef - -readability-uppercase-literal-suffix # Not important enough - -readability-redundant-string-cstr # Sometimes used to stop at first null byte - -readability-static-accessed-through-instance # - -readability-ambiguous-smartptr-reset-call # Fix is hard to read # Will fix later - -modernize-avoid-c-arrays - -readability-make-member-function-const # idk + lots of occurences - -readability-misleading-indentation # We need to handle cases with #if defined() - -bugprone-unchecked-optional-access - -performance-unnecessary-value-param # idk - -readability-avoid-nested-conditional-operator ================================================ FILE: .dockerignore ================================================ cmake-build-*/ build*/ local/ **/Dockerfile ================================================ FILE: .gdbinit ================================================ # Skip all std:: and __gnu_debug:: symbols skip -rfu ^std:: skip -rfu ^__gnu_debug:: # Skip all ImGui:: symbols skip -rfu ^ImGui:: # Trigger breakpoint when execution reaches triggerSafeShutdown() break triggerSafeShutdown break __glibcxx_assert_fail # Print backtrace after execution jumped to an invalid address define fixbt set $pc = *(void **)$rsp set $rsp = $rsp + 8 bt end ================================================ FILE: .gitattributes ================================================ lib/external/** linguist-vendored dist/*.sh eol=lf dist/**/*Dockerfile eol=lf ================================================ FILE: .github/FUNDING.yml ================================================ # Sponsor links github: WerWolv ko_fi: WerWolv custom: "https://werwolv.net/donate" ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yaml ================================================ name: Bug Report description: Something doesn't work correctly in ImHex. title: "[Bug] " labels: bug body: - type: dropdown id: os attributes: label: Operating System description: What Operating System are you using ImHex on? options: - Windows - Linux - MacOS validations: required: true - type: textarea attributes: label: What's the issue you encountered? description: | Describe the issue in detail and what you were doing beforehand. validations: required: true - type: textarea attributes: label: How can the issue be reproduced? description: Include a detailed step by step process for recreating your issue. validations: required: true - type: input attributes: label: ImHex Version description: | The version of ImHex you've been using when encountering the bug. If using a nightly, please add the commit hash as well placeholder: X.X.X validations: required: true - type: checkboxes attributes: label: ImHex Build Type options: - label: Nightly or built from sources - type: input attributes: label: Installation type description: | How did you install ImHex ? MSI/Portable/DMG/AppImage/Fedora package/etc.. validations: required: true - type: textarea attributes: label: Additional context? placeholder: | - Additional information about your environment. - If possible and useful, please upload the binary you've been editing when the bug occurred. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yaml ================================================ name: Feature Request description: Something you'd like to see added to ImHex in the future title: "[Feature] " labels: feature request body: - type: textarea attributes: label: What feature would you like to see? description: | Describe in detail what the feature should do and how it should work. validations: required: true - type: textarea attributes: label: How will this feature be useful to you and others? description: Describe how everybody will benefit from this feature if it gets added. validations: required: true - type: checkboxes attributes: label: Request Type options: - label: I can provide a PoC for this feature or am willing to work on it myself and submit a PR - type: textarea attributes: label: Additional context? ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ### Problem description ### Implementation description ### Screenshots ### Additional things ================================================ FILE: .github/codecov.yml ================================================ comment: false ignore: - "lib/third_party" # Third party libraries should be ignored - "lib/external" # Our own libraries should be checked in their own repositories - "tests" # https://about.codecov.io/blog/should-i-include-test-files-in-code-coverage-calculations/ ================================================ FILE: .github/scripts/delete-artifact.sh ================================================ #!/bin/sh set -xe ARTIFACT_NAME="$1" ARTIFACT_ID=$(gh api repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/artifacts --jq ".artifacts[] | select(.name==\"$ARTIFACT_NAME\") | .id") gh api -X DELETE repos/$GITHUB_REPOSITORY/actions/artifacts/$ARTIFACT_ID echo "Deleted artifact $ARTIFACT_NAME with ID $ARTIFACT_ID" ================================================ FILE: .github/workflows/analysis.yml ================================================ name: "CodeQL" on: schedule: - cron: '0 0 * * *' workflow_dispatch: jobs: codeql: name: 🐛 CodeQL runs-on: ubuntu-24.04 permissions: actions: read contents: read security-events: write steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: ✋ Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: 'cpp' - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 with: key: ${{ runner.os }}-analysis-build-${{ github.run_id }} restore-keys: ${{ runner.os }}-analysis-build max-size: 50M - name: 📜 Restore CMakeCache uses: actions/cache@v4 with: path: | build/CMakeCache.txt key: ${{ runner.os }}-analysis-build-${{ hashFiles('**/CMakeLists.txt') }} - name: ⬇️ Install dependencies run: | sudo apt update sudo bash dist/get_deps_debian.sh - name: 🛠️ Build run: | set -x mkdir -p build cd build CC=gcc-14 CXX=g++-14 cmake \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_INSTALL_PREFIX="$PWD/install" \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_FLAGS="-fuse-ld=lld" \ -DCMAKE_CXX_FLAGS="-fuse-ld=lld" \ -DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_EXCLUDE_PLUGINS="script_loader" \ -G Ninja \ .. ninja install - name: 🗯️ Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 ================================================ FILE: .github/workflows/build.yml ================================================ name: Build concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true on: push: branches: - 'master' - 'releases/**' - 'tests/**' - 'feature/**' pull_request: workflow_dispatch: env: BUILD_TYPE: RelWithDebInfo jobs: # Windows MINGW build win_mingw: strategy: fail-fast: false matrix: include: - architecture_name: "x86_64" msystem: "mingw64" runner_os: "windows-2025" - architecture_name: "arm64" msystem: "clangarm64" runner_os: "windows-11-arm" runs-on: ${{ matrix.runner_os }} name: 🪟 Windows MSYS2 ${{ matrix.architecture_name }} defaults: run: shell: msys2 {0} env: CCACHE_DIR: "${{ github.workspace }}/.ccache" permissions: id-token: write attestations: write steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 id: cache-ccache with: key: ${{ runner.os }}-mingw-ccache-${{ github.run_id }} restore-keys: ${{ runner.os }}-mingw-ccache max-size: 1G - name: 🟦 Install msys2 uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.msystem }} - name: ⬇️ Install dependencies run: | set -x dist/get_deps_msys2.sh - name: ⬇️ Install .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.100' - name: 📜 Set version variable run: | echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV # Windows cmake build - name: 🛠️ Configure CMake run: | set -x mkdir -p build cd build cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ -DCMAKE_INSTALL_PREFIX="$PWD/install" \ -DIMHEX_GENERATE_PACKAGE=ON \ -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \ -DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ -DUSE_SYSTEM_CAPSTONE=ON \ -DUSE_SYSTEM_MD4C=ON \ -DIMHEX_GENERATE_PDBS=ON \ -DIMHEX_REPLACE_DWARF_WITH_PDB=ON \ -DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" \ -DCPACK_WIX_ROOT="$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools" \ .. - name: 🛠️ Build run: | cd build ninja install - name: 🕯️ Install WiX Toolkit run: | "C:/Program Files/dotnet/dotnet.exe" tool install --global wix@6.0.2 "$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools/wix" extension add --global WixToolset.UI.wixext/6.0.2 - name: 🪲 Create PDBs for MSI run: | cd build mkdir cv2pdb cd cv2pdb wget https://github.com/rainers/cv2pdb/releases/download/v0.52/cv2pdb-0.52.zip unzip cv2pdb-0.52.zip cd .. cv2pdb/cv2pdb.exe imhex.exe cv2pdb/cv2pdb.exe imhex-gui.exe cv2pdb/cv2pdb.exe libimhex.dll for plugin in plugins/*.hexplug; do cv2pdb/cv2pdb.exe $plugin done rm -rf cv2pdb - name: 📦 Bundle MSI run: | cd build cpack mv ImHex-*.msi ../imhex-${{ env.IMHEX_VERSION }}-Windows-${{ matrix.architecture_name }}.msi echo "ImHex checks for the existence of this file to determine if it is running in portable mode. You should not delete this file" > $PWD/install/PORTABLE - name: 🪲 Create PDBs for ZIP run: | cd build/install mkdir cv2pdb cd cv2pdb wget https://github.com/rainers/cv2pdb/releases/download/v0.52/cv2pdb-0.52.zip unzip cv2pdb-0.52.zip cd .. cv2pdb/cv2pdb.exe imhex.exe cv2pdb/cv2pdb.exe imhex-gui.exe cv2pdb/cv2pdb.exe libimhex.dll for plugin in plugins/*.hexplug; do cv2pdb/cv2pdb.exe $plugin done rm -rf cv2pdb - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | imhex-*.msi - name: ⬆️ Upload Windows Installer uses: actions/upload-artifact@v4 id: upload-installer with: if-no-files-found: error name: Windows Installer ${{ matrix.architecture_name }} path: | imhex-*.msi - name: ⬆️ Upload Portable ZIP uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Windows Portable ${{ matrix.architecture_name }} path: | build/install/* - name: ⬇️ Download Mesa3D for NoGPU version if: ${{ matrix.architecture_name == 'x86_64' }} shell: bash run: | set -x echo "NoGPU version Powered by Mesa 3D : https://fdossena.com/?p=mesa%2Findex.frag" > build/install/MESA.md curl --connect-timeout 30 --retry 5 --retry-delay 0 --retry-max-time 30 https://downloads.fdossena.com/geth.php?r=mesa64-latest -L -o mesa.7z 7z e mesa.7z mv opengl32.dll build/install - name: ⬆️ Upload NoGPU Portable ZIP if: ${{ matrix.architecture_name == 'x86_64' }} uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Windows Portable NoGPU ${{ matrix.architecture_name }} path: | build/install/* win_msvc: strategy: fail-fast: false matrix: include: - architecture_name: "x86_64" vs_arch: "amd64" runner_os: "windows-2025" - architecture_name: "arm64" vs_arch: "amd64_arm64" runner_os: "windows-11-arm" runs-on: ${{ matrix.runner_os }} name: 🪟 Windows MSVC ${{ matrix.architecture_name }} env: CCACHE_DIR: "${{ github.workspace }}/.ccache" permissions: id-token: write attestations: write steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 🫧 Setup Visual Studio Dev Environment uses: ilammy/msvc-dev-cmd@v1 with: arch: ${{ matrix.vs_arch }} - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 id: cache-ccache with: key: ${{ runner.os }}-msvc-${{ matrix.vs_arch }}-ccache-${{ github.run_id }} restore-keys: ${{ runner.os }}-msvc-${{ matrix.vs_arch }}-ccache max-size: 1G - name: 📦 Install vcpkg uses: friendlyanon/setup-vcpkg@v1 with: { committish: 66c0373dc7fca549e5803087b9487edfe3aca0a1 } - name: ⬇️ Install dependencies run: | cp dist/vcpkg.json vcpkg.json - name: ⬇️ Install CMake and Ninja uses: lukka/get-cmake@latest - name: ⬇️ Install .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.100' - name: 📜 Set version variable run: | "IMHEX_VERSION=$(Get-Content VERSION -Raw)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append # Windows cmake build - name: 🛠️ Configure CMake run: | mkdir -p build cmake -G "Ninja" -B build ` --preset vs2022 ` -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" ` -DCMAKE_C_COMPILER="$($(Get-Command cl.exe).Path)" ` -DCMAKE_CXX_COMPILER="$($(Get-Command cl.exe).Path)" ` -DCMAKE_C_COMPILER_LAUNCHER=ccache ` -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ` -DCMAKE_INSTALL_PREFIX="$(Join-Path $PWD 'install')" ` -DIMHEX_GENERATE_PACKAGE=ON ` -DCMAKE_BUILD_TYPE="$env:BUILD_TYPE" ` -DIMHEX_PATTERNS_PULL_MASTER=ON ` -DIMHEX_COMMIT_HASH_LONG="$env:GITHUB_SHA" ` -DIMHEX_COMMIT_BRANCH="$($env:GITHUB_REF -replace '.*/', '')" ` -DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" ` -DCPACK_WIX_ROOT="$($env:USERPROFILE -replace '\\','/')/.dotnet/tools" ` . - name: 🛠️ Build run: | cd build ninja install - name: 🕯️ Install WiX Toolkit run: | & "C:/Program Files/dotnet/dotnet.exe" tool install --global wix@6.0.2 & "$($env:USERPROFILE -replace '\\','/')/.dotnet/tools/wix" extension add --global WixToolset.UI.wixext/6.0.2 - name: 📦 Bundle MSI run: | cd build cpack mv ImHex-*.msi ../imhex-${{ env.IMHEX_VERSION }}-Windows-MSVC-${{ matrix.architecture_name }}.msi - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | imhex-*.msi - name: ⬆️ Upload Windows Installer uses: actions/upload-artifact@v4 if: false # The MSVC builds should not really be used, they're still packaged for testing’s sake though with: if-no-files-found: error name: Windows Installer MSVC ${{ matrix.architecture_name }} path: | imhex-*.msi win-plugin-template-test: runs-on: windows-2022 name: 🧪 Plugin Template Test defaults: run: shell: msys2 {0} needs: win_mingw env: IMHEX_SDK_PATH: "${{ github.workspace }}/out/sdk" steps: - name: 🧰 Checkout ImHex uses: actions/checkout@v4 with: path: imhex - name: 🟦 Install msys2 uses: msys2/setup-msys2@v2 with: msystem: mingw64 - name: ⬇️ Install dependencies run: | set -x imhex/dist/get_deps_msys2.sh - name: 🧰 Checkout ImHex-Plugin-Template uses: actions/checkout@v4 with: repository: WerWolv/ImHex-Plugin-Template submodules: recursive path: template - name: ⬇️ Download artifact uses: actions/download-artifact@v4 with: name: Windows Portable x86_64 path: out - name: 🛠️ Build run: | set -x cd template mkdir -p build cd build cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \ -DUSE_SYSTEM_CAPSTONE=ON \ -DUSE_SYSTEM_MD4C=ON \ .. ninja - name: 🧪 Test if plugin can be loaded run: | export WORKSPACE=$(echo "${{ github.workspace }}" | tr '\\' '/') ${WORKSPACE}/out/imhex.exe --validate-plugin ${WORKSPACE}/template/build/example_plugin.hexplug # MacOS build macos-x86: runs-on: macos-15-intel permissions: id-token: write attestations: write name: 🍎 macOS 10.15 x86_64 steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📜 Set version variable run: | echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 with: key: ${{ runner.os }}-ccache-${{ github.run_id }} restore-keys: ${{ runner.os }}-ccache max-size: 1G - name: Set Xcode version run: | sudo xcode-select --install || true sudo xcode-select -s /Library/Developer/CommandLineTools - name: 📦 Install MacPorts run: | wget https://github.com/macports/macports-base/releases/download/v2.11.6/MacPorts-2.11.6-15-Sequoia.pkg sudo installer -pkg MacPorts-2.11.6-15-Sequoia.pkg -target / export PATH=/opt/local/bin:/opt/local/sbin:$PATH echo "PATH=/opt/local/bin:/opt/local/sbin:$PATH" >> $GITHUB_ENV echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> $GITHUB_ENV echo "universal_target 10.15" | sudo tee -a /opt/local/etc/macports/macports.conf echo "macos_deployment_target 10.15" | sudo tee -a /opt/local/etc/macports/macports.conf echo "macosx_sdk_version 10.15" | sudo tee -a /opt/local/etc/macports/macports.conf sudo port selfupdate - name: ⬇️ Install dependencies env: # Make brew not display useless errors HOMEBREW_TESTS: 1 run: | brew install llvm@21 automake sudo -E port install mbedtls3 nlohmann-json ccache freetype libmagic pkgconfig curl glfw ninja zlib xz bzip2 zstd libssh2 md4c - name: ⬇️ Install .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.100' # MacOS cmake build - name: 🛠️ Configure CMake run: | set -x mkdir -p build cd build CC=$(brew --prefix llvm@21)/bin/clang \ CXX=$(brew --prefix llvm@21)/bin/clang++ \ OBJC=$(brew --prefix llvm@21)/bin/clang \ OBJCXX=$(brew --prefix llvm@21)/bin/clang++ \ cmake -G "Ninja" \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \ -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ -DIMHEX_GENERATE_PACKAGE=ON \ -DIMHEX_SYSTEM_LIBRARY_PATH="$(brew --prefix llvm@21)/lib;$(brew --prefix llvm@21)/lib/unwind;$(brew --prefix llvm@21)/lib/c++;$(brew --prefix)/lib" \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \ -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_INSTALL_PREFIX="./install" \ -DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ .. - name: 🛠️ Build run: cd build && ninja install - name: ✒️ Fix Signature run: | set -x cd build/install codesign --remove-signature ImHex.app codesign --force --deep --sign - ImHex.app - name: 📁 Fix permissions run: | set -x cd build/install chmod -R 755 ImHex.app/ - name: 🔫 Kill XProtect run: | # See https://github.com/actions/runner-images/issues/7522 echo Killing XProtect...; sudo pkill -9 XProtect >/dev/null || true; echo Waiting for XProtect process...; while pgrep XProtect; do sleep 3; done; - name: 📦 Create DMG run: | brew install graphicsmagick imagemagick git clone https://github.com/sindresorhus/create-dmg cd create-dmg npm i && npm -g i cd ../build/install for i in $(seq 1 10); do create-dmg ImHex.app || true if ls -d *.dmg 1>/dev/null 2>/dev/null; then break; fi done mv *.dmg ../../imhex-${{ env.IMHEX_VERSION }}-macOS-x86_64.dmg - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | ./*.dmg - name: ⬆️ Upload DMG uses: actions/upload-artifact@v4 with: if-no-files-found: error name: macOS DMG x86_64 path: ./*.dmg macos-arm64: runs-on: ubuntu-24.04 name: 🍎 macOS 11 arm64 outputs: IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }} steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📁 Restore docker /cache uses: actions/cache@v4 with: path: cache key: macos-arm64-cache-${{ github.run_id }} restore-keys: macos-arm64-cache - name: 🐳 Inject /cache into docker uses: reproducible-containers/buildkit-cache-dance@v2 with: cache-source: cache cache-target: /cache - name: 🛠️ Build using docker id: build run: | echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_OUTPUT docker buildx build . -f dist/macOS/arm64.Dockerfile --progress=plain --build-arg 'JOBS=4' --build-arg "BUILD_TYPE=$(BUILD_TYPE)" --build-context imhex=$(pwd) --output out cp resources/dist/macos/Entitlements.plist out/Entitlements.plist - name: ⬆️ Upload artifacts uses: actions/upload-artifact@v4 with: name: macos_arm64_intermediate path: out/ # See https://github.com/actions/cache/issues/342#issuecomment-1711054115 - name: 🗑️ Delete old cache continue-on-error: true env: GH_TOKEN: ${{ github.token }} run: | gh extension install actions/gh-actions-cache gh cache delete "macos-arm64-cache" --confirm || true macos-arm64-package: runs-on: macos-15-intel name: 🍎 macOS 11 arm64 Packaging needs: macos-arm64 env: IMHEX_VERSION: ${{ needs.macos-arm64.outputs.IMHEX_VERSION }} permissions: id-token: write attestations: write steps: - name: ⬇️ Download artifact uses: actions/download-artifact@v4 with: name: macos_arm64_intermediate path: out - name: 🗑️ Delete artifact uses: geekyeggo/delete-artifact@v5 with: name: macos_arm64_intermediate - name: ✒️ Fix Signature run: | set -x cd out codesign --remove-signature ImHex.app codesign --force --deep --entitlements Entitlements.plist --sign - ImHex.app - name: 📁 Fix permissions run: | set -x cd out chmod -R 755 ImHex.app/ - name: 🔫 Kill XProtect run: | # See https://github.com/actions/runner-images/issues/7522 echo Killing XProtect...; sudo pkill -9 XProtect >/dev/null || true; echo Waiting for XProtect process...; while pgrep XProtect; do sleep 3; done; - name: 📦 Create DMG run: | brew install graphicsmagick imagemagick git clone https://github.com/sindresorhus/create-dmg cd create-dmg npm i && npm -g i cd ../out for i in $(seq 1 10); do create-dmg ImHex.app || true if ls -d *.dmg 1>/dev/null 2>/dev/null; then break; fi done mv *.dmg ../imhex-${{ env.IMHEX_VERSION }}-macOS${{ matrix.file_suffix }}-arm64.dmg - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | ./*.dmg - name: ⬆️ Upload DMG uses: actions/upload-artifact@v4 with: if-no-files-found: error name: macOS DMG arm64 path: ./*.dmg # Ubuntu build ubuntu: strategy: fail-fast: false matrix: include: - name: "Ubuntu" release_num: "24.04" image: "ubuntu:24.04" - name: "Ubuntu" release_num: "25.10" image: "ubuntu:25.10" - name: "Debian" release_num: "13" image: "debian:13" name: 🐧 ${{ matrix.name }} ${{ matrix.release_num }} x86_64 runs-on: ubuntu-24.04 container: image: "${{ matrix.image }}" options: --privileged permissions: id-token: write attestations: write steps: - name: ⬇️ Install setup dependencies run: apt update && apt install -y git curl - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 with: key: ${{ matrix.image }}-ccache-${{ github.run_id }} restore-keys: ${{ matrix.image }}-ccache max-size: 1G - name: ⬇️ Install dependencies run: | apt update bash dist/get_deps_debian.sh - name: ⬇️ Install .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.100' # Ubuntu cmake build - name: 🛠️ Configure CMake shell: bash run: | set -x git config --global --add safe.directory '*' mkdir -p build cd build CC=gcc-14 CXX=g++-14 cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ -DCMAKE_INSTALL_PREFIX="/usr" \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ -DIMHEX_ENABLE_LTO=ON \ -DIMHEX_USE_GTK_FILE_PICKER=ON \ -DDOTNET_EXECUTABLE="dotnet" \ .. - name: 🛠️ Build run: cd build && DESTDIR=DebDir ninja install - name: 📜 Set version variable run: | echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV - name: 📦 Bundle DEB run: | cp -r build/DEBIAN build/DebDir dpkg-deb -Zzstd --build build/DebDir mv build/DebDir.deb imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.deb - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | ./*.deb - name: ⬆️ Upload DEB uses: actions/upload-artifact@v4 with: if-no-files-found: error name: ${{ matrix.name }} ${{ matrix.release_num }} DEB x86_64 path: '*.deb' # AppImage build appimage: strategy: fail-fast: false matrix: include: - architecture: "x86_64" architecture_package: "amd64" architecture_appimage_builder: "x86_64" image: ubuntu-24.04 - architecture: "arm64" architecture_package: "arm64" architecture_appimage_builder: "aarch64" image: ubuntu-24.04-arm runs-on: ${{ matrix.image }} name: ⬇️ AppImage ${{ matrix.architecture }} permissions: id-token: write attestations: write steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📁 Restore docker /cache uses: actions/cache@v4 with: path: cache key: appimage-ccache-${{ matrix.architecture }}-${{ github.run_id }} restore-keys: appimage-ccache-${{ matrix.architecture }} - name: 🐳 Inject /cache into docker uses: reproducible-containers/buildkit-cache-dance@v2 with: cache-source: cache cache-target: /cache - name: 🛠️ Build using docker run: | docker buildx build . -f dist/AppImage/Dockerfile --progress=plain --build-arg "BUILD_TYPE=$BUILD_TYPE" \ --build-arg "GIT_COMMIT_HASH=$GITHUB_SHA" --build-arg "GIT_BRANCH=${GITHUB_REF##*/}" \ --build-arg "ARCHITECTURE_PACKAGE=${{ matrix.architecture_package }}" --build-arg "ARCHITECTURE_FILE_NAME=${{ matrix.architecture }}" --build-arg "ARCHITECTURE_APPIMAGE_BUILDER=${{ matrix.architecture_appimage_builder }}" \ --output out - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | out/*.AppImage out/*.AppImage.zsync - name: ⬆️ Upload AppImage uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Linux AppImage ${{ matrix.architecture }} path: 'out/*.AppImage' - name: ⬆️ Upload AppImage zsync uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Linux AppImage zsync ${{ matrix.architecture }} path: 'out/*.AppImage.zsync' # ArchLinux build archlinux-build: name: 🐧 ArchLinux x86_64 runs-on: ubuntu-24.04 container: image: archlinux:base-devel permissions: id-token: write attestations: write steps: - name: ⬇️ Update all packages run: | pacman -Syyu --noconfirm - name: ⬇️ Install setup dependencies run: | pacman -Syu git ccache --noconfirm - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: ⬇️ Install ImHex dependencies run: | dist/get_deps_archlinux.sh --noconfirm - name: ⬇️ Install .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.100' - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 with: key: archlinux-ccache-${{ github.run_id }} restore-keys: archlinux-ccache max-size: 1G # ArchLinux cmake build - name: 🛠️ Configure CMake run: | set -x mkdir -p build cd build CC=gcc CXX=g++ cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ -DCMAKE_INSTALL_PREFIX="/usr" \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DUSE_SYSTEM_FMT=ON \ -DUSE_SYSTEM_YARA=ON \ -DUSE_SYSTEM_NLOHMANN_JSON=ON \ -DUSE_SYSTEM_CAPSTONE=OFF \ -DUSE_SYSTEM_MD4C=ON \ -DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ -DIMHEX_ENABLE_LTO=ON \ -DIMHEX_USE_GTK_FILE_PICKER=ON \ .. - name: 🛠️ Build run: cd build && DESTDIR=installDir ninja install - name: 📜 Set version variable run: | echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV - name: ✒️ Prepare PKGBUILD run: | cp dist/Arch/PKGBUILD build sed -i 's/%version%/${{ env.IMHEX_VERSION }}/g' build/PKGBUILD # makepkg doesn't want to run as root, so I had to chmod 777 all over - name: 📦 Package ArchLinux .pkg.tar.zst run: | set -x cd build # the name is a small trick to make makepkg recognize it as the source # else, it would try to download the file from the release tar -cvf imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst -C installDir . chmod -R 777 . sudo -u nobody makepkg # Replace the old file rm imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst rm *imhex-bin-debug* # rm debug package which is created for some reason mv *.pkg.tar.zst imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | build/imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst - name: ⬆️ Upload imhex-archlinux.pkg.tar.zst uses: actions/upload-artifact@v4 with: if-no-files-found: error name: ArchLinux .pkg.tar.zst x86_64 path: | build/imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst # RPM distro builds rpm-build: strategy: fail-fast: false matrix: include: - name: Fedora release_num: 43 mock_config: fedora-43 name: 🐧 ${{ matrix.name }} ${{ matrix.release_num }} x86_64 runs-on: ubuntu-24.04 container: image: "almalinux:10" options: --privileged --pid=host --security-opt apparmor=unconfined permissions: id-token: write attestations: write steps: # This, together with the `--pid=host --security-opt apparmor=unconfined` docker options is required to allow # mock to work inside a Docker container running on Ubuntu again. # GitHub seems to have enabled AppArmor on their Ubuntu CI runners which limits Docker in ways that cause # programs inside it to fail. # Without this, mock will throw the unhelpful error message 'Insufficient Rights' # This step uses nsenter to execute commands on the host that disable AppArmor entirely. - name: 🛡️ Disable AppArmor on Host run: | nsenter -t 1 -m -u -n -i sudo systemctl disable --now apparmor.service nsenter -t 1 -m -u -n -i sudo aa-teardown || true nsenter -t 1 -m -u -n -i sudo sysctl --write kernel.apparmor_restrict_unprivileged_unconfined=0 nsenter -t 1 -m -u -n -i sudo sysctl --write kernel.apparmor_restrict_unprivileged_userns=0 - name: ⬇️ Install git-core and EPEL repo run: dnf install git-core epel-release -y - name: 🧰 Checkout uses: actions/checkout@v4 with: path: ImHex submodules: recursive - name: 📜 Setup DNF Cache if: false # Disabled for now since it fills up the cache very quickly uses: actions/cache@v4 with: path: /var/cache/dnf key: dnf-ccache-${{ matrix.mock_config }}-${{ github.run_id }} restore-keys: dnf-ccache-${{ matrix.mock_config }} - name: ⬇️ Update all packages and install dependencies run: | set -x dnf upgrade -y dnf install -y \ mock \ ccache - name: ⬇️ Install .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.100' - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 with: key: ${{ matrix.mock_config }}-rpm-${{ github.run_id }} restore-keys: ${{ matrix.mock_config }}-rpm max-size: 1G - name: 📜 Set version variable run: | echo "IMHEX_VERSION=`cat ImHex/VERSION`" >> $GITHUB_ENV - name: 🗜️ Create tarball from sources with dependencies run: tar --exclude-vcs -czf $GITHUB_WORKSPACE/imhex-$IMHEX_VERSION.tar.gz ImHex - name: ✒️ Modify spec file run: | sed -i \ -e 's/Version: VERSION$/Version: ${{ env.IMHEX_VERSION }}/g' \ -e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \ -e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \ -e '/BuildRequires: cmake/a BuildRequires: git-core' \ -e '/%files/a %{_datadir}/imhex/' \ $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec - name: 📜 Fix ccache on EL9 if: matrix.mock_config == 'alma+epel-9' run: sed -i '/\. \/opt\/rh\/gcc-toolset-14\/enable/a PATH=/usr/lib64/ccache:$PATH' $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec - name: 🟩 Copy spec file to build root run: mv $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec $GITHUB_WORKSPACE/imhex.spec # Fedora cmake build (in imhex.spec) - name: 📦 Build RPM run: | mock -r ${{ matrix.mock_config }}-x86_64 \ --define 'debug_package %{nil}' \ --enable-network -N -v \ --enable-plugin=ccache \ --plugin-option=ccache:compress=True \ --plugin-option=ccache:max_cache_size=200M \ --plugin-option=ccache:dir=$GITHUB_WORKSPACE/.ccache \ --spec $GITHUB_WORKSPACE/imhex.spec \ --sources $GITHUB_WORKSPACE \ --resultdir $GITHUB_WORKSPACE/results - name: 🟩 Move and rename finished RPM run: | mv $GITHUB_WORKSPACE/results/imhex-${{ env.IMHEX_VERSION }}-0.*.x86_64.rpm \ $GITHUB_WORKSPACE/imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.rpm - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.rpm - name: ⬆️ Upload RPM uses: actions/upload-artifact@v4 with: if-no-files-found: error name: ${{ matrix.name }} ${{ matrix.release_num }} RPM x86_64 path: | imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.rpm snap-build: strategy: fail-fast: false matrix: include: - architecture: "x86_64" image: ubuntu-24.04 - architecture: "arm64" image: ubuntu-24.04-arm name: 🐧 Snap ${{ matrix.architecture }} runs-on: ${{ matrix.image }} permissions: id-token: write attestations: write steps: - name: ⬇️ Install setup dependencies run: | sudo apt update && sudo apt install -y git curl snapd ccache for i in $(seq 1 5); do if sudo snap install snapcraft --classic; then break; fi echo "Retrying snap install..." sleep 10 done - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📜 Set version variable run: | export IMHEX_VERSION=$(cat VERSION) echo "IMHEX_VERSION=$IMHEX_VERSION" >> $GITHUB_ENV if [[ "$IMHEX_VERSION" == *.WIP ]]; then echo "IMHEX_VERSION_STRING=$IMHEX_VERSION-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV else echo "IMHEX_VERSION_STRING=$IMHEX_VERSION" >> $GITHUB_ENV fi echo "CCACHE=ccache" >> $GITHUB_ENV - name: 📜 Move snap directory to root run: | mkdir -p ./snap envsubst '${IMHEX_VERSION_STRING},${CCACHE}' < ./dist/snap/snapcraft.yaml > ./snap/snapcraft.yaml - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 with: key: ${{ matrix.architecture }}-snap-${{ github.run_id }} restore-keys: ${{ matrix.architecture }}-snap max-size: 1G - name: 🛠️ Build run: | sudo snapcraft --destructive-mode - name: 🟩 Rename Snap run: | mv *.snap imhex-${{ env.IMHEX_VERSION }}-${{ matrix.architecture }}.snap - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | *.snap - name: ⬆️ Upload Snap uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Snap ${{ matrix.architecture }} path: | *.snap flatpak-build: strategy: fail-fast: false matrix: include: - architecture: "x86_64" flatpak_arch: "x86_64" image: ubuntu-24.04 - architecture: "arm64" flatpak_arch: "aarch64" image: ubuntu-24.04-arm name: 🐧 Flatpak ${{ matrix.architecture }} runs-on: ${{ matrix.image }} permissions: id-token: write attestations: write steps: - name: ⬇️ Install setup dependencies run: | sudo apt update && sudo apt install -y git curl flatpak-builder sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo sudo flatpak remote-modify --enable flathub sudo flatpak install --noninteractive --system flathub org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📜 Set version variable run: | echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV - name: 🛠️ Build uses: flatpak/flatpak-github-actions/flatpak-builder@v6 with: bundle: imhex-${{ env.IMHEX_VERSION }}-${{ matrix.architecture }}.flatpak manifest-path: dist/flatpak/net.werwolv.ImHex.yaml cache-key: flatpak-builder-${{ matrix.architecture }} arch: ${{ matrix.flatpak_arch }} upload-artifact: false - name: 🗝️ Generate build provenance attestations uses: actions/attest-build-provenance@v2 if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }} with: subject-path: | imhex-${{ env.IMHEX_VERSION }}-${{ matrix.architecture }}.flatpak - name: ⬆️ Upload Flatpak uses: actions/upload-artifact@v4 with: if-no-files-found: error name: Flatpak ${{ matrix.architecture }} path: | imhex-${{ env.IMHEX_VERSION }}-${{ matrix.architecture }}.flatpak webassembly-build: runs-on: ubuntu-24.04 name: 🌍 Web steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📁 Restore docker /cache uses: actions/cache@v4 with: path: cache key: webassembly-ccache-${{ github.run_id }} restore-keys: webassembly-ccache - name: 🐳 Inject /cache into docker uses: reproducible-containers/buildkit-cache-dance@v2 with: cache-source: cache cache-target: /cache - name: 🔨 Copy necessary files run: | mkdir -p out/nightly cp dist/web/serve.py out/nightly/start_imhex_web.py - name: 🛠️ Build using docker run: | docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out/nightly --target raw - name: ⬇️ Download Release artifact if: ${{ github.event.repository.fork == false }} env: GH_TOKEN: ${{ github.token }} run: gh --repo $GITHUB_REPOSITORY release download --pattern "imhex-*-Web.zip" - name: 🔨 Fix permissions if: ${{ github.event.repository.fork == false }} run: | unzip imhex-*-Web.zip -d out chmod -c -R +rX "out/" - name: ⬆️ Upload artifacts uses: actions/upload-pages-artifact@v3 with: path: out/ - name: ⬆️ Upload package uses: actions/upload-artifact@v4 with: if-no-files-found: error name: ImHex Web path: out/nightly/* # See https://github.com/actions/cache/issues/342#issuecomment-1711054115 - name: 🗑️ Delete old cache continue-on-error: true env: GH_TOKEN: ${{ github.token }} run: | gh extension install actions/gh-actions-cache || true gh actions-cache delete "build-web-cache" --confirm || true webassembly-deploy: environment: name: ImHex Web url: ${{ steps.deployment.outputs.page_url }} permissions: pages: write id-token: write actions: write name: 📃 Deploy to GitHub Pages runs-on: ubuntu-24.04 if: ${{ github.ref == 'refs/heads/master' && github.event.repository.fork == false }} needs: webassembly-build steps: - name: 🧰 Checkout uses: actions/checkout@v4 - name: 🌍 Deploy WebAssembly Build to GitHub Pages id: deployment uses: actions/deploy-pages@v4 - name: 🗑️ Delete artifact env: GH_TOKEN: ${{ github.token }} run: | .github/scripts/delete-artifact.sh "github-pages" webassembly-docker-image-deploy: runs-on: ubuntu-latest if: github.ref == 'refs/heads/master' needs: webassembly-build name: 🐋 Deploy to ghcr.io permissions: contents: read packages: write steps: - name: 🧰 Checkout uses: actions/checkout@v4 - name: ⬇️ Download artifact uses: actions/download-artifact@v4 with: name: ImHex Web path: out - name: 📜 Login to ghcr.io uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: ⛓️ Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ghcr.io/${{ github.repository }}/imhex-web tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha - name: 🔨 Build and push Docker image uses: docker/build-push-action@v6 env: DOCKER_BUILD_RECORD_UPLOAD: false with: context: . file: dist/web/Host.Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} ================================================ FILE: .github/workflows/dl-cache.yml ================================================ # https://gist.github.com/iTrooz/d5bacca32c0974edc6c1ac3ad3ee82f3 # See https://github.com/cli/cli/issues/9125 # Extract archive with `tar -xf cache.tzst --transform 's@\.\./@#@g' -P` to avoid ../ errors name: Download cache key on: workflow_dispatch: inputs: cache_key: description: 'Cache key' required: true type: string jobs: cache-download: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Query cache version id: version env: GH_TOKEN: ${{ github.token }} run: | VERSION=$(gh api repos/$GITHUB_REPOSITORY/actions/caches \ --jq " .actions_caches[] | select(.ref == \"refs/heads/$GITHUB_REF_NAME\") | select(.key == \"${{ github.event.inputs.cache_key }}\") | .version ") echo "version=$VERSION" | tee $GITHUB_OUTPUT - name: Restore cache uses: iTrooz/cache/restore@restore_with_version with: # Path won't be actually used, we will match by 'version'. path: . key: ${{ github.event.inputs.cache_key }} version: ${{ steps.version.outputs.version }} - name: Upload cached folder as artifact uses: actions/upload-artifact@v4 with: name: cache-artifact path: | /home/runner/work/**/*.tzst ================================================ FILE: .github/workflows/nightly_release.yml ================================================ permissions: contents: write name: Nightly Release on: schedule: - cron: '0 0 * * *' workflow_dispatch: jobs: nightly-release: runs-on: ubuntu-24.04 name: 🌃 Update Nightly Release steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: path: ImHex fetch-depth: 0 fetch-tags: true - name: 🌃 Check for new commits id: check_commits run: | cd ImHex git config --global --add safe.directory $(pwd) if [ -z "$(git log nightly..HEAD --oneline)" ]; then echo "No new commits since last nightly. Exiting." echo "::set-output name=should_run::false" else echo "::set-output name=should_run::true" fi - name: 📜 Set version variable if: ${{ steps.check_commits.outputs.should_run == 'true' }} run: | project_version=`cat ImHex/VERSION` echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV # TODO: Replace by Github CLI when github.com/cli/cli/pull/12435 is closed - name: ⬇️ Download artifacts from latest workflow uses: dawidd6/action-download-artifact@v6 with: github_token: ${{ secrets.GITHUB_TOKEN }} workflow: build.yml branch: master workflow_conclusion: success skip_unpack: true - name: 🗜️ Unzip files when needed if: ${{ steps.check_commits.outputs.should_run == 'true' }} run: | set -x for zipfile in ./*.zip do if [ `zipinfo -1 "$zipfile" | wc -l` -eq 1 ]; then echo "unzipping $zipfile" unzip "$zipfile" rm "$zipfile" else echo "keeping $zipfile zipped" fi done - name: 🟩 Rename artifacts when needed if: ${{ steps.check_commits.outputs.should_run == 'true' }} run: | mv "Windows Portable x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip mv "Windows Portable arm64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip mv "ImHex Web.zip" imhex-${{ env.IMHEX_VERSION }}-Web.zip rm artifact.tar || true - name: 📖 Generate Release Notes if: ${{ steps.check_commits.outputs.should_run == 'true' }} id: release_notes continue-on-error: true run: | cd ImHex echo "## Nightly ${GITHUB_SHA::7} Changelog" > changelog.md git fetch --tags --recurse-submodules=no git log nightly..origin/master --oneline --no-merges --pretty=format:'* %s' >> changelog.md - name: 📦 Update Pre-Release if: ${{ steps.check_commits.outputs.should_run == 'true' }} run: | set -e cd ImHex # Move nightly tag to latest commit git tag -f nightly origin/master git push origin nightly --force # Auth for GitHub CLI echo "${{ github.token }}" | gh auth login --with-token # Delete existing assets for asset in $(gh release view nightly --json assets --jq '.assets[].name'); do gh release delete-asset nightly "$asset" --yes done # Update release notes gh release edit nightly --notes-file changelog.md # Upload new assets gh release upload nightly ../*.* --clobber - name: ⬆️ Publish x86_64 Snap package if: ${{ steps.check_commits.outputs.should_run == 'true' }} continue-on-error: true uses: snapcore/action-publish@v1 env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }} with: snap: imhex-${{ env.IMHEX_VERSION }}-x86_64.snap release: edge - name: ⬆️ Publish arm64 Snap package if: ${{ steps.check_commits.outputs.should_run == 'true' }} continue-on-error: true uses: snapcore/action-publish@v1 env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }} with: snap: imhex-${{ env.IMHEX_VERSION }}-arm64.snap release: edge website_update: name: 🌍 Update ImHex Landing Website needs: nightly-release runs-on: ubuntu-24.04 env: WEBSITE_DISPATCH_TOKEN: ${{ secrets.WEBSITE_DISPATCH_TOKEN }} steps: - name: ✉️ Dispatch Landing page update if: ${{ env.WEBSITE_DISPATCH_TOKEN != '' }} uses: peter-evans/repository-dispatch@v4 with: token: ${{ secrets.WEBSITE_DISPATCH_TOKEN }} repository: WerWolv/ImHexWebsite event-type: update_page ================================================ FILE: .github/workflows/release.yml ================================================ permissions: contents: write name: Release on: release: types: [published] workflow_dispatch: inputs: commit_hash: type: string description: 'The commit hash to build (defaults to the latest commit on the default branch)' required: false default: '' jobs: release-update-repos: runs-on: ubuntu-24.04 name: Release Update Repos steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: path: ImHex - name: 📜 Verify version and set version variable run: | set -x project_version=`cat ImHex/VERSION` tag_version="${{github.event.release.tag_name}}" tag_version="${tag_version:1}" if [ "$project_version" != "$tag_version" ] && [ ! -z "$tag_version" ]; then echo "::warning::$project_version and $tag_version are not the same ! Refusing to populate release" exit 1 fi echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV - name: 🎫 Create PatternLanguage release uses: ncipollo/release-action@v1 env: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} if: "${{ env.RELEASE_TOKEN != '' }}" with: tag: ImHex-v${{ env.IMHEX_VERSION }} repo: PatternLanguage token: ${{ secrets.RELEASE_TOKEN }} skipIfReleaseExists: true - name: 🎫 Create ImHex-Patterns release uses: ncipollo/release-action@v1 env: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} if: "${{ env.RELEASE_TOKEN != '' }}" with: tag: ImHex-v${{ env.IMHEX_VERSION }} repo: ImHex-Patterns token: ${{ secrets.RELEASE_TOKEN }} skipIfReleaseExists: true - name: 🎫 Create imhex-download-sdk release uses: ncipollo/release-action@v1 env: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} if: "${{ env.RELEASE_TOKEN != '' }}" with: tag: v${{ env.IMHEX_VERSION }} repo: imhex-download-sdk token: ${{ secrets.RELEASE_TOKEN }} skipIfReleaseExists: true release-upload-artifacts: runs-on: ubuntu-24.04 name: Release Upload Artifacts outputs: IMHEX_VERSION: ${{ steps.verify_version.outputs.IMHEX_VERSION }} steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: path: ImHex submodules: recursive - name: 📜 Verify version and set version variable id: verify_version run: | set -x project_version=`cat ImHex/VERSION` tag_version="${{github.event.release.tag_name}}" tag_version="${tag_version:1}" if [ "$project_version" != "$tag_version" ] && [ ! -z "$tag_version" ]; then echo "::warning::$project_version and $tag_version are not the same ! Refusing to populate release" exit 1 fi echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV echo "IMHEX_VERSION=$project_version" >> $GITHUB_OUTPUT - name: 🗜️ Create tarball from sources with dependencies run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex - name: ⬇️ Download artifacts from latest workflow uses: dawidd6/action-download-artifact@v6 with: github_token: ${{ secrets.GITHUB_TOKEN }} workflow: build.yml branch: ${{ github.event.release.target_commitish }} workflow_conclusion: success skip_unpack: true commit: ${{ github.event.inputs.commit_hash }} - name: 🗜️ Unzip files when needed run: | set -x for zipfile in ./*.zip do if [ `zipinfo -1 "$zipfile" | wc -l` -eq 1 ]; then echo "unzipping $zipfile" unzip "$zipfile" rm "$zipfile" else echo "keeping $zipfile zipped" fi done - name: 🟩 Rename artifacts when needed run: | mv "Windows Portable x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip || true mv "Windows Portable arm64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip || true mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip || true mv "ImHex Web.zip" imhex-${{ env.IMHEX_VERSION }}-Web.zip || true rm artifact.tar || true - name: ⬆️ Upload Unsigned x86_64 Windows Installer uses: actions/upload-artifact@v4 id: upload-installer-x86_64 with: if-no-files-found: error name: Windows Installer x86_64 path: | imhex-*-x86_64.msi - name: ⬆️ Upload Unsigned ARM64 Windows Installer if: false uses: actions/upload-artifact@v4 id: upload-installer-arm64 with: if-no-files-found: error name: Windows Installer ARM64 path: | imhex-*-arm64.msi - name: 🗑️ Delete unsigned installers run: | rm imhex-*-x86_64.msi - name: 🗝️ Sign x86_64 Installer uses: signpath/github-action-submit-signing-request@v1 with: api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33' project-slug: 'ImHex' signing-policy-slug: 'release-signing' github-artifact-id: '${{ steps.upload-installer-x86_64.outputs.artifact-id }}' wait-for-completion: true output-artifact-directory: '.' - name: 🗝️ Sign ARM64 Installer if: false uses: signpath/github-action-submit-signing-request@v1 with: api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33' project-slug: 'ImHex' signing-policy-slug: 'release-signing' github-artifact-id: '${{ steps.upload-installer-arm64.outputs.artifact-id }}' wait-for-completion: true output-artifact-directory: '.' - name: ⬆️ Upload everything to release uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 with: files: '*' release-update-aur: name: Release update AUR package needs: release-upload-artifacts runs-on: ubuntu-24.04 steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: path: ImHex - name: ⬇️ Download artifacts run: | tagname=${GITHUB_REF#refs/tags/} version=${tagname#v} wget https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-ArchLinux-x86_64.pkg.tar.zst - name: ✒️ Prepare PKGBUILD run: | set -x cp ImHex/dist/Arch/PKGBUILD . hash=`md5sum imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst | cut -d ' ' -f 1` sed -i 's/%version%/${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}/g' PKGBUILD sed -i "s/(SKIP)/($hash)/g" PKGBUILD - name: ⬆️ Publish AUR package env: AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }} if: "${{ env.AUR_SSH_PRIVATE_KEY != '' }}" uses: KSXGitHub/github-actions-deploy-aur@v2 with: pkgname: imhex-bin pkgbuild: ./PKGBUILD commit_username: iTrooz commit_email: hey@itrooz.fr ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} commit_message: Bump to version ${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }} ssh_keyscan_types: rsa,ecdsa,ed25519 release-update-winget: name: Release update winget package needs: release-upload-artifacts runs-on: windows-latest steps: - name: ⬇️ Download dependencies shell: pwsh run: | iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - name: ⬆️ Update winget manifest shell: pwsh env: WINGET_GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} if: "${{ env.WINGET_GITHUB_TOKEN != '' }}" run: | $tagname = $env:GITHUB_REF.Replace("refs/tags/", "") $version = $tagname.Replace("v", "") $url = "https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-Windows-x86_64.msi" .\wingetcreate.exe update WerWolv.ImHex -u $url --version $version if ($version -notmatch "-") { .\wingetcreate.exe submit .\manifests\w\WerWolv\ImHex\${version}\ --token $env:WINGET_GITHUB_TOKEN } release-update-snapstore: name: Release update snapstore package needs: release-upload-artifacts runs-on: ubuntu-24.04 steps: - name: ⬇️ Download artifacts run: | tagname=${GITHUB_REF#refs/tags/} version=${tagname#v} wget https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-x86_64.snap wget https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-arm64.snap - name: ⬆️ Publish x86_64 Snap package continue-on-error: true uses: snapcore/action-publish@v1 env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }} with: snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-x86_64.snap release: stable - name: ⬆️ Publish arm64 Snap package continue-on-error: true uses: snapcore/action-publish@v1 env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }} with: snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-arm64.snap release: stable ================================================ FILE: .github/workflows/stale_issues.yml ================================================ name: Close inactive issues on: schedule: - cron: "30 1 * * 0" workflow_dispatch: jobs: close-issues: if: false # Disabled for now until I actually have time to take care of all these issues runs-on: ubuntu-24.04 permissions: issues: write pull-requests: write steps: - uses: actions/stale@v5 with: operations-per-run: '200' ascending: true days-before-issue-stale: 334 days-before-issue-close: 30 stale-issue-label: "stale" stale-issue-message: | This issue is marked stale as it has been open for 11 months without activity. Please try the latest ImHex version. (Avaiable here: https://imhex.download/ for release and https://imhex.download/#nightly for development version) If the issue persists on the latest version, please make a comment on this issue again Without response, this issue will be closed in one month. close-issue-message: "" days-before-pr-stale: -1 days-before-pr-close: -1 repo-token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/tests.yml ================================================ name: "Unit Tests" on: push: branches: - 'master' - 'releases/**' - 'tests/**' - 'feature/**' pull_request: branches: - 'master' - 'releases/**' workflow_dispatch: jobs: tests: name: 🧪 Unit Tests runs-on: ubuntu-24.04 permissions: actions: read contents: read security-events: write steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: 📜 Setup ccache uses: hendrikmuhs/ccache-action@v1 with: key: ${{ runner.os }}-tests-build-${{ github.run_id }} restore-keys: ${{ runner.os }}-tests-build max-size: 50M - name: ⬇️ Install dependencies run: | sudo apt update sudo bash dist/get_deps_debian.sh - name: 🛠️ Build run: | set -x mkdir -p build cd build CC=gcc-14 CXX=g++-14 cmake \ -DCMAKE_BUILD_TYPE=Debug \ -DIMHEX_ENABLE_UNIT_TESTS=ON \ -DIMHEX_ENABLE_PLUGIN_TESTS=ON \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all --coverage" \ -DCMAKE_CXX_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all --coverage" \ -DIMHEX_OFFLINE_BUILD=ON \ -G Ninja \ .. ninja unit_tests ninja imhex_all - name: 🧪 Perform plcli Integration Tests run: | cd lib/external/pattern_language python tests/integration/integration.py ../../../build/imhex --pl - name: 🧪 Perform Unit Tests run: | cd build ctest --output-on-failure # Generate report from all gcov .gcda files #- name: 🧪 Generate coverage report # run: | # sudo apt install python3-pip python3-venv # python3 -m venv venv # . venv/bin/activate # pip3 install gcovr # cd build # gcovr --gcov-executable /usr/bin/gcov-14 --exclude '.*/yara_rules/' --exclude '.*/third_party/' --exclude '.*/external/' --root .. --xml coverage_report.xml --verbose --gcov-ignore-errors all # #- name: Upload coverage reports to Codecov # env: # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} # if: ${{ env.CODECOV_TOKEN }} # uses: codecov/codecov-action@v4 # with: # fail_ci_if_error: true # token: ${{ secrets.CODECOV_TOKEN }} # file: build/coverage_report.xml langs: name: 🧪 Langs runs-on: ubuntu-22.04 steps: - name: 🧰 Checkout uses: actions/checkout@v4 with: submodules: recursive - name: Check langs run: python3 tests/check_langs.py ================================================ FILE: .gitignore ================================================ /.vscode/ /.idea/ /.kdev4/ /.vs/ .venv/ /cmake-build-*/ /build*/ /local/ /venv/ /.cache/ /install/ /out/ /dist/ImHex.run.xml *.mgc *.kdev4 imgui.ini .DS_Store CMakeUserPresets.json Brewfile.lock.json vcpkg.json ================================================ FILE: .gitmodules ================================================ [submodule "lib/third_party/nativefiledialog"] path = lib/third_party/nativefiledialog url = https://github.com/btzy/nativefiledialog-extended ignore = dirty [submodule "lib/third_party/yara/yara"] path = lib/third_party/yara/yara url = https://github.com/VirusTotal/yara ignore = dirty [submodule "lib/third_party/xdgpp"] path = lib/third_party/xdgpp url = https://github.com/WerWolv/xdgpp ignore = dirty [submodule "lib/third_party/fmt"] path = lib/third_party/fmt url = https://github.com/fmtlib/fmt ignore = dirty [submodule "lib/third_party/capstone"] path = lib/third_party/capstone url = https://github.com/capstone-engine/capstone ignore = dirty [submodule "lib/third_party/edlib"] path = lib/third_party/edlib url = https://github.com/Martinsos/edlib ignore = dirty [submodule "lib/third_party/lunasvg"] path = lib/third_party/lunasvg url = https://github.com/sammycage/lunasvg ignore = dirty [submodule "lib/external/libromfs"] path = lib/external/libromfs url = https://github.com/WerWolv/libromfs [submodule "lib/external/pattern_language"] path = lib/external/pattern_language url = https://github.com/WerWolv/PatternLanguage [submodule "lib/external/libwolv"] path = lib/external/libwolv url = https://github.com/WerWolv/libwolv [submodule "lib/third_party/HashLibPlus"] path = lib/third_party/HashLibPlus url = https://github.com/WerWolv/HashLibPlus [submodule "lib/external/disassembler"] path = lib/external/disassembler url = https://github.com/WerWolv/Disassembler [submodule "lib/third_party/md4c"] path = lib/third_party/md4c url = https://github.com/mity/md4c ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.25) # Options ## General option(IMHEX_STRIP_RELEASE "Strip the release builds" ON ) option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF) option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF) option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF) option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF) option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals (Linux only)" OFF) option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON ) option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF) option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF) option(IMHEX_BUILD_HARDENING "Enable hardening flags for build" ON ) option(IMHEX_GENERATE_PACKAGE "Specify if a cpack package should be built. (Windows only)" OFF) option(IMHEX_MACOS_CREATE_BUNDLE "Creates a macOS .app bundle when building (macOS only)" ON ) option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build." OFF) option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF) option(IMHEX_ENABLE_CXX_MODULES "Enable C++20 Module compilation. Testing only!" OFF) option(IMHEX_ENABLE_CPPCHECK "Enable cppcheck static analysis" OFF) option(IMHEX_BUNDLE_PLUGIN_SDK "Enable bundling of Plugin SDK into install package" ON ) ## Testing option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" ON ) option(IMHEX_ENABLE_IMGUI_TEST_ENGINE "Enable the ImGui Test Engine" OFF) option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF) ## Debug info option(IMHEX_COMPRESS_DEBUG_INFO "Compress debug information" ON ) option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF) option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF) option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON ) option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF) ## Plugins option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF) option(IMHEX_ENABLE_PLUGIN_TESTS "Enable building plugin tests" ON ) option(IMHEX_INCLUDE_PLUGINS "Semicolon-separated list of plugins to include in the build (empty = build all)" "" ) option(IMHEX_EXCLUDE_PLUGINS "Semicolon-separated list of plugins to exclude from the build" "" ) set(IMHEX_BASE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}") set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules") # Optional IDE support include("${IMHEX_BASE_FOLDER}/cmake/ide_helpers.cmake") # Basic compiler and cmake configurations set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_SCAN_FOR_MODULES ${IMHEX_ENABLE_CXX_MODULES}) set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include("${IMHEX_BASE_FOLDER}/cmake/build_helpers.cmake") # Setup project loadVersion(IMHEX_VERSION IMHEX_VERSION_PLAIN) setVariableInParent(IMHEX_VERSION ${IMHEX_VERSION}) configureCMake() project(ImHex LANGUAGES C CXX VERSION ${IMHEX_VERSION_PLAIN} DESCRIPTION "The ImHex Hex Editor" HOMEPAGE_URL "https://imhex.werwolv.net" ) configureProject() # Add ImHex sources add_custom_target(imhex_all ALL) # Make sure project is configured correctly setDefaultBuiltTypeIfUnset() detectBadClone() verifyCompiler() detectBundledPlugins() # Add various defines detectOS() addDefines() # Configure packaging and install targets configurePackingResources() setUninstallTarget() addBundledLibraries() add_subdirectory(lib/libimhex) add_subdirectory(main) addPluginDirectories() add_subdirectory(lib/trace) # Add unit tests if (IMHEX_ENABLE_UNIT_TESTS) if (NOT TARGET unit_tests) enable_testing() add_custom_target(unit_tests) add_subdirectory(tests EXCLUDE_FROM_ALL) endif () endif () # Configure more resources that will be added to the install package if (IMHEX_BUNDLE_PLUGIN_SDK) generateSDKDirectory() endif() # Handle package generation createPackage() # Accommodate IDEs with FOLDER support tweakTargetsForIDESupport() ================================================ FILE: CMakePresets.json ================================================ { "version": 2, "cmakeMinimumRequired": { "major": 3, "minor": 20, "patch": 0 }, "configurePresets": [ { "name": "base", "displayName": "Base", "description": "Base configuration for all builds", "hidden": true, "binaryDir": "${sourceDir}/build/${presetName}", "generator": "Ninja", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", "IMHEX_PATTERNS_PULL_MASTER": "ON", "CMAKE_INSTALL_PREFIX": "./install", "USE_SYSTEM_CAPSTONE": "ON", "IMHEX_USE_DEFAULT_BUILD_SETTINGS": "ON" } }, { "name": "x86_64", "displayName": "x86_64 Build", "description": "x86_64 build", "inherits": [ "base" ] }, { "name": "xcode", "inherits": [ "base" ], "displayName": "Xcode", "description": "Xcode with external compiler override", "generator": "Xcode", "cacheVariables": { "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++", "CMAKE_CXX_FLAGS": "-fexperimental-library -Wno-shorten-64-to-32 -Wno-deprecated-declarations", "IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER": "ON" } }, { "name": "vs2022", "displayName": "Visual Studio 2022", "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "VCPKG_MANIFEST_DIR": "${sourceDir}/dist" } }, { "name": "vs2022-x86", "displayName": "Visual Studio 2022 x86", "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "VCPKG_MANIFEST_DIR": "${sourceDir}/dist" }, "environment": { "VSCMD_ARG_TGT_ARCH": "x86" } } ], "buildPresets": [ { "name": "x86_64", "description": "x86_64 build", "configurePreset": "x86_64", "targets": [ "imhex_all" ] } ], "testPresets": [ ] } ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at Discord @WerWolv#1337. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: CONTRIBUTING.md ================================================ # Contribution guide ## Introduction This document is a guide for developers who want to contribute to ImHex in any way. It contains information about the codebase, the build process and the general workflow. ## Making Changes ### Adding new features If you'd like to add new features, the best way to start is by joining our Discord and telling us about your idea. We can then discuss the best way to implement it and how it should be integrated into ImHex or if it should be done in a separate plugin. There are standalone plugin templates that use ImHex as a submodule. You can find them located in the README's [Plugin Development](README.md#plugin-development) section. ### Adding a new language If you'd like to support a new language in ImHex, the best way is by using the `dist/langtool.py` tool. It will create the necessary file for you and help you fill them out. First, run the tool with `python3 dist/langtool.py create plugins/builtin/romfs/lang ` where `` is the ISO 639-1 code of your language. This will create a new file in the language directory. Afterwards follow the prompts of the program to populate the entire file. Once you're done, rerun cmake and rebuild ImHex. Your language should now be available in the settings. ### Updating an existing language If you'd like to add missing keys to an existing language, you can also use the `dist/langtool.py` tool. Run it with `python3 dist/langtool.py translate plugins/builtin/romfs/lang ` where `` is the ISO 639-1 code of the language. This will one by one list all the missing translation keys that are present in the default translation file, and you can fill them in with the correct translation for your language. ## Codebase ImHex is written in C++ and usually uses the latest compiler and standard library features available in gcc on all supported OSes. At the time of writing this is C++23. ### Structure - `main`: Contains the main application code - Important to understand here is that the main ImHex application is basically just an empty shell. - All it does is create a Window and a OpenGL context using GLFW, load all available plugins, properly configure ImGui and render it to the screen. - Everything else is done inside of plugins. ImHex comes with a few plugins by default, most notably the `builtin` plugin which contains the majority of the application code. - In most cases, this code doesn't need to be modified. Most features should be self-contained inside a plugin. - `lib` - `libimhex`: Contains all helper utilities as well as various APIs for plugins to interact with ImHex. - The library's main purpose is for Dependency Inversion. The ImHex main application as well as libimhex do not know about the existence of plugins at build time. Plugins and the main application instead link against libimhex and use it as a common API to interact with each other. - Since libimhex itself doesn't know about the existence of plugins, it cannot depend on any of them. This includes localizations and things that get registered by plugins after launch. - Even if the builtin plugin is technically always available, it is still a plugin and should be treated that way. - All important APIs can be found in the `hex/api` include directory and are documented in the respective header file. - `external`: All libraries that need custom patches or aren't typically available in package managers go into here. - If you'd like to add new features to the Pattern language, please make a PR to https://github.com/WerWolv/PatternLanguage instead. ImHex usually depends on the latest commit of the master branch of this repo. - `plugins` - `builtin`: The builtin plugin. Contains the majority of the application code. - It's the heart of ImHex's functionality. It contains most of the default views, providers, etc. so if you want to add new functionality to ImHex, this is the place to start. - `tests`: Contains all unit tests for ImHex. These are run automatically by the CI and should be kept up to date, especially when adding new helper functions to libimhex. ### RomFS ImHex uses a custom library called [libromfs](https://github.com/WerWolv/libromfs). It's a simple static library which uses CMake's code generation feature to embed files into the binary at compile time so they can be accessed at runtime. All plugins have a `romfs` folder which contains all files that should be embedded into the binary. Resources that need to be embedded into the main application (this is usually not necessary), go into the `resources/romfs` folder. When adding, changing files or removing files, make sure to re-run CMake to update the generated code. Otherwise, the changes might not be reflected in the binary. ## Development Environment I personally use CLion for development since it makes configuring and building the project very easy on all platforms. ### Windows - Install MSYS2 from https://www.msys2.org/ and use the `dist/get_deps_msys2.sh` script to install all dependencies. ### Linux - Install all dependencies using one of the `dist/get_deps_*.sh` scripts depending on your distribution or install them manually with your package manager. ### macOS - Install all dependencies using brew and the `dist/Brewfile` script. ================================================ FILE: INSTALL.md ================================================ # Installing ImHex ## Official Releases The easiest way to install ImHex is to download the latest release from the [GitHub Releases page](https://github.com/WerWolv/ImHex/releases/latest). There's also a NoGPU version available for users who don't have a GPU or want to run ImHex in a VM without GPU passthrough. ### Windows #### Installer Simply run the installer to install ImHex on your system #### Portable Extract the zip file to any location on your system. ### macOS Simply use the drag-n-drop dmg package to install ImHex on your system. It's possible that you need to allow the app to run in the security settings. ### Linux #### AppImage To run the AppImage, make sure it's executable. Then simply run it. ```bash chmod +x imhex-*.AppImage ./imhex-*.AppImage ``` #### Flatpak To install the Flatpak, make sure you have the Flathub repository added to your system. Then simply run the following command: ```bash flatpak install flathub net.werwolv.ImHex # or install the file directly flatpak install ./imhex-*.flatpak ``` #### Snap ```bash snap install ./imhex-*.snap ``` #### Ubuntu DEB Package To install the DEB package, simply run the following command: ```bash sudo apt install ./imhex-*.deb ``` #### Arch Linux To install the Arch Linux package, simply run the following command: ```bash sudo pacman -U imhex-*.pkg.tar.zst ``` #### Fedora / RHEL / AlmaLinux RPM Package To install the RPM package, simply run the following command: ```bash sudo dnf install ./imhex-*.rpm ``` ## Nightly Builds The GitHub Actions CI builds a new release package on every commit made to repository. These builds are available on the [GitHub Actions page](https://github.com/WerWolv/ImHex/actions?query=workflow%3A%22Build%22). These builds are not guaranteed to be stable and may contain bugs, however they also contain new features that are not yet available in the official releases. ## Building from source Build instructions for Windows, Linux and macOS can be found under `/dist/compiling`: - Windows: [Link](dist/compiling/windows.md) - macOS: [Link](dist/compiling/macos.md) - Linux: [Link](dist/compiling/linux.md) ## Package managers ImHex is also available on various package managers. The officially supported ones are listed here: ### Windows - **Chocolatey** - [imhex](https://community.chocolatey.org/packages/imhex) (Thanks to @Jarcho) - `choco install imhex` - **Winget** - [WerWolv.ImHex](https://github.com/microsoft/winget-pkgs/tree/master/manifests/w/WerWolv/ImHex) - `winget install WerWolv.ImHex` ### Linux - **Arch Linux AUR** - [imhex-bin](https://aur.archlinux.org/packages/imhex-bin/) (Thanks to @iTrooz) - `yay -S imhex-bin` - [imhex](https://aur.archlinux.org/packages/imhex/) (Thanks to @KokaKiwi) - `yay -S imhex` - **Fedora** - [imhex](https://src.fedoraproject.org/rpms/imhex/) (Thanks to @jonathanspw) - `dnf install imhex` - **Flatpak** - [net.werwolv.Imhex](https://flathub.org/apps/details/net.werwolv.ImHex) (Thanks to @Mailaender) - `flatpak install flathub net.werwolv.ImHex` ### Available on other package managers Packages that aren't explicitly mentioned above are not officially supported but they will most likely still work. Contact the maintainer of the package if you have any issues. [![Packaging status](https://repology.org/badge/vertical-allrepos/imhex.svg)](https://repology.org/project/imhex/versions) ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: PLUGINS.md ================================================ # Plugins ImHex is entirely built around the possibility to easily load plugins (most of it's features are actually implemented as a plugin!). To install plugins, simply download the relevant `.hexplug` file and drop it in your `plugins` folder. The location of that folder can be found under `Help -> About -> ImHex Directories`. ## Available Plugins (If you're developing a Plugin on your own, please feel free to add it to this list) ### Official Plugins - [Extra Hashes Plugin](https://github.com/WerWolv/ImHex-Hashes-Plugin) - Adds support for a variety of new hashes to the Hashes view including Blake, Adler32, Murmur and Tiger - [Discord RPC Plugin](https://github.com/WerWolv/ImHex-Plugin-DiscordRPC) - Adds support for Discord Rich Presence ### Third-Party Plugins - [Pcap Plugin](https://github.com/Professor-plum/ImHex-Plugin-Pcap) - Adds support for reading packet capture files ================================================ FILE: PRIVACY.md ================================================ # Privacy Policy ImHex collects **anonymous** user statistics based on the user's preferences which are set on first launch and can be opted in or out at any moment through the settings interface. These statistics contain basic system information such as: ImHex Version, System Architecture, OS, OS Version or Linux Distro version of the GPU in use. This information is linked to a randomly generated ID which cannot be used to identify a specific user. Additionally, we allow uploading of anonymized crash log files in case of an error. These are never uploaded automatically but only after explicit consent by the user. This decision is not saved so logs can be uploaded on a per-error basis. Information collected may be analyzed by members of our development team and will never be shared with third parties outside of the team. We may occasionally share general usage statistics publically in a summarized manner (For example a graph stating 70% of users are using a specific OS). We will never share information about individual users, even if they are anonymous. ================================================ FILE: README.md ================================================

A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.
/ˈɪmhɛks/

'Build' workflow Status Discord Server Total Downloads Code Quality Translation Plugins

Download the latest version of ImHex! Download the latest nightly pre-release version of ImHex Use the Web version of ImHex right in your browser! Read the documentation of ImHex!

## Supporting If you like my work, please consider supporting me on GitHub Sponsors, Ko-Fi or PayPal. Thanks a lot!

GitHub donate button Ko-Fi donate button PayPal donate button

### Notable Sponsors | | | |:---------------------------------------------------------------------------------------------------:|-----------------------------------------------------------------------------------| | [![JetBrains logo](https://avatars.githubusercontent.com/u/878437?s=48)](https://www.jetbrains.com) | JetBrains, providing us with free All Products Pack licenses for development | | [![SignPath logo](https://avatars.githubusercontent.com/u/34448643?s=48)](https://signpath.io/) | SignPath, providing us with free Code Signing Certificates for our Windows builds | | [![AWS logo](https://avatars.githubusercontent.com/u/2232217?s=48)](https://aws.amazon.com) | Amazon, providing us with free AWS Cloud Credits for our CI | Would you like to appear here as well? Contact us at [imhex@werwolv.net](mailto:imhex@werwolv.net)! ## Screenshots ![Hex editor, patterns and data information](https://github.com/user-attachments/assets/902a7c4c-410d-490f-999e-14c856fec027) ![Bookmarks, data information, find view and data processor](https://github.com/user-attachments/assets/58eefa1f-31c9-4bb8-a1c1-8cdd8ddbd29f)
More Screenshots ![Data Processor decrypting some data and displaying it as an image](https://github.com/WerWolv/ImHex/assets/10835354/d0623081-3094-4840-a8a8-647b38724db8) ![STL Parser written in the Pattern Language visualizing a 3D model](https://github.com/WerWolv/ImHex/assets/10835354/62cbcd18-1c3f-4dd6-a877-2bf2bf4bb2a5) ![Data Information view displaying various stats about the file](https://github.com/WerWolv/ImHex/assets/10835354/d4706c01-c258-45c9-80b8-fe7a10d5a1de)
## Features
Featureful hex view - Byte patching - Patch management - Infinite Undo/Redo - "Copy bytes as..." - Bytes - Hex string - C, C++, C#, Rust, Python, Java & JavaScript array - ASCII-Art hex view - HTML self-contained div - Simple string and hex search - Goto from start, end and current cursor position - Colorful highlighting - Configurable foreground highlighting rules - Background highlighting using patterns, find results and bookmarks - Displaying data as a list of many different types - Hexadecimal integers (8, 16, 32, 64 bit) - Signed and unsigned decimal integers (8, 16, 32, 64 bit) - Floats (16, 32, 64 bit) - RGBA8 Colors - HexII - Binary - Decoding data as ASCII and custom encodings - Built-in support for UTF-8, UTF-16, ShiftJIS, most Windows encodings and many more - Paged data view
Custom C++-like pattern language for parsing highlighting a file's content - Automatic loading based on MIME types and magic values - Arrays, pointers, structs, unions, enums, bitfields, namespaces, little and big endian support, conditionals and much more! - Useful error messages, syntax highlighting and error marking - Support for visualizing many different types of data - Images - Audio - 3D Models - Coordinates - Time stamps
Theming support - Doesn't burn out your retinas when used in late-night sessions - Dark mode by default, but a light mode is available as well - Customizable colors and styles for all UI elements through shareable theme files - Support for custom fonts
Importing and Exporting data - Base64 files - IPS and IPS32 patches - Markdown reports - Binary arrays for various programming languages
Data Inspector - Interpreting data as many different types with endianness, decimal, hexadecimal and octal support and bit inversion - Unsigned and signed integers (8, 16, 24, 32, 48, 64 bit) - Floats (16, 32, 64 bit) - Signed and Unsigned LEB128 - ASCII, Wide and UTF-8 characters and strings - time32_t, time64_t, DOS date and time - GUIDs - RGBA8 and RGB65 Colors - Copying and modifying bytes through the inspector - Adding new data types through the pattern language - Support for hiding rows that aren't used
Node-based data pre-processor - Modify, decrypt and decode data before it's being displayed in the hex editor - Modify data without touching the underlying source - Support for adding custom nodes
Loading data from many different data sources - Local Files - Support for huge files with fast and efficient loading - Raw Disks - Loading data from raw disks and partitions - GDB Server - Access the RAM of a running process or embedded devices through GDB - Intel Hex and Motorola SREC data - Base64 encoded data - UDP Packets - Support for displaying raw data received over UDP - Process Memory - Inspect the entire address space of a running process - Remote Files over SSH with SFTP - Support for loading files from remote servers using SSH and SFTP
Data searching - Support for searching the entire file or only a selection - String extraction - Option to specify minimum length and character set (lower case, upper case, digits, symbols) - Option to specify encoding (ASCII, UTF-8, UTF-16 big and little endian) - Sequence search - Search for a sequence of bytes or characters - Option to ignore character case - Regex search - Search for strings using regular expressions - Binary Pattern - Search for sequences of bytes with optional wildcards - Numeric Value search - Search for signed/unsigned integers and floats - Search for ranges of values - Option to specify size and endianness - Option to ignore unaligned values
Data hashing support - Many different algorithms available - CRC8, CRC16 and CRC32 with custom initial values and polynomials - Many default polynomials available - MD5 - SHA-1, SHA-224, SHA-256, SHA-384, SHA-512 - Adler32 - AP - BKDR - Bernstein, Bernstein1 - DEK, DJB, ELF, FNV1, FNV1a, JS, PJW, RS, SDBM - OneAtTime, Rotating, ShiftAndXor, SuperFast - Murmur2_32, MurmurHash3_x86_32, MurmurHash3_x86_128, MurmurHash3_x64_128 - SipHash64, SipHash128 - XXHash32, XXHash64 - Tiger, Tiger2 - Blake2B, Blake2S - Hashing of specific regions of the loaded data - Hashing of arbitrary strings
Diffing support - Compare data of different data sources - Difference highlighting - Table view of differences
Integrated disassembler - Support for all architectures supported by Capstone - ARM32 (ARM, Thumb, Cortex-M, AArch32) - ARM64 - MIPS (MIPS32, MIPS64, MIPS32R6, Micro) - x86 (16-bit, 32-bit, 64-bit) - PowerPC (32-bit, 64-bit) - SPARC - IBM SystemZ - xCORE - M68K - TMS320C64X - M680X - Ethereum - RISC-V - WebAssembly - MOS65XX - Berkeley Packet Filter - Support for writing custom disassemblers for your own architectures
Bookmarks - Support for bookmarks with custom names and colors - Highlighting of bookmarked region in the hex editor - Jump to bookmarks - Open content of bookmark in a new tab - Add comments to bookmarks
Featureful data analyzer and visualizer - File magic-based file parser and MIME type database - Byte type distribution graph - Entropy graph - Highest and average entropy - Encrypted / Compressed file detection - Digram and Layered distribution graphs
YARA Rule support - Scan a file for vulnerabilities with official yara rules - Highlight matches in the hex editor - Jump to matches - Apply multiple rules at once
Helpful tools - Itanium, MSVC, Rust and D-Lang demangler based on LLVM - ASCII table - Regex replacer - Mathematical expression evaluator (Calculator) - Graphing calculator - Hexadecimal Color picker with support for many different formats - Base converter - Byte swapper - UNIX Permissions calculator - Wikipedia term definition finder - File utilities - File splitter - File combiner - File shredder - IEEE754 Float visualizer - Division by invariant multiplication calculator - TCP Client/Server - Euclidean algorithm calculator - HTTP Requests
Built-in Content updater - Download all files found in the database directly from within ImHex - Pattern files for decoding various file formats - Libraries for the pattern language - Magic files for file type detection - Custom data processor nodes - Custom encodings - Custom themes - Yara rules
Modern Interface - Support for multiple workspaces - Support for custom layouts - Detachable windows
Easy to get started - Support for many different languages - Simplified mode for beginners - Extensive documentation - Many example files available on [the Database](https://github.com/WerWolv/ImHex-Patterns) - Achievements guiding you through the features of ImHex - Interactive tutorials
## Pattern Language The Pattern Language is the completely custom programming language developed for ImHex. It allows you to define structures and data types in a C-like syntax and then use them to parse and highlight a file's content. - Source Code: [Link](https://github.com/WerWolv/PatternLanguage/) - Documentation: [Link](https://docs.werwolv.net/pattern-language/) ## Database For format patterns, libraries, magic and constant files, check out the [ImHex-Patterns](https://github.com/WerWolv/ImHex-Patterns) repository. **Feel free to PR your own files there as well!** ## Requirements To use ImHex, the following minimal system requirements need to be met. > [!IMPORTANT] > ImHex requires a GPU with OpenGL 3.0 support in general. > There are releases available (with the `-NoGPU` suffix) that are software rendered and don't require a GPU, however these can be a lot slower than the GPU accelerated versions. > > If possible at all, make ImHex use the dedicated GPU on your system instead of the integrated one. > ImHex will usually run fine with integrated GPUs as well but certain Intel HD GPU drivers on Windows are known to cause graphical artifacts. - **OS**: - **Windows**: Windows 7 or higher (Windows 10/11 recommended) - **macOS**: macOS 15 (Sequoia) or higher, - Lower versions should still work too, but you'll need to compile ImHex yourself. The release binaries will NOT work due to GitHub not having any macOS 15 or lower CI runners available. - The macOS build is not signed and will require you to manually allow them in the Security & Privacy settings. - **Linux**: "Modern" Linux. The following distributions have official releases available. Other distros are supported through the AppImage, Flatpak and Snap releases. - Ubuntu and Debian - Fedora - RHEL/AlmaLinux - Arch Linux - Basically any other distro will work as well when compiling ImHex from sources. - **FreeBSD**: Tested on FreeBSD 14.3 - Other versions will most likely work too but are untested - **CPU**: Officially supported are x86, AMD64 and ARM64, though any Little Endian CPU should work. - **GPU**: OpenGL 3.0 or higher - Integrated Intel HD iGPUs are supported, however certain drivers are known to cause various graphical artifacts, especially on Windows. Use at your own risk. - In case you don't have a GPU available, there are software rendered releases available for Windows and macOS - **RAM**: ~50MiB, more is required for more complex analysis - **Storage**: ~100MiB ## Installing Information on how to install ImHex can be found in the [Install](/INSTALL.md) guide ## Compiling To compile ImHex on any platform, GCC (or Clang) is required with a version that supports C++23 or higher. Windows and Linux releases are being built using latest available GCC. MacOS releases are being built using latest available LLVM Clang. Important to note is, the MSVC and AppleClang compilers are both **NOT** supported since they're both generally severely outdated and lack features GCC and LLVM Clang have. > [!NOTE] > Many dependencies are bundled into the repository using submodules so make sure to clone it using the `--recurse-submodules` option. > All dependencies that aren't bundled, can be installed using the dependency installer scripts found in the `/dist` folder. For more information, check out the [Compiling](/dist/compiling) guide. ## Contributing See [Contributing](/CONTRIBUTING.md) ## Plugin development To develop plugins for ImHex, use the following template project to get started. You then have access to the entirety of libimhex as well as the ImHex API and the Content Registry to interact with ImHex or to add new content. To build a plugin, you will need to use our SDK ### Getting the SDK locally You can build the SDK by compiling ImHex like this: - `cmake -G Ninja -DIMHEX_BUNDLE_PLUGIN_SDK=ON -B build` - `cd build` - `DESTDIR=install ninja install` The SDK will then be available at `install/usr/local/share/imhex/sdk`. You will need to set the variable `IMHEX_SDK_PATH` to that (absolute) path. ### Getting the SDK in a Github Actions CI You can use [this action](https://github.com/WerWolv/imhex-download-sdk) to automatically download the SDK to your Github Runner - [ImHex Plugin Template](https://github.com/WerWolv/ImHex-Plugin-Template) ## Credits ### Contributors - [AxCut](https://github.com/paxcut) for a gigantic amount of contributions to the Pattern Text Editor and tons of other parts of ImHex - [iTrooz](https://github.com/iTrooz) for getting ImHex onto the Web as well as hundreds of contributions in every part of the project - [jumanji144](https://github.com/jumanji144) for huge contributions to the Pattern Language and ImHex's infrastructure - [Mary](https://github.com/marysaka) for her immense help porting ImHex to macOS and help during development - [Roblabla](https://github.com/Roblabla) for adding MSI Installer support to ImHex - [Mailaender](https://github.com/Mailaender) for getting ImHex onto Flathub - Everybody else who has reported issues on Discord or GitHub that I had great conversations with :) ### Dependencies - Thanks a lot to ocornut for their amazing [Dear ImGui](https://github.com/ocornut/imgui) which is used for building the entire interface - Thanks to epezent for [ImPlot](https://github.com/epezent/implot) used to plot data in various places - Thanks to Nelarius for [ImNodes](https://github.com/Nelarius/imnodes) used as base for the data processor - Thanks to BalazsJako for [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) used for the pattern language syntax highlighting - Thanks to nlohmann for their [json](https://github.com/nlohmann/json) library used for configuration files - Thanks to vitaut for their [libfmt](https://github.com/fmtlib/fmt) library which makes formatting and logging so much better - Thanks to btzy for [nativefiledialog-extended](https://github.com/btzy/nativefiledialog-extended) and their great support, used for handling file dialogs on all platforms - Thanks to danyspin97 for [xdgpp](https://sr.ht/~danyspin97/xdgpp) used to handle folder paths on Linux - Thanks to aquynh for [capstone](https://github.com/aquynh/capstone) which is the base of the disassembly window - Thanks to rxi for [microtar](https://github.com/rxi/microtar) used for extracting downloaded store assets - Thanks to VirusTotal for [Yara](https://github.com/VirusTotal/yara) used by the Yara plugin - Thanks to Martinsos for [edlib](https://github.com/Martinsos/edlib) used for sequence searching in the diffing view - Thanks to ron4fun for [HashLibPlus](https://github.com/ron4fun/HashLibPlus) which implements every hashing algorithm under the sun - Thanks to mackron for [miniaudio](https://github.com/mackron/miniaudio) used to play audio files - Thanks to all other groups and organizations whose libraries are used in ImHex ### License The biggest part of ImHex is under the GPLv2-only license. Notable exceptions to this are the following parts which are under the LGPLv2.1 license: - **/lib/libimhex**: The library that allows Plugins to interact with ImHex. - **/plugins/ui**: The UI plugin library that contains some common UI elements that can be used by other plugins The reason for this is to allow for proprietary plugins to be developed for ImHex. ### Code Signing Policy Free code signing provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it. #### People with direct push access - [WerWolv](https://github.com/WerWolv) - [iTrooz](https://github.com/iTrooz) - [jumanji144](https://github.com/jumanji144) - [AxCut](https://github.com/paxcut) ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions Supported is generally only the latest version that can be downloaded from the Release tab. If you have issues, you might get instructed to use the Nightly release version instead. If you built ImHex yourself and experience issues that are not present in the version built by GitHub, you're on your own. ## Reporting a Vulnerability Any critical vulnerabilities can be reported through Discord (@werwolv). ================================================ FILE: VERSION ================================================ 1.39.0.WIP ================================================ FILE: changelog.md ================================================ ================================================ FILE: cmake/build_helpers.cmake ================================================ # Some libraries we use set the BUILD_SHARED_LIBS variable to ON, which causes CMake to # display a warning about options being set using set() instead of option(). # Explicitly set the policy to NEW to suppress the warning. set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) set(CMAKE_POLICY_DEFAULT_CMP0141 NEW) if (POLICY CMP0177) set(CMAKE_POLICY_DEFAULT_CMP0177 OLD) cmake_policy(SET CMP0177 OLD) endif() function(getTarget target type) get_target_property(IMPORTED_TARGET ${target} IMPORTED) if (IMPORTED_TARGET) set(${type} INTERFACE PARENT_SCOPE) else() set(${type} PRIVATE PARENT_SCOPE) endif() endfunction() function(addCFlag) if (ARGC EQUAL 1) add_compile_options($<$:${ARGV0}>) elseif (ARGC EQUAL 2) getTarget(${ARGV1} TYPE) target_compile_options(${ARGV1} ${TYPE} $<$:${ARGV0}>) endif() endfunction() function(addCXXFlag) if (ARGC EQUAL 1) add_compile_options($<$:${ARGV0}>) elseif (ARGC EQUAL 2) getTarget(${ARGV1} TYPE) target_compile_options(${ARGV1} ${TYPE} $<$:${ARGV0}>) endif() endfunction() function(addObjCFlag) if (ARGC EQUAL 1) add_compile_options($<$:${ARGV0}>) elseif (ARGC EQUAL 2) getTarget(${ARGV1} TYPE) target_compile_options(${ARGV1} ${TYPE} $<$:${ARGV0}>) endif() endfunction() function(addLinkerFlag) if (ARGC EQUAL 1) add_link_options(${ARGV0}) elseif (ARGC EQUAL 2) getTarget(${ARGV1} TYPE) target_link_options(${ARGV1} ${TYPE} ${ARGV0}) endif() endfunction() function(addCCXXFlag) addCFlag(${ARGV0} ${ARGV1}) addCXXFlag(${ARGV0} ${ARGV1}) endfunction() function(addCommonFlag) addCFlag(${ARGV0} ${ARGV1}) addCXXFlag(${ARGV0} ${ARGV1}) addObjCFlag(${ARGV0} ${ARGV1}) endfunction() function(addCppCheck target) if (NOT IMHEX_ENABLE_CPPCHECK) return() endif() find_program(cppcheck_exe NAMES cppcheck REQUIRED) if (NOT cppcheck_exe) return() endif() set(target_build_dir $) set(cppcheck_opts --enable=all --inline-suppr --quiet --std=c++23 --check-level=exhaustive --error-exitcode=10 --suppressions-list=${CMAKE_SOURCE_DIR}/dist/cppcheck.supp --checkers-report=${target_build_dir}/cppcheck-report.txt ) set_target_properties(${target} PROPERTIES CXX_CPPCHECK "${cppcheck_exe};${cppcheck_opts}" ) endfunction() set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Disable deprecated warnings" FORCE) include(FetchContent) if(IMHEX_STRIP_RELEASE) if(CMAKE_BUILD_TYPE STREQUAL "Release") set(CPACK_STRIP_FILES TRUE) endif() if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_link_options($<$:-s>) endif() endif() macro(addDefines) if (NOT IMHEX_VERSION) message(FATAL_ERROR "IMHEX_VERSION is not defined") endif () set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -DPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} -DPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR} -DPROJECT_VERSION_PATCH=${PROJECT_VERSION_PATCH} ") set(IMHEX_VERSION_STRING ${IMHEX_VERSION}) if (CMAKE_BUILD_TYPE STREQUAL "Release") set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}) add_compile_definitions(NDEBUG) elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-Debug) add_compile_definitions(DEBUG) elseif (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}) add_compile_definitions(NDEBUG) elseif (CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-MinSizeRel) add_compile_definitions(NDEBUG) endif () if (IMHEX_ENABLE_STD_ASSERTS) add_compile_definitions(_GLIBCXX_DEBUG _GLIBCXX_VERBOSE) endif() if (IMHEX_STATIC_LINK_PLUGINS) add_compile_definitions(IMHEX_STATIC_LINK_PLUGINS) endif () endmacro() function(addDefineToSource SOURCE DEFINE) set_property( SOURCE ${SOURCE} APPEND PROPERTY COMPILE_DEFINITIONS "${DEFINE}" ) # Disable precompiled headers for this file set_source_files_properties(${SOURCE} PROPERTIES SKIP_PRECOMPILE_HEADERS ON) endfunction() # Detect current OS / System macro(detectOS) if (WIN32) add_compile_definitions(OS_WINDOWS) set(CMAKE_INSTALL_BINDIR ".") set(CMAKE_INSTALL_LIBDIR ".") set(PLUGINS_INSTALL_LOCATION "plugins") add_compile_definitions(WIN32_LEAN_AND_MEAN) add_compile_definitions(NOMINMAX) add_compile_definitions(UNICODE) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) elseif (APPLE) add_compile_definitions(OS_MACOS) set(CMAKE_INSTALL_BINDIR ".") set(CMAKE_INSTALL_LIBDIR ".") set(PLUGINS_INSTALL_LOCATION "plugins") enable_language(OBJC) enable_language(OBJCXX) elseif (EMSCRIPTEN) add_compile_definitions(OS_WEB) elseif (UNIX AND NOT APPLE) add_compile_definitions(OS_LINUX) if (BSD AND BSD STREQUAL "FreeBSD") add_compile_definitions(OS_FREEBSD) endif() include(GNUInstallDirs) set(PLUGINS_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/imhex/plugins") # Add System plugin location for plugins to be loaded from # IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex") else () message(FATAL_ERROR "Unknown / unsupported system!") endif() endmacro() macro(configurePackingResources) set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if (WIN32) if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug")) set(APPLICATION_TYPE WIN32) endif () set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/resource.rc") if (IMHEX_GENERATE_PACKAGE) set(CPACK_GENERATOR "WIX") set(CPACK_PACKAGE_NAME "ImHex") set(CPACK_PACKAGE_VENDOR "WerWolv") set(CPACK_WIX_VERSION 4) set(CPACK_WIX_PRODUCT_GUID "*") set(CPACK_WIX_UPGRADE_GUID "05000E99-9659-42FD-A1CF-05C554B39285") set(CPACK_WIX_PRODUCT_ICON "${PROJECT_SOURCE_DIR}/resources/dist/windows/icon.ico") set(CPACK_WIX_UI_BANNER "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_banner.png") set(CPACK_WIX_UI_DIALOG "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_dialog.png") set(CPACK_WIX_CULTURES "en-US;de-DE;ja-JP;it-IT;pt-BR;zh-CN;zh-TW;ru-RU") set(CPACK_WIX_TEMPLATE "${PROJECT_SOURCE_DIR}/resources/dist/windows/WIX.template.in") set(CPACK_WIX_EXTENSIONS "WixToolset.UI.wixext") file(GLOB_RECURSE CPACK_WIX_EXTRA_SOURCES "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix/*.wxs") set(CPACK_PACKAGE_INSTALL_DIRECTORY "ImHex") set_property(INSTALL "$" PROPERTY CPACK_START_MENU_SHORTCUTS "ImHex" ) set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/resources/dist/windows/LICENSE.rtf") endif() elseif (APPLE OR ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/dist/macos/AppIcon.icns") set(BUNDLE_NAME "ImHex.app") if (IMHEX_MACOS_CREATE_BUNDLE) set(APPLICATION_TYPE MACOSX_BUNDLE) set_source_files_properties(${IMHEX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") set(MACOSX_BUNDLE_ICON_FILE "AppIcon.icns") set(MACOSX_BUNDLE_INFO_STRING "WerWolv") set(MACOSX_BUNDLE_BUNDLE_NAME "ImHex") set(MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/dist/macos/Info.plist.in") set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(MACOSX_BUNDLE_GUI_IDENTIFIER "net.WerWolv.ImHex") string(SUBSTRING "${IMHEX_COMMIT_HASH_LONG}" 0 7 COMMIT_HASH_SHORT) set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${COMMIT_HASH_SHORT}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") string(TIMESTAMP CURR_YEAR "%Y") set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2020 - ${CURR_YEAR} WerWolv. All rights reserved." ) if ("${CMAKE_GENERATOR}" STREQUAL "Xcode") set(IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${BUNDLE_NAME}") else () set(IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}") endif() set(PLUGINS_INSTALL_LOCATION "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins") set(CMAKE_INSTALL_LIBDIR "${IMHEX_BUNDLE_PATH}/Contents/Frameworks") endif() endif() endmacro() macro(addPluginDirectories) file(MAKE_DIRECTORY "plugins") foreach (plugin IN LISTS PLUGINS) add_subdirectory("plugins/${plugin}") if (TARGET ${plugin}) set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins") set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins") if (APPLE) if (IMHEX_MACOS_CREATE_BUNDLE) set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION}) endif () else () if (WIN32) get_target_property(target_type ${plugin} TYPE) if (target_type STREQUAL "SHARED_LIBRARY") install(TARGETS ${plugin} RUNTIME DESTINATION ${PLUGINS_INSTALL_LOCATION}) else () install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION}) endif() else() install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION}) endif() endif() add_dependencies(imhex_all ${plugin}) endif () endforeach() endmacro() macro(createPackage) if (WIN32) # Install binaries directly in the prefix, usually C:\Program Files\ImHex. set(CMAKE_INSTALL_BINDIR ".") set(PLUGIN_TARGET_FILES "") foreach (plugin IN LISTS PLUGINS) list(APPEND PLUGIN_TARGET_FILES "$") endforeach () if (DEFINED VCPKG_TARGET_TRIPLET) set(VCPKG_DEPS_FOLDER "") if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(VCPKG_DEPS_FOLDER "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/bin") else() set(VCPKG_DEPS_FOLDER "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/bin") endif() install(CODE "set(VCPKG_DEPS_FOLDER \"${VCPKG_DEPS_FOLDER}\")") endif() # Grab all dynamically linked dependencies. install(CODE "set(CMAKE_INSTALL_BINDIR \"${CMAKE_INSTALL_BINDIR}\")") install(CODE "set(PLUGIN_TARGET_FILES \"${PLUGIN_TARGET_FILES}\")") install(CODE [[ file(GET_RUNTIME_DEPENDENCIES EXECUTABLES ${PLUGIN_TARGET_FILES} $ $ RESOLVED_DEPENDENCIES_VAR _r_deps UNRESOLVED_DEPENDENCIES_VAR _u_deps CONFLICTING_DEPENDENCIES_PREFIX _c_deps DIRECTORIES ${DEP_FOLDERS} $ENV{PATH} POST_EXCLUDE_REGEXES ".*system32/.*\\.dll" ) if(_c_deps_FILENAMES AND _c_deps AND NOT (_c_deps STREQUAL "")) message(WARNING "Conflicting dependencies for library: \"${_c_deps}\"!") endif() if (DEFINED VCPKG_DEPS_FOLDER) file(GLOB VCPKG_DEPS "${VCPKG_DEPS_FOLDER}/*.dll") list(APPEND _r_deps ${VCPKG_DEPS}) endif() foreach(_file ${_r_deps}) file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}" TYPE SHARED_LIBRARY FOLLOW_SYMLINK_CHAIN FILES "${_file}" ) endforeach() ]]) downloadImHexPatternsFiles(".") elseif(UNIX AND NOT APPLE) set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION}) configure_file(${IMHEX_BASE_FOLDER}/dist/DEBIAN/control.in ${CMAKE_BINARY_DIR}/DEBIAN/control) install(FILES ${IMHEX_BASE_FOLDER}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex) install(FILES ${IMHEX_BASE_FOLDER}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) install(FILES ${IMHEX_BASE_FOLDER}/dist/imhex.mime.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mime/packages RENAME imhex.xml) install(FILES ${IMHEX_BASE_FOLDER}/resources/icon.svg DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.svg) downloadImHexPatternsFiles("./share/imhex") # install AppStream file install(FILES ${IMHEX_BASE_FOLDER}/dist/net.werwolv.ImHex.metainfo.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo) endif() if (APPLE) if (IMHEX_MACOS_CREATE_BUNDLE) set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS} "${IMHEX_SYSTEM_LIBRARY_PATH}") include(PostprocessBundle) set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION}) set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST}) set_property(TARGET main PROPERTY MACOSX_BUNDLE_BUNDLE_NAME "${MACOSX_BUNDLE_BUNDLE_NAME}") # Fix rpath install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $)") install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $)") downloadImHexPatternsFiles("${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS") install(TARGETS main BUNDLE DESTINATION ".") install(TARGETS updater DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS") install( FILES ${IMHEX_BASE_FOLDER}/dist/cli/imhex.sh DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS/cli" RENAME imhex PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) # Update library references to make the bundle portable postprocess_bundle(imhex_all main) # Enforce DragNDrop packaging. set(CPACK_GENERATOR "DragNDrop") set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns") set(CPACK_BUNDLE_PLIST "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Info.plist") if (IMHEX_RESIGN_BUNDLE) find_program(CODESIGN_PATH codesign) if (CODESIGN_PATH) install(CODE "message(STATUS \"Signing bundle '${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}'...\")") install(CODE "execute_process(COMMAND ${CODESIGN_PATH} --force --deep --entitlements ${CMAKE_SOURCE_DIR}/resources/macos/Entitlements.plist --sign - ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME} COMMAND_ERROR_IS_FATAL ANY)") endif() endif() install(CODE [[ message(STATUS "MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!") ]]) else() downloadImHexPatternsFiles("${IMHEX_MAIN_OUTPUT_DIRECTORY}") endif() else() install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) if (TARGET updater) install(TARGETS updater RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() if (TARGET main-forwarder) install(TARGETS main-forwarder BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() if (WIN32) install( FILES ${IMHEX_BASE_FOLDER}/dist/cli/imhex.bat DESTINATION ${CMAKE_INSTALL_BINDIR}/cli RENAME imhex.bat PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) else() install( FILES ${IMHEX_BASE_FOLDER}/dist/cli/imhex.sh DESTINATION ${CMAKE_INSTALL_PREFIX}/share/imhex RENAME imhex PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() endif() if (IMHEX_MACOS_CREATE_BUNDLE) set(CPACK_BUNDLE_NAME "ImHex") include(CPack) endif() endmacro() function(JOIN OUTPUT GLUE) set(_TMP_RESULT "") set(_GLUE "") # effective glue is empty at the beginning foreach(arg ${ARGN}) set(_TMP_RESULT "${_TMP_RESULT}${_GLUE}${arg}") set(_GLUE "${GLUE}") endforeach() set(${OUTPUT} "${_TMP_RESULT}" PARENT_SCOPE) endfunction() macro(configureCMake) message(STATUS "Configuring ImHex v${IMHEX_VERSION}") if (DEFINED CMAKE_TOOLCHAIN_FILE) message(STATUS "Using toolchain file: \"${CMAKE_TOOLCHAIN_FILE}\"") endif() set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "Enable position independent code for all targets" FORCE) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$:Embedded>") # Configure use of recommended build tools if (IMHEX_USE_DEFAULT_BUILD_SETTINGS) message(STATUS "Configuring CMake to use recommended build tools...") find_program(CCACHE_PATH ccache) find_program(NINJA_PATH ninja) find_program(LD_LLD_PATH ld.lld) find_program(AR_LLVMLIBS_PATH llvm-ar) find_program(RANLIB_LLVMLIBS_PATH llvm-ranlib) if (CCACHE_PATH) set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PATH}) set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PATH}) else () message(WARNING "ccache not found!") endif () if (AR_LLVMLIBS_PATH) set(CMAKE_AR ${AR_LLVMLIBS_PATH}) else () message(WARNING "llvm-ar not found, using default ar!") endif () if (RANLIB_LLVMLIBS_PATH) set(CMAKE_RANLIB ${RANLIB_LLVMLIBS_PATH}) else () message(WARNING "llvm-ranlib not found, using default ranlib!") endif () if (LD_LLD_PATH) set(CMAKE_LINKER ${LD_LLD_PATH}) if (NOT XCODE AND NOT MSVC) add_link_options("-fuse-ld=lld") add_link_options("-fuse-ld=lld") endif() else () message(WARNING "lld not found, using default linker!") endif () if (NINJA_PATH) set(CMAKE_GENERATOR Ninja) else () message(WARNING "ninja not found, using default generator!") endif () endif() endmacro() function(configureProject) # Enable C and C++ languages enable_language(C CXX) if (XCODE) # Support Xcode's multi configuration paradigm by placing built artifacts into separate directories set(IMHEX_MAIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Configs/$" PARENT_SCOPE) else() set(IMHEX_MAIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" PARENT_SCOPE) endif() # Enable LTO if desired and supported if (IMHEX_ENABLE_LTO) include(CheckIPOSupported) check_ipo_supported(RESULT result OUTPUT output_error) if (result OR WIN32) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION $<$:ON>) message(STATUS "LTO enabled!") else () message(WARNING "LTO is not supported: ${output_error}") endif () endif () endfunction() macro(setDefaultBuiltTypeIfUnset) if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Using RelWithDebInfo build type as it was left unset" FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "RelWithDebInfo") endif() endmacro() function(loadVersion version plain_version) set(VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/VERSION") set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${VERSION_FILE}) file(READ "${VERSION_FILE}" read_version) string(STRIP ${read_version} read_version) string(REPLACE ".WIP" "" read_version_plain ${read_version}) set(${version} ${read_version} PARENT_SCOPE) set(${plain_version} ${read_version_plain} PARENT_SCOPE) if (read_version MATCHES ".+\.WIP") set(IMHEX_PATTERNS_PULL_MASTER ON PARENT_SCOPE) endif() endfunction() function(detectBadClone) if (IMHEX_IGNORE_BAD_CLONE) return() endif() file (GLOB EXTERNAL_DIRS "lib/external/*" "lib/third_party/*") foreach (EXTERNAL_DIR ${EXTERNAL_DIRS}) if(NOT IS_DIRECTORY "${EXTERNAL_DIR}") continue() endif() file(GLOB_RECURSE RESULT "${EXTERNAL_DIR}/*") list(LENGTH RESULT ENTRY_COUNT) if(ENTRY_COUNT LESS_EQUAL 1) message(FATAL_ERROR "External dependency ${EXTERNAL_DIR} is empty!\nMake sure to correctly clone ImHex using the --recurse-submodules git option or initialize the submodules manually.") endif() endforeach () endfunction() function(verifyCompiler) if (IMHEX_IGNORE_BAD_COMPILER) return() endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.0.0") message(FATAL_ERROR "ImHex requires GCC 12.0.0 or newer. Please use the latest GCC version.") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "17.0.0") message(FATAL_ERROR "ImHex requires Clang 17.0.0 or newer. Please use the latest Clang version.") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") elseif (NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) message(FATAL_ERROR "ImHex can only be compiled with GCC or Clang. ${CMAKE_CXX_COMPILER_ID} is not supported.") endif() endfunction() macro(detectBundledPlugins) file(GLOB PLUGINS_DIRS "plugins/*") if (IMHEX_INCLUDE_PLUGINS) set(PLUGINS ${IMHEX_INCLUDE_PLUGINS}) else() foreach(PLUGIN_DIR ${PLUGINS_DIRS}) if (EXISTS "${PLUGIN_DIR}/CMakeLists.txt") get_filename_component(PLUGIN_NAME ${PLUGIN_DIR} NAME) if (NOT (${PLUGIN_NAME} IN_LIST IMHEX_EXCLUDE_PLUGINS)) list(APPEND PLUGINS ${PLUGIN_NAME}) endif () endif() endforeach() endif() foreach(PLUGIN_NAME ${PLUGINS}) message(STATUS "Enabled bundled plugin '${PLUGIN_NAME}'") endforeach() if (NOT PLUGINS) message(FATAL_ERROR "No bundled plugins enabled") endif() set(REQUIRED_PLUGINS builtin fonts ui) foreach(PLUGIN ${REQUIRED_PLUGINS}) list(FIND PLUGINS ${PLUGIN} PLUGIN_INDEX) if (PLUGIN_INDEX EQUAL -1) message(FATAL_ERROR "Required plugin '${PLUGIN}' is not enabled!") endif() endforeach() endmacro() macro(setVariableInParent variable value) get_directory_property(hasParent PARENT_DIRECTORY) if (hasParent) set(${variable} "${value}" PARENT_SCOPE) else () set(${variable} "${value}") endif () endmacro() function(downloadImHexPatternsFiles dest) if (NOT IMHEX_OFFLINE_BUILD) if (IMHEX_PATTERNS_PULL_MASTER) set(PATTERNS_BRANCH master) else () set(PATTERNS_BRANCH ImHex-v${IMHEX_VERSION}) endif () set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/ImHex-Patterns") install(CODE "set(PATTERNS_BRANCH \"${PATTERNS_BRANCH}\")") install(CODE "set(imhex_patterns_SOURCE_DIR \"${imhex_patterns_SOURCE_DIR}\")") install(CODE [[ message(STATUS "Downloading ImHex patterns from branch '${PATTERNS_BRANCH}'...") if (NOT EXISTS "${imhex_patterns_SOURCE_DIR}") file(MAKE_DIRECTORY "${imhex_patterns_SOURCE_DIR}") execute_process( COMMAND git clone --recurse-submodules --branch ${PATTERNS_BRANCH} https://github.com/WerWolv/ImHex-Patterns.git "${imhex_patterns_SOURCE_DIR}" COMMAND_ERROR_IS_FATAL ANY ) endif() ]]) else () set(imhex_patterns_SOURCE_DIR "") # Maybe patterns are cloned to a subdirectory if (NOT EXISTS ${imhex_patterns_SOURCE_DIR}) set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ImHex-Patterns") endif() # Or a sibling directory if (NOT EXISTS ${imhex_patterns_SOURCE_DIR}) set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../ImHex-Patterns") endif() endif () install(CODE "set(imhex_patterns_SOURCE_DIR \"${imhex_patterns_SOURCE_DIR}\")") if(XCODE) install(CODE [[ # The Xcode build has multiple configurations, which each need a copy of these files file(GLOB_RECURSE sourceFilePaths LIST_DIRECTORIES NO CONFIGURE_DEPENDS RELATIVE "${imhex_patterns_SOURCE_DIR}" "${imhex_patterns_SOURCE_DIR}/constants/*" "${imhex_patterns_SOURCE_DIR}/encodings/*" "${imhex_patterns_SOURCE_DIR}/includes/*" "${imhex_patterns_SOURCE_DIR}/patterns/*" "${imhex_patterns_SOURCE_DIR}/magic/*" "${imhex_patterns_SOURCE_DIR}/nodes/*" ) list(FILTER sourceFilePaths EXCLUDE REGEX "_schema.json$") foreach(relativePath IN LISTS sourceFilePaths) file(GENERATE OUTPUT "${dest}/${relativePath}" INPUT "${imhex_patterns_SOURCE_DIR}/${relativePath}") endforeach() ]]) else() if (NOT (imhex_patterns_SOURCE_DIR STREQUAL "")) set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns magic nodes) foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL}) install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION "${dest}" PATTERN "**/_schema.json" EXCLUDE) endforeach () endif() endif () endfunction() # Compress debug info. See https://github.com/WerWolv/ImHex/issues/1714 for relevant problem macro(setupDebugCompressionFlag) include(CheckCXXCompilerFlag) include(CheckLinkerFlag) check_cxx_compiler_flag(-gz=zstd ZSTD_AVAILABLE_COMPILER) check_linker_flag(CXX -gz=zstd ZSTD_AVAILABLE_LINKER) check_cxx_compiler_flag(-gz COMPRESS_AVAILABLE_COMPILER) check_linker_flag(CXX -gz COMPRESS_AVAILABLE_LINKER) if (NOT DEBUG_COMPRESSION_FLAG) # Cache variable if (ZSTD_AVAILABLE_COMPILER AND ZSTD_AVAILABLE_LINKER) message("Using Zstd compression for debug info because both compiler and linker support it") set(DEBUG_COMPRESSION_FLAG "-gz=zstd" CACHE STRING "Cache to use for debug info compression") elseif (COMPRESS_AVAILABLE_COMPILER AND COMPRESS_AVAILABLE_LINKER) message("Using default compression for debug info because both compiler and linker support it") set(DEBUG_COMPRESSION_FLAG "-gz" CACHE STRING "Cache to use for debug info compression") else() set(DEBUG_COMPRESSION_FLAG "" CACHE STRING "Cache to use for debug info compression") endif() endif() addCommonFlag(${DEBUG_COMPRESSION_FLAG}) endmacro() macro(setupCompilerFlags target) if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") addCommonFlag("/W4" ${target}) addCommonFlag("/wd4127" ${target}) # conditional expression is constant addCommonFlag("/wd4242" ${target}) # 'identifier': conversion from 'type1' to 'type2', possible loss of data addCommonFlag("/wd4244" ${target}) # 'conversion': conversion from 'type1' to 'type2', possible loss of data addCommonFlag("/wd4267" ${target}) # 'var': conversion from 'size_t' to 'type', possible loss of data addCommonFlag("/wd4305" ${target}) # truncation from 'double' to 'float' addCommonFlag("/wd4996" ${target}) # 'function': was declared deprecated addCommonFlag("/wd5244" ${target}) # 'include' in the purview of module 'module' appears erroneous if (IMHEX_STRICT_WARNINGS) addCommonFlag("/WX" ${target}) endif() elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") addCommonFlag("-Wall" ${target}) addCommonFlag("-Wextra" ${target}) addCommonFlag("-Wpedantic" ${target}) # Define strict compilation flags if (IMHEX_STRICT_WARNINGS) addCommonFlag("-Werror" ${target}) endif() if (UNIX AND NOT APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU") addCommonFlag("-rdynamic" ${target}) endif() addCXXFlag("-fexceptions" ${target}) addCXXFlag("-frtti" ${target}) addCommonFlag("-fno-omit-frame-pointer" ${target}) # Disable some warnings addCCXXFlag("-Wno-array-bounds" ${target}) addCCXXFlag("-Wno-deprecated-declarations" ${target}) addCCXXFlag("-Wno-unknown-pragmas" ${target}) addCXXFlag("-Wno-include-angled-in-module-purview" ${target}) # Enable hardening flags if (IMHEX_BUILD_HARDENING) if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") addCommonFlag("-U_FORTIFY_SOURCE" ${target}) addCommonFlag("-D_FORTIFY_SOURCE=3" ${target}) if (NOT EMSCRIPTEN) addCommonFlag("-fstack-protector-strong" ${target}) endif() endif() endif() endif() if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") if (WIN32) addLinkerFlag("-Wa,mbig-obj" ${target}) endif () endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND APPLE) addCCXXFlag("-Wno-unknown-warning-option" ${target}) # On macOS, when using clang from Homebrew, properly setup the libc++ library path so # it's using the one from Homebrew instead of the system one. execute_process(COMMAND brew --prefix llvm OUTPUT_VARIABLE LLVM_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT LLVM_PREFIX STREQUAL "" AND ${CMAKE_CXX_COMPILER} STREQUAL "${LLVM_PREFIX}/bin/clang++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${LLVM_PREFIX}/lib/c++") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L${LLVM_PREFIX}/lib/c++") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -L${LLVM_PREFIX}/lib/c++") endif() if (CMAKE_BUILD_TYPE STREQUAL "Debug") add_compile_definitions(_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG) endif() endif() if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") addCommonFlag("/bigobj" ${target}) addCFlag("/std:clatest" ${target}) addCXXFlag("/std:c++latest" ${target}) endif() # Disable some warnings for gcc if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") addCCXXFlag("-Wno-restrict" ${target}) addCCXXFlag("-Wno-stringop-overread" ${target}) addCCXXFlag("-Wno-stringop-overflow" ${target}) addCCXXFlag("-Wno-dangling-reference" ${target}) endif() # Define emscripten-specific disabled warnings if (EMSCRIPTEN) addCCXXFlag("-pthread" ${target}) addCCXXFlag("-Wno-dollar-in-identifier-extension" ${target}) addCCXXFlag("-Wno-pthreads-mem-growth" ${target}) endif () if (IMHEX_COMPRESS_DEBUG_INFO) setupDebugCompressionFlag() endif() # Only generate minimal debug information for stacktraces in RelWithDebInfo builds if (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") addCCXXFlag("-g1" ${target}) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # Add flags for debug info in inline functions addCCXXFlag("-gstatement-frontiers" ${target}) addCCXXFlag("-ginline-points" ${target}) endif() endif() endmacro() # uninstall target macro(setUninstallTarget) if(NOT TARGET uninstall) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif() endmacro() macro(addBundledLibraries) # Make sure the build is using vcpkg on Windows and Emscripten, otherwise none of these dependencies will be found if (MSVC OR EMSCRIPTEN) if (NOT (CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")) message(AUTHOR_WARNING "Your current environment probably needs to be setup to use vcpkg, otherwise none of the dependencies will be found!") endif() endif() set(EXTERNAL_LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/lib/external") set(THIRD_PARTY_LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/lib/third_party") set(BUILD_SHARED_LIBS OFF) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/microtar EXCLUDE_FROM_ALL) add_subdirectory(${EXTERNAL_LIBS_FOLDER}/libwolv EXCLUDE_FROM_ALL) set(XDGPP_INCLUDE_DIRS "${THIRD_PARTY_LIBS_FOLDER}/xdgpp") set(FPHSA_NAME_MISMATCHED ON CACHE BOOL "") if(NOT USE_SYSTEM_FMT) set(FMT_INSTALL OFF CACHE BOOL "Disable install targets for libfmt" FORCE) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/fmt EXCLUDE_FROM_ALL) set(FMT_LIBRARIES fmt::fmt-header-only) else() find_package(fmt REQUIRED) set(FMT_LIBRARIES fmt::fmt) endif() if (IMHEX_USE_GTK_FILE_PICKER) set(NFD_PORTAL OFF CACHE BOOL "Use Portals for Linux file dialogs" FORCE) else () set(NFD_PORTAL ON CACHE BOOL "Use GTK for Linux file dialogs" FORCE) endif () if (NOT EMSCRIPTEN) # curl find_package(CURL REQUIRED) # nfd if (NOT USE_SYSTEM_NFD) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/nativefiledialog EXCLUDE_FROM_ALL) set(NFD_LIBRARIES nfd) else() find_package(nfd) set(NFD_LIBRARIES nfd) endif() endif() if(NOT USE_SYSTEM_NLOHMANN_JSON) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/nlohmann_json EXCLUDE_FROM_ALL) set(NLOHMANN_JSON_LIBRARIES nlohmann_json) else() find_package(nlohmann_json 3.10.2 REQUIRED) set(NLOHMANN_JSON_LIBRARIES nlohmann_json::nlohmann_json) endif() if (NOT USE_SYSTEM_LUNASVG) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/lunasvg EXCLUDE_FROM_ALL) set(LUNASVG_LIBRARIES lunasvg) else() find_package(lunasvg REQUIRED) set(LUNASVG_LIBRARIES lunasvg::lunasvg) endif() if (NOT USE_SYSTEM_MD4C) set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Disable md2html executable" FORCE) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/md4c EXCLUDE_FROM_ALL) add_library(md4c_lib INTERFACE) add_library(md4c::md4c ALIAS md4c_lib) target_include_directories(md4c_lib INTERFACE ${THIRD_PARTY_LIBS_FOLDER}/md4c/src) target_link_libraries(md4c_lib INTERFACE md4c) else() find_package(md4c REQUIRED) endif() if (NOT USE_SYSTEM_LLVM) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/llvm-demangle EXCLUDE_FROM_ALL) else() find_package(LLVM REQUIRED Demangle) endif() if (USE_SYSTEM_BOOST) find_package(Boost REQUIRED CONFIG COMPONENTS regex) set(BOOST_LIBRARIES Boost::regex) else() add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/boost ${CMAKE_CURRENT_BINARY_DIR}/boost EXCLUDE_FROM_ALL) set(BOOST_LIBRARIES boost::regex) endif() set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF CACHE BOOL "" FORCE) set(LIBPL_ENABLE_PRECOMPILED_HEADERS ${IMHEX_ENABLE_PRECOMPILED_HEADERS} CACHE BOOL "" FORCE) set(LIBPL_SHARED_LIBRARY OFF CACHE BOOL "" FORCE) add_subdirectory(${EXTERNAL_LIBS_FOLDER}/pattern_language EXCLUDE_FROM_ALL) add_subdirectory(${EXTERNAL_LIBS_FOLDER}/disassembler EXCLUDE_FROM_ALL) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/imgui) if (LIBPL_SHARED_LIBRARY) install( TARGETS libpl DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() if (WIN32) set_target_properties( libpl PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) endif() enableUnityBuild(libpl) find_package(mbedTLS 3.4.0 REQUIRED) find_package(Magic 5.39 REQUIRED) endmacro() function(enableUnityBuild TARGET) if (IMHEX_ENABLE_UNITY_BUILD) set_target_properties(${TARGET} PROPERTIES UNITY_BUILD ON UNITY_BUILD_MODE BATCH) endif () endfunction() function(setSDKPaths) if (WIN32) set(SDK_PATH "./sdk" PARENT_SCOPE) elseif (APPLE) set(SDK_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources/sdk" PARENT_SCOPE) else() set(SDK_PATH "share/imhex/sdk" PARENT_SCOPE) endif() set(SDK_BUILD_PATH "${CMAKE_BINARY_DIR}/sdk" PARENT_SCOPE) endfunction() function(generateSDKDirectory) setSDKPaths() install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/libimhex DESTINATION "${SDK_PATH}/lib" PATTERN "**/source/*" EXCLUDE) install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/trace DESTINATION "${SDK_PATH}/lib" PATTERN "**/source/*" EXCLUDE) install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/external DESTINATION "${SDK_PATH}/lib") install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/imgui DESTINATION "${SDK_PATH}/lib/third_party" PATTERN "**/source/*" EXCLUDE) if (NOT USE_SYSTEM_FMT) install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/fmt DESTINATION "${SDK_PATH}/lib/third_party") endif() if (NOT USE_SYSTEM_NLOHMANN_JSON) install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/nlohmann_json DESTINATION "${SDK_PATH}/lib/third_party") endif() if (NOT USE_SYSTEM_BOOST) install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/boost DESTINATION "${SDK_PATH}/lib/third_party") endif() install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/modules DESTINATION "${SDK_PATH}/cmake") install(FILES ${CMAKE_SOURCE_DIR}/cmake/build_helpers.cmake DESTINATION "${SDK_PATH}/cmake") install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/sdk/ DESTINATION "${SDK_PATH}") install(TARGETS libimhex ARCHIVE DESTINATION "${SDK_PATH}/lib") install(TARGETS tracing ARCHIVE DESTINATION "${SDK_PATH}/lib") install(DIRECTORY ${CMAKE_SOURCE_DIR}/plugins/ui/include DESTINATION "${SDK_PATH}/lib/ui/include") install(FILES ${CMAKE_SOURCE_DIR}/plugins/ui/CMakeLists.txt DESTINATION "${SDK_PATH}/lib/ui/") if (WIN32) install(TARGETS ui ARCHIVE DESTINATION "${SDK_PATH}/lib") endif() install(DIRECTORY ${CMAKE_SOURCE_DIR}/plugins/fonts/include DESTINATION "${SDK_PATH}/lib/fonts/include") install(FILES ${CMAKE_SOURCE_DIR}/plugins/fonts/CMakeLists.txt DESTINATION "${SDK_PATH}/lib/fonts/") if (WIN32) install(TARGETS fonts ARCHIVE DESTINATION "${SDK_PATH}/lib") endif() endfunction() function(addIncludesFromLibrary target library) get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES) target_include_directories(${target} PRIVATE ${library_include_dirs}) endfunction() function(precompileHeaders target includeFolder) if (NOT IMHEX_ENABLE_PRECOMPILED_HEADERS) return() endif() file(GLOB_RECURSE TARGET_INCLUDES "${includeFolder}/**/*.hpp") file(GLOB_RECURSE LIBIMHEX_INCLUDES "${CMAKE_SOURCE_DIR}/lib/libimhex/include/**/*.hpp") set(SYSTEM_INCLUDES ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;") set(INCLUDES "${SYSTEM_INCLUDES};${TARGET_INCLUDES};${LIBIMHEX_INCLUDES}") string(REPLACE ">" "$" INCLUDES "${INCLUDES}") target_precompile_headers(${target} PUBLIC "$<$:${INCLUDES}>" ) endfunction() ================================================ FILE: cmake/cmake_uninstall.cmake.in ================================================ if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") endif() file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif() else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif() endforeach() ================================================ FILE: cmake/ide_helpers.cmake ================================================ option(IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER "Enable choice of compiler for Xcode builds, despite CMake's best efforts" OFF) option(IMHEX_IDE_HELPERS_INTRUSIVE_IDE_TWEAKS "Enable intrusive CMake tweaks to better support IDEs with folder support" OFF) # The CMake infrastructure silently ignores the CMAKE_<>_COMPILER settings when # using the `Xcode` generator. # # A particularly nasty (and potentially only) way of getting around this is to # temporarily lie about the generator being used, while CMake determines and # locks in the compiler to use. # # Needless to say, this is hacky and fragile. Use at your own risk! if (IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER AND CMAKE_GENERATOR STREQUAL "Xcode") set(CMAKE_GENERATOR "Unknown") enable_language(C CXX) set(CMAKE_GENERATOR "Xcode") set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_C_COMPILER}") set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CXX_COMPILER}") if (CLANG) set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_C_COMPILER}") set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_CXX_COMPILER}") endif() # By default Xcode passes a `-index-store-path=<...>` parameter to the compiler # during builds to build code completion indexes. This is not supported by # anything other than AppleClang set(CMAKE_XCODE_ATTRIBUTE_COMPILER_INDEX_STORE_ENABLE "NO") endif() # Generate a launch/build scheme for all targets set(CMAKE_XCODE_GENERATE_SCHEME YES) # Utility function that helps avoid messing with non-standard targets macro(returnIfTargetIsNonTweakable target) get_target_property(targetIsAliased ${target} ALIASED_TARGET) get_target_property(targetIsImported ${target} IMPORTED) if (targetIsAliased OR targetIsImported) return() endif() get_target_property(targetType ${target} TYPE) if (targetType MATCHES "INTERFACE_LIBRARY|UNKNOWN_LIBRARY") return() endif() endmacro() # Targets usually don't specify their private headers, nor group their source files # which results in very spotty coverage by IDEs with folders support # # Unfortunately, CMake does not have a `target_source_group` like construct yet, therefore # we have to play by the limitations of `source_group`. # # A particularly problematic part is that the function must be called within the directoryies # scope for the grouping to take effect. # # See: https://discourse.cmake.org/t/topic/7388 function(tweakTargetForIDESupport target) returnIfTargetIsNonTweakable(${target}) # Don't assume directory structure of third parties get_target_property(targetSourceDir ${target} SOURCE_DIR) if (targetSourceDir MATCHES "third_party") return() endif() # Add headers to target get_target_property(targetSourceDir ${target} SOURCE_DIR) if (targetSourceDir) file(GLOB_RECURSE targetPrivateHeaders CONFIGURE_DEPENDS "${targetSourceDir}/include/*.hpp") target_sources(${target} PRIVATE "${targetPrivateHeaders}") endif() # Organize target sources into directory tree get_target_property(sources ${target} SOURCES) foreach(file IN LISTS sources) get_filename_component(path "${file}" ABSOLUTE) if (NOT path MATCHES "^${targetSourceDir}") continue() endif() source_group(TREE "${targetSourceDir}" PREFIX "Source Tree" FILES "${file}") endforeach() endfunction() if (IMHEX_IDE_HELPERS_INTRUSIVE_IDE_TWEAKS) # See tweakTargetForIDESupport for rationale function(add_library target) _add_library(${target} ${ARGN}) tweakTargetForIDESupport(${target}) endfunction() function(add_executable target) _add_executable(${target} ${ARGN}) tweakTargetForIDESupport(${target}) endfunction() endif() # Adjust target's FOLDER property, which is an IDE only preference function(_tweakTarget target path) get_target_property(targetType ${target} TYPE) if (TARGET generator-${target}) set_target_properties(generator-${target} PROPERTIES FOLDER "romfs/${target}") endif() if (TARGET romfs_file_packer-${target}) set_target_properties(romfs_file_packer-${target} PROPERTIES FOLDER "romfs/${target}") endif() if (TARGET libromfs-${target}) set_target_properties(libromfs-${target} PROPERTIES FOLDER "romfs/${target}") endif() if (${targetType} MATCHES "EXECUTABLE|LIBRARY") set_target_properties(${target} PROPERTIES FOLDER "${path}") endif() endfunction() macro(_tweakTargetsRecursive dir) get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES) foreach(subdir IN LISTS subdirectories) _tweakTargetsRecursive("${subdir}") endforeach() if(${dir} STREQUAL ${CMAKE_SOURCE_DIR}) return() endif() get_property(targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS) file(RELATIVE_PATH rdir ${CMAKE_SOURCE_DIR} "${dir}/..") foreach(target ${targets}) _tweakTarget(${target} "${rdir}") endforeach() endmacro() # Tweak all targets this CMake build is aware about function(tweakTargetsForIDESupport) set_property(GLOBAL PROPERTY USE_FOLDERS ON) _tweakTargetsRecursive("${CMAKE_SOURCE_DIR}") endfunction() ================================================ FILE: cmake/modules/FindBacktrace.cmake ================================================ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindBacktrace ------------- Find provider for `backtrace(3) `__. Checks if OS supports ``backtrace(3)`` via either ``libc`` or custom library. This module defines the following variables: ``Backtrace_HEADER`` The header file needed for ``backtrace(3)``. Cached. Could be forcibly set by user. ``Backtrace_INCLUDE_DIRS`` The include directories needed to use ``backtrace(3)`` header. ``Backtrace_LIBRARIES`` The libraries (linker flags) needed to use ``backtrace(3)``, if any. ``Backtrace_FOUND`` Is set if and only if ``backtrace(3)`` support detected. The following cache variables are also available to set or use: ``Backtrace_LIBRARY`` The external library providing backtrace, if any. ``Backtrace_INCLUDE_DIR`` The directory holding the ``backtrace(3)`` header. Typical usage is to generate of header file using :command:`configure_file` with the contents like the following:: #cmakedefine01 Backtrace_FOUND #if Backtrace_FOUND # include <${Backtrace_HEADER}> #endif And then reference that generated header file in actual source. #]=======================================================================] include(CMakePushCheckState) include(CheckSymbolExists) include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) # List of variables to be provided to find_package_handle_standard_args() set(_Backtrace_STD_ARGS Backtrace_INCLUDE_DIR) if(Backtrace_HEADER) set(_Backtrace_HEADER_TRY "${Backtrace_HEADER}") else(Backtrace_HEADER) set(_Backtrace_HEADER_TRY "execinfo.h") endif(Backtrace_HEADER) find_path(Backtrace_INCLUDE_DIR "${_Backtrace_HEADER_TRY}") set(Backtrace_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR}) if (NOT DEFINED Backtrace_LIBRARY) # First, check if we already have backtrace(), e.g., in libc cmake_push_check_state(RESET) set(CMAKE_REQUIRED_INCLUDES ${Backtrace_INCLUDE_DIRS}) set(CMAKE_REQUIRED_QUIET ${Backtrace_FIND_QUIETLY}) check_symbol_exists("backtrace" "${_Backtrace_HEADER_TRY}" _Backtrace_SYM_FOUND) cmake_pop_check_state() endif() if(_Backtrace_SYM_FOUND) # Avoid repeating the message() call below each time CMake is run. if(NOT Backtrace_FIND_QUIETLY AND NOT DEFINED Backtrace_LIBRARY) message(STATUS "backtrace facility detected in default set of libraries") endif() set(Backtrace_LIBRARY "" CACHE FILEPATH "Library providing backtrace(3), empty for default set of libraries") else() # Check for external library, for non-glibc systems if(Backtrace_INCLUDE_DIR) # OpenBSD has libbacktrace renamed to libexecinfo find_library(Backtrace_LIBRARY "execinfo") else() # respect user wishes set(_Backtrace_HEADER_TRY "backtrace.h") find_path(Backtrace_INCLUDE_DIR ${_Backtrace_HEADER_TRY}) find_library(Backtrace_LIBRARY "backtrace") endif() # Prepend list with library path as it's more common practice set(_Backtrace_STD_ARGS Backtrace_LIBRARY ${_Backtrace_STD_ARGS}) endif() set(Backtrace_LIBRARIES ${Backtrace_LIBRARY}) set(Backtrace_HEADER "${_Backtrace_HEADER_TRY}" CACHE STRING "Header providing backtrace(3) facility") find_package_handle_standard_args(Backtrace FOUND_VAR Backtrace_FOUND REQUIRED_VARS ${_Backtrace_STD_ARGS}) mark_as_advanced(Backtrace_HEADER Backtrace_INCLUDE_DIR Backtrace_LIBRARY) ================================================ FILE: cmake/modules/FindCapstone.cmake ================================================ find_path(CAPSTONE_INCLUDE_DIR capstone.h PATH_SUFFIXES capstone) find_library(CAPSTONE_LIBRARY NAMES capstone) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Capstone DEFAULT_MSG CAPSTONE_LIBRARY CAPSTONE_INCLUDE_DIR) mark_as_advanced(CAPSTONE_INCLUDE_DIR CAPSTONE_LIBRARY) ================================================ FILE: cmake/modules/FindCoreClrEmbed.cmake ================================================ set(CoreClrEmbed_FOUND FALSE) set(CORECLR_ARCH "linux-x64") set(CORECLR_SUBARCH "x64") if (WIN32) set(CORECLR_ARCH "win-x64") endif() if (UNIX) if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") set(CORECLR_ARCH "linux-arm64") set(CORECLR_SUBARCH "arm64") endif() endif() if (APPLE) if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") set(CORECLR_ARCH "osx-arm64") set(CORECLR_SUBARCH "arm64") else() set(CORECLR_ARCH "osx-x64") endif() endif() if (NOT DOTNET_EXECUTABLE) set(DOTNET_EXECUTABLE dotnet) endif () set(CORECLR_VERSION "8.0") execute_process(COMMAND ${DOTNET_EXECUTABLE} "--list-runtimes" OUTPUT_VARIABLE CORECLR_LIST_RUNTIMES_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE) if (CORECLR_LIST_RUNTIMES_OUTPUT STREQUAL "") message(STATUS "Unable to find any .NET runtimes") return() endif () set(_ALL_RUNTIMES ${CORECLR_LIST_RUNTIMES_OUTPUT}) string(REPLACE "\n" ";" _ALL_RUNTIMES_LIST ${_ALL_RUNTIMES}) foreach(X ${_ALL_RUNTIMES_LIST}) string(REGEX MATCH "Microsoft\.NETCore\.App ([0-9]+)\.([0-9]+)\.([a-zA-Z0-9.-]+) [\[](.*)Microsoft\.NETCore\.App[\]]" CORECLR_VERSION_REGEX_MATCH ${X}) set(_RUNTIME_VERSION ${CMAKE_MATCH_1}.${CMAKE_MATCH_2}) if (CMAKE_MATCH_1 AND CMAKE_MATCH_4) if (${_RUNTIME_VERSION} STREQUAL ${CORECLR_VERSION}) set(CORECLR_RUNTIME_VERSION ${_RUNTIME_VERSION}) set(CORECLR_RUNTIME_VERSION_FULL ${CORECLR_VERSION}.${CMAKE_MATCH_3}) set(CORECLR_RUNTIME_ROOT_PATH ${CMAKE_MATCH_4}) message(STATUS "Found matching .NET runtime version '${CORECLR_RUNTIME_VERSION_FULL}' path='${CORECLR_RUNTIME_ROOT_PATH}'") endif() endif() endforeach() if (CORECLR_RUNTIME_ROOT_PATH) get_filename_component(CORECLR_RUNTIME_ROOT_PATH ${CORECLR_RUNTIME_ROOT_PATH} DIRECTORY) endif() set(CoreClrEmbed_ROOT_PATH "${CORECLR_RUNTIME_ROOT_PATH}") file(GLOB _CORECLR_HOST_ARCH_PATH_LIST "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.*-${CORECLR_SUBARCH}") if (_CORECLR_HOST_ARCH_PATH_LIST) foreach(_CORECLR_HOST_ARCH_PATH ${_CORECLR_HOST_ARCH_PATH_LIST}) get_filename_component(_CORECLR_HOST_ARCH_FILENAME ${_CORECLR_HOST_ARCH_PATH} NAME) string(REPLACE "Microsoft.NETCore.App.Host." "" _CORECLR_COMPUTED_ARCH "${_CORECLR_HOST_ARCH_FILENAME}") if (_CORECLR_COMPUTED_ARCH) set(CORECLR_ARCH "${_CORECLR_COMPUTED_ARCH}") break() endif() endforeach() endif() set(CORECLR_HOST_BASE_PATH "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.${CORECLR_ARCH}/${CORECLR_RUNTIME_VERSION_FULL}") file(GLOB _CORECLR_FOUND_PATH ${CORECLR_HOST_BASE_PATH}) if (_CORECLR_FOUND_PATH) set(CORECLR_NETHOST_ROOT "${_CORECLR_FOUND_PATH}/runtimes/${CORECLR_ARCH}/native") endif() find_library(CoreClrEmbed_LIBRARY nethost PATHS ${CORECLR_NETHOST_ROOT} ) find_path(CoreClrEmbed_INCLUDE_DIR nethost.h PATHS ${CORECLR_NETHOST_ROOT} ) find_file(CoreClrEmbed_SHARED_LIBRARY nethost.dll nethost.so libnethost.so nethost.dylib libnethost.dylib PATHS ${CORECLR_NETHOST_ROOT}) if (CoreClrEmbed_INCLUDE_DIR AND CoreClrEmbed_LIBRARY) set(CoreClrEmbed_FOUND TRUE) set(CoreClrEmbed_LIBRARIES "${CoreClrEmbed_LIBRARY}" CACHE STRING "CoreClrEmbed libraries" FORCE) set(CoreClrEmbed_SHARED_LIBRARIES "${CoreClrEmbed_SHARED_LIBRARY}" CACHE STRING "CoreClrEmbed shared libraries" FORCE) set(CoreClrEmbed_INCLUDE_DIRS "${CoreClrEmbed_INCLUDE_DIR}" CACHE STRING "CoreClrEmbed include directories" FORCE) set(CoreClrEmbed_VERSION "${CORECLR_RUNTIME_VERSION_FULL}" CACHE STRING "CoreClrEmbed version" FORCE) endif() ================================================ FILE: cmake/modules/FindGLFW.cmake ================================================ #.rst: # Find GLFW # --------- # # Finds the GLFW library using its cmake config if that exists, otherwise # falls back to finding it manually. This module defines: # # GLFW_FOUND - True if GLFW library is found # GLFW::GLFW - GLFW imported target # # Additionally, in case the config was not found, these variables are defined # for internal usage: # # GLFW_LIBRARY - GLFW library # GLFW_DLL_DEBUG - GLFW debug DLL on Windows, if found # GLFW_DLL_RELEASE - GLFW release DLL on Windows, if found # GLFW_INCLUDE_DIR - Root include dir # # # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, # 2020, 2021, 2022 Vladimír Vondruš # Copyright © 2016 Jonathan Hale # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # GLFW installs cmake package config files which handles dependencies in case # GLFW is built statically. Try to find first, quietly, so it doesn't print # loud messages when it's not found, since that's okay. If the glfw target # already exists, it means we're using it through a CMake subproject -- don't # attempt to find the package in that case. if(NOT TARGET glfw) find_package(glfw3 CONFIG QUIET) endif() # If either a glfw config file was found or we have a subproject, point # GLFW::GLFW to that and exit -- nothing else to do here. if(TARGET glfw) if(NOT TARGET GLFW::GLFW) # Aliases of (global) targets are only supported in CMake 3.11, so we # work around it by this. This is easier than fetching all possible # properties (which are impossible to track of) and then attempting to # rebuild them into a new target. add_library(GLFW::GLFW INTERFACE IMPORTED) set_target_properties(GLFW::GLFW PROPERTIES INTERFACE_LINK_LIBRARIES glfw) endif() # Just to make FPHSA print some meaningful location, nothing else get_target_property(_GLFW_INTERFACE_INCLUDE_DIRECTORIES glfw INTERFACE_INCLUDE_DIRECTORIES) include(FindPackageHandleStandardArgs) find_package_handle_standard_args("GLFW" DEFAULT_MSG _GLFW_INTERFACE_INCLUDE_DIRECTORIES) if(CORRADE_TARGET_WINDOWS) # .dll is in LOCATION, .lib is in IMPLIB. Yay, useful! get_target_property(GLFW_DLL_DEBUG glfw IMPORTED_LOCATION_DEBUG) get_target_property(GLFW_DLL_RELEASE glfw IMPORTED_LOCATION_RELEASE) endif() return() endif() if(CORRADE_TARGET_WINDOWS) if(MSVC) if(MSVC_VERSION VERSION_LESS 1910) set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2015) elseif(MSVC_VERSION VERSION_LESS 1920) set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2017) elseif(MSVC_VERSION VERSION_LESS 1930) set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2019) elseif(MSVC_VERSION VERSION_LESS 1940) set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2022) else() message(FATAL_ERROR "Unsupported MSVC version") endif() elseif(MINGW) set(_GLFW_LIBRARY_PATH_SUFFIX lib-mingw-w64) else() message(FATAL_ERROR "Unsupported compiler") endif() endif() # In case no config file was found, try manually finding the library. Prefer # the glfw3dll as it's a dynamic library. find_library(GLFW_LIBRARY NAMES glfw glfw3dll glfw3 PATH_SUFFIXES ${_GLFW_LIBRARY_PATH_SUFFIX}) if(CORRADE_TARGET_WINDOWS AND GLFW_LIBRARY MATCHES "glfw3dll.(lib|a)$") # TODO: debug? find_file(GLFW_DLL_RELEASE NAMES glfw3.dll PATH_SUFFIXES ${_GLFW_LIBRARY_PATH_SUFFIX}) endif() # Include dir find_path(GLFW_INCLUDE_DIR NAMES GLFW/glfw3.h) include(FindPackageHandleStandardArgs) find_package_handle_standard_args("GLFW" DEFAULT_MSG GLFW_LIBRARY GLFW_INCLUDE_DIR) if(NOT TARGET GLFW::GLFW) add_library(GLFW::GLFW UNKNOWN IMPORTED) # Work around BUGGY framework support on macOS # https://cmake.org/Bug/view.php?id=14105 if(CORRADE_TARGET_APPLE AND GLFW_LIBRARY MATCHES "\\.framework$") set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY}/GLFW) else() set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY}) endif() set_property(TARGET GLFW::GLFW PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GLFW_INCLUDE_DIR}) endif() mark_as_advanced(GLFW_LIBRARY GLFW_INCLUDE_DIR) ================================================ FILE: cmake/modules/FindLZ4.cmake ================================================ find_path(LZ4_INCLUDE_DIR NAMES lz4.h HINTS "${LZ4_INCLUDEDIR}" "${LZ4_HINTS}/include" PATHS /usr/local/include /usr/include ) find_library(LZ4_LIBRARY NAMES lz4 liblz4 HINTS "${LZ4_LIBDIR}" "${LZ4_HINTS}/lib" PATHS /usr/local/lib /usr/lib ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR ) if( LZ4_FOUND ) include( CheckIncludeFile ) include( CMakePushCheckState ) set( LZ4_INCLUDE_DIRS ${LZ4_INCLUDE_DIR} ) set( LZ4_LIBRARIES ${LZ4_LIBRARY} ) cmake_push_check_state() set( CMAKE_REQUIRED_INCLUDES ${LZ4_INCLUDE_DIRS} ) check_include_file( lz4frame.h HAVE_LZ4FRAME_H ) cmake_pop_check_state() if (WIN32) set ( LZ4_DLL_DIR "${LZ4_HINTS}/bin" CACHE PATH "Path to LZ4 DLL" ) file( GLOB _lz4_dll RELATIVE "${LZ4_DLL_DIR}" "${LZ4_DLL_DIR}/lz4*.dll" ) set ( LZ4_DLL ${_lz4_dll} # We're storing filenames only. Should we use STRING instead? CACHE FILEPATH "LZ4 DLL file name" ) file( GLOB _lz4_pdb RELATIVE "${LZ4_DLL_DIR}" "${LZ4_DLL_DIR}/lz4*.pdb" ) set ( LZ4_PDB ${_lz4_pdb} CACHE FILEPATH "LZ4 PDB file name" ) mark_as_advanced( LZ4_DLL_DIR LZ4_DLL LZ4_PDB ) endif() else() set( LZ4_INCLUDE_DIRS ) set( LZ4_LIBRARIES ) endif() mark_as_advanced( LZ4_LIBRARIES LZ4_INCLUDE_DIRS ) add_library( LZ4::lz4 INTERFACE IMPORTED ) set_property( TARGET LZ4::lz4 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LZ4_INCLUDE_DIRS} ) set_property( TARGET LZ4::lz4 PROPERTY INTERFACE_LINK_LIBRARIES ${LZ4_LIBRARIES} ) ================================================ FILE: cmake/modules/FindMagic.cmake ================================================ find_path(LIBMAGIC_INCLUDE_DIR magic.h) find_library(LIBMAGIC_LIBRARY NAMES magic) find_package_handle_standard_args(Magic DEFAULT_MSG LIBMAGIC_LIBRARY LIBMAGIC_INCLUDE_DIR ) mark_as_advanced( LIBMAGIC_INCLUDE_DIR LIBMAGIC_LIBRARY Magic_FOUND ) ================================================ FILE: cmake/modules/FindPackageHandleStandardArgs.cmake ================================================ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPackageHandleStandardArgs ----------------------------- This module provides functions intended to be used in :ref:`Find Modules` implementing :command:`find_package()` calls. .. command:: find_package_handle_standard_args This command handles the ``REQUIRED``, ``QUIET`` and version-related arguments of :command:`find_package`. It also sets the ``_FOUND`` variable. The package is considered found if all variables listed contain valid results, e.g. valid filepaths. There are two signatures: .. code-block:: cmake find_package_handle_standard_args( (DEFAULT_MSG|) ... ) find_package_handle_standard_args( [FOUND_VAR ] [REQUIRED_VARS ...] [VERSION_VAR ] [HANDLE_VERSION_RANGE] [HANDLE_COMPONENTS] [CONFIG_MODE] [NAME_MISMATCHED] [REASON_FAILURE_MESSAGE ] [FAIL_MESSAGE ] ) The ``_FOUND`` variable will be set to ``TRUE`` if all the variables ``...`` are valid and any optional constraints are satisfied, and ``FALSE`` otherwise. A success or failure message may be displayed based on the results and on whether the ``REQUIRED`` and/or ``QUIET`` option was given to the :command:`find_package` call. The options are: ``(DEFAULT_MSG|)`` In the simple signature this specifies the failure message. Use ``DEFAULT_MSG`` to ask for a default message to be computed (recommended). Not valid in the full signature. ``FOUND_VAR `` .. deprecated:: 3.3 Specifies either ``_FOUND`` or ``_FOUND`` as the result variable. This exists only for compatibility with older versions of CMake and is now ignored. Result variables of both names are always set for compatibility. ``REQUIRED_VARS ...`` Specify the variables which are required for this package. These may be named in the generated failure message asking the user to set the missing variable values. Therefore these should typically be cache entries such as ``FOO_LIBRARY`` and not output variables like ``FOO_LIBRARIES``. .. versionchanged:: 3.18 If ``HANDLE_COMPONENTS`` is specified, this option can be omitted. ``VERSION_VAR `` Specify the name of a variable that holds the version of the package that has been found. This version will be checked against the (potentially) specified required version given to the :command:`find_package` call, including its ``EXACT`` option. The default messages include information about the required version and the version which has been actually found, both if the version is ok or not. ``HANDLE_VERSION_RANGE`` .. versionadded:: 3.19 Enable handling of a version range, if one is specified. Without this option, a developer warning will be displayed if a version range is specified. ``HANDLE_COMPONENTS`` Enable handling of package components. In this case, the command will report which components have been found and which are missing, and the ``_FOUND`` variable will be set to ``FALSE`` if any of the required components (i.e. not the ones listed after the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are missing. ``CONFIG_MODE`` Specify that the calling find module is a wrapper around a call to ``find_package( NO_MODULE)``. This implies a ``VERSION_VAR`` value of ``_VERSION``. The command will automatically check whether the package configuration file was found. ``REASON_FAILURE_MESSAGE `` .. versionadded:: 3.16 Specify a custom message of the reason for the failure which will be appended to the default generated message. ``FAIL_MESSAGE `` Specify a custom failure message instead of using the default generated message. Not recommended. ``NAME_MISMATCHED`` .. versionadded:: 3.17 Indicate that the ```` does not match ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a warning, but it may be intentional for usage of the command for components of a larger package. Example for the simple signature: .. code-block:: cmake find_package_handle_standard_args(LibXml2 DEFAULT_MSG LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) The ``LibXml2`` package is considered to be found if both ``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found and ``REQUIRED`` was used, it fails with a :command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was used or not. If it is found, success will be reported, including the content of the first ````. On repeated CMake runs, the same message will not be printed again. .. note:: If ```` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the calling module, a warning that there is a mismatch is given. The ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using the old signature and the ``NAME_MISMATCHED`` argument using the new signature. To avoid forcing the caller to require newer versions of CMake for usage, the variable's value will be used if defined when the ``NAME_MISMATCHED`` argument is not passed for the new signature (but using both is an error).. Example for the full signature: .. code-block:: cmake find_package_handle_standard_args(LibArchive REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR VERSION_VAR LibArchive_VERSION) In this case, the ``LibArchive`` package is considered to be found if both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. Also the version of ``LibArchive`` will be checked by using the version contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, the default messages will be printed. Another example for the full signature: .. code-block:: cmake find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) find_package_handle_standard_args(Automoc4 CONFIG_MODE) In this case, a ``FindAutmoc4.cmake`` module wraps a call to ``find_package(Automoc4 NO_MODULE)`` and adds an additional search directory for ``automoc4``. Then the call to ``find_package_handle_standard_args`` produces a proper success/failure message. .. command:: find_package_check_version .. versionadded:: 3.19 Helper function which can be used to check if a ```` is valid against version-related arguments of :command:`find_package`. .. code-block:: cmake find_package_check_version( [HANDLE_VERSION_RANGE] [RESULT_MESSAGE_VARIABLE ] ) The ```` will hold a boolean value giving the result of the check. The options are: ``HANDLE_VERSION_RANGE`` Enable handling of a version range, if one is specified. Without this option, a developer warning will be displayed if a version range is specified. ``RESULT_MESSAGE_VARIABLE `` Specify a variable to get back a message describing the result of the check. Example for the usage: .. code-block:: cmake find_package_check_version(1.2.3 result HANDLE_VERSION_RANGE RESULT_MESSAGE_VARIABLE reason) if (result) message (STATUS "${reason}") else() message (FATAL_ERROR "${reason}") endif() #]=======================================================================] include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) cmake_policy(PUSH) # numbers and boolean constants cmake_policy (SET CMP0012 NEW) # IN_LIST operator cmake_policy (SET CMP0057 NEW) # internal helper macro macro(_FPHSA_FAILURE_MESSAGE _msg) set (__msg "${_msg}") if (FPHSA_REASON_FAILURE_MESSAGE) string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") endif() if (${_NAME}_FIND_REQUIRED) message(FATAL_ERROR "${__msg}") else () if (NOT ${_NAME}_FIND_QUIETLY) message(STATUS "${__msg}") endif () endif () endmacro() # internal helper macro to generate the failure message when used in CONFIG_MODE: macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: if(${_NAME}_CONFIG) _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") else() # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. # List them all in the error message: if(${_NAME}_CONSIDERED_CONFIGS) set(configsText "") list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) math(EXPR configsCount "${configsCount} - 1") foreach(currentConfigIndex RANGE ${configsCount}) list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) string(APPEND configsText "\n ${filename} (version ${version})") endforeach() if (${_NAME}_NOT_FOUND_MESSAGE) if (FPHSA_REASON_FAILURE_MESSAGE) string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") else() set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") endif() else() string(APPEND configsText "\n") endif() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") else() # Simple case: No Config-file was found at all: _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") endif() endif() endmacro() function(FIND_PACKAGE_CHECK_VERSION version result) cmake_parse_arguments (PARSE_ARGV 2 FPCV "HANDLE_VERSION_RANGE;NO_AUTHOR_WARNING_VERSION_RANGE" "RESULT_MESSAGE_VARIABLE" "") if (FPCV_UNPARSED_ARGUMENTS) message (FATAL_ERROR "find_package_check_version(): ${FPCV_UNPARSED_ARGUMENTS}: unexpected arguments") endif() if ("RESULT_MESSAGE_VARIABLE" IN_LIST FPCV_KEYWORDS_MISSING_VALUES) message (FATAL_ERROR "find_package_check_version(): RESULT_MESSAGE_VARIABLE expects an argument") endif() set (${result} FALSE PARENT_SCOPE) if (FPCV_RESULT_MESSAGE_VARIABLE) unset (${FPCV_RESULT_MESSAGE_VARIABLE} PARENT_SCOPE) endif() if (_CMAKE_FPHSA_PACKAGE_NAME) set (package "${_CMAKE_FPHSA_PACKAGE_NAME}") elseif (CMAKE_FIND_PACKAGE_NAME) set (package "${CMAKE_FIND_PACKAGE_NAME}") else() message (FATAL_ERROR "find_package_check_version(): Cannot be used outside a 'Find Module'") endif() if (NOT FPCV_NO_AUTHOR_WARNING_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE AND NOT FPCV_HANDLE_VERSION_RANGE) message(AUTHOR_WARNING "`find_package()` specify a version range but the option " "HANDLE_VERSION_RANGE` is not passed to `find_package_check_version()`. " "Only the lower endpoint of the range will be used.") endif() set (version_ok FALSE) unset (version_msg) if (FPCV_HANDLE_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE) if ((${package}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" AND version VERSION_GREATER_EQUAL ${package}_FIND_VERSION_MIN) AND ((${package}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND version VERSION_LESS_EQUAL ${package}_FIND_VERSION_MAX) OR (${package}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND version VERSION_LESS ${package}_FIND_VERSION_MAX))) set (version_ok TRUE) set(version_msg "(found suitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\")") else() set(version_msg "Found unsuitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\"") endif() elseif (DEFINED ${package}_FIND_VERSION) if(${package}_FIND_VERSION_EXACT) # exact version required # count the dots in the version string string(REGEX REPLACE "[^.]" "" version_dots "${version}") # add one dot because there is one dot more than there are components string(LENGTH "${version_dots}." version_dots) if (version_dots GREATER ${package}_FIND_VERSION_COUNT) # Because of the C++ implementation of find_package() ${package}_FIND_VERSION_COUNT # is at most 4 here. Therefore a simple lookup table is used. if (${package}_FIND_VERSION_COUNT EQUAL 1) set(version_regex "[^.]*") elseif (${package}_FIND_VERSION_COUNT EQUAL 2) set(version_regex "[^.]*\\.[^.]*") elseif (${package}_FIND_VERSION_COUNT EQUAL 3) set(version_regex "[^.]*\\.[^.]*\\.[^.]*") else() set(version_regex "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") endif() string(REGEX REPLACE "^(${version_regex})\\..*" "\\1" version_head "${version}") if (NOT ${package}_FIND_VERSION VERSION_EQUAL version_head) set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") else () set(version_ok TRUE) set(version_msg "(found suitable exact version \"${_FOUND_VERSION}\")") endif () else () if (NOT ${package}_FIND_VERSION VERSION_EQUAL version) set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") else () set(version_ok TRUE) set(version_msg "(found suitable exact version \"${version}\")") endif () endif () else() # minimum version if (${package}_FIND_VERSION VERSION_GREATER version) set(version_msg "Found unsuitable version \"${version}\", but required is at least \"${${package}_FIND_VERSION}\"") else() set(version_ok TRUE) set(version_msg "(found suitable version \"${version}\", minimum required is \"${${package}_FIND_VERSION}\")") endif() endif() else () set(version_ok TRUE) set(version_msg "(found version \"${version}\")") endif() set (${result} ${version_ok} PARENT_SCOPE) if (FPCV_RESULT_MESSAGE_VARIABLE) set (${FPCV_RESULT_MESSAGE_VARIABLE} "${version_msg}" PARENT_SCOPE) endif() endfunction() function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) # Set up the arguments for `cmake_parse_arguments`. set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED HANDLE_VERSION_RANGE) set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) set(multiValueArgs REQUIRED_VARS) # Check whether we are in 'simple' or 'extended' mode: set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) unset(FPHSA_NAME_MISMATCHED_override) if (DEFINED FPHSA_NAME_MISMATCHED) # If the variable NAME_MISMATCHED variable is set, error if it is passed as # an argument. The former is for old signatures, the latter is for new # signatures. list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx) if (NOT name_mismatched_idx EQUAL "-1") message(FATAL_ERROR "The `NAME_MISMATCHED` argument may only be specified by the argument or " "the variable, not both.") endif () # But use the variable if it is not an argument to avoid forcing minimum # CMake version bumps for calling modules. set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}") endif () if(${INDEX} EQUAL -1) set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) set(FPHSA_REQUIRED_VARS ${ARGN}) set(FPHSA_VERSION_VAR) else() cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) if(FPHSA_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") endif() if(NOT FPHSA_FAIL_MESSAGE) set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") endif() # In config-mode, we rely on the variable _CONFIG, which is set by find_package() # when it successfully found the config-file, including version checking: if(FPHSA_CONFIG_MODE) list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) set(FPHSA_VERSION_VAR ${_NAME}_VERSION) endif() if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS) message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") endif() endif() if (DEFINED FPHSA_NAME_MISMATCHED_override) set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}") endif () if (DEFINED CMAKE_FIND_PACKAGE_NAME AND NOT FPHSA_NAME_MISMATCHED AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME) message(AUTHOR_WARNING "The package name passed to `find_package_handle_standard_args` " "(${_NAME}) does not match the name of the calling package " "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling " "code that expects `find_package` result variables (e.g., `_FOUND`) " "to follow a certain pattern.") endif () if (${_NAME}_FIND_VERSION_RANGE AND NOT FPHSA_HANDLE_VERSION_RANGE) message(AUTHOR_WARNING "`find_package()` specify a version range but the module ${_NAME} does " "not support this capability. Only the lower endpoint of the range " "will be used.") endif() # to propagate package name to FIND_PACKAGE_CHECK_VERSION set(_CMAKE_FPHSA_PACKAGE_NAME "${_NAME}") # now that we collected all arguments, process them if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") endif() if (FPHSA_REQUIRED_VARS) list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) endif() string(TOUPPER ${_NAME} _NAME_UPPER) string(TOLOWER ${_NAME} _NAME_LOWER) if(FPHSA_FOUND_VAR) set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND) set(_FOUND_VAR_MIXED ${_NAME}_FOUND) if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER) set(_FOUND_VAR ${FPHSA_FOUND_VAR}) else() message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.") endif() else() set(_FOUND_VAR ${_NAME_UPPER}_FOUND) endif() # collect all variables which were not found, so they can be printed, so the # user knows better what went wrong (#6375) set(MISSING_VARS "") set(DETAILS "") # check if all passed variables are valid set(FPHSA_FOUND_${_NAME} TRUE) foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) if(NOT ${_CURRENT_VAR}) set(FPHSA_FOUND_${_NAME} FALSE) string(APPEND MISSING_VARS " ${_CURRENT_VAR}") else() string(APPEND DETAILS "[${${_CURRENT_VAR}}]") endif() endforeach() if(FPHSA_FOUND_${_NAME}) set(${_NAME}_FOUND TRUE) set(${_NAME_UPPER}_FOUND TRUE) else() set(${_NAME}_FOUND FALSE) set(${_NAME_UPPER}_FOUND FALSE) endif() # component handling unset(FOUND_COMPONENTS_MSG) unset(MISSING_COMPONENTS_MSG) if(FPHSA_HANDLE_COMPONENTS) foreach(comp ${${_NAME}_FIND_COMPONENTS}) if(${_NAME}_${comp}_FOUND) if(NOT DEFINED FOUND_COMPONENTS_MSG) set(FOUND_COMPONENTS_MSG "found components:") endif() string(APPEND FOUND_COMPONENTS_MSG " ${comp}") else() if(NOT DEFINED MISSING_COMPONENTS_MSG) set(MISSING_COMPONENTS_MSG "missing components:") endif() string(APPEND MISSING_COMPONENTS_MSG " ${comp}") if(${_NAME}_FIND_REQUIRED_${comp}) set(${_NAME}_FOUND FALSE) string(APPEND MISSING_VARS " ${comp}") endif() endif() endforeach() set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") string(APPEND DETAILS "[c${COMPONENT_MSG}]") endif() # version handling: set(VERSION_MSG "") set(VERSION_OK TRUE) # check that the version variable is not empty to avoid emitting a misleading # message (i.e. `Found unsuitable version ""`) if (DEFINED ${_NAME}_FIND_VERSION) if(DEFINED ${FPHSA_VERSION_VAR}) if(NOT "${${FPHSA_VERSION_VAR}}" STREQUAL "") set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) if (FPHSA_HANDLE_VERSION_RANGE) set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE) else() set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE) endif() find_package_check_version ("${_FOUND_VERSION}" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG ${FPCV_HANDLE_VERSION_RANGE}) else() set(VERSION_OK FALSE) endif() endif() if("${${FPHSA_VERSION_VAR}}" STREQUAL "") # if the package was not found, but a version was given, add that to the output: if(${_NAME}_FIND_VERSION_EXACT) set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") elseif (FPHSA_HANDLE_VERSION_RANGE AND ${_NAME}_FIND_VERSION_RANGE) set(VERSION_MSG "(Required is version range \"${${_NAME}_FIND_VERSION_RANGE}\")") else() set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") endif() endif() else () # Check with DEFINED as the found version may be 0. if(DEFINED ${FPHSA_VERSION_VAR}) set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") endif() endif () if(VERSION_OK) string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") else() set(${_NAME}_FOUND FALSE) endif() # print the result: if (${_NAME}_FOUND) FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") else () if(FPHSA_CONFIG_MODE) _FPHSA_HANDLE_FAILURE_CONFIG_MODE() else() if(NOT VERSION_OK) set(RESULT_MSG) if (_FIRST_REQUIRED_VAR) string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}") endif() if (COMPONENT_MSG) if (RESULT_MSG) string (APPEND RESULT_MSG ", ") endif() string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}") endif() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})") else() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") endif() endif() endif () set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) endfunction() cmake_policy(POP) ================================================ FILE: cmake/modules/FindPackageMessage.cmake ================================================ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPackageMessage ------------------ .. code-block:: cmake find_package_message( "message for user" "find result details") This function is intended to be used in FindXXX.cmake modules files. It will print a message once for each unique find result. This is useful for telling the user where a package was found. The first argument specifies the name (XXX) of the package. The second argument specifies the message to display. The third argument lists details about the find result so that if they change the message will be displayed again. The macro also obeys the QUIET argument to the find_package command. Example: .. code-block:: cmake if(X11_FOUND) find_package_message(X11 "Found X11: ${X11_X11_LIB}" "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") else() ... endif() #]=======================================================================] function(find_package_message pkg msg details) # Avoid printing a message repeatedly for the same find result. if(NOT ${pkg}_FIND_QUIETLY) string(REPLACE "\n" "" details "${details}") set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") # The message has not yet been printed. message(STATUS "${msg}") # Save the find details in the cache to avoid printing the same # message again. set("${DETAILS_VAR}" "${details}" CACHE INTERNAL "Details about finding ${pkg}") endif() endif() endfunction() ================================================ FILE: cmake/modules/FindYara.cmake ================================================ find_library(YARA_LIBRARIES NAMES yara) find_file(yara.h YARA_INCLUDE_DIRS) mark_as_advanced(YARA_LIBRARIES YARA_INCLUDE_DIRS) ================================================ FILE: cmake/modules/FindZSTD.cmake ================================================ # Copyright (c) Meta Platforms, Inc. and affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # - Try to find Facebook zstd library # This will define # ZSTD_FOUND # ZSTD_INCLUDE_DIR # ZSTD_LIBRARY # find_path(ZSTD_INCLUDE_DIR NAMES zstd.h) find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd) find_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static) include(SelectLibraryConfigurations) SELECT_LIBRARY_CONFIGURATIONS(ZSTD) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS( ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR ) if (ZSTD_FOUND) message(STATUS "Found Zstd: ${ZSTD_LIBRARY}") endif() mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) add_library(ZSTD::zstd INTERFACE IMPORTED) set_property(TARGET ZSTD::zstd PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIR}) set_property(TARGET ZSTD::zstd PROPERTY INTERFACE_LINK_LIBRARIES ${ZSTD_LIBRARY}) ================================================ FILE: cmake/modules/Findlibssh2.cmake ================================================ find_path(LIBSSH2_INCLUDE_DIR libssh2.h) find_library(LIBSSH2_LIBRARY NAMES ssh2 libssh2) if(LIBSSH2_INCLUDE_DIR) file(STRINGS "${LIBSSH2_INCLUDE_DIR}/libssh2.h" libssh2_version_str REGEX "^#define[\t ]+LIBSSH2_VERSION[\t ]+\"(.*)\"") string(REGEX REPLACE "^.*\"([^\"]+)\"" "\\1" LIBSSH2_VERSION "${libssh2_version_str}") endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LibSSH2 REQUIRED_VARS LIBSSH2_LIBRARY LIBSSH2_INCLUDE_DIR VERSION_VAR LIBSSH2_VERSION) mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY) ================================================ FILE: cmake/modules/FindmbedTLS.cmake ================================================ # - Try to find mbedTLS # Once done this will define # # Read-Only variables # MBEDTLS_FOUND - system has mbedTLS # MBEDTLS_INCLUDE_DIR - the mbedTLS include directory # MBEDTLS_LIBRARY_DIR - the mbedTLS library directory # MBEDTLS_LIBRARIES - Link these to use mbedTLS # MBEDTLS_LIBRARY - path to mbedTLS library # MBEDX509_LIBRARY - path to mbedTLS X.509 library # MBEDCRYPTO_LIBRARY - path to mbedTLS Crypto library # Copyright (c) 2004-2007 Sara Golemon # Copyright (c) 2005,2006 Mikhail Gusarov # Copyright (c) 2006-2007 The Written Word, Inc. # Copyright (c) 2007 Eli Fant # Copyright (c) 2009-2019 Daniel Stenberg # Copyright (C) 2008, 2009 Simon Josefsson # All rights reserved. FIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/version.h) SET(MBEDTLS_FIND_QUIETLY TRUE) FIND_LIBRARY(MBEDTLS_LIBRARY NAMES mbedtls libmbedtls libmbedx509) FIND_LIBRARY(MBEDX509_LIBRARY NAMES mbedx509 libmbedx509) FIND_LIBRARY(MBEDCRYPTO_LIBRARY NAMES mbedcrypto libmbedcrypto) FIND_LIBRARY(TFPSACRYPTO_LIBRARY NAMES libtfpsacrypto tfpsacrypto) IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND (MBEDCRYPTO_LIBRARY OR TFPSACRYPTO_LIBRARY)) SET(MBEDTLS_FOUND TRUE) ENDIF() IF(MBEDTLS_FOUND) # split mbedTLS into -L and -l linker options, so we can set them for pkg-config GET_FILENAME_COMPONENT(MBEDTLS_LIBRARY_DIR ${MBEDTLS_LIBRARY} PATH) GET_FILENAME_COMPONENT(MBEDTLS_LIBRARY_FILE ${MBEDTLS_LIBRARY} NAME_WE) GET_FILENAME_COMPONENT(MBEDX509_LIBRARY_FILE ${MBEDX509_LIBRARY} NAME_WE) GET_FILENAME_COMPONENT(MBEDCRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY} NAME_WE) GET_FILENAME_COMPONENT(TFPSACRYPTO_LIBRARY_FILE ${TFPSACRYPTO_LIBRARY} NAME_WE) STRING(REGEX REPLACE "^lib" "" MBEDTLS_LIBRARY_FILE ${MBEDTLS_LIBRARY_FILE}) STRING(REGEX REPLACE "^lib" "" MBEDX509_LIBRARY_FILE ${MBEDX509_LIBRARY_FILE}) STRING(REGEX REPLACE "^lib" "" MBEDCRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY_FILE}) STRING(REGEX REPLACE "^lib" "" TFPSACRYPTO_LIBRARY_FILE ${TFPSACRYPTO_LIBRARY_FILE}) if (TFPSACRYPTO_LIBRARY) SET(MBEDTLS_CRYPTO_LIBRARY_FILE ${TFPSACRYPTO_LIBRARY_FILE}) elseif (MBEDCRYPTO_LIBRARY) SET(MBEDTLS_CRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY_FILE}) else () MESSAGE(FATAL_ERROR "Could not find mbedTLS Crypto library") endif() if (MSVC) SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY_FILE}.lib ${MBEDX509_LIBRARY_FILE}.lib ${MBEDTLS_CRYPTO_LIBRARY_FILE}.lib) else() SET(MBEDTLS_LIBRARIES "-L${MBEDTLS_LIBRARY_DIR} -l${MBEDTLS_LIBRARY_FILE} -l${MBEDX509_LIBRARY_FILE} -l${MBEDTLS_CRYPTO_LIBRARY_FILE}") endif() IF(NOT MBEDTLS_FIND_QUIETLY) MESSAGE(STATUS "Found mbedTLS:") FILE(READ ${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h MBEDTLSCONTENT) STRING(REGEX MATCH "MBEDTLS_VERSION_STRING +\"[0-9|.]+\"" MBEDTLSMATCH ${MBEDTLSCONTENT}) IF (MBEDTLSMATCH) STRING(REGEX REPLACE "MBEDTLS_VERSION_STRING +\"([0-9|.]+)\"" "\\1" MBEDTLS_VERSION ${MBEDTLSMATCH}) MESSAGE(STATUS " version ${MBEDTLS_VERSION}") ENDIF(MBEDTLSMATCH) MESSAGE(STATUS " TLS: ${MBEDTLS_LIBRARY}") MESSAGE(STATUS " X509: ${MBEDX509_LIBRARY}") MESSAGE(STATUS " Crypto: ${MBEDCRYPTO_LIBRARY}") ENDIF(NOT MBEDTLS_FIND_QUIETLY) ELSE(MBEDTLS_FOUND) IF(mbedTLS_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find mbedTLS") ENDIF(mbedTLS_FIND_REQUIRED) ENDIF(MBEDTLS_FOUND) MARK_AS_ADVANCED( MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARY_DIR MBEDTLS_LIBRARIES MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY ) ================================================ FILE: cmake/modules/ImHexPlugin.cmake ================================================ macro(add_imhex_plugin) setSDKPaths() # Parse arguments set(options LIBRARY_PLUGIN) set(oneValueArgs NAME IMHEX_VERSION) set(multiValueArgs SOURCES INCLUDES LIBRARIES FEATURES) cmake_parse_arguments(IMHEX_PLUGIN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if (IMHEX_PLUGIN_IMHEX_VERSION) message(STATUS "Compiling plugin ${IMHEX_PLUGIN_NAME} for ImHex Version ${IMHEX_PLUGIN_IMHEX_VERSION}") set(IMHEX_VERSION_STRING "${IMHEX_PLUGIN_IMHEX_VERSION}") endif() if (IMHEX_STATIC_LINK_PLUGINS) set(IMHEX_PLUGIN_LIBRARY_TYPE STATIC) target_link_libraries(libimhex PUBLIC ${IMHEX_PLUGIN_NAME}) configure_file(${CMAKE_SOURCE_DIR}/dist/web/plugin-bundle.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/plugin-bundle.cpp @ONLY) target_sources(main PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/plugin-bundle.cpp) set(IMHEX_PLUGIN_SUFFIX ".hexplug") else() if (IMHEX_PLUGIN_LIBRARY_PLUGIN) set(IMHEX_PLUGIN_LIBRARY_TYPE SHARED) set(IMHEX_PLUGIN_SUFFIX ".hexpluglib") else() set(IMHEX_PLUGIN_LIBRARY_TYPE MODULE) set(IMHEX_PLUGIN_SUFFIX ".hexplug") endif() endif() if (IMHEX_PLUGIN_LIBRARY_PLUGIN) install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION "${SDK_PATH}/lib/plugins/${IMHEX_PLUGIN_NAME}") endif() # Define new project for plugin project(${IMHEX_PLUGIN_NAME}) if (IMHEX_PLUGIN_IMPORTED) add_library(${IMHEX_PLUGIN_NAME} SHARED IMPORTED GLOBAL) if (WIN32) set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/${IMHEX_PLUGIN_NAME}${IMHEX_PLUGIN_SUFFIX}" IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/../lib${IMHEX_PLUGIN_NAME}.dll.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include/include") elseif (APPLE) set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../../../../MacOS/plugins/${IMHEX_PLUGIN_NAME}${IMHEX_PLUGIN_SUFFIX}" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include/include") else() set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/${IMHEX_PLUGIN_NAME}${IMHEX_PLUGIN_SUFFIX}" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include/include") endif() else() # Create a new shared library for the plugin source code add_library(${IMHEX_PLUGIN_NAME} ${IMHEX_PLUGIN_LIBRARY_TYPE} ${IMHEX_PLUGIN_SOURCES}) # Add include directories and link libraries target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES}) target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_LIBRARIES}) target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${FMT_LIBRARIES} imgui_all_includes libwolv) addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl) addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl-gen) precompileHeaders(${IMHEX_PLUGIN_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/include") # Add IMHEX_PROJECT_NAME and IMHEX_VERSION define target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}") target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_VERSION="${IMHEX_VERSION_STRING}") target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PLUGIN_NAME=${IMHEX_PLUGIN_NAME}) # Enable required compiler flags enableUnityBuild(${IMHEX_PLUGIN_NAME}) setupCompilerFlags(${IMHEX_PLUGIN_NAME}) addCppCheck(${IMHEX_PLUGIN_NAME}) # Configure build properties set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins" CXX_STANDARD 23 PREFIX "" SUFFIX ${IMHEX_PLUGIN_SUFFIX} ) # Set rpath of plugin libraries to the plugins folder if (WIN32) if (IMHEX_PLUGIN_LIBRARY_PLUGIN) set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) endif() elseif (APPLE) set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES BUILD_RPATH "@executable_path/../Frameworks;@executable_path/plugins") endif() # Setup a romfs for the plugin list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs) set(LIBROMFS_PROJECT_NAME ${IMHEX_PLUGIN_NAME}) add_subdirectory(${IMHEX_BASE_FOLDER}/lib/external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/libromfs) target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE ${LIBROMFS_LIBRARY}) set(FEATURE_DEFINE_CONTENT) if (IMHEX_PLUGIN_FEATURES) list(LENGTH IMHEX_PLUGIN_FEATURES IMHEX_FEATURE_COUNT) math(EXPR IMHEX_FEATURE_COUNT "${IMHEX_FEATURE_COUNT} - 1" OUTPUT_FORMAT DECIMAL) foreach(index RANGE 0 ${IMHEX_FEATURE_COUNT} 2) list(SUBLIST IMHEX_PLUGIN_FEATURES ${index} 2 IMHEX_PLUGIN_FEATURE) list(GET IMHEX_PLUGIN_FEATURE 0 feature_define) list(GET IMHEX_PLUGIN_FEATURE 1 feature_description) string(TOUPPER ${feature_define} feature_define) add_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature_define}=0) set(FEATURE_DEFINE_CONTENT "${FEATURE_DEFINE_CONTENT}{ \"${feature_description}\", IMHEX_FEATURE_ENABLED(${feature_define}) },") endforeach() endif() target_compile_options(${IMHEX_PLUGIN_NAME} PRIVATE -DIMHEX_PLUGIN_FEATURES_CONTENT=${FEATURE_DEFINE_CONTENT}) # Add the new plugin to the main dependency list so it gets built by default if (TARGET imhex_all) add_dependencies(imhex_all ${IMHEX_PLUGIN_NAME}) endif() if (IMHEX_EXTERNAL_PLUGIN_BUILD) install(TARGETS ${IMHEX_PLUGIN_NAME} DESTINATION ".") endif() # Fix rpath if (APPLE) set_target_properties( ${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks;@executable_path/plugins" ) elseif (UNIX) set(PLUGIN_RPATH "") list(APPEND PLUGIN_RPATH "$ORIGIN") if (IMHEX_PLUGIN_ADD_INSTALL_PREFIX_TO_RPATH) list(APPEND PLUGIN_RPATH "${CMAKE_INSTALL_PREFIX}/lib") endif() set_target_properties( ${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH_USE_ORIGIN ON INSTALL_RPATH "${PLUGIN_RPATH}" ) endif() if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt AND IMHEX_ENABLE_UNIT_TESTS AND IMHEX_ENABLE_PLUGIN_TESTS) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests) target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_NAME}_tests) target_compile_definitions(${IMHEX_PLUGIN_NAME}_tests PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}-tests") endif() endif() endmacro() macro(add_romfs_resource input output) if (NOT EXISTS ${input}) message(WARNING "Resource file ${input} does not exist") endif() configure_file(${input} ${CMAKE_CURRENT_BINARY_DIR}/romfs/${output} COPYONLY) list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/romfs) endmacro() macro (enable_plugin_feature feature) string(TOUPPER ${feature} feature) if (NOT (feature IN_LIST IMHEX_PLUGIN_FEATURES)) message(FATAL_ERROR "Feature ${feature} is not enabled for plugin ${IMHEX_PLUGIN_NAME}") endif() remove_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=0) add_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=1) endmacro() ================================================ FILE: cmake/modules/PostprocessBundle.cmake ================================================ # Adapted from the Dolphin project: https://dolphin-emu.org/ # This module can be used in two different ways. # # When invoked as `cmake -P PostprocessBundle.cmake`, it fixes up an # application folder to be standalone. It bundles all required libraries from # the system and fixes up library IDs. Any additional shared libraries, like # plugins, that are found under Contents/MacOS/ will be made standalone as well. # # When called with `include(PostprocessBundle)`, it defines a helper # function `postprocess_bundle` that sets up the command form of the # module as a post-build step. if(CMAKE_GENERATOR) # Being called as include(PostprocessBundle), so define a helper function. set(_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}") function(postprocess_bundle out_target in_target) install(CODE "set(BUNDLE_PATH ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME})") install(CODE "set(CODE_SIGN_CERTIFICATE_ID ${CODE_SIGN_CERTIFICATE_ID})") install(CODE "set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS})") install(SCRIPT ${_POSTPROCESS_BUNDLE_MODULE_LOCATION}) endfunction() return() endif() # IMHEX PATCH BEGIN # The function defined above doesn't keep in mind that if we are cross-compiling to MacOS, APPLE must be 1, # so we force it here (where else would this script be run anyway ? This seems to be MacOS-specific code) SET(APPLE 1) # IMHEX PATCHE END get_filename_component(BUNDLE_PATH "${BUNDLE_PATH}" ABSOLUTE) message(STATUS "Fixing up application bundle: ${BUNDLE_PATH}") # Make sure to fix up any included ImHex plugin. file(GLOB_RECURSE plugins "${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexplug") # BundleUtilities doesn't support DYLD_FALLBACK_LIBRARY_PATH behavior, which # makes it sometimes break on libraries that do weird things with @rpath. Specify # equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625 # is fixed and in our minimum CMake version. set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins" "${BUNDLE_PATH}/Contents/Frameworks") message(STATUS "Fixing up application bundle: ${extra_dirs}") # BundleUtilities is overly verbose, so disable most of its messages #function(message) # if(NOT ARGV MATCHES "^STATUS;") # _message(${ARGV}) # endif() #endfunction() include(BundleUtilities) set(BU_CHMOD_BUNDLE_ITEMS ON) fixup_bundle("${BUNDLE_PATH}" "${plugins}" "${extra_dirs}") if (CODE_SIGN_CERTIFICATE_ID) # Hack around Apple Silicon signing bugs by copying the real app, signing it and moving it back. # IMPORTANT: DON'T USE ${CMAKE_COMMAND} -E copy_directory HERE (this follow symbolic links). execute_process(COMMAND cp -R "${BUNDLE_PATH}" "${BUNDLE_PATH}.temp") execute_process(COMMAND codesign --deep --force --sign "${CODE_SIGN_CERTIFICATE_ID}" "${BUNDLE_PATH}.temp") execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${BUNDLE_PATH}") execute_process(COMMAND ${CMAKE_COMMAND} -E rename "${BUNDLE_PATH}.temp" "${BUNDLE_PATH}") endif() # Add a necessary rpath to the imhex binary get_bundle_main_executable("${BUNDLE_PATH}" IMHEX_EXECUTABLE) file(GLOB_RECURSE plugin_libs "${BUNDLE_PATH}/Contents/MacOS/*.hexpluglib") foreach(plugin_lib ${plugin_libs}) get_filename_component(plugin_lib_name ${plugin_lib} NAME) set(plugin_lib_dest "${BUNDLE_PATH}/Contents/MacOS/plugins/${plugin_lib_name}") configure_file(${plugin_lib} "${plugin_lib_dest}" COPYONLY) message(STATUS "Copying plugin library: ${plugin_lib} to ${plugin_lib_dest}") endforeach () ================================================ FILE: cmake/sdk/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.20) project(ImHexSDK) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "" FORCE) include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_helpers.cmake") set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) include(ImHexPlugin) function(add_subdirectory_if_exists folder) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${folder}/CMakeLists.txt") add_subdirectory("${folder}" EXCLUDE_FROM_ALL) endif() endfunction() set(IMHEX_EXTERNAL_PLUGIN_BUILD ON PARENT_SCOPE) set(IMHEX_EXTERNAL_PLUGIN_BUILD ON) add_custom_target(imhex_all) add_subdirectory(lib/third_party/imgui EXCLUDE_FROM_ALL) set(FMT_INSTALL OFF CACHE BOOL "" FORCE) add_subdirectory_if_exists(lib/third_party/fmt) set(FMT_LIBRARIES fmt::fmt-header-only PARENT_SCOPE) set(FMT_LIBRARIES fmt::fmt-header-only) add_subdirectory_if_exists(lib/third_party/nlohmann_json) set(NLOHMANN_JSON_LIBRARIES nlohmann_json PARENT_SCOPE) set(NLOHMANN_JSON_LIBRARIES nlohmann_json) add_subdirectory_if_exists(lib/third_party/boost) set(BOOST_LIBRARIES boost::regex PARENT_SCOPE) set(BOOST_LIBRARIES boost::regex) add_subdirectory(lib/external/libwolv EXCLUDE_FROM_ALL) set(LIBPL_ENABLE_CLI OFF CACHE BOOL "" FORCE) add_subdirectory(lib/external/pattern_language EXCLUDE_FROM_ALL) set(IMHEX_PLUGIN_IMPORTED ON) add_subdirectory(lib/libimhex) add_subdirectory(lib/trace) add_subdirectory(lib/fonts) add_subdirectory(lib/ui) set(IMHEX_PLUGIN_IMPORTED OFF) if (WIN32) set_target_properties(libimhex PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../libimhex.dll" IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/liblibimhex.dll.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include") set_target_properties(tracing PROPERTIES IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/libtracing.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/trace/include") elseif (APPLE) file(GLOB LIBIMHEX_DYLIB "${CMAKE_CURRENT_SOURCE_DIR}/../../Frameworks/libimhex.*.dylib") set_target_properties(libimhex PROPERTIES IMPORTED_LOCATION "${LIBIMHEX_DYLIB}" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include") set_target_properties(tracing PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../libtracing.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/trace/include") else() set_target_properties(libimhex PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/libimhex.so" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include") set_target_properties(tracing PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libtracing.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/trace/include") endif() ================================================ FILE: cmake/sdk/template/CMakeLists.txt ================================================ # ImHex Plugin Template # ===================== # This is the official CMake template for making your own ImHex plugins # To use this template, copy the file into its own directory and modify it to your needs # For the most part, this is a regular CMake project with some extra functions provided by the ImHex SDK # # [NOTE FOR NON-C++ PLUGINS] # The template is laid out for a C++ plugin, however you can write your plugin in any language you want # and just make the plugin statically link against your code. The only thing that's required is a .cpp file with # the IMHEX_PLUGIN_SETUP() macro used in it. This macro is used to setup the plugin and register it with ImHex # # [CMAKE FUNCTIONS] # add_imhex_plugin(): Registers a new plugin # NAME: The name of the plugin # IMHEX_VERSION: The ImHex version this plugin is compatible with. If unset, the plugin will be loaded on all versions (this may not work though) # SOURCES: Source files of the plugin # INCLUDES: Include directories of the plugin # LIBRARIES: Libraries to link against # FEATURES: Optional features that can be enabled or disabled # LIBRARY_PLUGIN: If set, turns this plugin into a library plugin. Library plugins can be linked against by other plugins # # add_romfs_resource(filePath romfsPath): Adds a file to the romfs of the plugin # The RomFS is a virtual filesystem whose files can be accessed by the plugin using the functions in the `romfs::` namespace # This function is used to add a single file to the romfs. You can however also simply create a `romfs` directory in your plugin directory and place your files and folders in there # filePath: The path to the file on the disk # romfsPath: The path to the file in the romfs # # enable_plugin_feature(feature): Enables a plugin feature # Features are optional parts of the plugin that may or may not be available depending on build settings # When a feature is enabled, `IMHEX_FEATURE_ENABLED(feature)` will be defined to true. Otherwise, it will be defined to false # Use the `IMHEX_PLUGIN_FEATURES` macro in the main plugin file to define names to each feature and have them be listed in the plugin list # feature: The name of the feature to enable cmake_minimum_required(VERSION 3.20) project(ImHexPlugin) # Include the ImHex SDK # For this to work, you need to set the IMHEX_SDK_PATH environment variable to the path of the ImHex SDK # # On Windows, the SDK is next to the ImHex executable # On Linux, the SDK is usually in /usr/share/imhex/sdk but this may vary depending on your distribution # On MacOS, the SDK is located inside of the ImHex.app bundle under ImHex.app/Contents/Resources/sdk if (NOT EXISTS $ENV{IMHEX_SDK_PATH}) message(FATAL_ERROR "The IMHEX_SDK_PATH environment variable is not set") endif() add_subdirectory($ENV{IMHEX_SDK_PATH} ImHexSDK) # Register the plugin # This will configure everything you need to make your plugin work # Modify the arguments to your needs. Right now it defines a plugin called `example_plugin` # with a single source file called `example_plugin.cpp` in the `source` directory # By default you have access to the libimhex library to interact with ImHex # as well as libwolv, libromfs, libfmt and ImGui, but you can link against any libraries you want add_imhex_plugin( NAME example_plugin SOURCES source/example_plugin.cpp ) ================================================ FILE: cmake/sdk/template/source/example_plugin.cpp ================================================ #include // Browse through the headers in lib/libimhex/include/hex/api/ to see what you can do with the API. // Most important ones are the things under imhex_api and content_registry // This is the main entry point of your plugin. The code in the body of this construct will be executed // when ImHex starts up and loads the plugin. // The strings in the header are used to display information about the plugin in the UI. IMHEX_PLUGIN_SETUP("Example Plugin", "Author", "Description") { // Put your init code here } ================================================ FILE: dist/AppImage/AppImageBuilder.yml ================================================ # appimage-builder recipe see https://appimage-builder.readthedocs.io for details version: 1 AppDir: path: .AppDir app_info: id: imhex name: ImHex icon: imhex version: "{{VERSION}}" exec: usr/bin/imhex exec_args: $@ apt: arch: - all - "{{ARCHITECTURE_PACKAGE}}" allow_unauthenticated: true sources: - sourceline: 'deb [arch=amd64] https://us.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse' - sourceline: 'deb [arch=arm64] https://ports.ubuntu.com/ubuntu-ports/ noble main restricted universe multiverse' include: - libgdk-pixbuf2.0-0 - libgdk-pixbuf2.0-common - shared-mime-info - librsvg2-common - libbz2-1.0 - libcap2 - libdbus-1-3 - libfontconfig1 - libgpg-error0 - liblzma5 - libnss-mdns - libpcre3 - libselinux1 - libtinfo6 - libmd4c-dev - libmd4c-html0-dev files: include: - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libLLVM-13.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libOpenGL.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libX11.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXau.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXcomposite.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXcursor.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXdamage.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXdmcp.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXext.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXfixes.so.3" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXi.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXinerama.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXrandr.so.2" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXrender.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXxf86vm.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libatk-1.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libatk-bridge-2.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libatspi.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libblkid.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libbrotlicommon.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libbrotlidec.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libbsd.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libcairo-gobject.so.2" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libcairo.so.2" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libcurl-gnutls.so.4" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libdatrie.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libedit.so.2" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libelf.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libepoxy.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libffi.so.8" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libfontconfig.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libfreetype.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libfribidi.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgcrypt.so.20" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgdk-3.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgdk_pixbuf-2.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgio-2.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libglfw.so.3" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libglib-2.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgmodule-2.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgmp.so.10" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgnutls.so.30" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgobject-2.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgraphite2.so.3" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libharfbuzz.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libhogweed.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libicudata.so.70" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libicuuc.so.70" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libidn2.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libjpeg.so.8" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/liblber-2.5.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libldap-2.5.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/liblz4.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmagic.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmbedcrypto.so.7" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmbedtls.so.14" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmbedx509.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmd.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmount.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libnettle.so.8" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libp11-kit.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpango-1.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpangocairo-1.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpangoft2-1.0.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpcre2-8.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpixman-1.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpng16.so.16" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libsasl2.so.2" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libsensors.so.5" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libstdc++.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libsystemd.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libtasn1.so.6" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libthai.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libunistring.so.2" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libuuid.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libvulkan.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libwayland-client.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libwayland-cursor.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libwayland-egl.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxcb-dri2.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxcb-dri3.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxcb-present.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxcb-sync.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxkbcommon.so.0" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxml2.so.2" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxshmfence.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libzstd.so.1" - "/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmd4c.so" exclude: - usr/share/man - usr/share/doc/*/README.* - usr/share/doc/*/changelog.* - usr/share/doc/*/NEWS.* - usr/share/doc/*/TODO.* runtime: env: APPDIR_LIBRARY_PATH: '$APPDIR/usr/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu:$APPDIR/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu:$APPDIR/usr/lib:$APPDIR/usr/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders' GTK_EXE_PREFIX: $APPDIR/usr GTK_DATA_PREFIX: $APPDIR XDG_DATA_DIRS: '/usr/local/share:/usr/share:$APPDIR/usr/share:$XDG_DATA_DIRS:$APPDIR/usr/local/share:$APPDIR/usr/local/lib:$APPDIR/usr/local/share' XDG_CONFIG_DIRS: '$XDG_CONFIG_DIRS:$APPDIR/usr/local/share' AppImage: arch: "{{ARCHITECTURE_APPIMAGE_BUILDER}}" comp: zstd update-information: gh-releases-zsync|WerWolv|ImHex|latest|imhex-*-{{ARCHITECTURE_FILE_NAME}}.AppImage.zsync file_name: imhex-{{VERSION}}-{{ARCHITECTURE_FILE_NAME}}.AppImage ================================================ FILE: dist/AppImage/Dockerfile ================================================ FROM ubuntu:24.04 as build # Used to invalidate layer cache but not mount cache # See https://github.com/moby/moby/issues/41715#issuecomment-733976493 ARG UNIQUEKEY 1 COPY dist/get_deps_debian.sh /tmp RUN --mount=type=cache,target=/var/apt/cache < # Contributor: Morten Linderud pkgname=imhex-bin pkgver=%version% pkgrel=1 pkgdesc="A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM. " arch=("x86_64") url="https://github.com/WerWolv/ImHex" license=('GPL2') depends=(glfw mbedtls fontconfig freetype2 libglvnd dbus gtk3 curl fmt yara zlib bzip2 xz zstd libssh2) makedepends=(git nlohmann-json) provides=(imhex) conflicts=(imhex) source=("$url/releases/download/v$pkgver/imhex-$pkgver-ArchLinux-x86_64.pkg.tar.zst") md5sums=(SKIP) package() { install -Dm755 "$srcdir/usr/bin/imhex" "$pkgdir/usr/bin/imhex" install -Dm755 "$srcdir/usr/bin/imhex-updater" "$pkgdir/usr/bin/imhex-updater" install -Dm644 "$srcdir/usr/lib/libimhex.so.$pkgver" "$pkgdir/usr/lib/libimhex.so.$pkgver" for plugin in "$srcdir/usr/lib/imhex/plugins/"*.hexplug*; do install -Dm644 "$plugin" "$pkgdir/usr/lib/imhex/plugins/${plugin##*/}" done install -d "$pkgdir/usr/share/imhex" cp -r "$srcdir/usr/share/imhex/"{constants,encodings,includes,magic,patterns} "$pkgdir/usr/share/imhex" cp -r "$srcdir/usr/share/"{applications,licenses,pixmaps,mime} "$pkgdir/usr/share" } ================================================ FILE: dist/DEBIAN/control.in ================================================ Package: ImHex Version: ${PROJECT_VERSION} Section: editors Priority: optional Architecture: amd64 License: GNU GPL-2 Depends: libfontconfig1, libglfw3 | libglfw3-wayland, libmagic1, libmbedtls14, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal, libssh2-1, libmd4c0 Maintainer: WerWolv Description: ImHex Hex Editor A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM. ================================================ FILE: dist/ImHex-9999.ebuild ================================================ # app-editors/ImHex # Copyright 2020 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=7 DESCRIPTION="A hex editor for reverse engineers, programmers, and eyesight" HOMEPAGE="https://github.com/WerWolv/ImHex" SRC_URI="" EGIT_REPO_URI="https://github.com/WerWolv/ImHex.git" inherit git-r3 cmake LICENSE="GPL-2" SLOT="0" KEYWORDS="~amd64" IUSE="" DEPEND="" RDEPEND="${DEPEND} media-libs/glfw sys-apps/file net-libs/mbedtls sys-apps/dbus sys-apps/xdg-desktop-portal sys-libs/zlib app-arch/bzip2 app-arch/lzma app-arch/zstd app-arch/lz4 net-libs/libssh2 dev-libs/md4c " BDEPEND="${DEPEND} dev-cpp/nlohmann_json " ================================================ FILE: dist/cli/imhex.bat ================================================ @echo off start "" "%~dp0..\imhex.exe" %* ================================================ FILE: dist/cli/imhex.sh ================================================ #!/bin/sh script_path=$(readlink -f "$0") script_dir=$(dirname "${script_path}") "${script_dir}/../imhex" "$@" > /dev/null 2>&1 & ================================================ FILE: dist/compiling/docker.md ================================================ For a TLDR of commands see [How to build](#How-to-build) # Introduction The original CI we used (vanilla Github Actions) was great for specifying what steps to execute to build packages. It could even do some custom steps with reusable actions. But it had problem: no local reproducibility. This meant that: - We couldn't test code properly locally, we were dependent on GitHub to do it - If something was wrong and we had to debug the build script, it was *long and painful* because we had to wait for Github runners to finish builds, and couldn't quickly iterate To solve this, we are now trying to move the CI build script to docker containers (so using Dockerfiles) # How to build Commands are available in the [CI](../../.github/workflows/build.yml) and you should prefer copying them from there. But here is a general command that should work for every build we have: ``` docker buildx build . -f --progress plain --build-arg 'JOBS=4' --build-arg 'BUILD_TYPE=Debug' --build-context imhex=$(pwd) --output local ``` where `` should be replaced by the wanted Dockerfile base d on the build you want to do: | Wanted build | Dockerfile path | Target | |--------------|-----------------------------|--------| | MacOS M1 | dist/macOS/arm64.Dockerfile | - | | AppImage | dist/appimage/Dockerfile | - | | Web version | dist/web/Dockerfile | raw | We'll explain this command in the next section # Useful knowledge about Docker builds Docker-based builds work with a Dockerfile. You run the Dockerfile, it builds the package. We are using a base environment (often given to us by dockerhub) (e.g. ubuntu:22.04) which is really just a root filesystem, and we then run shell commands in that env, just like a shell script Docker-based builds have two kind of caches used: - layer cache, which mean that if a layer (instruction) hasn't been changed, and previous layers haven't changed, it will not be run again - a `COPY` layer will be invalidated if one of the file copied has changed - mount cache, which are per-instructions mounts that will be cached and restored in the next run. Mounts on different folders will not collide Docker cache tends to grow very quickly when constantly making changes in the Dockerfile and rebuilding (a.k.a debugging what's going on), you can clear it with something like `docker system prune -a` In the command saw earlier: - `.` is the base folder that the Dockerfile will be allowed to see - `-f ` is to specify the Dockerfile path - `--progress plain` is to allow you to see the output of instructions - `--build-arg =` is to allow to specify arguments to the build (like -DKEY=VALUE in CMake) - `--build-context key=` is to specify folders other than the base folder that the Dockerfile is allowed to see - `--output ` is the path to write the output package to. If not specified, Docker will create an image as the output (probably not what you want) - `--target ` specifies which docker target to build ================================================ FILE: dist/compiling/linux.md ================================================ ### Compiling ImHex on Linux On Linux, ImHex is built through regular GCC (or optionally Clang). 1. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules` 2. Install the dependencies using one of the `dist/get_deps_*.sh` scripts. Choose the one that matches your distro. 3. Build ImHex itself using the following commands: ```sh cd ImHex mkdir -p build cd build CC=gcc-14 CXX=g++-14 \ cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="/usr" \ .. ninja install ``` All paths follow the XDG Base Directories standard, and can thus be modified with the environment variables `XDG_CONFIG_HOME`, `XDG_CONFIG_DIRS`, `XDG_DATA_HOME` and `XDG_DATA_DIRS`. ================================================ FILE: dist/compiling/macos.md ================================================ ### Compiling ImHex on macOS On macOS, ImHex is built through regular GCC and LLVM clang. 1. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules` 2. Install all the dependencies using `brew bundle --file dist/macOS/Brewfile` 3. Build ImHex itself using the following commands: ```sh cd ImHex mkdir -p build cd build CC=$(brew --prefix llvm)/bin/clang \ CXX=$(brew --prefix llvm)/bin/clang++ \ OBJC=$(brew --prefix llvm)/bin/clang \ OBJCXX=$(brew --prefix llvm)/bin/clang++ \ cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="./install" \ -DIMHEX_GENERATE_PACKAGE=ON \ .. ninja install ``` If your MacOS installation doesn't have graphic acceleration, you can check the [MacOS NoGPU guide](./macos_nogpu.md) ================================================ FILE: dist/compiling/macos_nogpu.md ================================================ ### Compiling and running ImHex on macOS without a GPU In order to run ImHex on a macOS installation without a GPU, you need a custom build of GLFW. You can build it this way: Note: only tested on macOS x86 1. `git clone --depth 1 https://github.com/glfw/glfw` 2. `git apply {IMHEX_DIR}/dist/macOS/0001-glfw-SW.patch` (file is [here](../macOS/0001-glfw-SW.patch) in the ImHex repository. [Source](https://github.com/glfw/glfw/issues/2080).) 3. `cmake -G "Ninja" -DBUILD_SHARED_LIBS=ON ..` 4. `ninja install`, or `ninja` and figure out how to make ImHex detect the shared library ================================================ FILE: dist/compiling/windows.md ================================================ ### Compiling ImHex on Windows On Windows, ImHex is built through [msys2 / mingw](https://www.msys2.org/)'s gcc. 1. Download and install msys2 from their [website](https://www.msys2.org/). 2. Open the `MSYS2 MinGW x64` shell 3. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules` 4. Install all the dependencies using `./ImHex/dist/get_deps_msys2.sh` 5. Build ImHex itself using the following commands: ```sh cd ImHex mkdir build cd build cmake -G "Ninja" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="./install" \ -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \ .. ninja install ``` ImHex will look for any extra resources either in various folders directly next to the executable or in `%localappdata%/imhex` For low RAM-usage system, you can use `mingw32-make -j N install` instead, to reduce RAM usage at compile time. Where `N` is amount of jobs you are willling to run at once. Roughly ~1 GB of RAM usage per job. ================================================ FILE: dist/cppcheck.supp ================================================ missingIncludeSystem constParameter unusedFunction preprocessorErrorDirective checkersReport noExplicitConstructor unmatchedSuppression useInitializationList useStlAlgorithm knownConditionTrueFalse internalAstError unsignedPositive variableScope unusedPrivateFunction constParameterCallback *:*/lib/third_party/* *:*/external/pattern_language/external/* *:*/external/disassembler/external/* *:*/lib/libimhex/source/ui/imgui_imhex_extensions.cpp ================================================ FILE: dist/flake.nix ================================================ { description = "ImHex"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; }; outputs = { self, nixpkgs }: let supportedSystems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); in { devShells = forAllSystems (system: let pkgs = import nixpkgs { inherit system; }; in { default = pkgs.mkShell { buildInputs = [ pkgs.cmake pkgs.clang pkgs.lld pkgs.nghttp3 pkgs.pkg-config pkgs.glfw pkgs.fontconfig pkgs.file pkgs.mbedtls pkgs.freetype pkgs.dbus pkgs.gtk3 pkgs.curl pkgs.fmt pkgs.yara pkgs.nlohmann_json pkgs.ninja pkgs.zlib pkgs.bzip2 pkgs.xz pkgs.zstd pkgs.lz4 pkgs.libssh2 pkgs.md4c ]; shellHook = '' export CC=${pkgs.clang}/bin/clang export CXX=${pkgs.clang}/bin/clang++ ''; }; }); }; } ================================================ FILE: dist/flatpak/net.werwolv.ImHex.yaml ================================================ app-id: net.werwolv.ImHex runtime: org.freedesktop.Platform runtime-version: '24.08' sdk: org.freedesktop.Sdk command: imhex rename-desktop-file: imhex.desktop finish-args: - --share=ipc - --share=network - --socket=wayland - --socket=fallback-x11 - --filesystem=host - --device=dri modules: - name: glfw buildsystem: cmake-ninja builddir: true config-opts: - -DBUILD_SHARED_LIBS=ON - -DGLFW_BUILD_EXAMPLES=OFF - -DGLFW_BUILD_TESTS=OFF - -DGLFW_BUILD_DOCS=OFF sources: - type: archive url: https://github.com/glfw/glfw/releases/download/3.4/glfw-3.4.zip sha256: b5ec004b2712fd08e8861dc271428f048775200a2df719ccf575143ba749a3e9 cleanup: - /include - /lib/pkgconfig - /lib/pkgconfig - /lib64/pkgconfig - /lib/cmake - /lib64/cmake - name: mbedtls buildsystem: cmake-ninja config-opts: - -DCMAKE_C_FLAGS=-fPIC - -DENABLE_TESTING=OFF - -DENABLE_PROGRAMS=OFF sources: - type: archive url: https://github.com/ARMmbed/mbedtls/archive/refs/tags/v3.4.0.tar.gz sha256: 1b899f355022e8d02c4d313196a0a16af86c5a692456fa99d302915b8cf0320a cleanup: - /include - /lib/pkgconfig - /lib64/pkgconfig - /lib/cmake - /lib64/cmake - name: fmt buildsystem: cmake-ninja config-opts: - -DBUILD_SHARED_LIBS=ON - -DFMT_TEST=OFF sources: - type: archive url: https://github.com/fmtlib/fmt/releases/download/10.0.0/fmt-10.0.0.zip sha256: 4943cb165f3f587f26da834d3056ee8733c397e024145ca7d2a8a96bb71ac281 cleanup: - /include - /lib/pkgconfig - /lib64/pkgconfig - /lib/cmake - /lib64/cmake - name: yara buildsystem: autotools sources: - type: git url: https://github.com/VirusTotal/yara.git tag: v4.3.1 commit: a6f6ce1d6d74a03c396660db25765f2a794d9e30 cleanup: - /include - /lib/pkgconfig - /lib64/pkgconfig - /lib/cmake - /lib64/cmake - name: libssh2 buildsystem: cmake-ninja config-opts: - -DBUILD_SHARED_LIBS=ON - -DENABLE_ZLIB_COMPRESSION=OFF - -DENABLE_MANUAL=OFF - -DENABLE_EXAMPLES=OFF - -DENABLE_TESTING=OFF sources: - type: git url: https://github.com/libssh2/libssh2.git tag: libssh2-1.11.1 cleanup: - /include - /lib/pkgconfig - /lib64/pkgconfig - /lib/cmake - /lib64/cmake - name: md4c buildsystem: cmake-ninja config-opts: - -DBUILD_SHARED_LIBS=ON - -DMD4C_BUILD_TESTS=OFF - -DMD4C_BUILD_EXAMPLES=OFF - -DMD4C_BUILD_DOCS=OFF sources: - type: git url: https://github.com/mity/md4c.git tag: release-0.5.2 - name: imhex buildsystem: cmake-ninja builddir: true config-opts: - -DUSE_SYSTEM_CURL=ON - -DUSE_SYSTEM_FMT=ON - -DUSE_SYSTEM_YARA=ON - -DIMHEX_OFFLINE_BUILD=ON - -DIMHEX_BUNDLE_PLUGIN_SDK=OFF - -DCMAKE_INSTALL_LIBDIR=lib - -DCMAKE_INSTALL_RPATH='$ORIGIN/../lib:$ORIGIN/../lib64' sources: - type: dir path: ../.. - type: git url: https://github.com/WerWolv/ImHex-Patterns.git tag: master dest: ImHex-Patterns x-checker-data: type: git tag-pattern: ^ImHex-v([\d.]+)$ post-install: - mkdir -p ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps - cp ${FLATPAK_DEST}/share/pixmaps/imhex.svg ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/${FLATPAK_ID}.svg - desktop-file-edit --set-key="Icon" --set-value="${FLATPAK_ID}" "${FLATPAK_DEST}/share/applications/imhex.desktop" ================================================ FILE: dist/fonts/move_private_use_area.py ================================================ from fontTools.ttLib import TTFont from fontTools.ttLib.tables._c_m_a_p import CmapSubtable import argparse # Default PUAs SOURCE_PUA_START = 0xEA00 SOURCE_PUA_END = 0x100F2 TARGET_PUA_START = 0xF0000 def move_pua_glyphs(input_font_path, output_font_path): font = TTFont(input_font_path) cmap_table = font['cmap'] glyph_set = font.getGlyphSet() # Track moved glyphs moved = 0 new_mapping = {} # Collect original mappings in the PUA for cmap in cmap_table.tables: if cmap.isUnicode(): for codepoint, glyph_name in cmap.cmap.items(): if SOURCE_PUA_START <= codepoint <= SOURCE_PUA_END: offset = codepoint - SOURCE_PUA_START new_codepoint = TARGET_PUA_START + offset new_mapping[new_codepoint] = glyph_name moved += 1 if moved == 0: print("No glyphs found in the source Private Use Area.") return # Remove old PUA entries from existing cmap subtables for cmap in cmap_table.tables: if cmap.isUnicode(): cmap.cmap = { cp: gn for cp, gn in cmap.cmap.items() if not (SOURCE_PUA_START <= cp <= SOURCE_PUA_END) } # Create or update a format 12 cmap subtable found_format12 = False for cmap in cmap_table.tables: if cmap.format == 12 and cmap.platformID == 3 and cmap.platEncID in (10, 1): cmap.cmap.update(new_mapping) found_format12 = True break if not found_format12: # Create a new format 12 subtable cmap12 = CmapSubtable.newSubtable(12) cmap12.platformID = 3 cmap12.platEncID = 10 # UCS-4 cmap12.language = 0 cmap12.cmap = new_mapping cmap_table.tables.append(cmap12) print(f"Moved {moved} glyphs from U+{SOURCE_PUA_START:X}–U+{SOURCE_PUA_END:X} to U+{TARGET_PUA_START:X}+") font.save(output_font_path) print(f"Saved modified font to {output_font_path}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Move PUA glyphs in a TTF file to another Unicode range.") parser.add_argument("input", help="Input TTF file path") parser.add_argument("output", help="Output TTF file path") args = parser.parse_args() move_pua_glyphs(args.input, args.output) ================================================ FILE: dist/fonts/ttf_to_header_file.py ================================================ import argparse from fontTools.ttLib import TTFont import os def unicode_to_utf8_escape(codepoint): return ''.join([f'\\x{b:02x}' for b in chr(codepoint).encode('utf-8')]) def format_macro_name(prefix, glyph_name): # Convert names like 'repo-forked' -> 'ICON_VS_REPO_FORKED' return "ICON_" + prefix + "_" + glyph_name.upper().replace('-', '_') def generate_font_header(font_path, output_path, font_macro_name, font_file_macro): font = TTFont(font_path) # Use cmap to get Unicode to glyph mapping codepoint_to_names = {} for table in font["cmap"].tables: if table.isUnicode(): for codepoint, glyph_name in table.cmap.items(): codepoint_to_names.setdefault(codepoint, []).append(glyph_name) if not codepoint_to_names: print("No Unicode-mapped glyphs found in the font.") return # Remove any glyph that is lower than 0xFF codepoint_to_names = {cp: names for cp, names in codepoint_to_names.items() if cp >= 0xFF} min_cp = min(codepoint_to_names) max_cp = max(codepoint_to_names) with open(output_path, "w", encoding="utf-8") as out: out.write("#pragma once\n\n") out.write(f'#define FONT_ICON_FILE_NAME_{font_macro_name} "{font_file_macro}"\n\n') out.write(f"#define ICON_MIN_{font_macro_name} 0x{min_cp:04x}\n") out.write(f"#define ICON_MAX_16_{font_macro_name} 0x{max_cp:04x}\n") out.write(f"#define ICON_MAX_{font_macro_name} 0x{max_cp:04x}\n") written = set() for codepoint in sorted(codepoint_to_names): utf8 = unicode_to_utf8_escape(codepoint) comment = f"// U+{codepoint:04X}" glyph_names = sorted(set(codepoint_to_names[codepoint])) for i, glyph_name in enumerate(glyph_names): macro = format_macro_name(font_macro_name, glyph_name) if macro in written: continue out.write(f"#define {macro} \"{utf8}\"\t{comment}\n") written.add(macro) print(f"Header generated at {output_path}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Generate C header file from TTF glyphs.") parser.add_argument("font", help="Input .ttf font file") parser.add_argument("output", help="Output .h file") parser.add_argument("macro_name", help="Macro prefix") args = parser.parse_args() generate_font_header(args.font, args.output, args.macro_name, os.path.basename(args.font)) ================================================ FILE: dist/gen_release_notes.py ================================================ import subprocess import sys def get_commits(branch: str, start_tag: str, end_tag: str) -> list[str]: try: commits_raw = subprocess.check_output([ "git", "--no-pager", "log", branch, "--no-color", "--pretty=oneline", "--abbrev-commit", f"{start_tag}..{end_tag}"], stderr=subprocess.DEVNULL).decode("UTF-8").split("\n") except: return [] commits = [] for line in commits_raw: commits.append(line[9:]) return commits def main(args: list) -> int: if len(args) != 2: print(f"Usage: {args[0]} prev_minor") return 1 last_minor_version = f"v1.{args[1]}" master_commits = get_commits("master", f"{last_minor_version}.0", "master") for i in range(1, 100): branch_commits = get_commits(f"releases/{last_minor_version}.X", f"{last_minor_version}.0", f"{last_minor_version}.{i}") if len(branch_commits) == 0: break master_commits = [commit for commit in master_commits if commit not in branch_commits] sorted_commits = {} for commit in master_commits: if commit == "": continue try: category, commit_name = commit.split(":", 1) if category not in sorted_commits: sorted_commits[category] = [] sorted_commits[category].append(commit_name) except: print(f"Failed to parse commit: {commit}") for category in sorted_commits: print(f"## {category}\n") for commit in sorted_commits[category]: print(f"- {commit}") print(f"\n") if __name__ == "__main__": exit(main(sys.argv)) ================================================ FILE: dist/get_deps_archlinux.sh ================================================ #!/usr/bin/env sh pacman -S $@ --needed \ cmake \ gcc \ lld \ glfw \ fontconfig \ file \ mbedtls \ freetype2 \ dbus \ gtk3 \ curl \ fmt \ yara \ nlohmann-json \ ninja \ zlib \ bzip2 \ xz \ zstd \ lz4 \ libssh2 \ md4c ================================================ FILE: dist/get_deps_debian.sh ================================================ #!/usr/bin/env sh # Install pkgconf (adds minimum dependencies) only if the equivalent pkf-config is not already installed. if ! which pkg-config then PKGCONF="pkgconf" fi apt install -y \ build-essential \ gcc-14 \ g++-14 \ lld \ ${PKGCONF:-} \ cmake \ ccache \ libglfw3-dev \ libglm-dev \ libmagic-dev \ libmbedtls-dev \ libfontconfig-dev \ libfreetype-dev \ libdbus-1-dev \ libcurl4-gnutls-dev \ libgtk-3-dev \ ninja-build \ zlib1g-dev \ libbz2-dev \ liblzma-dev \ libzstd-dev \ liblz4-dev \ libssh2-1-dev \ libmd4c-dev \ libmd4c-html0-dev ================================================ FILE: dist/get_deps_fedora.sh ================================================ #!/usr/bin/env sh dnf install -y \ cmake \ dbus-devel \ file-devel \ fontconfig-devel \ freetype-devel \ libcurl-devel \ gcc-c++ \ git \ mesa-libGL-devel \ glfw-devel \ lld \ mbedtls-devel \ gtk3-devel \ libzstd-devel \ zlib-devel \ bzip2-devel \ xz-devel \ lz4-devel \ libssh2-devel ================================================ FILE: dist/get_deps_msys2.sh ================================================ #!/usr/bin/env sh pacman -S --needed --noconfirm pactoys unzip git pacboy -S --needed --noconfirm \ gcc:p \ lld:p \ cmake:p \ ccache:p \ glfw:p \ file:p \ curl-winssl:p \ mbedtls:p \ freetype:p \ dlfcn:p \ ninja:p \ capstone:p \ zlib:p \ bzip2:p \ xz:p \ zstd:p \ lz4:p \ libssh2-wincng:p \ md4c:p ================================================ FILE: dist/get_deps_tumbleweed.sh ================================================ #!/usr/bin/env sh zypper install \ cmake \ ninja \ gcc14 \ gcc14-c++ \ fontconfig-devel \ freetype2-devel \ libcurl-devel \ dbus-1-devel \ file-devel \ Mesa-libGL-devel \ libglfw-devel \ mbedtls-devel \ gtk3-devel \ libzstd-devel \ zlib-devel \ bzip3-devel \ xz-devel \ lz4-dev \ libssh2-devel \ md4c-devel ================================================ FILE: dist/imhex.desktop ================================================ [Desktop Entry] Version=1.0 Name=ImHex Comment=ImHex Hex Editor GenericName=Hex Editor Exec=imhex %U Icon=imhex Type=Application StartupNotify=true Categories=Development;IDE; StartupWMClass=imhex Keywords=static-analysis;reverse-engineering;disassembler;disassembly;hacking;forensics;hex-editor;cybersecurity;security;binary-analysis; MimeType=application/vnd.imhex.proj; Actions=NewFile; [Desktop Action NewFile] Exec=imhex --new Name=Create New File ================================================ FILE: dist/imhex.mime.xml ================================================ ImHex Project ================================================ FILE: dist/langtool.py ================================================ #!/usr/bin/env python3 from pathlib import Path import argparse import json # This fixes a CJK full-width character input issue # which makes left halves of deleted characters displayed on screen # pylint: disable=unused-import import re import readline DEFAULT_LANG = "en_US" DEFAULT_LANG_PATH = "plugins/*/romfs/lang/" INVALID_TRANSLATION = "" def main(): parser = argparse.ArgumentParser( prog="langtool", description="ImHex translate tool", ) parser.add_argument( "command", choices=[ "check", "translate", "update", "create", "retranslate", "untranslate", "fmtzh", ], ) parser.add_argument( "-c", "--langdir", default=DEFAULT_LANG_PATH, help="Language folder glob" ) parser.add_argument("-l", "--lang", default="", help="Language to translate") parser.add_argument( "-r", "--reflang", default="", help="Language for reference when translating" ) parser.add_argument( "-k", "--keys", help="Keys to re-translate (only in re/untranslate mode)" ) args = parser.parse_args() command = args.command lang = args.lang print(f"Running in {command} mode") lang_files_glob = f"{lang}.json" if lang != "" else "*.json" lang_folders = set(Path(".").glob(args.langdir)) if len(lang_folders) == 0: print(f"Error: {args.langdir} matches nothing") return 1 for lang_folder in lang_folders: if not lang_folder.is_dir(): print(f"Error: {lang_folder} is not a folder") return 1 default_lang_data = {} default_lang_path = lang_folder / Path(DEFAULT_LANG + ".json") if not default_lang_path.exists(): print( f"Error: Default language file {default_lang_path} does not exist in {lang_folder}" ) return 1 with default_lang_path.open("r", encoding="utf-8") as file: default_lang_data = json.load(file) reference_lang_data = None reference_lang_path = lang_folder / Path(args.reflang + ".json") if reference_lang_path.exists(): with reference_lang_path.open("r", encoding="utf-8") as file: reference_lang_data = json.load(file) if command == "create" and lang != "": lang_file_path = lang_folder / Path(lang + ".json") if lang_file_path.exists(): continue exist_lang_data = None for lang_folder1 in lang_folders: lang_file_path1 = lang_folder1 / Path(lang + ".json") if lang_file_path1.exists(): with lang_file_path1.open("r", encoding="utf-8") as file: exist_lang_data = json.load(file) break print(f"Creating new language file '{lang_file_path}'") with lang_file_path.open("w", encoding="utf-8") as new_lang_file: new_lang_data = { } json.dump(new_lang_data, new_lang_file, indent=4, ensure_ascii=False) lang_files = set(lang_folder.glob(lang_files_glob)) if len(lang_files) == 0: print(f"Warn: Language file for '{lang}' does not exist in '{lang_folder}'") for lang_file_path in lang_files: if ( lang_file_path.stem == f"{DEFAULT_LANG}.json" or lang_file_path.stem == f"{args.reflang}.json" ): continue print(f"\nProcessing '{lang_file_path}'") if not (command == "update" or command == "create"): print("\n----------------------------\n") with lang_file_path.open("r+", encoding="utf-8") as target_lang_file: lang_data = json.load(target_lang_file) for key, value in default_lang_data.items(): has_translation = ( key in lang_data and lang_data[key] != INVALID_TRANSLATION ) if ( has_translation and not ( (command == "retranslate" or command == "untranslate") and re.compile(args.keys).fullmatch(key) ) and not command == "fmtzh" ): continue if command == "check": print( f"Error: Translation {lang_file_path} is missing translation for key '{key}'" ) elif ( command == "translate" or command == "retranslate" or command == "untranslate" ): if command == "untranslate" and not has_translation: continue reference_tranlsation = ( " '%s'" % reference_lang_data[key] if ( reference_lang_data and key in reference_lang_data ) else "" ) print( f"\033[1m'{key}' '{value}'{reference_tranlsation}\033[0m => ", end="", ) if has_translation: translation = lang_data[key] print(f" <= \033[1m'{translation}'\033[0m") print() # for a new line if command == "untranslate": lang_data[key] = INVALID_TRANSLATION continue try: new_value = input("=> ") lang_data[key] = new_value except KeyboardInterrupt: break elif command == "update" or command == "create": lang_data[key] = INVALID_TRANSLATION elif command == "fmtzh": if has_translation: lang_data[key] = fmtzh( lang_data[key] ) keys_to_remove = [] for key, value in lang_data.items(): if key not in default_lang_data: keys_to_remove.append(key) for key in keys_to_remove: lang_data.pop(key) print( f"Removed unused key '{key}' from translation '{lang_file_path}'" ) target_lang_file.seek(0) target_lang_file.truncate() json.dump( lang_data, target_lang_file, indent=4, sort_keys=True, ensure_ascii=False, ) def fmtzh(text: str) -> str: text = re.sub(r"(\.{3}|\.{6})", "……", text) text = text.replace("!", "!") text = re.sub(r"([^\.\na-zA-Z\d])\.$", "\1。", text, flags=re.M) text = text.replace("?", "?") return text if __name__ == "__main__": exit(main()) ================================================ FILE: dist/macOS/0001-glfw-SW.patch ================================================ From 9c8665af4c2e2ce66555c15c05c72027bfdf0cb6 Mon Sep 17 00:00:00 2001 From: iTrooz Date: Mon, 29 Aug 2022 17:29:38 +0200 Subject: [PATCH] Use software rendering on MacOS --- src/nsgl_context.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nsgl_context.m b/src/nsgl_context.m index fc1f7521..e5906575 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -198,7 +198,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, NSOpenGLPixelFormatAttribute attribs[40]; int index = 0; - ADD_ATTRIB(NSOpenGLPFAAccelerated); + ADD_ATTRIB(NSOpenGLPFARendererID);ADD_ATTRIB(kCGLRendererGenericFloatID); ADD_ATTRIB(NSOpenGLPFAClosestPolicy); if (ctxconfig->nsgl.offline) -- 2.37.2 ================================================ FILE: dist/macOS/Brewfile ================================================ brew "mbedtls" brew "nlohmann-json" brew "cmake" brew "ccache" brew "freetype2" brew "libmagic" brew "pkg-config" brew "curl" brew "llvm" brew "glfw" brew "ninja" brew "zlib" brew "xz" brew "bzip2" brew "zstd" brew "libssh2" brew "md4c" ================================================ FILE: dist/macOS/arm64.Dockerfile ================================================ # This base image is also known as "crosscompile". See arm64.crosscompile.Dockerfile FROM ghcr.io/werwolv/macos-crosscompile:6d89b20ac5ebedb6f680f94637591c94cb36f40b as build ENV MACOSX_DEPLOYMENT_TARGET 11.0 # -- DOWNLOADING STUFF # Update vcpkg RUN < net.werwolv.ImHex ImHex Modern REing Hex Editor

ImHex is a modern Hex Editor for Reverse Engineers, Developers, Malware Analysists and Hackers who value their retinas when working at 3 AM once again.

Features:

  • Featureful hex view
  • Custom C++-like pattern language for parsing and highlighting a file's content
  • Data importing and exporting
  • Data inspector allowing interpretation of data as many different types
  • Huge file support with fast and efficient loading
  • Strings search
  • File hashing support
  • Disassembler supporting many different architectures: ARM, x86, PowerPC, MIPS, and more
  • Bookmarks
  • Data analyzer
  • Helpful tools such as an Itanium and MSVC demangler, ASCII table, Regex replacer, mathematical expression evaluator (calculator), hexadecimal color picker and many more
  • Doesn't burn out your retinas when used in late-night sessions
CC0-1.0 GPL-2.0-only WerWolv https://imhex.werwolv.net https://github.com/WerWolv/ImHex/issues https://docs.werwolv.net/imhex https://github.com/sponsors/WerWolv https://imhex.werwolv.net/discord https://github.com/WerWolv/ImHex https://github.com/WerWolv/ImHex/blob/master/CONTRIBUTING.md imhex.desktop https://raw.githubusercontent.com/flathub/net.werwolv.ImHex/master/screenshots/screenshot1.png Using a pattern to parse and highlight different sections of an ELF executable file https://raw.githubusercontent.com/flathub/net.werwolv.ImHex/master/screenshots/screenshot2.png Managing bookmarks, diffing two files and decrypting a region of data using the data preprocessor #babec9 #0f0f0f hey@werwolv.net keyboard pointing
================================================ FILE: dist/rpm/imhex.spec ================================================ %define source_date_epoch_from_changelog 0 Name: imhex Version: VERSION Release: 0%{?dist} Summary: A hex editor for reverse engineers and programmers License: GPL-2.0-only AND Zlib AND MIT AND Apache-2.0 # imhex is gplv2. capstone is custom. # see license dir for full breakdown URL: https://imhex.werwolv.net/ # We need the archive with deps bundled Source0: https://github.com/WerWolv/%{name}/releases/download/v%{version}/Full.Sources.tar.gz#/%{name}-%{version}.tar.gz BuildRequires: cmake BuildRequires: desktop-file-utils BuildRequires: dbus-devel BuildRequires: file-devel BuildRequires: freetype-devel BuildRequires: fmt-devel BuildRequires: gcc-c++ BuildRequires: libappstream-glib BuildRequires: libglvnd-devel BuildRequires: glfw-devel BuildRequires: json-devel BuildRequires: libcurl-devel BuildRequires: libarchive-devel BuildRequires: libzstd-devel BuildRequires: zlib-devel BuildRequires: bzip2-devel BuildRequires: xz-devel BuildRequires: llvm-devel BuildRequires: mbedtls-devel BuildRequires: yara-devel BuildRequires: nativefiledialog-extended-devel BuildRequires: lz4-devel BuildRequires: libssh2-devel %if 0%{?rhel} == 9 BuildRequires: gcc-toolset-14 %endif %if 0%{?fedora} || 0%{?rhel} > 9 BuildRequires: capstone-devel %endif BuildRequires: lunasvg-devel Provides: bundled(gnulib) %if 0%{?rhel} == 10 Provides: bundled(capstone) = 5.0.1 %endif Provides: bundled(imgui) = 1.90.8 Provides: bundled(libromfs) Provides: bundled(microtar) Provides: bundled(libpl) = %{version} Provides: bundled(xdgpp) # working on packaging this, bundling for now as to now delay updates Provides: bundled(miniaudio) = 0.11.11 # [7:02 PM] WerWolv: We're not supporting 32 bit anyways soooo # [11:38 AM] WerWolv: Officially supported are x86_64 and aarch64 ExclusiveArch: x86_64 %{arm64} %description ImHex is a Hex Editor, a tool to display, decode and analyze binary data to reverse engineer their format, extract informations or patch values in them. What makes ImHex special is that it has many advanced features that can often only be found in paid applications. Such features are a completely custom binary template and pattern language to decode and highlight structures in the data, a graphical node-based data processor to pre-process values before they're displayed, a disassembler, diffing support, bookmarks and much much more. At the same time ImHex is completely free and open source under the GPLv2 language. %package devel Summary: Development files for %{name} License: GPL-2.0-only %description devel %{summary} %prep %autosetup -n ImHex -p1 # remove bundled libs we aren't using rm -rf lib/third_party/{curl,fmt,llvm,nlohmann_json,yara} %if 0%{?fedora} || 0%{?rhel} > 9 rm -rf lib/third_party/capstone %endif # rhel 9 doesn't support all of the new appstream metainfo tags %if 0%{?rhel} && 0%{?rhel} < 10 sed -i -e '/url type="vcs-browser"/d' \ -e '/url type="contribute"/d' \ dist/net.werwolv.ImHex.metainfo.xml %endif %build %if 0%{?rhel} == 9 . /opt/rh/gcc-toolset-14/enable %set_build_flags CXXFLAGS+=" -std=gnu++2b" %endif %cmake \ -D CMAKE_BUILD_TYPE=Release \ -D IMHEX_STRIP_RELEASE=OFF \ -D IMHEX_OFFLINE_BUILD=ON \ -D USE_SYSTEM_NLOHMANN_JSON=ON \ -D USE_SYSTEM_FMT=ON \ -D USE_SYSTEM_CURL=ON \ -D USE_SYSTEM_LLVM=ON \ -D USE_SYSTEM_MD4C=OFF \ %if 0%{?fedora} || 0%{?rhel} > 9 -D USE_SYSTEM_CAPSTONE=ON \ %endif -D USE_SYSTEM_LUNASVG=ON \ -D USE_SYSTEM_YARA=ON \ -D USE_SYSTEM_NFD=ON \ -D IMHEX_ENABLE_UNIT_TESTS=ON \ %if 0%{?rhel} -D IMHEX_BUILD_HARDENING=OFF %endif # disable built-in build hardening because it is already # done in rhel buildroots. adding the flags again from # upstream generates build errors %cmake_build %check # build binaries required for tests %cmake_build --target unit_tests %ctest --exclude-regex '(Helpers/StoreAPI|Helpers/TipsAPI|Helpers/ContentAPI)' # Helpers/*API exclude tests that require network access %install %cmake_install desktop-file-validate %{buildroot}%{_datadir}/applications/%{name}.desktop # this is a symlink for the old appdata name that we don't need rm -f %{buildroot}%{_metainfodir}/net.werwolv.ImHex.appdata.xml # AppData appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/net.werwolv.ImHex.metainfo.xml # install licenses %if 0%{?rhel} == 9 cp -a lib/third_party/capstone/LICENSES/LICENSE.TXT %{buildroot}%{_datadir}/licenses/%{name}/capstone-LICENSE %endif cp -a lib/third_party/microtar/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/microtar-LICENSE cp -a lib/third_party/xdgpp/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/xdgpp-LICENSE %files %license %{_datadir}/licenses/%{name}/ %doc README.md %{_bindir}/imhex %{_datadir}/imhex/imhex %{_datadir}/pixmaps/%{name}.* %{_datadir}/applications/%{name}.desktop %{_libdir}/libimhex.so.* %{_libdir}/%{name}/ %{_metainfodir}/net.werwolv.ImHex.metainfo.xml %exclude %{_bindir}/imhex-updater %{_datadir}/mime/packages/%{name}.xml %files devel %{_libdir}/libimhex.so %{_datadir}/%{name}/sdk/ ================================================ FILE: dist/snap/snapcraft.yaml ================================================ name: imhex title: ImHex base: core24 version: ${IMHEX_VERSION_STRING} summary: Hex editor for reverse engineering description: ImHex is a hex editor for reverse engineering, reverse engineering, and analyzing binary files. It provides a powerful and flexible interface for working with binary data, including features like pattern matching, scripting, and a customizable user interface. grade: stable confinement: classic contact: https://github.com/WerWolv/ImHex/discussions issues: https://github.com/WerWolv/ImHex/issues source-code: https://github.com/WerWolv/ImHex website: https://imhex.werwolv.net donation: https://github.com/sponsors/WerWolv license: GPL-2.0-only icon: resources/icon.svg adopt-info: imhex platforms: amd64: arm64: apps: imhex: command: usr/local/bin/imhex desktop: usr/local/share/applications/imhex.desktop environment: LD_LIBRARY_PATH: '$SNAP/usr/local/lib:$SNAP/usr/local/lib/imhex:$SNAP/usr/lib/x86_64-linux-gnu:$SNAP/usr/lib/aarch64-linux-gnu:$LD_LIBRARY_PATH' XDG_DATA_DIRS: '$XDG_DATA_DIRS:$SNAP/usr/local/share:$SNAP/usr/local/lib:$SNAP/usr/local/share' XDG_CONFIG_DIRS: '$XDG_CONFIG_DIRS:$SNAP/usr/local/share' XDG_DATA_HOME: '$XDG_DATA_HOME:$SNAP_DATA' parts: imhex: plugin: cmake source: . build-environment: - CC: /usr/bin/gcc-14 - CXX: /usr/bin/g++-14 cmake-parameters: - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_C_COMPILER_LAUNCHER=${CCACHE} - -DCMAKE_CXX_COMPILER_LAUNCHER=${CCACHE} - -DIMHEX_PATTERNS_PULL_MASTER=ON - -DIMHEX_BUNDLE_PLUGIN_SDK=OFF cmake-generator: Ninja build-packages: - cmake - ninja-build - gcc-14 - g++-14 - git - pkg-config - libglfw3-dev - libmagic-dev - libmbedtls-dev - libfontconfig-dev - libfreetype-dev - libdbus-1-dev - libcurl4-gnutls-dev - libgtk-3-dev - zlib1g-dev - libbz2-dev - liblzma-dev - libzstd-dev - liblz4-dev - libssh2-1-dev - libmd4c-dev - libmd4c-html0-dev stage-packages: - libglfw3 - libmagic1 - libmbedtls14 - libfontconfig1 - libfreetype6 - libdbus-1-3 - libcurl4-gnutls-dev - libgtk-3-0 - zlib1g - libbz2-1.0 - liblzma5 - libzstd1 - liblz4-1 - libssh2-1 prime: - -usr/include/* - -usr/local/include/* - -usr/lib/**/*.a - -usr/local/lib/**/*.a - -usr/lib/**/*.la - -usr/local/lib/**/*.la - -usr/share/doc/* - -usr/share/man/* ================================================ FILE: dist/web/Dockerfile ================================================ FROM emscripten/emsdk:4.0.21 AS build # Used to invalidate layer cache but not mount cache # See https://github.com/moby/moby/issues/41715#issuecomment-733976493 ARG UNIQUEKEY=1 RUN apt update RUN apt install -y git ccache autoconf automake libtool pkg-config ninja-build RUN <> /emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake EOF ENV VCPKG_DEFAULT_BINARY_CACHE=/cache/vcpkg RUN --mount=type=cache,target=/cache < imhex.wasm.size FROM scratch AS raw COPY --from=build [ \ # ImHex \ "/build/imhex.wasm", \ "/build/imhex.wasm.size", \ "/build/imhex.js", \ \ # Static files \ "/build/index.html", \ "/build/style.css", \ "/build/wasm-config.js", \ "/build/enable-threads.js", \ "/build/favicon.ico", \ "/build/icon.svg", \ "/build/manifest.json", \ "/build/robots.txt", \ "/build/sitemap.xml", \ \ # Destination \ "./" \ ] FROM nginx COPY --from=raw . /usr/share/nginx/html RUN chmod -R 755 /usr/share/nginx/html ================================================ FILE: dist/web/Host.Dockerfile ================================================ FROM python:3.12-slim WORKDIR /imhex COPY ./out/ . EXPOSE 9090 CMD [ "python", "/imhex/start_imhex_web.py" ] ================================================ FILE: dist/web/compose.yml ================================================ # docker compose -f dist/web/compose.yml up --build services: imhex_web: image: imhex_web:latest build: context: ../../ # ImHex folder dockerfile: ./dist/web/Dockerfile ports: - 8080:80 ================================================ FILE: dist/web/plugin-bundle.cpp.in ================================================ #include extern "C" void forceLinkPlugin_@IMHEX_PLUGIN_NAME@(); namespace { struct StaticLoad { StaticLoad() { forceLinkPlugin_@IMHEX_PLUGIN_NAME@(); } }; } static StaticLoad staticLoad; ================================================ FILE: dist/web/serve.py ================================================ import http.server import os class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler): def end_headers(self): self.send_header("Cross-Origin-Embedder-Policy", "require-corp") self.send_header("Cross-Origin-Opener-Policy", "same-origin") http.server.SimpleHTTPRequestHandler.end_headers(self) if __name__ == '__main__': os.chdir(".") httpd = http.server.HTTPServer(("0.0.0.0", 9090), MyHttpRequestHandler) print(f"Serving {os.getcwd()} on http://{httpd.server_address[0]}:{httpd.server_address[1]}") httpd.serve_forever() ================================================ FILE: dist/web/source/enable-threads.js ================================================ // NOTE: This file creates a service worker that cross-origin-isolates the page (read more here: https://web.dev/coop-coep/) which allows us to use wasm threads. // Normally you would set the COOP and COEP headers on the server to do this, but Github Pages doesn't allow this, so this is a hack to do that. /* Edited version of: coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */ // From here: https://github.com/gzuidhof/coi-serviceworker if(typeof window === 'undefined') { self.addEventListener("install", () => self.skipWaiting()); self.addEventListener("activate", e => e.waitUntil(self.clients.claim())); async function handleFetch(request) { if(request.cache === "only-if-cached" && request.mode !== "same-origin") { return; } if(request.mode === "no-cors") { // We need to set `credentials` to "omit" for no-cors requests, per this comment: https://bugs.chromium.org/p/chromium/issues/detail?id=1309901#c7 request = new Request(request.url, { cache: request.cache, credentials: "omit", headers: request.headers, integrity: request.integrity, destination: request.destination, keepalive: request.keepalive, method: request.method, mode: request.mode, redirect: request.redirect, referrer: request.referrer, referrerPolicy: request.referrerPolicy, signal: request.signal, }); } let r = await fetch(request).catch(e => console.error(e)); if(r.status === 0) { return r; } const headers = new Headers(r.headers); headers.set("Cross-Origin-Embedder-Policy", "require-corp"); // or: require-corp headers.set("Cross-Origin-Opener-Policy", "same-origin"); return new Response(r.body, { status: r.status, statusText: r.statusText, headers }); } self.addEventListener("fetch", function(e) { e.respondWith(handleFetch(e.request)); // respondWith must be executed synchonously (but can be passed a Promise) }); } else { (async function() { if(window.crossOriginIsolated !== false) return; if (!('serviceWorker' in navigator)) { alert("Your browser doesn't support service workers.\nIf you're using Firefox, you need to not be in a private window.") } let registration = await navigator.serviceWorker.register(window.document.currentScript.src).catch(e => console.error("COOP/COEP Service Worker failed to register:", e)); if(registration) { console.log("COOP/COEP Service Worker registered", registration.scope); registration.addEventListener("updatefound", () => { console.log("Reloading page to make use of updated COOP/COEP Service Worker."); window.location.reload(); }); // If the registration is active, but it's not controlling the page if(registration.active && !navigator.serviceWorker.controller) { console.log("Reloading page to make use of COOP/COEP Service Worker."); window.location.reload(); } } })(); } // Code to deregister: // let registrations = await navigator.serviceWorker.getRegistrations(); // for(let registration of registrations) { // await registration.unregister(); // } ================================================ FILE: dist/web/source/index.html ================================================ ImHex Web - Free Online Hex Editor for Reverse Engineers

A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.

Available both natively and on the web

ImHex runs directly in your web browser with the help of Emscripten and WebAssembly.

Not loading in your Browser? Try the native version

================================================ FILE: dist/web/source/manifest.json ================================================ { "name": "ImHex", "description": "🔍 A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.", "background_color": "#0F0F0F", "theme_color": "#0F0F0F", "categories": [ "education", "productivity", "utilities" ], "icons": [ { "src": "icon.svg", "type": "image/svg", "sizes": "640x640" } ], "start_url": ".", "display": "standalone" } ================================================ FILE: dist/web/source/robots.txt ================================================ User-agent: * Allow: / Sitemap: https://imhex.werwolv.net/sitemap.xml ================================================ FILE: dist/web/source/sitemap.xml ================================================ https://web.imhex.werwolv.net/ 2024-01-02T11:44:00+00:00 1.00 English https://web.imhex.werwolv.net?lang=en-US 2024-01-02T11:44:00+00:00 1.00 Deutsch https://web.imhex.werwolv.net?lang=de-DE 2024-01-02T11:44:00+00:00 1.00 Português https://web.imhex.werwolv.net?lang=pt-BR 2024-01-02T11:44:00+00:00 中国 https://web.imhex.werwolv.net?lang=zh-CN 2024-01-02T11:44:00+00:00 國語 https://web.imhex.werwolv.net?lang=zh-TW 2024-01-02T11:44:00+00:00 日本語 https://web.imhex.werwolv.net?lang=ja-JP 2024-01-02T11:44:00+00:00 한국어 https://web.imhex.werwolv.net?lang=ko-KR 2024-01-02T11:44:00+00:00 Español https://web.imhex.werwolv.net?lang=es-ES 2024-01-02T11:44:00+00:00 Italiano https://web.imhex.werwolv.net?lang=it-IT 2024-01-02T11:44:00+00:00 Русский https://web.imhex.werwolv.net?lang=ru-RU 2024-01-02T11:44:00+00:00 ================================================ FILE: dist/web/source/style.css ================================================ html, body { height: 100%; margin: 0; user-select: none; } body { display: flex; align-items: center; background-color: #121212; overflow: hidden; } .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: none; border: 0 none; image-rendering: smooth; } h1, h2, h5 { color: #F0F0F0; font-size: 20px; font-family: monospace; width: 100%; text-align: center; margin-top: 60px; margin-bottom: 10px; } h2 { margin-top: 15px; font-size: 17px; } h5 { margin-top: 0; font-size: 17px; } #not_working { opacity: 0; } #not_working.visible { opacity: 1; transition: opacity 2s ease; } a { color: #7893ff; text-decoration: none; } a:hover { text-shadow: #3a4677 0 0 10px; } .footer { width: 100%; height: 20px; position: absolute; bottom: 0; text-align: center; color: #F0F0F0; background-color: #0A0A0A; padding: 10px; font-family: monospace; font-size: 15px; display: flex; justify-content: center; align-items: center; flex-direction: row; gap: 10%; } .centered { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 100%; height: 100%; } .lds-ripple { display: inline-block; position: relative; width: 80px; height: 80px; } .lds-ripple div { position: absolute; border: 4px solid #fff; opacity: 1; border-radius: 50%; animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; } .lds-ripple div:nth-child(2) { animation-delay: -0.5s; } @keyframes lds-ripple { 0% { top: 36px; left: 36px; width: 0; height: 0; opacity: 0; } 4.9% { top: 36px; left: 36px; width: 0; height: 0; opacity: 0; } 5% { top: 36px; left: 36px; width: 0; height: 0; opacity: 1; } 100% { top: 0; left: 0; width: 72px; height: 72px; opacity: 0; } } :root { --progress: 0%; } .progress-bar-container { margin: 100px auto; width: 600px; text-align: center; } .progress { padding: 6px; border-radius: 30px; background: rgba(0, 0, 0, 0.25); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25), 0 1px rgba(255, 255, 255, 0.08); } .progress-bar { color: rgba(240, 240, 240, 0.9); height: 18px; border-radius: 30px; font-size: 13px; font-family: monospace; font-weight: bold; text-wrap: avoid; white-space: nowrap; overflow: hidden; background-image: linear-gradient( to bottom, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.0) ); } .progress-moved .progress-bar { width: var(--progress); background-color: #3864cb; } #logo { height: 25%; margin-top: 50px; } .canvas-fixed { position: absolute; top: 0; left: 0; } .imhex-web-canvas { width: 100%; height: 100%; display: block; overflow: hidden; image-rendering: smooth; margin: 0; padding: 0; z-index: 1; } .imhex-web-canvas-wrapper { position: relative; width: 100%; height: 100%; overflow: hidden; background-size: 100% 100%; } ================================================ FILE: dist/web/source/wasm-config.js ================================================ let wasmSize = null; // See comment in dist/web/Dockerfile about imhex.wasm.size fetch("imhex.wasm.size").then(async (resp) => { wasmSize = parseInt((await resp.text()).trim()); console.log(`Real WASM binary size is ${wasmSize} bytes`); }); // Monkeypatch WebAssembly to have a progress bar // inspired from: https://github.com/WordPress/wordpress-playground/pull/46 (but had to be modified) function monkeyPatch(progressFun) { const _instantiateStreaming = WebAssembly.instantiateStreaming; WebAssembly.instantiateStreaming = async (responsePromise, ...args) => { // Do not collect wasm content length here see above let response = await responsePromise const file = response.url.substring( new URL(response.url).origin.length + 1 ); const reportingResponse = new Response( new ReadableStream( { async start(controller) { const reader = response.clone().body.getReader(); let loaded = 0; for (; ;) { const { done, value } = await reader.read(); if (done) { if(wasmSize) progressFun(file, wasmSize); break; } loaded += value.byteLength; progressFun(file, loaded); controller.enqueue(value); } controller.close(); } }, { status: response.status, statusText: response.statusText } ) ); for (const pair of response.headers.entries()) { reportingResponse.headers.set(pair[0], pair[1]); } return _instantiateStreaming(reportingResponse, ...args); } } monkeyPatch((file, done) => { if (!wasmSize) return; if (done > wasmSize) { console.warn(`Downloaded binary size ${done} is larger than expected WASM size ${wasmSize}`); return; } const percent = ((done / wasmSize) * 100).toFixed(0); const mibNow = (done / 1024**2).toFixed(1); const mibTotal = (wasmSize / 1024**2).toFixed(1); let root = document.querySelector(':root'); if (root != null) { root.style.setProperty("--progress", `${percent}%`) let progressBar = document.getElementById("progress-bar-content"); if (progressBar != null) { progressBar.innerHTML = `${percent}%  [${mibNow} MiB / ${mibTotal} MiB]`; } } }); function glfwSetCursorCustom(wnd, shape) { let body = document.getElementsByTagName("body")[0] switch (shape) { case 0x00036001: // GLFW_ARROW_CURSOR body.style.cursor = "default"; break; case 0x00036002: // GLFW_IBEAM_CURSOR body.style.cursor = "text"; break; case 0x00036003: // GLFW_CROSSHAIR_CURSOR body.style.cursor = "crosshair"; break; case 0x00036004: // GLFW_HAND_CURSOR body.style.cursor = "pointer"; break; case 0x00036005: // GLFW_HRESIZE_CURSOR body.style.cursor = "ew-resize"; break; case 0x00036006: // GLFW_VRESIZE_CURSOR body.style.cursor = "ns-resize"; break; default: body.style.cursor = "default"; break; } } function glfwCreateStandardCursorCustom(shape) { return shape } var notWorkingTimer = setTimeout(() => { document.getElementById("not_working").classList.add("visible") }, 5000); var Module = { preRun: () => { ENV.IMHEX_SKIP_SPLASH_SCREEN = "1"; }, postRun: function() { }, onRuntimeInitialized: function() { // Triggered when the wasm module is loaded and ready to use. let loading = document.getElementById("loading"); if (loading != null) document.getElementById("loading").style.display = "none" document.getElementById("canvas").style.display = "initial" clearTimeout(notWorkingTimer); }, print: (function() { })(), printErr: function(text) { }, canvas: (function() { const canvas = document.getElementById('canvas'); canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost, please reload the page'); e.preventDefault(); }, false); // Turn long touches into right-clicks let timer = null; canvas.addEventListener('touchstart', event => { timer = setTimeout(() => { let eventArgs = { bubbles: true, cancelable: true, view: window, screenX: event.touches[0].screenX, screenY: event.touches[0].screenY, clientX: event.touches[0].clientX, clientY: event.touches[0].clientY, button: 2, buttons: 2, relatedTarget: event.target, region: event.region } canvas.dispatchEvent(new MouseEvent('mousedown', eventArgs)); canvas.dispatchEvent(new MouseEvent('mouseup', eventArgs)); }, 400); }); canvas.addEventListener('touchend', event => { if (timer) { clearTimeout(timer); timer = null; } }); if (typeof WebGL2RenderingContext !== 'undefined') { let gl = canvas.getContext('webgl2', { stencil: true }); if (!gl) { console.error('WebGL 2 not available, falling back to WebGL'); gl = canvas.getContext('webgl', { stencil: true }); } if (!gl) { alert('WebGL not available with stencil buffer'); } return canvas; } else { alert('WebGL 2 not supported by this browser'); } })(), setStatus: function(text) { }, totalDependencies: 0, monitorRunDependencies: function(left) { }, instantiateWasm: async function(imports, successCallback) { imports.env.glfwSetCursor = glfwSetCursorCustom imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom let result = await instantiateAsync(null, findWasmBinary(), imports); successCallback(result.instance, result.module) }, arguments: [] }; // Handle passing arguments to the wasm module const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); if (urlParams.has("lang")) { Module["arguments"].push("--language"); Module["arguments"].push(urlParams.get("lang")); } else if (urlParams.has("save-editor")) { Module["arguments"].push("--save-editor"); Module["arguments"].push("gist"); Module["arguments"].push(urlParams.get("save-editor")); } // Prevent some default browser shortcuts from preventing ImHex ones to work document.addEventListener('keydown', e => { if (e.ctrlKey) { if (e.which == 83) e.preventDefault(); } }) ================================================ FILE: lib/libimhex/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) project(libimhex) set(CMAKE_CXX_STANDARD 23) set(LIBIMHEX_SOURCES source/api/imhex_api.cpp source/api/content_registry.cpp source/api/event_manager.cpp source/api/task_manager.cpp source/api/shortcut_manager.cpp source/api/plugin_manager.cpp source/api/project_file_manager.cpp source/api/theme_manager.cpp source/api/layout_manager.cpp source/api/workspace_manager.cpp source/api/achievement_manager.cpp source/api/localization_manager.cpp source/api/tutorial_manager.cpp source/data_processor/attribute.cpp source/data_processor/link.cpp source/data_processor/node.cpp source/helpers/utils.cpp source/helpers/utils_linux.cpp source/helpers/fs.cpp source/helpers/magic.cpp source/helpers/crypto.cpp source/helpers/http_requests.cpp source/helpers/http_requests_native.cpp source/helpers/http_requests_emscripten.cpp source/helpers/opengl.cpp source/helpers/patches.cpp source/helpers/encoding_file.cpp source/helpers/logger.cpp source/helpers/tar.cpp source/helpers/debugging.cpp source/helpers/default_paths.cpp source/helpers/imgui_hooks.cpp source/helpers/semantic_version.cpp source/helpers/keys.cpp source/helpers/udp_server.cpp source/helpers/scaling.cpp source/helpers/binary_pattern.cpp source/test/tests.cpp source/providers/provider.cpp source/providers/cached_provider.cpp source/providers/memory_provider.cpp source/providers/undo/stack.cpp source/ui/imgui_imhex_extensions.cpp source/ui/view.cpp source/ui/popup.cpp source/ui/toast.cpp source/ui/banner.cpp source/mcp/client.cpp source/mcp/server.cpp source/subcommands/subcommands.cpp ) if (APPLE) set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES} source/helpers/utils_macos.m source/helpers/macos_menu.m ) endif () if (IMHEX_EXTERNAL_PLUGIN_BUILD) add_library(libimhex IMPORTED SHARED GLOBAL) set(LIBIMHEX_LIBRARY_TYPE INTERFACE) else() if (IMHEX_STATIC_LINK_PLUGINS) add_library(libimhex STATIC ${LIBIMHEX_SOURCES}) else() add_library(libimhex SHARED ${LIBIMHEX_SOURCES}) endif() if (IMHEX_ENABLE_CXX_MODULES) target_sources(libimhex PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES include/hex.cppm ) endif() set(LIBIMHEX_LIBRARY_TYPE PUBLIC) target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}") endif() addCppCheck(libimhex) if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_BRANCH) set(GIT_COMMIT_HASH_LONG "${IMHEX_COMMIT_HASH_LONG}") set(GIT_BRANCH "${IMHEX_COMMIT_BRANCH}") else() # Get the current working branch execute_process( COMMAND git rev-parse --abbrev-ref HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE RESULT_BRANCH ERROR_QUIET ) execute_process( COMMAND git log -1 --format=%H WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_COMMIT_HASH_LONG OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE RESULT_HASH_LONG ERROR_QUIET ) endif () if (GIT_COMMIT_HASH_LONG STREQUAL "" OR GIT_BRANCH STREQUAL "") message(WARNING "Failed to to determine commit hash/branch") else() addDefineToSource(source/api/imhex_api.cpp "GIT_COMMIT_HASH_LONG=\"${GIT_COMMIT_HASH_LONG}\"") addDefineToSource(source/api/imhex_api.cpp "GIT_BRANCH=\"${GIT_BRANCH}\"") endif () addDefineToSource(source/api/imhex_api.cpp "IMHEX_VERSION=\"${IMHEX_VERSION_STRING}\"") string(TIMESTAMP IMHEX_BUILD_DATE UTC) addDefineToSource(source/api/imhex_api.cpp "IMHEX_BUILD_DATE=\"${IMHEX_BUILD_DATE}\"") enableUnityBuild(libimhex) setupCompilerFlags(libimhex) include(GenerateExportHeader) generate_export_header(libimhex) target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS}) if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD) if (WIN32) set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) if (NOT MSVC) target_link_options(libimhex PRIVATE -Wl,--export-all-symbols) endif() target_link_libraries(libimhex PRIVATE Netapi32.lib) target_compile_definitions(libimhex PRIVATE EXPORT_SYMBOLS=1) elseif (APPLE) find_library(FOUNDATION NAMES Foundation) find_library(USERNOTIFICATIONS NAMES UserNotifications) target_link_libraries(libimhex PUBLIC ${FOUNDATION} ${USERNOTIFICATIONS}) endif () target_link_libraries(libimhex PRIVATE libpl microtar ${NFD_LIBRARIES} magic) target_link_libraries(libimhex PUBLIC libwolv libpl_includes libpl-gen ${IMGUI_LIBRARIES} ${JTHREAD_LIBRARIES}) if (IMHEX_ENABLE_IMGUI_TEST_ENGINE) target_link_libraries(libimhex PUBLIC imgui_test_engine) endif() if (NOT WIN32) target_link_libraries(libimhex PRIVATE dl) endif() if (NOT EMSCRIPTEN) # curl is only used in non-emscripten builds target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} CURL::libcurl) endif() target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_INCLUDE_DIR} ${LIBBACKTRACE_INCLUDE_DIRS} ${MAGIC_INCLUDE_DIRS}) target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARIES}) target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS}) precompileHeaders(libimhex "${CMAKE_CURRENT_SOURCE_DIR}/include") endif() target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${FMT_LIBRARIES} ${LUNASVG_LIBRARIES} ${BOOST_LIBRARIES} tracing) set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE) add_dependencies(imhex_all libimhex) install(FILES "$" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS}) set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set_target_properties(libimhex PROPERTIES PREFIX "") ================================================ FILE: lib/libimhex/LICENSE ================================================ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ================================================ FILE: lib/libimhex/include/hex/api/achievement_manager.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { class AchievementManager; class Achievement { public: explicit Achievement(UnlocalizedString unlocalizedCategory, UnlocalizedString unlocalizedName) : m_unlocalizedCategory(std::move(unlocalizedCategory)), m_unlocalizedName(std::move(unlocalizedName)) { } /** * @brief Returns the unlocalized name of the achievement * @return Unlocalized name of the achievement */ [[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { return m_unlocalizedName; } /** * @brief Returns the unlocalized category of the achievement * @return Unlocalized category of the achievement */ [[nodiscard]] const UnlocalizedString &getUnlocalizedCategory() const { return m_unlocalizedCategory; } /** * @brief Returns whether the achievement is unlocked * @return Whether the achievement is unlocked */ [[nodiscard]] bool isUnlocked() const { return m_progress == m_maxProgress; } /** * @brief Sets the description of the achievement * @param description Description of the achievement * @return Reference to the achievement */ Achievement& setDescription(std::string description) { m_unlocalizedDescription = std::move(description); return *this; } /** * @brief Adds a requirement to the achievement. The achievement will only be unlockable if all requirements are unlocked. * @param requirement Unlocalized name of the requirement * @return Reference to the achievement */ Achievement& addRequirement(std::string requirement) { m_requirements.emplace_back(std::move(requirement)); return *this; } /** * @brief Adds a visibility requirement to the achievement. The achievement will only be visible if all requirements are unlocked. * @param requirement Unlocalized name of the requirement * @return Reference to the achievement */ Achievement& addVisibilityRequirement(std::string requirement) { m_visibilityRequirements.emplace_back(std::move(requirement)); return *this; } /** * @brief Marks the achievement as blacked. Blacked achievements are visible but their name and description are hidden. * @return Reference to the achievement */ Achievement& setBlacked() { m_blacked = true; return *this; } /** * @brief Marks the achievement as invisible. Invisible achievements are not visible at all. * @return Reference to the achievement */ Achievement& setInvisible() { m_invisible = true; return *this; } /** * @brief Returns whether the achievement is blacked * @return Whether the achievement is blacked */ [[nodiscard]] bool isBlacked() const { return m_blacked; } /** * @brief Returns whether the achievement is invisible * @return Whether the achievement is invisible */ [[nodiscard]] bool isInvisible() const { return m_invisible; } /** * @brief Returns the list of requirements of the achievement * @return List of requirements of the achievement */ [[nodiscard]] const std::vector &getRequirements() const { return m_requirements; } /** * @brief Returns the list of visibility requirements of the achievement * @return List of visibility requirements of the achievement */ [[nodiscard]] const std::vector &getVisibilityRequirements() const { return m_visibilityRequirements; } /** * @brief Returns the unlocalized description of the achievement * @return Unlocalized description of the achievement */ [[nodiscard]] const UnlocalizedString &getUnlocalizedDescription() const { return m_unlocalizedDescription; } /** * @brief Returns the icon of the achievement * @return Icon of the achievement */ [[nodiscard]] const char* getIcon() const { return m_icon.c_str(); } /** * @brief Sets the icon of the achievement * @param icon Icon glyph * @return Reference to the achievement */ Achievement& setIcon(std::string icon) { m_icon = std::move(icon); return *this; } /** * @brief Specifies the required progress to unlock the achievement. This is the number of times this achievement has to be triggered to unlock it. The default is 1. * @param progress Required progress * @return Reference to the achievement */ Achievement& setRequiredProgress(u32 progress) { m_maxProgress = progress; return *this; } /** * @brief Returns the required progress to unlock the achievement * @return Required progress to unlock the achievement */ [[nodiscard]] u32 getRequiredProgress() const { return m_maxProgress; } /** * @brief Returns the current progress of the achievement * @return Current progress of the achievement */ [[nodiscard]] u32 getProgress() const { return m_progress; } /** * @brief Sets the callback to call when the achievement is clicked * @param callback Callback to call when the achievement is clicked */ void setClickCallback(const std::function &callback) { m_clickCallback = callback; } /** * @brief Returns the callback to call when the achievement is clicked * @return Callback to call when the achievement is clicked */ [[nodiscard]] const std::function &getClickCallback() const { return m_clickCallback; } /** * @brief Returns whether the achievement is temporary. Temporary achievements have been added by challenge projects for example and will be removed when the project is closed. * @return Whether the achievement is temporary */ [[nodiscard]] bool isTemporary() const { return m_temporary; } /** * @brief Sets whether the achievement is unlocked * @param unlocked Whether the achievement is unlocked */ void setUnlocked(bool unlocked) { if (unlocked) { if (m_progress < m_maxProgress) m_progress++; } else { m_progress = 0; } } protected: void setProgress(u32 progress) { m_progress = progress; } private: UnlocalizedString m_unlocalizedCategory, m_unlocalizedName; UnlocalizedString m_unlocalizedDescription; bool m_blacked = false; bool m_invisible = false; std::vector m_requirements, m_visibilityRequirements; std::function m_clickCallback; std::string m_icon; u32 m_progress = 0; u32 m_maxProgress = 1; bool m_temporary = false; friend class AchievementManager; }; class AchievementManager { static bool s_initialized; public: AchievementManager() = delete; struct AchievementNode { Achievement *achievement; std::vector children, parents; std::vector visibilityParents; ImVec2 position; [[nodiscard]] bool hasParents() const { return !this->parents.empty(); } [[nodiscard]] bool isUnlockable() const { return std::ranges::all_of(this->parents, [](const auto &parent) { return parent->achievement->isUnlocked(); }); } [[nodiscard]] bool isVisible() const { return std::ranges::all_of(this->visibilityParents, [](const auto &parent) { return parent->achievement->isUnlocked(); }); } [[nodiscard]] bool isUnlocked() const { return this->achievement->isUnlocked(); } }; /** * @brief Adds a new achievement * @tparam T Type of the achievement * @param args Arguments to pass to the constructor of the achievement * @return Reference to the achievement */ template T = Achievement> static Achievement& addAchievement(auto && ... args) { auto newAchievement = std::make_unique(std::forward(args)...); return addAchievementImpl(std::move(newAchievement)); } /** * @brief Adds a new temporary achievement * @tparam T Type of the achievement * @param args Arguments to pass to the constructor of the achievement * @return Reference to the achievement */ template T = Achievement> static Achievement& addTemporaryAchievement(auto && ... args) { auto &achievement = addAchievement(std::forward(args)...); achievement.m_temporary = true; return achievement; } /** * @brief Unlocks an achievement * @param unlocalizedCategory Unlocalized category of the achievement * @param unlocalizedName Unlocalized name of the achievement */ static void unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName); /** * @brief Returns all registered achievements * @return All achievements */ static const std::unordered_map>>& getAchievements(); /** * @brief Returns all achievement start nodes * @note Start nodes are all nodes that don't have any parents * @param rebuild Whether to rebuild the list of start nodes * @return All achievement start nodes */ static const std::unordered_map>& getAchievementStartNodes(bool rebuild = true); /** * @brief Returns all achievement nodes * @param rebuild Whether to rebuild the list of nodes * @return All achievement nodes */ static const std::unordered_map>& getAchievementNodes(bool rebuild = true); /** * @brief Loads the progress of all achievements from the achievements save file */ static void loadProgress(); /** * @brief Stores the progress of all achievements to the achievements save file */ static void storeProgress(); /** * @brief Removes all temporary achievements from the tree */ static void clearTemporary(); /** * \brief Returns the current progress of all achievements * \return A pair containing the number of unlocked achievements and the total number of achievements */ static std::pair getProgress(); private: static void achievementAdded(); static Achievement& addAchievementImpl(std::unique_ptr &&newAchievement); }; } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/background_services.hpp ================================================ #pragma once #include #include #include EXPORT_MODULE namespace hex { /* Background Service Registry. Allows adding new background services */ namespace ContentRegistry::BackgroundServices { namespace impl { using Callback = std::function; void stopServices(); } void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/command_palette.hpp ================================================ #pragma once #include #include #include #include #include #include EXPORT_MODULE namespace hex { /* Command Palette Command Registry. Allows adding of new commands to the command palette */ namespace ContentRegistry::CommandPalette { enum class Type : u32 { SymbolCommand, KeywordCommand }; namespace impl { using QueryResultCallback = std::function; struct QueryResult { std::string name; QueryResultCallback callback; }; using ContentDisplayCallback = std::function; using DisplayCallback = std::function; using ExecuteCallback = std::function(std::string)>; using QueryCallback = std::function(std::string)>; struct Entry { Type type; std::string command; UnlocalizedString unlocalizedDescription; DisplayCallback displayCallback; ExecuteCallback executeCallback; }; struct Handler { Type type; std::string command; QueryCallback queryCallback; DisplayCallback displayCallback; }; struct ContentDisplay { bool showSearchBox; ContentDisplayCallback callback; }; const std::vector& getEntries(); const std::vector& getHandlers(); std::optional& getDisplayedContent(); } /** * @brief Adds a new command to the command palette * @param type The type of the command * @param command The command to add * @param unlocalizedDescription The description of the command * @param displayCallback The callback that will be called when the command is displayed in the command palette * @param executeCallback The callback that will be called when the command is executed */ void add( Type type, const std::string &command, const UnlocalizedString &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback = [](auto) { return std::nullopt; }); /** * @brief Adds a new command handler to the command palette * @param type The type of the command * @param command The command to add * @param queryCallback The callback that will be called when the command palette wants to load the name and callback items * @param displayCallback The callback that will be called when the command is displayed in the command palette */ void addHandler( Type type, const std::string &command, const impl::QueryCallback &queryCallback, const impl::DisplayCallback &displayCallback); /** * @brief Specify UI content that will be displayed inside the command palette * @param displayCallback Display callback that will be called to display the content */ void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback); /** * @brief Opens the command palette window, displaying a user defined interface * @param displayCallback Display callback that will be called to display the content */ void openWithContent(const impl::ContentDisplayCallback &displayCallback); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/communication_interface.hpp ================================================ #pragma once #include #include #include #include #include EXPORT_MODULE namespace hex { /* Network Communication Interface Registry. Allows adding new communication interface endpoints */ namespace ContentRegistry::CommunicationInterface { namespace impl { using NetworkCallback = std::function; const std::map& getNetworkEndpoints(); } void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback); } namespace ContentRegistry::MCP { namespace impl { std::unique_ptr& getMcpServerInstance(); void setEnabled(bool enabled); } bool isEnabled(); bool isConnected(); void registerTool(std::string_view capabilities, std::function function); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/data_formatter.hpp ================================================ #pragma once #include #include #include #include #include EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif /* Data Formatter Registry. Allows adding formatters that are used in the Copy-As menu for example */ namespace ContentRegistry::DataFormatter { namespace impl { using Callback = std::function; struct ExportMenuEntry { UnlocalizedString unlocalizedName; Callback callback; }; struct FindOccurrence { Region region; std::endian endian = std::endian::native; enum class DecodeType : u8 { ASCII, UTF8, Binary, UTF16, Unsigned, Signed, Float, Double } decodeType; bool selected; std::string string; }; using FindExporterCallback = std::function(const std::vector&, std::function)>; struct FindExporterEntry { UnlocalizedString unlocalizedName; std::string fileExtension; FindExporterCallback callback; }; /** * @brief Retrieves a list of all registered data formatters used by the 'File -> Export' menu */ const std::vector& getExportMenuEntries(); /** * @brief Retrieves a list of all registered data formatters used in the Results section of the 'Find' view */ const std::vector& getFindExporterEntries(); } /** * @brief Adds a new data formatter * @param unlocalizedName The unlocalized name of the formatter * @param callback The function to call to format the data */ void addExportMenuEntry(const UnlocalizedString &unlocalizedName, const impl::Callback &callback); /** * @brief Adds a new data exporter for Find results * @param unlocalizedName The unlocalized name of the formatter * @param fileExtension The file extension to use for the exported file * @param callback The function to call to format the data */ void addFindExportFormatter(const UnlocalizedString &unlocalizedName, const std::string &fileExtension, const impl::FindExporterCallback &callback); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/data_information.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif /* Data Information Registry. Allows adding new analyzers to the data information view */ namespace ContentRegistry::DataInformation { class InformationSection { public: InformationSection(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = "", bool hasSettings = false) : m_unlocalizedName(unlocalizedName), m_unlocalizedDescription(unlocalizedDescription), m_hasSettings(hasSettings) { } virtual ~InformationSection() = default; [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; } [[nodiscard]] const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; } virtual void process(Task &task, prv::Provider *provider, Region region) = 0; virtual void reset() = 0; virtual void drawSettings() { } virtual void drawContent() = 0; [[nodiscard]] bool isValid() const { return m_valid; } void markValid(bool valid = true) { m_valid = valid; } [[nodiscard]] bool isEnabled() const { return m_enabled; } void setEnabled(bool enabled) { m_enabled = enabled; } [[nodiscard]] bool isAnalyzing() const { return m_analyzing; } void setAnalyzing(bool analyzing) { m_analyzing = analyzing; } virtual void load(const nlohmann::json &data); [[nodiscard]] virtual nlohmann::json store(); [[nodiscard]] bool hasSettings() const { return m_hasSettings; } private: UnlocalizedString m_unlocalizedName, m_unlocalizedDescription; bool m_hasSettings; std::atomic m_analyzing = false; std::atomic m_valid = false; std::atomic m_enabled = true; }; namespace impl { using CreateCallback = std::function()>; const std::vector& getInformationSectionConstructors(); void addInformationSectionCreator(const CreateCallback &callback); } template void addInformationSection(auto && ...args) { impl::addInformationSectionCreator([args...] { return std::make_unique(std::forward(args)...); }); } } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/data_inspector.hpp ================================================ #pragma once #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { /* Data Inspector Registry. Allows adding of new types to the data inspector */ namespace ContentRegistry::DataInspector { enum class NumberDisplayStyle : u8 { Decimal, Hexadecimal, Octal }; namespace impl { struct DoNotUseThisByItselfTag {}; using DisplayFunction = std::function; using EditingFunction = std::function>(std::string&, std::endian, DoNotUseThisByItselfTag)>; using GeneratorFunction = std::function &, std::endian, NumberDisplayStyle)>; struct Entry { UnlocalizedString unlocalizedName; size_t requiredSize; size_t maxSize; GeneratorFunction generatorFunction; std::optional editingFunction; }; const std::vector& getEntries(); } namespace EditWidget { class Widget { public: using Function = std::function(const std::string&, std::endian)>; explicit Widget(const Function &function) : m_function(function) {} virtual ~Widget() = default; virtual std::optional> draw(std::string &value, std::endian endian) = 0; std::optional> operator()(std::string &value, std::endian endian, impl::DoNotUseThisByItselfTag) { return draw(value, endian); } std::vector getBytes(const std::string &value, std::endian endian) const { return m_function(value, endian); } private: Function m_function; }; struct TextInput : Widget { explicit TextInput(const Function &function) : Widget(function) {} std::optional> draw(std::string &value, std::endian endian) override; }; } /** * @brief Adds a new entry to the data inspector * @param unlocalizedName The unlocalized name of the entry * @param requiredSize The minimum required number of bytes available for the entry to appear * @param displayGeneratorFunction The function that will be called to generate the display function * @param editingFunction The function that will be called to edit the data */ void add( const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional editingFunction = std::nullopt ); /** * @brief Adds a new entry to the data inspector * @param unlocalizedName The unlocalized name of the entry * @param requiredSize The minimum required number of bytes available for the entry to appear * @param maxSize The maximum number of bytes to read from the data * @param displayGeneratorFunction The function that will be called to generate the display function * @param editingFunction The function that will be called to edit the data */ void add( const UnlocalizedString &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional editingFunction = std::nullopt ); /** * @brief Allows adding new menu items to data inspector row context menus. Call this function inside the * draw function of the data inspector row definition. * @param function Callback that will draw menu items */ void drawMenuItems(const std::function &function); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/data_processor.hpp ================================================ #pragma once #include #include #include #include #include EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace dp { class Node; } #endif /* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */ namespace ContentRegistry::DataProcessor { namespace impl { using CreatorFunction = std::function()>; struct Entry { UnlocalizedString unlocalizedCategory; UnlocalizedString unlocalizedName; CreatorFunction creatorFunction; }; void add(const Entry &entry); const std::vector& getEntries(); } /** * @brief Adds a new node to the data processor * @tparam T The custom node class that extends dp::Node * @tparam Args Arguments types * @param unlocalizedCategory The unlocalized category name of the node * @param unlocalizedName The unlocalized name of the node * @param args Arguments passed to the constructor of the node */ template T, typename... Args> void add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, Args &&...args) { add(impl::Entry { unlocalizedCategory, unlocalizedName, [unlocalizedName, ...args = std::forward(args)]() mutable { auto node = std::make_unique(std::forward(args)...); node->setUnlocalizedName(unlocalizedName); return node; } }); } /** * @brief Adds a separator to the data processor right click menu */ void addSeparator(); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/diffing.hpp ================================================ #pragma once #include #include #include #include #include EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif /* Diffing Registry. Allows adding new diffing algorithms */ namespace ContentRegistry::Diffing { enum class DifferenceType : u8 { Match = 0, Insertion = 1, Deletion = 2, Mismatch = 3 }; using DiffTree = wolv::container::IntervalTree; class Algorithm { public: explicit Algorithm(UnlocalizedString unlocalizedName, UnlocalizedString unlocalizedDescription) : m_unlocalizedName(std::move(unlocalizedName)), m_unlocalizedDescription(std::move(unlocalizedDescription)) { } virtual ~Algorithm() = default; virtual std::vector analyze(prv::Provider *providerA, prv::Provider *providerB) const = 0; virtual void drawSettings() { } const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; } const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; } private: UnlocalizedString m_unlocalizedName, m_unlocalizedDescription; }; namespace impl { const std::vector>& getAlgorithms(); void addAlgorithm(std::unique_ptr &&hash); } /** * @brief Adds a new hash * @tparam T The hash type that extends hex::Hash * @param args The arguments to pass to the constructor of the hash */ template void addAlgorithm(Args && ... args) { impl::addAlgorithm(std::make_unique(std::forward(args)...)); } } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/disassemblers.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { /* Disassembler Registry. Allows adding new disassembler architectures */ namespace ContentRegistry::Disassemblers { struct Instruction { u64 address; u64 offset; size_t size; std::string bytes; std::string mnemonic; std::string operators; }; class Architecture { public: explicit Architecture(std::string name) : m_name(std::move(name)) {} virtual ~Architecture() = default; virtual bool start() = 0; virtual void end() = 0; virtual std::optional disassemble(u64 imageBaseAddress, u64 instructionLoadAddress, u64 instructionDataAddress, std::span code) = 0; virtual void drawSettings() = 0; [[nodiscard]] const std::string& getName() const { return m_name; } private: std::string m_name; }; namespace impl { using CreatorFunction = std::function()>; void addArchitectureCreator(CreatorFunction function); const std::map& getArchitectures(); } template T> void add(auto && ...args) { impl::addArchitectureCreator([...args = std::move(args)] { return std::make_unique(args...); }); } } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/experiments.hpp ================================================ #pragma once #include #include #include #include EXPORT_MODULE namespace hex { /* Experiments Registry. Allows adding new experiments */ namespace ContentRegistry::Experiments { namespace impl { struct Experiment { UnlocalizedString unlocalizedName, unlocalizedDescription; bool enabled; }; const std::map& getExperiments(); } void addExperiment( const std::string &experimentName, const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = "" ); void enableExperiement(const std::string &experimentName, bool enabled); [[nodiscard]] bool isExperimentEnabled(const std::string &experimentName); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/file_type_handler.hpp ================================================ #pragma once #include #include #include #include #include EXPORT_MODULE namespace hex { /* File Handler Registry. Allows adding handlers for opening files specific file types */ namespace ContentRegistry::FileTypeHandler { namespace impl { using Callback = std::function; struct Entry { std::vector extensions; Callback callback; }; const std::vector& getEntries(); } /** * @brief Adds a new file handler * @param extensions The file extensions to handle * @param callback The function to call to handle the file */ void add(const std::vector &extensions, const impl::Callback &callback); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/hashes.hpp ================================================ #pragma once #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif /* Hash Registry. Allows adding new hashes to the Hash view */ namespace ContentRegistry::Hashes { class Hash { public: explicit Hash(UnlocalizedString unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {} virtual ~Hash() = default; class Function { public: using Callback = std::function(const Region&, prv::Provider *)>; Function(Hash *type, std::string name, Callback callback) : m_type(type), m_name(std::move(name)), m_callback(std::move(callback)) { } [[nodiscard]] Hash *getType() { return m_type; } [[nodiscard]] const Hash *getType() const { return m_type; } [[nodiscard]] const std::string& getName() const { return m_name; } std::vector get(const Region& region, prv::Provider *provider) const { return m_callback(region, provider); } private: Hash *m_type; std::string m_name; Callback m_callback; }; virtual void draw() { } [[nodiscard]] virtual Function create(std::string name) = 0; [[nodiscard]] virtual nlohmann::json store() const = 0; virtual void load(const nlohmann::json &json) = 0; [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; } protected: [[nodiscard]] Function create(const std::string &name, const Function::Callback &callback) { return { this, name, callback }; } private: UnlocalizedString m_unlocalizedName; }; namespace impl { const std::vector>& getHashes(); void add(std::unique_ptr &&hash); } /** * @brief Adds a new hash * @tparam T The hash type that extends hex::Hash * @param args The arguments to pass to the constructor of the hash */ template void add(Args && ... args) { impl::add(std::make_unique(std::forward(args)...)); } } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/hex_editor.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { /* Hex Editor Registry. Allows adding new functionality to the hex editor */ namespace ContentRegistry::HexEditor { class DataVisualizer { public: DataVisualizer(UnlocalizedString unlocalizedName, u16 bytesPerCell, u16 maxCharsPerCell) : m_unlocalizedName(std::move(unlocalizedName)), m_bytesPerCell(bytesPerCell), m_maxCharsPerCell(maxCharsPerCell) { } virtual ~DataVisualizer() = default; virtual void draw(u64 address, const u8 *data, size_t size, bool upperCase) = 0; virtual bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) = 0; [[nodiscard]] u16 getBytesPerCell() const { return m_bytesPerCell; } [[nodiscard]] u16 getMaxCharsPerCell() const { return m_maxCharsPerCell; } [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; } [[nodiscard]] static int DefaultTextInputFlags(); protected: bool drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const; bool drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const; private: UnlocalizedString m_unlocalizedName; u16 m_bytesPerCell; u16 m_maxCharsPerCell; }; struct MiniMapVisualizer { using Callback = std::function, std::vector&)>; UnlocalizedString unlocalizedName; Callback callback; }; namespace impl { void addDataVisualizer(std::shared_ptr &&visualizer); const std::vector>& getVisualizers(); const std::vector>& getMiniMapVisualizers(); } /** * @brief Adds a new cell data visualizer * @tparam T The data visualizer type that extends hex::DataVisualizer * @param args The arguments to pass to the constructor of the data visualizer */ template T, typename... Args> void addDataVisualizer(Args &&...args) { return impl::addDataVisualizer(std::make_shared(std::forward(args)...)); } /** * @brief Gets a data visualizer by its unlocalized name * @param unlocalizedName Unlocalized name of the data visualizer * @return The data visualizer, or nullptr if it doesn't exist */ std::shared_ptr getVisualizerByName(const UnlocalizedString &unlocalizedName); /** * @brief Adds a new minimap visualizer * @param unlocalizedName Unlocalized name of the minimap visualizer * @param callback The callback that will be called to get the color of a line */ void addMiniMapVisualizer(UnlocalizedString unlocalizedName, MiniMapVisualizer::Callback callback); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/pattern_language.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif /* Pattern Language Function Registry. Allows adding of new functions that may be used inside the pattern language */ namespace ContentRegistry::PatternLanguage { namespace impl { using VisualizerFunctionCallback = std::function)>; struct FunctionDefinition { pl::api::Namespace ns; std::string name; pl::api::FunctionParameterCount parameterCount; pl::api::FunctionCallback callback; bool dangerous; }; struct TypeDefinition { pl::api::Namespace ns; std::string name; pl::api::FunctionParameterCount parameterCount; pl::api::TypeCallback callback; }; struct Visualizer { pl::api::FunctionParameterCount parameterCount; VisualizerFunctionCallback callback; }; const std::map& getVisualizers(); const std::map& getInlineVisualizers(); const std::map& getPragmas(); const std::vector& getFunctions(); const std::vector& getTypes(); } /** * @brief Provides access to the current provider's pattern language runtime * @return Runtime */ pl::PatternLanguage& getRuntime(); /** * @brief Provides access to the current provider's pattern language runtime's lock * @return Lock */ std::mutex& getRuntimeLock(); /** * @brief Configures the pattern language runtime using ImHex's default settings * @param runtime The pattern language runtime to configure * @param provider The provider to use for data access */ void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider); /** * @brief Adds a new pragma to the pattern language * @param name The name of the pragma * @param handler The handler that will be called when the pragma is encountered */ void addPragma(const std::string &name, const pl::api::PragmaHandler &handler); /** * @brief Adds a new function to the pattern language * @param ns The namespace of the function * @param name The name of the function * @param parameterCount The amount of parameters the function takes * @param func The function callback */ void addFunction( const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func ); /** * @brief Adds a new dangerous function to the pattern language * @note Dangerous functions are functions that require the user to explicitly allow them to be used * @param ns The namespace of the function * @param name The name of the function * @param parameterCount The amount of parameters the function takes * @param func The function callback */ void addDangerousFunction( const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func ); /** * @brief Adds a new type to the pattern language * @param ns The namespace of the type * @param name The name of the type * @param parameterCount The amount of non-type template parameters the type takes * @param func The type callback */ void addType( const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::TypeCallback &func ); /** * @brief Adds a new visualizer to the pattern language * @note Visualizers are extensions to the [[hex::visualize]] attribute, used to visualize data * @param name The name of the visualizer * @param function The function callback * @param parameterCount The amount of parameters the function takes */ void addVisualizer( const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount ); /** * @brief Adds a new inline visualizer to the pattern language * @note Inline visualizers are extensions to the [[hex::inline_visualize]] attribute, used to visualize data * @param name The name of the visualizer * @param function The function callback * @param parameterCount The amount of parameters the function takes */ void addInlineVisualizer( const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount ); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/provider.hpp ================================================ #pragma once #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { /* Provider Registry. Allows adding new data providers to be created from the UI */ namespace ContentRegistry::Provider { namespace impl { void addProviderName(const UnlocalizedString &unlocalizedName, const char *icon); using ProviderCreationFunction = std::function()>; void add(const std::string &typeName, ProviderCreationFunction creationFunction); struct Entry { UnlocalizedString unlocalizedName; const char *icon; }; const std::vector& getEntries(); } /** * @brief Adds a new provider to the list of providers * @tparam T The provider type that extends hex::prv::Provider * @param addToList Whether to display the provider in the Other Providers list in the welcome screen and File menu */ template T> void add(bool addToList = true) { const T provider; auto typeName = provider.getTypeName(); impl::add(typeName, []() -> std::unique_ptr { return std::make_unique(); }); if (addToList) impl::addProviderName(typeName, provider.getIcon()); } } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/reports.hpp ================================================ #pragma once #include #include #include #include EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif /* Reports Registry. Allows adding new sections to exported reports */ namespace ContentRegistry::Reports { namespace impl { using Callback = std::function; struct ReportGenerator { Callback callback; }; const std::vector& getGenerators(); } void addReportProvider(impl::Callback callback); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/settings.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { /* Settings Registry. Allows adding of new entries into the ImHex preferences window. */ namespace ContentRegistry::Settings { namespace Widgets { class Widget { public: virtual ~Widget() = default; virtual bool draw(const std::string &name) = 0; virtual void load(const nlohmann::json &data) = 0; virtual nlohmann::json store() = 0; class Interface { public: friend class Widget; Interface& requiresRestart() { m_requiresRestart = true; return *this; } Interface& setEnabledCallback(std::function callback) { m_enabledCallback = std::move(callback); return *this; } Interface& setChangedCallback(std::function callback) { m_changedCallback = std::move(callback); return *this; } Interface& setTooltip(const UnlocalizedString &tooltip) { m_tooltip = tooltip; return *this; } [[nodiscard]] Widget& getWidget() const { return *m_widget; } private: explicit Interface(Widget *widget) : m_widget(widget) {} Widget *m_widget; bool m_requiresRestart = false; std::function m_enabledCallback; std::function m_changedCallback; std::optional m_tooltip; }; [[nodiscard]] bool doesRequireRestart() const { return m_interface.m_requiresRestart; } [[nodiscard]] bool isEnabled() const { return !m_interface.m_enabledCallback || m_interface.m_enabledCallback(); } [[nodiscard]] const std::optional& getTooltip() const { return m_interface.m_tooltip; } void onChanged() { if (m_interface.m_changedCallback) m_interface.m_changedCallback(*this); } [[nodiscard]] Interface& getInterface() { return m_interface; } private: Interface m_interface = Interface(this); }; class Checkbox : public Widget { public: explicit Checkbox(bool defaultValue) : m_value(defaultValue) { } bool draw(const std::string &name) override; void load(const nlohmann::json &data) override; nlohmann::json store() override; [[nodiscard]] bool isChecked() const { return m_value; } protected: bool m_value; }; class SliderInteger : public Widget { public: SliderInteger(i32 defaultValue, i32 min, i32 max) : m_value(defaultValue), m_min(min), m_max(max) { } bool draw(const std::string &name) override; void load(const nlohmann::json &data) override; nlohmann::json store() override; [[nodiscard]] i32 getValue() const { return m_value; } protected: int m_value; i32 m_min, m_max; }; class SliderFloat : public Widget { public: SliderFloat(float defaultValue, float min, float max) : m_value(defaultValue), m_min(min), m_max(max) { } bool draw(const std::string &name) override; void load(const nlohmann::json &data) override; nlohmann::json store() override; [[nodiscard]] float getValue() const { return m_value; } protected: float m_value; float m_min, m_max; }; class SliderDataSize : public Widget { public: SliderDataSize(u64 defaultValue, u64 min, u64 max, u64 stepSize) : m_value(defaultValue), m_min(min), m_max(max), m_stepSize(stepSize) { } bool draw(const std::string &name) override; void load(const nlohmann::json &data) override; nlohmann::json store() override; [[nodiscard]] i32 getValue() const { return m_value; } protected: u64 m_value; u64 m_min, m_max; u64 m_stepSize; }; class ColorPicker : public Widget { public: explicit ColorPicker(ImColor defaultColor, ImGuiColorEditFlags flags = 0); bool draw(const std::string &name) override; void load(const nlohmann::json &data) override; nlohmann::json store() override; [[nodiscard]] ImColor getColor() const; protected: std::array m_value = {}, m_defaultValue = {}; ImGuiColorEditFlags m_flags; }; class DropDown : public Widget { public: explicit DropDown(const std::vector &items, const std::vector &settingsValues, const nlohmann::json &defaultItem) : m_items(items.begin(), items.end()), m_settingsValues(settingsValues), m_defaultItem(defaultItem) { } explicit DropDown(const std::vector &items, const std::vector &settingsValues, const nlohmann::json &defaultItem) : m_items(items), m_settingsValues(settingsValues), m_defaultItem(defaultItem) { } bool draw(const std::string &name) override; void load(const nlohmann::json &data) override; nlohmann::json store() override; [[nodiscard]] const nlohmann::json& getValue() const; protected: std::vector m_items; std::vector m_settingsValues; nlohmann::json m_defaultItem; int m_value = -1; }; class TextBox : public Widget { public: explicit TextBox(std::string defaultValue) : m_value(std::move(defaultValue)) { } bool draw(const std::string &name) override; void load(const nlohmann::json &data) override; nlohmann::json store() override; [[nodiscard]] const std::string& getValue() const { return m_value; } protected: std::string m_value; }; class FilePicker : public Widget { public: bool draw(const std::string &name) override; void load(const nlohmann::json &data) override; nlohmann::json store() override; [[nodiscard]] const std::fs::path& getPath() const { return m_path; } protected: std::fs::path m_path; }; class Label : public Widget { public: bool draw(const std::string &name) override; void load(const nlohmann::json &) override {} nlohmann::json store() override { return {}; } }; class Spacer : public Widget { public: bool draw(const std::string &name) override; void load(const nlohmann::json &) override {} nlohmann::json store() override { return {}; } }; } namespace impl { struct Entry { UnlocalizedString unlocalizedName; std::unique_ptr widget; }; struct SubCategory { UnlocalizedString unlocalizedName; std::vector entries; }; struct Category { UnlocalizedString unlocalizedName; UnlocalizedString unlocalizedDescription; std::vector subCategories; }; void load(); void store(); void clear(); const std::vector& getSettings(); nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue); const nlohmann::json& getSettingsData(); Widgets::Widget* add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, std::unique_ptr &&widget); void printSettingReadError(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json::exception &e); void runOnChangeHandlers(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value); } template T> Widgets::Widget::Interface& add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, auto && ... args) { return impl::add( unlocalizedCategory, unlocalizedSubCategory, unlocalizedName, std::make_unique(std::forward(args)...) )->getInterface(); } void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription); class SettingsValue { public: SettingsValue(nlohmann::json value) : m_value(std::move(value)) {} template requires (!(std::is_reference_v || std::is_const_v)) [[nodiscard]] T get(T defaultValue) const { try { auto result = m_value; if (result.is_number() && std::same_as) result = m_value.get() != 0; if (m_value.is_null()) result = defaultValue; return result.get(); } catch (const nlohmann::json::exception &) { return defaultValue; } } private: nlohmann::json m_value; }; template requires (!(std::is_reference_v || std::is_const_v)) [[nodiscard]] T read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, T defaultValue) { auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue); try { if (setting.is_number() && std::same_as) setting = setting.template get() != 0; if (setting.is_null()) setting = defaultValue; return setting.template get(); } catch (const nlohmann::json::exception &e) { impl::printSettingReadError(unlocalizedCategory, unlocalizedName, e); return defaultValue; } } template requires (!(std::is_reference_v || std::is_const_v)) void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, T value) { impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value; impl::runOnChangeHandlers(unlocalizedCategory, unlocalizedName, value); impl::store(); } using OnChangeCallback = std::function; u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback); void removeOnChangeHandler(u64 id); using OnSaveCallback = std::function; u64 onSave(const OnSaveCallback &callback); template requires (!(std::is_reference_v || std::is_const_v)) class SettingsVariable { public: explicit(false) SettingsVariable(T defaultValue) noexcept : m_defaultValue(std::move(defaultValue)) { } SettingsVariable(const SettingsVariable&) = delete; SettingsVariable& operator=(const SettingsVariable&) = delete; SettingsVariable(SettingsVariable&&) = delete; SettingsVariable& operator=(SettingsVariable&&) = delete; ~SettingsVariable() { if (m_onChangeId > 0) removeOnChangeHandler(m_onChangeId); } [[nodiscard]] T get() const { registerChangeHandler(); if (!m_value.has_value()) { m_value = read( UnlocalizedCategory.value.data(), UnlocalizedName.value.data(), m_defaultValue ); } return m_value.value_or(m_defaultValue); } void set(T value) { registerChangeHandler(); write( UnlocalizedCategory.value.data(), UnlocalizedName.value.data(), std::move(value) ); } explicit(false) operator T() const { return get(); } SettingsVariable& operator=(T value) { set(std::move(value)); return *this; } private: void registerChangeHandler() const { if (m_onChangeId > 0) return; m_onChangeId = onChange(UnlocalizedCategory.value.data(), UnlocalizedName.value.data(), [this](const SettingsValue &value) { m_value = value.get(m_defaultValue); }); } private: mutable std::optional m_value; T m_defaultValue; mutable u64 m_onChangeId = 0; }; } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/tools.hpp ================================================ #pragma once #include #include #include #include EXPORT_MODULE namespace hex { /* Tools Registry. Allows adding new entries to the tools window */ namespace ContentRegistry::Tools { namespace impl { using Callback = std::function; struct Entry { UnlocalizedString unlocalizedName; const char *icon; Callback function; }; const std::vector& getEntries(); } /** * @brief Adds a new tool to the tools window * @param unlocalizedName The unlocalized name of the tool * @param function The function that will be called to draw the tool */ void add(const UnlocalizedString &unlocalizedName, const char *icon, const impl::Callback &function); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/user_interface.hpp ================================================ #pragma once #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { /* User Interface Registry. Allows adding new items to various interfaces */ namespace ContentRegistry::UserInterface { struct Icon { Icon(const char *glyph, ImGuiCustomCol color = ImGuiCustomCol(0x00)) : glyph(glyph), color(color) {} std::string glyph; ImGuiCustomCol color; }; namespace impl { using DrawCallback = std::function; using MenuCallback = std::function; using EnabledCallback = std::function; using SelectedCallback = std::function; using ClickCallback = std::function; using ToggleCallback = std::function; struct MainMenuItem { UnlocalizedString unlocalizedName; }; struct MenuItem { std::vector unlocalizedNames; Icon icon; Shortcut shortcut; View *view; MenuCallback callback; EnabledCallback enabledCallback; SelectedCallback selectedCallback; i32 toolbarIndex; }; struct SidebarItem { std::string icon; DrawCallback callback; EnabledCallback enabledCallback; }; struct TitleBarButton { std::string icon; ImGuiCustomCol color; UnlocalizedString unlocalizedTooltip; ClickCallback callback; }; struct WelcomeScreenQuickSettingsToggle { std::string onIcon, offIcon; UnlocalizedString unlocalizedTooltip; ToggleCallback callback; mutable bool state; }; constexpr static auto SeparatorValue = "$SEPARATOR$"; constexpr static auto SubMenuValue = "$SUBMENU$"; constexpr static auto TaskBarMenuValue = "$TASKBAR$"; const std::multimap& getMainMenuItems(); const std::multimap& getMenuItems(); const std::vector& getToolbarMenuItems(); std::multimap& getMenuItemsMutable(); const std::vector& getWelcomeScreenEntries(); const std::vector& getFooterItems(); const std::vector& getToolbarItems(); const std::vector& getSidebarItems(); const std::vector& getTitlebarButtons(); const std::vector& getWelcomeScreenQuickSettingsToggles(); } /** * @brief Adds a new top-level main menu entry * @param unlocalizedName The unlocalized name of the entry * @param priority The priority of the entry. Lower values are displayed first */ void registerMainMenuItem(const UnlocalizedString &unlocalizedName, u32 priority); /** * @brief Adds a new main menu entry * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries * @param icon The icon to use for the entry * @param priority The priority of the entry. Lower values are displayed first * @param shortcut The shortcut to use for the entry * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled * @param view The view to use for the entry. If nullptr, the shortcut will work globally */ void addMenuItem( const std::vector &unlocalizedMainMenuNames, const Icon &icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view ); /** * @brief Adds a new main menu entry * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries * @param icon The icon to use for the entry * @param priority The priority of the entry. Lower values are displayed first * @param shortcut The shortcut to use for the entry * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled * @param selectedCallback The function to call to determine if the entry is selected * @param view The view to use for the entry. If nullptr, the shortcut will work globally */ void addMenuItem( const std::vector &unlocalizedMainMenuNames, const Icon &icon, u32 priority, Shortcut shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, const impl::SelectedCallback &selectedCallback = []{ return false; }, View *view = nullptr ); /** * @brief Adds a new main menu entry * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries * @param priority The priority of the entry. Lower values are displayed first * @param shortcut The shortcut to use for the entry * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled * @param selectedCallback The function to call to determine if the entry is selected * @param view The view to use for the entry. If nullptr, the shortcut will work globally */ void addMenuItem( const std::vector &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, const impl::SelectedCallback &selectedCallback = []{ return false; }, View *view = nullptr ); /** * @brief Adds a new main menu sub-menu entry * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries * @param priority The priority of the entry. Lower values are displayed first * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled * @param view The view to use for the entry. If nullptr, the item will always be visible * @param showOnWelcomeScreen If this entry should be shown on the welcome screen */ void addMenuItemSubMenu( std::vector unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, View *view = nullptr, bool showOnWelcomeScreen = false ); /** * @brief Adds a new main menu sub-menu entry * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries * @param icon The icon to use for the entry * @param priority The priority of the entry. Lower values are displayed first * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled * @param view The view to use for the entry. If nullptr, the item will always be visible * @param showOnWelcomeScreen If this entry should be shown on the welcome screen */ void addMenuItemSubMenu( std::vector unlocalizedMainMenuNames, const char *icon, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, View *view = nullptr, bool showOnWelcomeScreen = false ); /** * @brief Adds a new main menu separator * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries * @param priority The priority of the entry. Lower values are displayed first * @param view The view to use for the entry. If nullptr, the item will always be visible */ void addMenuItemSeparator(std::vector unlocalizedMainMenuNames, u32 priority, View *view = nullptr); /** * @brief Adds a new main menu entry * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries * @param priority The priority of the entry. Lower values are displayed first * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled */ void addTaskBarMenuItem( std::vector unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback ); /** * @brief Adds a new welcome screen entry * @param function The function to call to draw the entry */ void addWelcomeScreenEntry(const impl::DrawCallback &function); /** * @brief Adds a new footer item * @param function The function to call to draw the item */ void addFooterItem(const impl::DrawCallback &function); /** * @brief Adds a new toolbar item * @param function The function to call to draw the item */ void addToolbarItem(const impl::DrawCallback &function); /** * @brief Adds a menu item to the toolbar * @param unlocalizedNames Unlocalized name of the menu item * @param color Color of the toolbar icon */ void addMenuItemToToolbar(const std::vector &unlocalizedNames, ImGuiCustomCol color); /** * @brief Reconstructs the toolbar items list after they have been modified */ void updateToolbarItems(); /** * @brief Adds a new sidebar item * @param icon The icon to use for the item * @param function The function to call to draw the item * @param enabledCallback The function */ void addSidebarItem( const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback = []{ return true; } ); /** * @brief Adds a new title bar button * @param icon The icon to use for the button * @param color The color of the icon * @param unlocalizedTooltip The unlocalized tooltip to use for the button * @param function The function to call when the button is clicked */ void addTitleBarButton( const std::string &icon, ImGuiCustomCol color, const UnlocalizedString &unlocalizedTooltip, const impl::ClickCallback &function ); /** * @brief Adds a new welcome screen quick settings toggle * @param icon The icon to use for the button * @param unlocalizedTooltip The unlocalized tooltip to use for the button * @param defaultState The default state of the toggle * @param function The function to call when the button is clicked */ void addWelcomeScreenQuickSettingsToggle( const std::string &icon, const UnlocalizedString &unlocalizedTooltip, bool defaultState, const impl::ToggleCallback &function ); /** * @brief Adds a new welcome screen quick settings toggle * @param onIcon The icon to use for the button when it's on * @param offIcon The icon to use for the button when it's off * @param unlocalizedTooltip The unlocalized tooltip to use for the button * @param defaultState The default state of the toggle * @param function The function to call when the button is clicked */ void addWelcomeScreenQuickSettingsToggle( const std::string &onIcon, const std::string &offIcon, const UnlocalizedString &unlocalizedTooltip, bool defaultState, const impl::ToggleCallback &function ); } } ================================================ FILE: lib/libimhex/include/hex/api/content_registry/views.hpp ================================================ #pragma once #include #include #include #include #include #include EXPORT_MODULE namespace hex { /* View Registry. Allows adding of new windows */ namespace ContentRegistry::Views { namespace impl { void add(std::unique_ptr &&view); void setFullScreenView(std::unique_ptr &&view); const std::map>& getEntries(); const std::unique_ptr& getFullScreenView(); } /** * @brief Adds a new view to ImHex * @tparam T The custom view class that extends View * @tparam Args Arguments types * @param args Arguments passed to the constructor of the view */ template T, typename... Args> void add(Args &&...args) { return impl::add(std::make_unique(std::forward(args)...)); } /** * @brief Sets a view as a full-screen view. This will cause the view to take up the entire ImHex window * @tparam T The custom view class that extends View * @tparam Args Arguments types * @param args Arguments passed to the constructor of the view */ template T, typename... Args> void setFullScreenView(Args &&...args) { return impl::setFullScreenView(std::make_unique(std::forward(args)...)); } /** * @brief Gets a view by its unlocalized name * @param unlocalizedName The unlocalized name of the view * @return The view if it exists, nullptr otherwise */ View* getViewByName(const UnlocalizedString &unlocalizedName); /** * @brief Gets the currently focused view * @return The view that is focused right now. nullptr if none is focused */ View* getFocusedView(); } } ================================================ FILE: lib/libimhex/include/hex/api/event_manager.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #define EVENT_DEF_IMPL(event_name, event_name_string, should_log, ...) \ struct event_name final : public hex::impl::Event<__VA_ARGS__> { \ constexpr static auto Id = [] { return hex::impl::EventId(event_name_string); }(); \ constexpr static auto ShouldLog = (should_log); \ explicit event_name(Callback func) noexcept : Event(std::move(func)) { } \ \ static EventManager::EventList::iterator subscribe(Event::Callback function) { \ return EventManager::subscribe(std::move(function)); \ } \ template \ static EventManager::EventList::iterator subscribe(Event::BaseCallback function) \ requires (!std::same_as) { \ return EventManager::subscribe([function = std::move(function)](auto && ...) { function(); }); \ } \ static void subscribe(void *token, Event::Callback function) { \ EventManager::subscribe(token, std::move(function)); \ } \ template \ static void subscribe(void *token, Event::BaseCallback function) \ requires (!std::same_as) { \ return EventManager::subscribe(token, [function = std::move(function)](auto && ...) { function(); }); \ } \ static void unsubscribe(const EventManager::EventList::iterator &token) noexcept { \ EventManager::unsubscribe(token); \ } \ static void unsubscribe(void *token) noexcept { \ EventManager::unsubscribe(token); \ } \ static void post(auto &&...args) { \ EventManager::post(std::forward(args)...); \ } \ } #define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__) #define EVENT_DEF_NO_LOG(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, false, __VA_ARGS__) EXPORT_MODULE namespace hex { namespace impl { class EventId { public: explicit constexpr EventId(const char *eventName) { m_hash = 0x811C'9DC5; for (const char c : std::string_view(eventName)) { m_hash = (m_hash >> 5) | (m_hash << 27); m_hash ^= c; } } constexpr bool operator==(const EventId &other) const { return m_hash == other.m_hash; } constexpr auto operator<=>(const EventId &other) const { return m_hash <=> other.m_hash; } private: u32 m_hash; }; struct EventBase { EventBase() noexcept = default; virtual ~EventBase() = default; }; template struct Event : EventBase { using Callback = std::function; using BaseCallback = std::function; explicit Event(Callback func) noexcept : m_func(std::move(func)) { } template void call(auto&& ... params) const { #if defined(DEBUG) m_func(std::forward(params)...); #else try { m_func(std::forward(params)...); } catch (const std::exception &e) { log::error("An exception occurred while handling event {}: {}", wolv::type::getTypeName(), e.what()); throw; } #endif } private: Callback m_func; }; template concept EventType = std::derived_from; } /** * @brief The EventManager allows subscribing to and posting events to different parts of the program. * To create a new event, use the EVENT_DEF macro. This will create a new event type with the given name and parameters. * Events should be created in an `events_*.hpp` category file under the `events` folder, and never directly here. */ class EventManager { public: using EventList = std::multimap>; /** * @brief Subscribes to an event * @tparam E Event * @param function Function to call when the event is posted * @return Token to unsubscribe from the event */ template static EventList::iterator subscribe(E::Callback function) { std::lock_guard lock(getEventMutex()); auto &events = getEvents(); return events.insert({ E::Id, std::make_unique(function) }); } /** * @brief Subscribes to an event * @tparam E Event * @param token Unique token to register the event to. Later required to unsubscribe again * @param function Function to call when the event is posted */ template static void subscribe(void *token, E::Callback function) { std::lock_guard lock(getEventMutex()); if (isAlreadyRegistered(token, E::Id)) { log::fatal("The token '{}' has already registered the same event ('{}')", token, wolv::type::getTypeName()); return; } getTokenStore().insert({ token, subscribe(std::move(function)) }); } /** * @brief Unsubscribes from an event * @param token Token returned by subscribe */ static void unsubscribe(const EventList::iterator &token) noexcept { std::lock_guard lock(getEventMutex()); getEvents().erase(token); } /** * @brief Unsubscribes from an event * @tparam E Event * @param token Token passed to subscribe */ template static void unsubscribe(void *token) noexcept { std::lock_guard lock(getEventMutex()); unsubscribe(token, E::Id); } /** * @brief Posts an event to all subscribers of it * @tparam E Event * @param args Arguments to pass to the event */ template static void post(auto && ...args) { std::lock_guard lock(getEventMutex()); const auto &[begin, end] = getEvents().equal_range(E::Id); for (auto it = begin; it != end; ++it) { const auto &[id, event] = *it; (*static_cast(event.get())).template call(std::forward(args)...); } #if defined (DEBUG) if constexpr (E::ShouldLog) log::debug("Event posted: '{}'", wolv::type::getTypeName()); #endif } /** * @brief Unsubscribe all subscribers from all events */ static void clear() noexcept { std::lock_guard lock(getEventMutex()); getEvents().clear(); getTokenStore().clear(); } private: static std::multimap& getTokenStore(); static EventList& getEvents(); static std::recursive_mutex& getEventMutex(); static bool isAlreadyRegistered(void *token, impl::EventId id); static void unsubscribe(void *token, impl::EventId id); }; } ================================================ FILE: lib/libimhex/include/hex/api/events/events_gui.hpp ================================================ #pragma once #include /* Forward declarations */ struct GLFWwindow; using ImGuiID = unsigned int; namespace hex { class View; } /* GUI events definitions */ namespace hex { /** * @brief Signals a newly opened view * * This event is sent when the view has just been opened by the Window manager. * * FIXME: This is currently only used for the introduction tutorial. * If the event's only purpose is this, maybe rename it? * * @param view the new view reference */ EVENT_DEF(EventViewOpened, View*); /** * @brief Signals a newly closed view * * This event is sent when the view has just been closed. * * @param view the closed view reference */ EVENT_DEF(EventViewClosed, View*); /** * @brief Signals a change in the DPI scale. * * This event is called once at startup to signal native scale definition (by passing the same value twice). * On Windows OS, this event can also be posted if the window DPI has been changed. * * @param oldScale the old scale * @param newScale the current scale that's now in use */ EVENT_DEF(EventDPIChanged, float, float); /** * @brief Signals the focus of the ImHex main window. * * This is directly tied as a GLFW window focus callback, and will be called accordingly when GLFW detects * a change in focus. * * @param isFocused true if the window is focused */ EVENT_DEF(EventWindowFocused, bool); /** * @brief Signals a window being closed. * * Allows reactive clean up of running tasks, and prevents ImHex from closing * by displaying an exit confirmation popup. * * @param window The window reference */ EVENT_DEF(EventWindowClosing, GLFWwindow*); /** * @brief Informs that the main window is deinitializing * * Allows for lifecycle cleanup before ImHex shutdown. * * @param window The window reference */ EVENT_DEF(EventWindowDeinitializing, GLFWwindow*); /** * @brief Signals a theme change in the host OS * * Allows ImHex to react to OS theme changes dynamically during execution. */ EVENT_DEF(EventOSThemeChanged); } /* silent (no-logging) GUI events definitions */ namespace hex { /** * @brief Signals the start of a new ImGui frame */ EVENT_DEF_NO_LOG(EventFrameBegin); /** * @brief Signals the end of an ImGui frame */ EVENT_DEF_NO_LOG(EventFrameEnd); /** * @brief Windows OS: Sets the taskbar icon state * * This event is used on Windows OS to display progress through the taskbar icon (the famous "green loading bar" * in the taskbar). * * @param progressState the progress state (converted from the TaskProgressState enum) * @param progressType the type of progress (converted from the TaskProgressType enum) * @param percentage actual progress percentage (expected from 0 to 100) * * @see hex::ImHexApi::System::TaskProgressState * @see hex::ImHexApi::System::TaskProgressType */ EVENT_DEF_NO_LOG(EventSetTaskBarIconState, u32, u32, u32); /** * @brief Informs of an ImGui element being rendered * * @param elementId the element's ID * @param boundingBox the bounding box (composed of 4 floats) */ EVENT_DEF_NO_LOG(EventImGuiElementRendered, ImGuiID, const std::array&); } ================================================ FILE: lib/libimhex/include/hex/api/events/events_interaction.hpp ================================================ #pragma once #include #include #include #include /* Forward declarations */ namespace hex { class Achievement; } /* Interaction events definitions */ namespace hex { /** * @brief Signals a file was loaded * * FIXME: this event is unused and should be scrapped. * * @param path the loaded file's path */ EVENT_DEF(EventFileLoaded, std::fs::path); /** * @brief Signals a change in the current data * * Enables provider reaction to data change, especially the data inspector. * * This is caused by the following: * - an explicit provider reload, requested by the user (Ctrl+R) * - any user action that results in the creation of an "undo" stack action (generally a data modification) * * @param provider the Provider subject to the data change */ EVENT_DEF(EventDataChanged, prv::Provider *); /** * @brief Signals a change in highlighting * * The event's only purpose is for the Hex editor to clear highlights when receiving this event. */ EVENT_DEF(EventHighlightingChanged); /** * @brief Informs of a provider region being selected * * This is very generally used to signal user actions that select a specific region within the provider. * It is also used to pass on regions when the provider changes. * * @param providerRegion the provider-aware region being selected */ EVENT_DEF(EventRegionSelected, ImHexApi::HexEditor::ProviderRegion); /** * @brief Signals a theme change * * On Windows OS, this is used to reflect the theme color onto the window frame. */ EVENT_DEF(EventThemeChanged); /** * @brief Signals that a bookmark was created * * For now, this event's only purpose is to unlock an achievement. * * @param entry the new bookmark */ EVENT_DEF(EventBookmarkCreated, ImHexApi::Bookmarks::Entry&); /** * @brief Called upon creation of an IPS patch. * As for now, the event only serves a purpose for the achievement unlock. * * @param data the pointer to the patch content's start * @param size the patch data size * @param kind the patch's kind */ EVENT_DEF(EventPatchCreated, const u8*, u64, const PatchKind); /** * @brief Signals the beginning of evaluation of the current pattern * * This allows resetting the drawer view for the pattern data while we wait for the execution completion. */ EVENT_DEF(EventPatternEvaluating); /** * @brief Signals the completion of the pattern evaluation * * This causes another reset in the drawer view, to refresh the table displayed to the user. * * @param code the execution's status code */ EVENT_DEF(EventPatternExecuted, const std::string&); /** * @brief Denotes when pattern editor has changed * * FIXME: this event is unused and should be scrapped. */ EVENT_DEF(EventPatternEditorChanged, const std::string&); /** * @brief Signals that a Content Store item was downloaded * * FIXME: this event is unused and should be scrapped. * * @param path the item's path on the filesystem */ EVENT_DEF(EventStoreContentDownloaded, const std::fs::path&); /** * @brief Signals the removal of a Content Store item * * Note: at the time of the event firing, the item has already been removed from the filesystem. * * FIXME: this event is unused and should be scrapped. * * @param path the item's old file path where it used to be in the filesystem */ EVENT_DEF(EventStoreContentRemoved, const std::fs::path&); /** * @brief Signals the unlocking of an achievement * * This is used by the achievement manager to refresh the achievement display, as well as store progress to * the appropriate storage file. * * @param achievement the achievement that was unlocked */ EVENT_DEF(EventAchievementUnlocked, const Achievement&); /** * @brief Signals a click on the search box * * As there are different behaviours depending on the click (left or right) done by the user, * this allows the consequences of said click to be registered in their own components. * * @param button the ImGuiMouseButton's value */ EVENT_DEF(EventSearchBoxClicked, u32); /** * @brief Updates on whether a file is being dragged into ImHex * * Allows ImGUi to display a file dragging information on screen when a file is being dragged. * * @param isFileDragged true if a file is being dragged */ EVENT_DEF(EventFileDragged, bool); /** * @brief Triggers loading when a file is dropped * * The event fires when a file is dropped into ImHex, which passes it to file handlers to load it. * * @param path the dropped file's path */ EVENT_DEF(EventFileDropped, std::fs::path); } ================================================ FILE: lib/libimhex/include/hex/api/events/events_lifecycle.hpp ================================================ #pragma once #include #include struct ImGuiTestEngine; /* Lifecycle events definitions */ namespace hex { /** * @brief Called when Imhex finished startup, and will enter the main window rendering loop */ EVENT_DEF(EventImHexStartupFinished); /** * @brief Called when the user presses the close button on the main window * * This is currently only used and implemented on macOS */ EVENT_DEF(EventCloseButtonPressed); /** * @brief Called when ImHex is closing, to trigger the last shutdown hooks * * This is the last event to fire before complete graceful shutdown. */ EVENT_DEF(EventImHexClosing); /** * @brief Signals that it's ImHex first launch ever * * This event allows for the launch of the ImHex tutorial (also called Out of Box experience). */ EVENT_DEF(EventFirstLaunch); /** * FIXME: this event is unused and should be scrapped. */ EVENT_DEF(EventAnySettingChanged); /** * @brief Ensures correct plugin cleanup on crash * * This event is fired when catching an unexpected error that cannot be recovered and * which forces Imhex to close immediately. * * Subscribing to this event ensures that the plugin can correctly clean up any mission-critical tasks * before forceful shutdown. * * @param signal the POSIX signal code */ EVENT_DEF(EventAbnormalTermination, int); /** * @brief Informs of the ImHex versions (and difference, if any) * * Called on every startup to inform subscribers of the two versions picked up: * - the version of the previous launch, gathered from the settings file * - the current version, gathered directly from C++ code * * In most cases, and unless ImHex was updated, the two parameters will be the same. * * FIXME: Maybe rename the event to signal a startup information, instead of the misleading * title that the event could be fired when ImHex detects that it was updated since last launch? * * @param previousLaunchVersion ImHex's version during the previous launch * @param currentVersion ImHex's current version for this startup */ EVENT_DEF(EventImHexUpdated, SemanticVersion, SemanticVersion); /** * @brief Called when ImHex managed to catch an error in a general try/catch to prevent/recover from a crash */ EVENT_DEF(EventCrashRecovered, const std::exception &); /** * @brief Called when a project has been loaded */ EVENT_DEF(EventProjectOpened); /** * @brief Called when a project is saved/saved as */ EVENT_DEF(EventProjectSaved); /** * @brief Called when a native message was received from another ImHex instance * @param rawData Raw bytes received from other instance */ EVENT_DEF(EventNativeMessageReceived, std::vector); /** * @brief Called when ImGui is initialized to register tests * @param testEngine Pointer to the ImGui Test Engine Context */ EVENT_DEF(EventRegisterImGuiTests, ImGuiTestEngine*); } ================================================ FILE: lib/libimhex/include/hex/api/events/events_provider.hpp ================================================ #pragma once #include #include /* Provider events definitions */ namespace hex { namespace prv { class Provider; } /** * @brief Called when the provider is created. * This event is responsible for (optionally) initializing the provider and calling EventProviderOpened * (although the event can also be called manually without problem) */ EVENT_DEF(EventProviderCreated, std::shared_ptr); /** * @brief Called as a continuation of EventProviderCreated * this event is normally called immediately after EventProviderCreated successfully initialized the provider. * If no initialization (Provider::skipLoadInterface() has been set), this event should be called manually * If skipLoadInterface failed, this event is not called * * @note this is not related to Provider::open() */ EVENT_DEF(EventProviderOpened, prv::Provider *); /** * @brief Signals a change in provider (in-place) * * Note: if the provider was deleted, the new ("current") provider will be `nullptr` * * @param oldProvider the old provider * @param currentProvider the current provider */ EVENT_DEF(EventProviderChanged, prv::Provider *, prv::Provider *); /** * @brief Signals that a provider was saved * * @param provider the saved provider */ EVENT_DEF(EventProviderSaved, prv::Provider *); /** * @brief Signals a provider is closing * * FIXME: as for now, this behaves as a request more than an event. Also, the boolean is always set to true, * and serves no purpose. This should be moved into the Provider requests section and declared accordingly. * * @param provider the closing provider * @param shouldClose whether the provider should close */ EVENT_DEF(EventProviderClosing, prv::Provider *, bool *); /** * @brief Signals that a provider was closed * * As this is a closure information broadcast, the provider should generally not be accessed, as it could * result in problems. * * @param provider the now-closed provider */ EVENT_DEF(EventProviderClosed, prv::Provider *); /** * @brief Signals that a provider is being deleted * * Provider's data should not be accessed. * * @param provider the provider */ EVENT_DEF(EventProviderDeleted, prv::Provider *); } /* Provider data events definitions */ namespace hex { /** * @brief Signals the dirtying of a provider * * Any data modification that occurs in a provider dirties it, until its state is either saved or restored. * This event signals that fact to subscribers so additional code can be executed for certain cases. */ EVENT_DEF(EventProviderDirtied, prv::Provider *); /** * @brief Signals an insertion of new data into a provider * * @param provider the provider * @param offset the start of the insertion * @param size the new data's size */ EVENT_DEF(EventProviderDataInserted, prv::Provider *, u64, u64); /** * @brief Signals a modification in the provider's data * * @param provider the provider * @param offset the data modification's offset (start address) * @param size the buffer's size * @param buffer the modified data written at this address */ EVENT_DEF(EventProviderDataModified, prv::Provider *, u64, u64, const u8*); /** * @brief Signals a removal of some of the provider's data * * @param provider the provider * @param offset the deletion offset (start address) * @param size the deleted data's size */ EVENT_DEF(EventProviderDataRemoved, prv::Provider *, u64, u64); } ================================================ FILE: lib/libimhex/include/hex/api/events/requests_gui.hpp ================================================ #pragma once #include /* GUI requests definitions */ namespace hex { /** * @brief Requests the opening of a new window. * * @param name the window's name */ EVENT_DEF(RequestOpenWindow, std::string); /** * @brief Centralized request to update ImHex's main window title * * This request can be called to make ImHex refresh its main window title, taking into account a new project * or file opened/closed. */ EVENT_DEF(RequestUpdateWindowTitle); /** * @brief Requests a theme type (light or dark) change * * @param themeType either `Light` or `Dark` */ EVENT_DEF(RequestChangeTheme, std::string); /** * @brief Requests the opening of a popup * * @param name the popup's name */ EVENT_DEF(RequestOpenPopup, std::string); /** * @brief Requests updating of the active post-processing shader * * @param vertexShader the vertex shader source code * @param fragmentShader the fragment shader source code */ EVENT_DEF(RequestSetPostProcessingShader, std::string, std::string); } ================================================ FILE: lib/libimhex/include/hex/api/events/requests_interaction.hpp ================================================ #pragma once #include #include #include /* Forward declarations */ namespace pl::ptrn { class Pattern; } /* Interaction requests definitions */ namespace hex { /** * @brief Requests a selection change in the Hex editor * * This request is handled by the Hex editor, which proceeds to check if the selection is valid. * If it is invalid, the Hex editor fires the `EventRegionSelected` event with nullptr region info. * * @param region the region that should be selected */ EVENT_DEF(RequestHexEditorSelectionChange, ImHexApi::HexEditor::ProviderRegion); /** * @brief Requests the Pattern editor to move selection * * Requests the Pattern editor to move the cursor's position to reflect the user's click or movement. * * @param line the target line * @param column the target column */ EVENT_DEF(RequestPatternEditorSelectionChange, u32, u32); /** * @brief Requests a jump to a given pattern * * This request is fired by the Hex editor when the user asks to jump to the pattern. * It is then caught and reflected by the Pattern data component. * * @param pattern the pattern to jump to */ EVENT_DEF(RequestJumpToPattern, const pl::ptrn::Pattern*); /** * @brief Requests to add a bookmark * * @param region the region to be bookmarked * @param name the bookmark's name * @param comment a comment * @param color the color * @param id the bookmark's unique ID */ EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t, u64*); /** * @brief Requests a bookmark removal * * @param id the bookmark's unique ID */ EVENT_DEF(RequestRemoveBookmark, u64); /** * @brief Request the Pattern editor to set its code * * This request allows the rest of ImHex to interface with the Pattern editor component, by setting its code. * This allows for `.hexpat` file loading, and more. * * @param code the code's string */ EVENT_DEF(RequestSetPatternLanguageCode, std::string); /** * @brief Requests the Pattern editor to run the current code * */ EVENT_DEF(RequestTriggerPatternEvaluation); /** * @brief Requests ImHex to open and process a file * * @param path the file's path */ EVENT_DEF(RequestOpenFile, std::fs::path); /** * @brief Adds a virtual file in the Pattern editor * * @param path the file's path * @param data the file's data * @param region the impacted region */ EVENT_DEF(RequestAddVirtualFile, std::fs::path, std::vector, Region); /** * @brief Requests the command palette to be opened */ EVENT_DEF(RequestOpenCommandPalette); } ================================================ FILE: lib/libimhex/include/hex/api/events/requests_lifecycle.hpp ================================================ #pragma once #include #include /* Lifecycle requests definitions */ namespace hex { /** * @brief Emit a request to add an initialization task to the list * * These tasks will be executed at startup. * * @param name Name of the init task * @param isAsync Whether the task is asynchronous (true if yes) * @param callbackFunction The function to call to execute the task */ EVENT_DEF(RequestAddInitTask, std::string, bool, std::function); /** * @brief Emit a request to add an exit task to the list * * These tasks will be executed during the exit phase. * * FIXME: request is unused and should be scrapped. * * @param name Name of the exit task * @param callbackFunction The function to call to execute the task */ EVENT_DEF(RequestAddExitTask, std::string, std::function); /** * @brief Requests ImHex's graceful shutdown * * If there are no questions (bool set to true), ImHex closes immediately. * If set to false, there is a procedure run to prompt a confirmation to the user. * * @param noQuestions true if no questions */ EVENT_DEF(RequestCloseImHex, bool); /** * @brief Requests ImHex's restart * * This event is necessary for ImHex to restart in the main loop for native and web platforms, * as ImHex cannot simply close and re-open. * * This event serves no purpose on Linux, Windows and macOS platforms. */ EVENT_DEF(RequestRestartImHex); /** * @brief Requests the initialization of theme handlers * * This is called during ImGui bootstrapping, and should not be called at any other time. */ EVENT_DEF(RequestInitThemeHandlers); /** * @brief Send a subcommand to the main Imhex instance * * This request is called to send a subcommand to the main ImHex instance. * This subcommand will then be executed by a handler when ImHex finishing initializing * (`EventImHexStartupFinished`). * * FIXME: change the name so that it is prefixed with "Request" like every other request. * * @param name the subcommand's name * @param data the subcommand's data */ EVENT_DEF(SendMessageToMainInstance, const std::string, const std::vector&); } ================================================ FILE: lib/libimhex/include/hex/api/events/requests_provider.hpp ================================================ #pragma once #include /* Provider requests definitions */ namespace hex { /** * @brief Creates a provider from its unlocalized name, and add it to the provider list */ EVENT_DEF(RequestCreateProvider, std::string, bool, bool, std::shared_ptr *); /** * @brief Used internally when opening a provider through the API */ EVENT_DEF(RequestOpenProvider, std::shared_ptr); /** * @brief Move the data from all PerProvider instances from one provider to another * * The 'from' provider should not have any per provider data after this, and should be immediately deleted * * FIXME: rename with the "Request" prefix to apply standard naming convention. */ EVENT_DEF(MovePerProviderData, prv::Provider *, prv::Provider *); } ================================================ FILE: lib/libimhex/include/hex/api/imhex_api/bookmarks.hpp ================================================ #pragma once #include #include EXPORT_MODULE namespace hex { /* Functions to interact with Bookmarks */ namespace ImHexApi::Bookmarks { struct Entry { Region region; std::string name; std::string comment; u32 color; bool locked; u64 id; }; /** * @brief Adds a new bookmark * @param address The address of the bookmark * @param size The size of the bookmark * @param name The name of the bookmark * @param comment The comment of the bookmark * @param color The color of the bookmark or 0x00 for the default color * @return Bookmark ID */ u64 add(u64 address, size_t size, const std::string &name, const std::string &comment, color_t color = 0x00000000); /** * @brief Adds a new bookmark * @param region The region of the bookmark * @param name The name of the bookmark * @param comment The comment of the bookmark * @param color The color of the bookmark or 0x00 for the default color * @return Bookmark ID */ u64 add(Region region, const std::string &name, const std::string &comment, color_t color = 0x00000000); /** * @brief Removes a bookmark * @param id The ID of the bookmark to remove */ void remove(u64 id); } } ================================================ FILE: lib/libimhex/include/hex/api/imhex_api/fonts.hpp ================================================ #pragma once #include #include #include #include #include #if !defined(HEX_MODULE_EXPORT) struct ImFont; #endif EXPORT_MODULE namespace hex { /* Functions for adding new font types */ namespace ImHexApi::Fonts { struct Offset { float x, y; }; struct MergeFont { std::string name; std::span fontData; Offset offset; std::optional fontSizeMultiplier; }; class Font { public: explicit Font(UnlocalizedString fontName); void push(float size = 0.0F) const; void pushBold(float size = 0.0F) const; void pushItalic(float size = 0.0F) const; void pop() const; [[nodiscard]] operator ImFont*() const; [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_fontName; } private: void push(float size, ImFont *font) const; private: UnlocalizedString m_fontName; }; struct FontDefinition { ImFont* regular; ImFont* bold; ImFont* italic; }; namespace impl { const std::vector& getMergeFonts(); std::map& getFontDefinitions(); } void registerMergeFont(const std::string &name, const std::span &data, Offset offset = {}, std::optional fontSizeMultiplier = std::nullopt); void registerFont(const Font& font); FontDefinition getFont(const UnlocalizedString &fontName); void setDefaultFont(const Font& font); const Font& getDefaultFont(); float getDpi(); float pixelsToPoints(float pixels); float pointsToPixels(float points); } } ================================================ FILE: lib/libimhex/include/hex/api/imhex_api/hex_editor.hpp ================================================ #pragma once #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif /* Functions to query information from the Hex Editor and interact with it */ namespace ImHexApi::HexEditor { using TooltipFunction = std::function; class Highlighting { public: Highlighting() = default; Highlighting(Region region, color_t color); [[nodiscard]] const Region& getRegion() const { return m_region; } [[nodiscard]] const color_t& getColor() const { return m_color; } private: Region m_region = {}; color_t m_color = 0x00; }; class Tooltip { public: Tooltip() = default; Tooltip(Region region, std::string value, color_t color); [[nodiscard]] const Region& getRegion() const { return m_region; } [[nodiscard]] const color_t& getColor() const { return m_color; } [[nodiscard]] const std::string& getValue() const { return m_value; } private: Region m_region = {}; std::string m_value; color_t m_color = 0x00; }; struct ProviderRegion : Region { prv::Provider *provider; [[nodiscard]] prv::Provider *getProvider() const { return this->provider; } [[nodiscard]] Region getRegion() const { return { this->address, this->size }; } }; namespace impl { using HighlightingFunction = std::function(u64, const u8*, size_t, bool)>; using HoveringFunction = std::function(const prv::Provider *, u64, size_t)>; const std::map& getBackgroundHighlights(); const std::map& getBackgroundHighlightingFunctions(); const std::map& getForegroundHighlights(); const std::map& getForegroundHighlightingFunctions(); const std::map& getHoveringFunctions(); const std::map& getTooltips(); const std::map& getTooltipFunctions(); void setCurrentSelection(const std::optional ®ion); void setHoveredRegion(const prv::Provider *provider, const Region ®ion); } /** * @brief Adds a background color highlighting to the Hex Editor * @param region The region to highlight * @param color The color to use for the highlighting * @return Unique ID used to remove the highlighting again later */ u32 addBackgroundHighlight(const Region ®ion, color_t color); /** * @brief Removes a background color highlighting from the Hex Editor * @param id The ID of the highlighting to remove */ void removeBackgroundHighlight(u32 id); /** * @brief Adds a foreground color highlighting to the Hex Editor * @param region The region to highlight * @param color The color to use for the highlighting * @return Unique ID used to remove the highlighting again later */ u32 addForegroundHighlight(const Region ®ion, color_t color); /** * @brief Removes a foreground color highlighting from the Hex Editor * @param id The ID of the highlighting to remove */ void removeForegroundHighlight(u32 id); /** * @brief Adds a hover tooltip to the Hex Editor * @param region The region to add the tooltip to * @param value Text to display in the tooltip * @param color The color of the tooltip * @return Unique ID used to remove the tooltip again later */ u32 addTooltip(Region region, std::string value, color_t color); /** * @brief Removes a hover tooltip from the Hex Editor * @param id The ID of the tooltip to remove */ void removeTooltip(u32 id); /** * @brief Adds a background color highlighting to the Hex Editor using a callback function * @param function Function that draws the highlighting based on the hovered region * @return Unique ID used to remove the highlighting again later */ u32 addTooltipProvider(TooltipFunction function); /** * @brief Removes a background color highlighting from the Hex Editor * @param id The ID of the highlighting to remove */ void removeTooltipProvider(u32 id); /** * @brief Adds a background color highlighting to the Hex Editor using a callback function * @param function Function that draws the highlighting based on the hovered region * @return Unique ID used to remove the highlighting again later */ u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function); /** * @brief Removes a background color highlighting from the Hex Editor * @param id The ID of the highlighting to remove */ void removeBackgroundHighlightingProvider(u32 id); /** * @brief Adds a foreground color highlighting to the Hex Editor using a callback function * @param function Function that draws the highlighting based on the hovered region * @return Unique ID used to remove the highlighting again later */ u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function); /** * @brief Removes a foreground color highlighting from the Hex Editor * @param id The ID of the highlighting to remove */ void removeForegroundHighlightingProvider(u32 id); /** * @brief Adds a hovering provider to the Hex Editor using a callback function * @param function Function that draws the highlighting based on the hovered region * @return Unique ID used to remove the highlighting again later */ u32 addHoverHighlightProvider(const impl::HoveringFunction &function); /** * @brief Removes a hovering color highlighting from the Hex Editor * @param id The ID of the highlighting to remove */ void removeHoverHighlightProvider(u32 id); /** * @brief Checks if there's a valid selection in the Hex Editor right now */ bool isSelectionValid(); /** * @brief Clears the current selection in the Hex Editor */ void clearSelection(); /** * @brief Gets the current selection in the Hex Editor * @return The current selection */ std::optional getSelection(); /** * @brief Sets the current selection in the Hex Editor * @param region The region to select * @param provider The provider to select the region in */ void setSelection(const Region ®ion, prv::Provider *provider = nullptr); /** * @brief Sets the current selection in the Hex Editor * @param region The region to select */ void setSelection(const ProviderRegion ®ion); /** * @brief Sets the current selection in the Hex Editor * @param address The address to select * @param size The size of the selection * @param provider The provider to select the region in */ void setSelection(u64 address, size_t size, prv::Provider *provider = nullptr); /** * @brief Adds a virtual file to the list in the Hex Editor * @param path The path of the file * @param data The data of the file * @param region The location of the file in the Hex Editor if available */ void addVirtualFile(const std::string &path, std::vector data, Region region = Region::Invalid()); /** * @brief Gets the currently hovered cell region in the Hex Editor * @return */ const std::optional& getHoveredRegion(const prv::Provider *provider); } } ================================================ FILE: lib/libimhex/include/hex/api/imhex_api/messaging.hpp ================================================ #pragma once #include #include #include #include #include EXPORT_MODULE namespace hex { /** * Cross-instance messaging system * This allows you to send messages to the "main" instance of ImHex running, from any other instance */ namespace ImHexApi::Messaging { namespace impl { using MessagingHandler = std::function &)>; const std::map& getHandlers(); void runHandler(const std::string &eventName, const std::vector &args); } /** * @brief Register the handler for this specific event name */ void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler); } } ================================================ FILE: lib/libimhex/include/hex/api/imhex_api/provider.hpp ================================================ #pragma once #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { /** * Helper methods about the providers * @note the "current provider" or "currently selected provider" refers to the currently selected provider in the UI; * the provider the user is actually editing. */ namespace ImHexApi::Provider { namespace impl { void resetClosingProvider(); std::set getClosingProviders(); } /** * @brief Gets the currently selected data provider * @return The currently selected data provider, or nullptr is there is none */ prv::Provider *get(); /** * @brief Gets a list of all currently loaded data providers * @return The currently loaded data providers */ std::vector getProviders(); /** * @brief Sets the currently selected data provider * @param index Index of the provider to select */ void setCurrentProvider(i64 index); /** * @brief Sets the currently selected data provider * @param provider The provider to select */ void setCurrentProvider(NonNull provider); /** * @brief Gets the index of the currently selected data provider * @return Index of the selected provider */ i64 getCurrentProviderIndex(); /** * @brief Checks whether the currently selected data provider is valid * @return Whether the currently selected data provider is valid */ bool isValid(); /** * @brief Marks the **currently selected** data provider as dirty */ void markDirty(); /** * @brief Marks **all data providers** as clean */ void resetDirty(); /** * @brief Checks whether **any of the data providers** is dirty * @return Whether any data provider is dirty */ bool isDirty(); /** * @brief Adds a newly created provider to the list of providers, and mark it as the selected one. * @param provider The provider to add * @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation) * @param select Whether to select the provider after adding it */ void add(std::shared_ptr &&provider, bool skipLoadInterface = false, bool select = true); /** * @brief Creates a new provider and adds it to the list of providers * @tparam T The type of the provider to create * @param args Arguments to pass to the provider's constructor */ template T> void add(auto &&...args) { add(std::make_unique(std::forward(args)...)); } /** * @brief Removes a provider from the list of providers * @param provider The provider to remove * @param noQuestions Whether to skip asking the user for confirmation */ void remove(prv::Provider *provider, bool noQuestions = false); /** * @brief Creates a new provider using its unlocalized name and add it to the list of providers * @param unlocalizedName The unlocalized name of the provider to create * @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation) * @param select Whether to select the provider after adding it */ std::shared_ptr createProvider( const UnlocalizedString &unlocalizedName, bool skipLoadInterface = false, bool select = true ); /** * @brief Opens a provider, making its data available to ImHex and handling any error that may occur * @param provider The provider to open */ void openProvider(std::shared_ptr provider); } } ================================================ FILE: lib/libimhex/include/hex/api/imhex_api/system.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #if !defined(HEX_MODULE_EXPORT) using ImGuiID = unsigned int; struct ImVec2; struct ImFontAtlas; #endif struct GLFWwindow; EXPORT_MODULE namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace impl { class AutoResetBase; } #endif /* Functions to interact with various ImHex system settings */ namespace ImHexApi::System { struct ProgramArguments { int argc; char **argv; char **envp; }; struct InitialWindowProperties { i32 x, y; u32 width, height; bool maximized; }; enum class TaskProgressState { Reset, Progress, Flash }; enum class TaskProgressType { Normal, Warning, Error }; namespace impl { void setMainInstanceStatus(bool status); void setMainWindowPosition(i32 x, i32 y); void setMainWindowSize(u32 width, u32 height); void setMainDockSpaceId(ImGuiID id); void setMainWindowHandle(GLFWwindow *window); void setMainWindowFocusState(bool focused); void setGlobalScale(float scale); void setNativeScale(float scale); void setBorderlessWindowMode(bool enabled); void setMultiWindowMode(bool enabled); void setInitialWindowProperties(InitialWindowProperties properties); void setGPUVendor(const std::string &vendor); void setGLRenderer(const std::string &renderer); void setGLVersion(SemanticVersion version); void addInitArgument(const std::string &key, const std::string &value = { }); void setLastFrameTime(double time); bool isWindowResizable(); void addAutoResetObject(hex::impl::AutoResetBase *object); void removeAutoResetObject(hex::impl::AutoResetBase *object); void cleanup(); bool frameRateUnlockRequested(); void resetFrameRateUnlockRequested(); } /** * @brief Closes ImHex * @param noQuestions Whether to skip asking the user for confirmation */ void closeImHex(bool noQuestions = false); /** * @brief Restarts ImHex */ void restartImHex(); /** * @brief Sets the progress bar in the task bar * @param state The state of the progress bar * @param type The type of the progress bar progress * @param progress The progress of the progress bar */ void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress); /** * @brief Gets the current target FPS * @return The current target FPS */ float getTargetFPS(); /** * @brief Sets the target FPS * @param fps The target FPS */ void setTargetFPS(float fps); /** * @brief Gets the current global scale * @return The current global scale */ float getGlobalScale(); /** * @brief Gets the current native scale * @return The current native scale */ float getNativeScale(); float getBackingScaleFactor(); /** * @brief Gets the current main window position * @return Position of the main window */ ImVec2 getMainWindowPosition(); /** * @brief Gets the current main window size * @return Size of the main window */ ImVec2 getMainWindowSize(); /** * @brief Gets the current main dock space ID * @return ID of the main dock space */ ImGuiID getMainDockSpaceId(); /** * @brief Gets the main window's GLFW window handle * @return GLFW window handle */ GLFWwindow* getMainWindowHandle(); /** * @brief Checks if the main window is currently focused * @return Whether the main window is focused */ bool isMainWindowFocused(); /** * @brief Checks if borderless window mode is enabled currently * @return Whether borderless window mode is enabled */ bool isBorderlessWindowModeEnabled(); /** * @brief Checks if multi-window mode is enabled currently * @return Whether multi-window mode is enabled */ bool isMultiWindowModeEnabled(); /** * @brief Gets the init arguments passed to ImHex from the splash screen * @return Init arguments */ const std::map& getInitArguments(); /** * @brief Gets a init arguments passed to ImHex from the splash screen * @param key The key of the init argument * @return Init argument */ std::string getInitArgument(const std::string &key); /** * @brief Sets if ImHex should follow the system theme * @param enabled Whether to follow the system theme */ void enableSystemThemeDetection(bool enabled); /** * @brief Checks if ImHex follows the system theme * @return Whether ImHex follows the system theme */ bool usesSystemThemeDetection(); /** * @brief Gets the currently set additional folder paths * @return The currently set additional folder paths */ const std::vector& getAdditionalFolderPaths(); /** * @brief Sets the additional folder paths * @param paths The additional folder paths */ void setAdditionalFolderPaths(const std::vector &paths); /** * @brief Gets the current GPU vendor * @return The current GPU vendor */ const std::string& getGPUVendor(); /** * @brief Gets the current GPU vendor * @return The current GPU vendor */ const std::string& getGLRenderer(); /** * @brief Gets the current OpenGL version * @return The current OpenGL version */ const SemanticVersion& getGLVersion(); /** * @brief Checks if ImHex is being run in a "Corporate Environment" * This function simply checks for common telltale signs such as if the machine is joined a * domain. It's not super accurate, but it's still useful for statistics * @return True if it is */ bool isCorporateEnvironment(); /** * @brief Checks if ImHex is running in portable mode * @return Whether ImHex is running in portable mode */ bool isPortableVersion(); /** * @brief Gets the current Operating System name * @return Operating System name */ std::string getOSName(); /** * @brief Gets the current Operating System version * @return Operating System version */ std::string getOSVersion(); /** * @brief Gets the current CPU architecture * @return CPU architecture */ std::string getArchitecture(); struct LinuxDistro { std::string name; std::string version; }; /** * @brief Gets information related to the Linux distribution, if running on Linux */ std::optional getLinuxDistro(); /** * @brief Gets the current ImHex version * @return ImHex version */ const SemanticVersion& getImHexVersion(); /** * @brief Gets the current git commit hash * @param longHash Whether to return the full hash or the shortened version * @return Git commit hash */ std::string getCommitHash(bool longHash = false); /** * @brief Gets the current git commit branch * @return Git commit branch */ std::string getCommitBranch(); /** * @brief Gets the time ImHex was built * @return The time ImHex was built */ std::optional getBuildTime(); /** * @brief Checks if ImHex was built in debug mode * @return True if ImHex was built in debug mode, false otherwise */ bool isDebugBuild(); /** * @brief Checks if this version of ImHex is a nightly build * @return True if this version is a nightly, false if it's a release */ bool isNightlyBuild(); /** * @brief Checks if there's an update available for the current version of ImHex * @return Optional string returning the version string of the new version, or std::nullopt if no update is available */ std::optional checkForUpdate(); enum class UpdateType { Stable, Nightly }; /** * @brief Triggers the update process * @param updateType The update channel * @return If the update process was successfully started */ bool updateImHex(UpdateType updateType); /** * @brief Add a new startup task that will be run while ImHex's splash screen is shown * @param name Name to be shown in the UI * @param async Whether to run the task asynchronously * @param function The function to run */ void addStartupTask(const std::string &name, bool async, const std::function &function); /** * @brief Gets the time the previous frame took * @return Previous frame time */ double getLastFrameTime(); /** * @brief Sets the window resizable * @param resizable Whether the window should be resizable */ void setWindowResizable(bool resizable); /** * @brief Checks if this window is the main instance of ImHex * @return True if this is the main instance, false if another instance is already running */ bool isMainInstance(); /** * @brief Gets the initial window properties * @return Initial window properties */ std::optional getInitialWindowProperties(); /** * @brief Gets the module handle of libimhex * @return Module handle */ void* getLibImHexModuleHandle(); /** * Adds a new migration routine that will be executed when upgrading from a lower version than specified in migrationVersion * @param migrationVersion Upgrade point version * @param function Function to run */ void addMigrationRoutine(SemanticVersion migrationVersion, std::function function); /** * @brief Unlocks the frame rate temporarily, allowing animations to run smoothly */ void unlockFrameRate(); /** * @brief Sets the current post-processing shader to use * @param vertexShader The vertex shader to use * @param fragmentShader The fragment shader to use */ void setPostProcessingShader(const std::string &vertexShader, const std::string &fragmentShader); } } ================================================ FILE: lib/libimhex/include/hex/api/layout_manager.hpp ================================================ #pragma once #include #include #if !defined(HEX_MODULE_EXPORT) struct ImGuiTextBuffer; #endif EXPORT_MODULE namespace hex { class LayoutManager { public: struct Layout { std::string name; std::fs::path path; }; using LoadCallback = std::function; using StoreCallback = std::function; /** * @brief Save the current layout * @param name Name of the layout */ static void save(const std::string &name); /** * @brief Load a layout from a file * @param path Path to the layout file */ static void load(const std::fs::path &path); /** * @brief Saves the current layout to a string * @return String containing the layout */ static std::string saveToString(); /** * @brief Load a layout from a string * @param content Layout string */ static void loadFromString(const std::string &content); /** * @brief Get a list of all layouts * @return List of all added layouts */ static const std::vector &getLayouts(); /** * @brief Removes the layout with the given name * @param name Name of the layout */ static void removeLayout(const std::string &name); /** * @brief Handles loading of layouts if needed * @note This function should only be called by ImHex */ static void process(); /** * @brief Reload all layouts */ static void reload(); /** * @brief Reset the layout manager */ static void reset(); /** * @brief Checks is the current layout is locked */ static bool isLayoutLocked(); /** * @brief Locks or unlocks the current layout * @note If the layout is locked, it cannot be modified by the user anymore * @param locked True to lock the layout, false to unlock it */ static void lockLayout(bool locked); /** * @brief Closes all views */ static void closeAllViews(); static void registerLoadCallback(const LoadCallback &callback); static void registerStoreCallback(const StoreCallback &callback); static void onStore(ImGuiTextBuffer *buffer); static void onLoad(std::string_view line); private: LayoutManager() = default; }; } ================================================ FILE: lib/libimhex/include/hex/api/localization_manager.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { struct UnlocalizedString; using LanguageId = std::string; namespace LocalizationManager { struct PathEntry { std::string path; std::function callback; }; struct LanguageDefinition { LanguageId id; std::string name, nativeName; LanguageId fallbackLanguageId; bool hidden; std::vector languageFilePaths; }; void addLanguages(const std::string_view &languageList, std::function callback); void setLanguage(const LanguageId &languageId); [[nodiscard]] const LanguageId& getSelectedLanguageId(); [[nodiscard]] const std::string& get(const LanguageId& languageId, const UnlocalizedString &unlocalizedString); [[nodiscard]] const std::map& getLanguageDefinitions(); [[nodiscard]] const LanguageDefinition& getLanguageDefinition(const LanguageId &languageId); } class LangConst; class Lang { public: explicit Lang(const char *unlocalizedString); explicit Lang(const std::string &unlocalizedString); explicit(false) Lang(const LangConst &localizedString); explicit Lang(const UnlocalizedString &unlocalizedString); explicit Lang(std::string_view unlocalizedString); [[nodiscard]] operator std::string() const; [[nodiscard]] operator std::string_view() const; [[nodiscard]] operator const char *() const; const char* get() const; private: std::size_t m_entryHash; }; class LangConst { public: [[nodiscard]] operator std::string() const; [[nodiscard]] operator std::string_view() const; [[nodiscard]] operator const char *() const; const char* get() const; constexpr static size_t hash(std::string_view string) { constexpr u64 p = 131; constexpr u64 m = std::numeric_limits::max() - 4; u64 total = 0; u64 currentMultiplier = 1; for (char c : string) { total = (total + currentMultiplier * c) % m; currentMultiplier = (currentMultiplier * p) % m; } return total; } private: constexpr explicit LangConst(std::size_t hash, const char *unlocalizedString) : m_entryHash(hash), m_unlocalizedString(unlocalizedString) {} template friend consteval LangConst operator""_lang(); friend class Lang; private: std::size_t m_entryHash; const char *m_unlocalizedString = nullptr; }; struct UnlocalizedString { public: UnlocalizedString() = default; UnlocalizedString(const std::string &string) : m_unlocalizedString(string) { } UnlocalizedString(const char *string) : m_unlocalizedString(string) { } UnlocalizedString(const Lang& arg) = delete; UnlocalizedString(std::string &&string) : m_unlocalizedString(std::move(string)) { } UnlocalizedString(UnlocalizedString &&) = default; UnlocalizedString(const UnlocalizedString &) = default; UnlocalizedString &operator=(const UnlocalizedString &) = default; UnlocalizedString &operator=(UnlocalizedString &&) = default; UnlocalizedString &operator=(const std::string &string) { m_unlocalizedString = string; return *this; } UnlocalizedString &operator=(std::string &&string) { m_unlocalizedString = std::move(string); return *this; } [[nodiscard]] operator std::string() const { return m_unlocalizedString; } [[nodiscard]] operator std::string_view() const { return m_unlocalizedString; } [[nodiscard]] operator const char *() const { return m_unlocalizedString.c_str(); } [[nodiscard]] const std::string &get() const { return m_unlocalizedString; } [[nodiscard]] bool empty() const { return m_unlocalizedString.empty(); } auto operator<=>(const UnlocalizedString &) const = default; auto operator<=>(const std::string &other) const { return m_unlocalizedString <=> other; } private: std::string m_unlocalizedString; }; template [[nodiscard]] consteval LangConst operator""_lang() { return LangConst(LangConst::hash(String.value.data()), String.value.data()); } // {fmt} formatter for hex::Lang and hex::LangConst inline auto format_as(const hex::Lang &entry) { return entry.get(); } inline auto format_as(const hex::LangConst &entry) { return entry.get(); } } template<> struct std::hash { std::size_t operator()(const hex::UnlocalizedString &string) const noexcept { return std::hash{}(string.get()); } }; namespace fmt { template auto format(const hex::Lang &entry, Args &&... args) { return fmt::format(fmt::runtime(entry.get()), std::forward(args)...); } } ================================================ FILE: lib/libimhex/include/hex/api/plugin_manager.hpp ================================================ #pragma once #include #include #include #include #include #include #include #if !defined(HEX_MODULE_EXPORT) struct ImGuiContext; #endif EXPORT_MODULE namespace hex { struct SubCommand { enum class Type : u8 { Option, SubCommand }; std::string commandLong; std::string commandShort; std::string commandDescription; std::function&)> callback; Type type = Type::Option; }; struct Feature { std::string name; bool enabled; }; struct PluginFunctions { using InitializePluginFunc = void (*)(); using InitializeLibraryFunc = void (*)(); using GetPluginNameFunc = const char *(*)(); using GetLibraryNameFunc = const char *(*)(); using GetPluginAuthorFunc = const char *(*)(); using GetPluginDescriptionFunc = const char *(*)(); using GetCompatibleVersionFunc = const char *(*)(); using SetImGuiContextFunc = void (*)(ImGuiContext *); using GetSubCommandsFunc = void* (*)(); using GetFeaturesFunc = void* (*)(); using IsBuiltinPluginFunc = bool (*)(); InitializePluginFunc initializePluginFunction = nullptr; InitializeLibraryFunc initializeLibraryFunction = nullptr; GetPluginNameFunc getPluginNameFunction = nullptr; GetLibraryNameFunc getLibraryNameFunction = nullptr; GetPluginAuthorFunc getPluginAuthorFunction = nullptr; GetPluginDescriptionFunc getPluginDescriptionFunction = nullptr; GetCompatibleVersionFunc getCompatibleVersionFunction = nullptr; SetImGuiContextFunc setImGuiContextFunction = nullptr; SetImGuiContextFunc setImGuiContextLibraryFunction = nullptr; GetSubCommandsFunc getSubCommandsFunction = nullptr; GetFeaturesFunc getFeaturesFunction = nullptr; IsBuiltinPluginFunc isBuiltinPluginFunction = nullptr; }; class Plugin { public: explicit Plugin(const std::fs::path &path); explicit Plugin(const std::string &name, const PluginFunctions &functions); Plugin(const Plugin &) = delete; Plugin(Plugin &&other) noexcept; ~Plugin(); Plugin& operator=(const Plugin &) = delete; Plugin& operator=(Plugin &&other) noexcept; [[nodiscard]] bool initializePlugin() const; [[nodiscard]] std::string getPluginName() const; [[nodiscard]] std::string getPluginAuthor() const; [[nodiscard]] std::string getPluginDescription() const; [[nodiscard]] std::string getCompatibleVersion() const; void setImGuiContext(ImGuiContext *ctx) const; [[nodiscard]] const std::fs::path &getPath() const; [[nodiscard]] bool isLoaded() const; [[nodiscard]] bool isValid() const; [[nodiscard]] bool isInitialized() const; [[nodiscard]] bool isBuiltinPlugin() const; [[nodiscard]] std::span getSubCommands() const; [[nodiscard]] std::span getFeatures() const; [[nodiscard]] bool isLibraryPlugin() const; [[nodiscard]] bool wasAddedManually() const; void setEnabled(bool enabled); private: uintptr_t m_handle = 0; std::fs::path m_path; mutable bool m_initialized = false; bool m_addedManually = false; bool m_enabled = true; PluginFunctions m_functions = {}; template [[nodiscard]] auto getPluginFunction(const std::string &symbol) { return reinterpret_cast(this->getPluginFunction(symbol)); } [[nodiscard]] void *getPluginFunction(const std::string &symbol) const; }; class PluginManager { public: PluginManager() = delete; static bool load(); static bool load(const std::fs::path &pluginFolder); static bool loadLibraries(); static bool loadLibraries(const std::fs::path &libraryFolder); static void unload(); static void reload(); static void initializeNewPlugins(); static void addLoadPath(const std::fs::path &path); static void addPlugin(const std::string &name, PluginFunctions functions); static Plugin* getPlugin(const std::string &name); static const std::list& getPlugins(); static const std::vector& getPluginPaths(); static const std::vector& getPluginLoadPaths(); static bool isPluginLoaded(const std::fs::path &path); static void setPluginEnabled(const Plugin &plugin, bool enabled); private: static std::list& getPluginsMutable(); static AutoReset> s_pluginPaths, s_pluginLoadPaths; static AutoReset> s_loadedLibraries; }; } ================================================ FILE: lib/libimhex/include/hex/api/project_file_manager.hpp ================================================ #pragma once #include /** * @brief Project file manager * * The project file manager is used to load and store project files. It is used by all features of ImHex * that want to store any data to a Project File. * */ EXPORT_MODULE namespace hex { namespace prv { class Provider; } class ProjectFile { public: struct Handler { using Function = std::function; std::fs::path basePath; //< Base path for where to store the files in the project file bool required; //< If true, ImHex will display an error if this handler fails to load or store data Function load, store; //< Functions to load and store data }; struct ProviderHandler { using Function = std::function; std::fs::path basePath; //< Base path for where to store the files in the project file bool required; //< If true, ImHex will display an error if this handler fails to load or store data Function load, store; //< Functions to load and store data }; /** * @brief Set implementations for loading and restoring a project * * @param loadFun function to use to load a project in ImHex * @param storeFun function to use to store a project to disk */ static void setProjectFunctions( const std::function &loadFun, const std::function, bool)> &storeFun ); /** * @brief Load a project file * * @param filePath Path to the project file * @return true if the project file was loaded successfully * @return false if the project file was not loaded successfully */ static bool load(const std::fs::path &filePath); /** * @brief Store a project file * * @param filePath Path to the project file * @param updateLocation update the project location so subssequent saves will save there * @return true if the project file was stored successfully * @return false if the project file was not stored successfully */ static bool store(std::optional filePath = std::nullopt, bool updateLocation = true); /** * @brief Check if a project file is currently loaded * * @return true if a project file is currently loaded * @return false if no project file is currently loaded */ static bool hasPath(); /** * @brief Clear the currently loaded project file */ static void clearPath(); /** * @brief Get the path to the currently loaded project file * @return Path to the currently loaded project file */ static std::fs::path getPath(); /** * @brief Set the path to the currently loaded project file * @param path Path to the currently loaded project file */ static void setPath(const std::fs::path &path); /** * @brief Register a handler for storing and loading global data from a project file * * @param handler The handler to register */ static void registerHandler(const Handler &handler); /** * @brief Register a handler for storing and loading per-provider data from a project file * * @param handler The handler to register */ static void registerPerProviderHandler(const ProviderHandler &handler); /** * @brief Get the list of registered handlers * @return List of registered handlers */ static const std::vector& getHandlers(); /** * @brief Get the list of registered per-provider handlers * @return List of registered per-provider handlers */ static const std::vector& getProviderHandlers(); private: ProjectFile() = default; }; } ================================================ FILE: lib/libimhex/include/hex/api/shortcut_manager.hpp ================================================ #pragma once #include #include #include #include #include #include #include #if !defined(HEX_MODULE_EXPORT) struct ImGuiWindow; #endif struct KeyEquivalent { bool valid; bool ctrl, opt, cmd, shift; int key; }; EXPORT_MODULE namespace hex { class View; class Key { public: constexpr Key() = default; constexpr Key(Keys key) : m_key(static_cast(key)) { } bool operator==(const Key &) const = default; auto operator<=>(const Key &) const = default; [[nodiscard]] constexpr u32 getKeyCode() const { return m_key; } private: u32 m_key = 0; }; constexpr static auto CTRL = Key(static_cast(0x0100'0000)); constexpr static auto ALT = Key(static_cast(0x0200'0000)); constexpr static auto SHIFT = Key(static_cast(0x0400'0000)); constexpr static auto SUPER = Key(static_cast(0x0800'0000)); constexpr static auto CurrentView = Key(static_cast(0x1000'0000)); constexpr static auto AllowWhileTyping = Key(static_cast(0x2000'0000)); constexpr static auto CTRLCMD = Key(static_cast(0x4000'0000)); constexpr static auto ShowOnWelcomeScreen = Key(static_cast(0x8000'0000)); class Shortcut { public: Shortcut() = default; Shortcut(Keys key); explicit Shortcut(std::set keys); Shortcut(const Shortcut &other) = default; Shortcut(Shortcut &&) noexcept = default; constexpr static auto None = Keys(0); Shortcut& operator=(const Shortcut &other) = default; Shortcut& operator=(Shortcut &&) noexcept = default; Shortcut operator+(const Key &other) const; Shortcut &operator+=(const Key &other); bool operator<(const Shortcut &other) const; bool operator==(const Shortcut &other) const; bool isLocal() const; std::string toString() const; KeyEquivalent toKeyEquivalent() const; const std::set& getKeys() const; bool has(Key key) const; bool matches(const Shortcut &other) const; private: friend Shortcut operator+(const Key &lhs, const Key &rhs); std::set m_keys; }; Shortcut operator+(const Key &lhs, const Key &rhs); /** * @brief The ShortcutManager handles global and view-specific shortcuts. * New shortcuts can be constructed using the + operator on Key objects. For example: CTRL + ALT + Keys::A */ class ShortcutManager { public: using Callback = std::function; using EnabledCallback = std::function; struct ShortcutEntry { Shortcut shortcut; std::vector unlocalizedName; Callback callback; EnabledCallback enabledCallback; }; /** * @brief Add a global shortcut. Global shortcuts can be triggered regardless of what view is currently focused * @param shortcut The shortcut to add. * @param unlocalizedName The unlocalized name of the shortcut * @param callback The callback to call when the shortcut is triggered. * @param enabledCallback Callback that's called to check if this shortcut is enabled */ static void addGlobalShortcut(const Shortcut &shortcut, const std::vector &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; }); static void addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; }); /** * @brief Add a view-specific shortcut. View-specific shortcuts can only be triggered when the specified view is focused. * @param view The view to add the shortcut to. * @param shortcut The shortcut to add. * @param unlocalizedName The unlocalized name of the shortcut * @param callback The callback to call when the shortcut is triggered. * @param enabledCallback Callback that's called to check if this shortcut is enabled */ static void addShortcut(View *view, const Shortcut &shortcut, const std::vector &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; }); static void addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; }); /** * @brief Process a key event. This should be called from the main loop. * @param currentView Current view to process * @param ctrl Whether the CTRL key is pressed * @param alt Whether the ALT key is pressed * @param shift Whether the SHIFT key is pressed * @param super Whether the SUPER key is pressed * @param focused Whether the current view is focused * @param keyCode The key code of the key that was pressed */ static void process(const View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode); /** * @brief Process a key event. This should be called from the main loop. * @param ctrl Whether the CTRL key is pressed * @param alt Whether the ALT key is pressed * @param shift Whether the SHIFT key is pressed * @param super Whether the SUPER key is pressed * @param keyCode The key code of the key that was pressed */ static void processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode); /** * @brief Runs the callback of a shortcut as if it was pressed on the keyboard * @param shortcut Shortcut to run * @param view View the shortcut belongs to or nullptr to run a global shortcut * @return True if a callback was executed, false if not */ static bool runShortcut(const Shortcut &shortcut, const View *view = nullptr); /** * @brief Clear all shortcuts */ static void clearShortcuts(); static Shortcut getShortcutByName(const std::vector &unlocalizedName, const View *view = nullptr); static void resumeShortcuts(); static void pauseShortcuts(); static void enableMacOSMode(); [[nodiscard]] static std::optional getLastActivatedMenu(); static void resetLastActivatedMenu(); [[nodiscard]] static std::optional getPreviousShortcut(); [[nodiscard]] static std::vector getGlobalShortcuts(); [[nodiscard]] static std::vector getViewShortcuts(const View *view); [[nodiscard]] static bool updateShortcut(const Shortcut &oldShortcut, Shortcut newShortcut, View *view = nullptr); }; } ================================================ FILE: lib/libimhex/include/hex/api/task_manager.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include EXPORT_MODULE namespace hex { class TaskHolder; class TaskManager; /** * @brief A type representing a running asynchronous task */ class Task { public: Task() = default; Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, bool blocking, std::function function); Task(const Task&) = delete; Task(Task &&other) noexcept; ~Task(); /** * @brief Updates the current process value of the task * @param value Current value */ void update(u64 value); void update() const; void increment(); /** * @brief Sets the maximum value of the task * @param value Maximum value of the task */ void setMaxValue(u64 value); /** * @brief Interrupts the task * For regular Tasks, this just throws an exception to stop the task. * If a custom interrupt callback is set, an exception is thrown and the callback is called. */ void interrupt(); /** * @brief Sets a callback that is called when the task is interrupted * @param callback Callback to be called */ void setInterruptCallback(std::function callback); [[nodiscard]] bool isBackgroundTask() const; [[nodiscard]] bool isBlocking() const; [[nodiscard]] bool isFinished() const; [[nodiscard]] bool hadException() const; [[nodiscard]] bool wasInterrupted() const; [[nodiscard]] bool shouldInterrupt() const; void clearException(); [[nodiscard]] std::string getExceptionMessage() const; [[nodiscard]] const UnlocalizedString &getUnlocalizedName(); [[nodiscard]] u64 getValue() const; [[nodiscard]] u64 getMaxValue() const; void wait() const; private: void finish(); void interruption(); void exception(const char *message); private: mutable std::mutex m_mutex; UnlocalizedString m_unlocalizedName; std::atomic m_currValue = 0, m_maxValue = 0; std::function m_interruptCallback; std::function m_function; std::atomic m_shouldInterrupt = false; std::atomic m_background = true; std::atomic m_blocking = false; std::atomic_flag m_interrupted; std::atomic_flag m_finished; std::atomic_flag m_hadException; std::string m_exceptionMessage; struct TaskInterruptor: public std::exception { TaskInterruptor() { trace::disableExceptionCaptureForCurrentThread(); } virtual ~TaskInterruptor() = default; [[nodiscard]] const char* what() const noexcept override { return "Task Interrupted"; } }; friend class TaskHolder; friend class TaskManager; }; /** * @brief A type holding a weak reference to a Task */ class TaskHolder { public: TaskHolder() = default; explicit TaskHolder(std::weak_ptr task) : m_task(std::move(task)) { } [[nodiscard]] bool isRunning() const; [[nodiscard]] bool hadException() const; [[nodiscard]] bool wasInterrupted() const; [[nodiscard]] bool shouldInterrupt() const; [[nodiscard]] u32 getProgress() const; void interrupt() const; void wait() const; private: std::weak_ptr m_task; }; /** * @brief The Task Manager is responsible for running and managing asynchronous tasks */ class TaskManager { public: TaskManager() = delete; static void init(); static void exit(); constexpr static auto NoProgress = 0; /** * @brief Creates a new asynchronous task that gets displayed in the Task Manager in the footer * @param unlocalizedName Name of the task * @param maxValue Maximum value of the task * @param function Function to be executed * @return A TaskHolder holding a weak reference to the task */ static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function); /** * @brief Creates a new asynchronous task that gets displayed in the Task Manager in the footer * @param unlocalizedName Name of the task * @param maxValue Maximum value of the task * @param function Function to be executed * @return A TaskHolder holding a weak reference to the task */ static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function); /** * @brief Creates a new asynchronous task that does not get displayed in the Task Manager * @param unlocalizedName Name of the task * @param function Function to be executed * @return A TaskHolder holding a weak reference to the task */ static TaskHolder createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function); /** * @brief Creates a new asynchronous task that does not get displayed in the Task Manager * @param unlocalizedName Name of the task * @param function Function to be executed * @return A TaskHolder holding a weak reference to the task */ static TaskHolder createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function); /** * @brief Creates a new asynchronous task that shows a blocking modal window * @param unlocalizedName Name of the task * @param maxValue Maximum value of the task * @param function Function to be executed * @return A TaskHolder holding a weak reference to the task */ static TaskHolder createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function); /** * @brief Creates a new asynchronous task that shows a blocking modal window * @param unlocalizedName Name of the task * @param maxValue Maximum value of the task * @param function Function to be executed * @return A TaskHolder holding a weak reference to the task */ static TaskHolder createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function); /** * @brief Creates a new synchronous task that will execute the given function at the start of the next frame * @param function Function to be executed */ static void doLater(const std::function &function); /** * @brief Creates a new synchronous task that will execute the given function at the start of the next frame * @param function Function to be executed * @param location Source location of the function call. This is used to make sure repeated calls to the function at the same location are only executed once */ static void doLaterOnce(const std::function &function, std::source_location location = std::source_location::current()); /** * @brief Creates a callback that will be executed when all tasks are finished * @param function Function to be executed */ static void runWhenTasksFinished(const std::function &function); /** * @brief Sets the name of the current thread * @param name Name of the thread */ static void setCurrentThreadName(const std::string &name); /** * @brief Gets the name of the current thread * @return Name of the thread */ static std::string_view getCurrentThreadName(); /** * @brief Sets the ID of the main thread * @param threadId ID of the main thread */ static void setMainThreadId(std::thread::id threadId); /** * @brief Checks if the current thread is the main thread * @return True if the current thread is the main thread, false otherwise */ static bool isMainThread(); /** * @brief Cleans up finished tasks */ static void collectGarbage(); static Task& getCurrentTask(); static size_t getRunningTaskCount(); static size_t getRunningBackgroundTaskCount(); static size_t getRunningBlockingTaskCount(); static const std::list>& getRunningTasks(); static void runDeferredCalls(); static void addTaskCompletionCallback(const std::function& function); private: static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function function); }; } ================================================ FILE: lib/libimhex/include/hex/api/theme_manager.hpp ================================================ #pragma once #include #include #include #include #include #include EXPORT_MODULE namespace hex { /** * @brief The Theme Manager takes care of loading and applying themes */ class ThemeManager { public: constexpr static auto NativeTheme = "Native"; using ColorMap = std::map; struct Style { std::variant value; float min; float max; bool needsScaling; }; using StyleMap = std::map; /** * @brief Changes the current theme to the one with the given name * @param name Name of the theme to change to */ static void changeTheme(std::string name); /** * @brief Adds a theme from json data * @param content JSON data of the theme */ static void addTheme(const std::string &content); /** * @brief Adds a theme handler to handle color values loaded from a theme file * @param name Name of the handler * @param colorMap Map of color names to their respective constants * @param getFunction Function to get the color value of a constant * @param setFunction Function to set the color value of a constant */ static void addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function &getFunction, const std::function &setFunction); /** * @brief Adds a style handler to handle style values loaded from a theme file * @param name Name of the handler * @param styleMap Map of style names to their respective constants */ static void addStyleHandler(const std::string &name, const StyleMap &styleMap); static void reapplyCurrentTheme(); static std::vector getThemeNames(); static const std::string &getImageTheme(); static std::optional parseColorString(const std::string &colorString); static nlohmann::json exportCurrentTheme(const std::string &name); static void reset(); static void setAccentColor(const ImColor &color); public: struct ThemeHandler { ColorMap colorMap; std::function getFunction; std::function setFunction; }; struct StyleHandler { StyleMap styleMap; }; static const std::map& getThemeHandlers(); static const std::map& getStyleHandlers(); private: ThemeManager() = default; }; } ================================================ FILE: lib/libimhex/include/hex/api/tutorial_manager.hpp ================================================ #pragma once #include #include #include #include #include #include struct ImRect; EXPORT_MODULE namespace hex { class TutorialManager { public: enum class Position : u8 { None = 0, Top = 1, Bottom = 2, Left = 4, Right = 8 }; using DrawFunction = std::function; struct Tutorial { Tutorial() = delete; Tutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) : m_unlocalizedName(unlocalizedName), m_unlocalizedDescription(unlocalizedDescription) { } struct Step { explicit Step(Tutorial *parent) : m_parent(parent) { } /** * @brief Adds a highlighting with text to a specific element * @param unlocalizedText Unlocalized text to display next to the highlighting * @param ids ID of the element to highlight * @return Current step */ Step& addHighlight(const UnlocalizedString &unlocalizedText, std::initializer_list> &&ids); /** * @brief Adds a highlighting to a specific element * @param ids ID of the element to highlight * @return Current step */ Step& addHighlight(std::initializer_list> &&ids); /** * @brief Sets the text that will be displayed in the tutorial message box * @param unlocalizedTitle Title of the message box * @param unlocalizedMessage Main message of the message box * @param position Position of the message box * @return Current step */ Step& setMessage(const UnlocalizedString &unlocalizedTitle, const UnlocalizedString &unlocalizedMessage, Position position = Position::None); /** * @brief Allows this step to be skipped by clicking on the advance button * @return Current step */ Step& allowSkip(); Step& onAppear(std::function callback); Step& onComplete(std::function callback); /** * @brief Checks if this step is the current step * @return True if this step is the current step */ bool isCurrent() const; /** * @brief Completes this step if it is the current step */ void complete() const; private: struct Highlight { UnlocalizedString unlocalizedText; std::vector> highlightIds; }; struct Message { Position position; UnlocalizedString unlocalizedTitle; UnlocalizedString unlocalizedMessage; bool allowSkip; }; private: void addHighlights() const; void removeHighlights() const; void advance(i32 steps = 1) const; friend class TutorialManager; Tutorial *m_parent; std::vector m_highlights; std::optional m_message; std::function m_onAppear, m_onComplete; DrawFunction m_drawFunction; }; Step& addStep(); const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; } const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; } private: friend class TutorialManager; void start(); UnlocalizedString m_unlocalizedName; UnlocalizedString m_unlocalizedDescription; std::list m_steps; decltype(m_steps)::iterator m_currentStep, m_latestStep; }; static void init(); /** * @brief Gets a list of all tutorials * @return List of all tutorials */ static const std::map& getTutorials(); /** * @brief Gets the currently running tutorial * @return Iterator pointing to the current tutorial */ static std::map::iterator getCurrentTutorial(); /** * @brief Creates a new tutorial that can be started later * @param unlocalizedName Name of the tutorial * @param unlocalizedDescription * @return Reference to created tutorial */ static Tutorial& createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription); /** * @brief Starts the tutorial with the given name * @param unlocalizedName Name of tutorial to start */ static void startTutorial(const UnlocalizedString &unlocalizedName); static void stopCurrentTutorial(); static void startHelpHover(); static void addInteractiveHelpText(std::initializer_list> &&ids, UnlocalizedString unlocalizedString); static void addInteractiveHelpLink(std::initializer_list> &&ids, std::string link); static void setLastItemInteractiveHelpPopup(std::function callback); static void setLastItemInteractiveHelpLink(std::string link); /** * @brief Draws the tutorial * @note This function should only be called by the main GUI */ static void drawTutorial(); /** * @brief Resets the tutorial manager */ static void reset(); static void setRenderer(std::function renderer); static void postElementRendered(ImGuiID id, const ImRect &boundingBox); private: TutorialManager() = delete; static void drawHighlights(); static void drawMessageBox(std::optional message); }; inline TutorialManager::Position operator|(TutorialManager::Position a, TutorialManager::Position b) { return static_cast(static_cast(a) | static_cast(b)); } inline TutorialManager::Position operator&(TutorialManager::Position a, TutorialManager::Position b) { return static_cast(static_cast(a) & static_cast(b)); } } ================================================ FILE: lib/libimhex/include/hex/api/workspace_manager.hpp ================================================ #pragma once #include #include #include #include EXPORT_MODULE namespace hex { class WorkspaceManager { public: struct Workspace { std::string layout; std::fs::path path; bool builtin; }; static void createWorkspace(const std::string &name, const std::string &layout = ""); static void switchWorkspace(const std::string &name); static void importFromFile(const std::fs::path &path); static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}, bool builtin = false); static void removeWorkspace(const std::string &name); static const std::map& getWorkspaces(); static const std::map::iterator& getCurrentWorkspace(); static void reset(); static void reload(); static void process(); private: WorkspaceManager() = default; }; } ================================================ FILE: lib/libimhex/include/hex/api_urls.hpp ================================================ #pragma once constexpr static auto ImHexApiURL = "https://api.werwolv.net/imhex"; constexpr static auto GitHubApiURL = "https://api.github.com/repos/WerWolv/ImHex"; ================================================ FILE: lib/libimhex/include/hex/data_processor/attribute.hpp ================================================ #pragma once #include #include #include #include #include #include namespace hex::dp { class Node; class Attribute { public: enum class Type { Integer, Float, Buffer }; enum class IOType { In, Out }; Attribute(IOType ioType, Type type, UnlocalizedString unlocalizedName); ~Attribute(); [[nodiscard]] int getId() const { return m_id; } void setId(int id) { m_id = id; } [[nodiscard]] IOType getIOType() const { return m_ioType; } [[nodiscard]] Type getType() const { return m_type; } [[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { return m_unlocalizedName; } void addConnectedAttribute(int linkId, Attribute *to) { m_connectedAttributes.insert({ linkId, to }); } void removeConnectedAttribute(int linkId) { m_connectedAttributes.erase(linkId); } [[nodiscard]] std::map &getConnectedAttributes() { return m_connectedAttributes; } [[nodiscard]] Node *getParentNode() const { return m_parentNode; } [[nodiscard]] std::vector& getOutputData() { if (!m_outputData.empty()) return m_outputData; else return m_defaultData; } void clearOutputData() { m_outputData.clear(); } [[nodiscard]] std::vector& getDefaultData() { return m_defaultData; } static void setIdCounter(int id); private: int m_id; IOType m_ioType; Type m_type; UnlocalizedString m_unlocalizedName; std::map m_connectedAttributes; Node *m_parentNode = nullptr; std::vector m_outputData; std::vector m_defaultData; friend class Node; void setParentNode(Node *node) { m_parentNode = node; } static int s_idCounter; }; } ================================================ FILE: lib/libimhex/include/hex/data_processor/link.hpp ================================================ #pragma once namespace hex::dp { class Link { public: Link(int from, int to); [[nodiscard]] int getId() const { return m_id; } void setId(int id) { m_id = id; } [[nodiscard]] int getFromId() const { return m_from; } [[nodiscard]] int getToId() const { return m_to; } static void setIdCounter(int id); private: int m_id; int m_from, m_to; static int s_idCounter; }; } ================================================ FILE: lib/libimhex/include/hex/data_processor/node.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include namespace hex::prv { class Provider; class Overlay; } namespace hex::dp { class Node { public: Node(UnlocalizedString unlocalizedTitle, std::vector attributes); virtual ~Node() = default; [[nodiscard]] int getId() const { return m_id; } void setId(int id) { m_id = id; } [[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { return m_unlocalizedName; } void setUnlocalizedName(const UnlocalizedString &unlocalizedName) { m_unlocalizedName = unlocalizedName; } [[nodiscard]] const UnlocalizedString &getUnlocalizedTitle() const { return m_unlocalizedTitle; } void setUnlocalizedTitle(std::string title) { m_unlocalizedTitle = std::move(title); } [[nodiscard]] std::vector &getAttributes() { return m_attributes; } [[nodiscard]] const std::vector &getAttributes() const { return m_attributes; } void setCurrentOverlay(prv::Overlay *overlay) { m_overlay = overlay; } void draw(); virtual void process() = 0; virtual void reset() { } virtual void store(nlohmann::json &j) const { std::ignore = j; } virtual void load(const nlohmann::json &j) { std::ignore = j; } struct NodeError: public std::exception { Node *node; std::string message; NodeError(Node *node, std::string message) : node(node), message(std::move(message)) {} [[nodiscard]] const char* what() const noexcept override { return this->message.c_str(); } }; void resetOutputData() { for (auto &attribute : m_attributes) attribute.clearOutputData(); } void resetProcessedInputs() { m_processedInputs.clear(); } void setPosition(ImVec2 pos) { m_position = pos; } [[nodiscard]] ImVec2 getPosition() const { return m_position; } static void setIdCounter(int id); const std::vector& getBufferOnInput(u32 index); const i128& getIntegerOnInput(u32 index); const double& getFloatOnInput(u32 index); void setBufferOnOutput(u32 index, std::span data); void setIntegerOnOutput(u32 index, i128 integer); void setFloatOnOutput(u32 index, double floatingPoint); static void interrupt(); protected: virtual void drawNode() { } private: int m_id; UnlocalizedString m_unlocalizedTitle, m_unlocalizedName; std::vector m_attributes; std::set m_processedInputs; prv::Overlay *m_overlay = nullptr; ImVec2 m_position; static int s_idCounter; Attribute& getAttribute(u32 index); Attribute *getConnectedInputAttribute(u32 index); void markInputProcessed(u32 index); void unmarkInputProcessed(u32 index); protected: [[noreturn]] void throwNodeError(const std::string &msg); void setOverlayData(u64 address, const std::vector &data); void setAttributes(std::vector attributes); }; } ================================================ FILE: lib/libimhex/include/hex/helpers/auto_reset.hpp ================================================ #pragma once #include #include namespace hex { namespace impl { class AutoResetBase { public: virtual ~AutoResetBase() = default; private: friend void ImHexApi::System::impl::cleanup(); virtual void reset() = 0; }; } template class AutoReset : public impl::AutoResetBase { public: using Type = T; AutoReset() noexcept { try { ImHexApi::System::impl::addAutoResetObject(this); } catch (std::exception &e) { log::error("Failed to register AutoReset object: {}", e.what()); } } explicit(false) AutoReset(const T &value) : AutoReset() { m_value = value; m_valid = true; } explicit(false) AutoReset(T &&value) noexcept : AutoReset() { m_value = std::move(value); m_valid = true; } ~AutoReset() override { ImHexApi::System::impl::removeAutoResetObject(this); } T* operator->() { return &m_value; } const T* operator->() const { return &m_value; } T& operator*() { return m_value; } const T& operator*() const { return m_value; } operator T&() { return m_value; } operator const T&() const { return m_value; } AutoReset& operator=(const T &value) { m_value = value; m_valid = true; return *this; } AutoReset& operator=(T &&value) noexcept { m_value = std::move(value); m_valid = true; return *this; } [[nodiscard]] bool isValid() const { return m_valid; } private: void reset() override { if constexpr (requires(T t) { t.reset(); }) { m_value.reset(); } else if constexpr (requires(T t) { t.clear(); }) { m_value.clear(); } else if constexpr (std::is_pointer_v) { m_value = nullptr; // cppcheck-suppress nullPointer } else { m_value = { }; } m_valid = false; } private: bool m_valid = true; T m_value; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/binary_pattern.hpp ================================================ #pragma once #include #include #include namespace hex { class BinaryPattern { public: BinaryPattern() = default; explicit BinaryPattern(const std::string &pattern); [[nodiscard]] bool isValid() const; [[nodiscard]] u64 getSize() const; [[nodiscard]] bool matches(const std::vector &bytes) const; [[nodiscard]] bool matchesByte(u8 byte, u32 offset) const; struct Pattern { u8 mask, value; }; private: std::vector m_patterns; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/concepts.hpp ================================================ #pragma once #include #include #include namespace hex { template struct always_false : std::false_type { }; template concept has_size = sizeof(T) == Size; template class ICloneable { public: virtual ~ICloneable() = default; [[nodiscard]] virtual std::unique_ptr clone() const = 0; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/crypto.hpp ================================================ #pragma once #include #include #include #include #include #define CRYPTO_ERROR_INVALID_KEY_LENGTH (-1) #define CRYPTO_ERROR_INVALID_MODE (-2) namespace hex::prv { class Provider; } namespace hex::crypt { void initialize(); void exit(); u8 crc8(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut); u16 crc16(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut); u32 crc32(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut); std::array md5(prv::Provider *&data, u64 offset, size_t size); std::array sha1(prv::Provider *&data, u64 offset, size_t size); std::array sha224(prv::Provider *&data, u64 offset, size_t size); std::array sha256(prv::Provider *&data, u64 offset, size_t size); std::array sha384(prv::Provider *&data, u64 offset, size_t size); std::array sha512(prv::Provider *&data, u64 offset, size_t size); std::array md5(const std::vector &data); std::array sha1(const std::vector &data); std::array sha224(const std::vector &data); std::array sha256(const std::vector &data); std::array sha384(const std::vector &data); std::array sha512(const std::vector &data); std::vector decode64(const std::vector &input); std::vector encode64(const std::vector &input); std::vector decode16(const std::string &input); std::string encode16(const std::vector &input); i128 decodeSleb128(const std::vector &bytes); u128 decodeUleb128(const std::vector &bytes); std::vector encodeSleb128(i128 value); std::vector encodeUleb128(u128 value); enum class AESMode : u8 { ECB = 0, CBC = 1, CFB128 = 2, CTR = 3, GCM = 4, CCM = 5, OFB = 6, XTS = 7 }; enum class KeyLength : u8 { Key128Bits = 0, Key192Bits = 1, Key256Bits = 2 }; wolv::util::Expected, int> aesDecrypt(AESMode mode, KeyLength keyLength, const std::vector &key, std::array nonce, std::array iv, const std::vector &input); } ================================================ FILE: lib/libimhex/include/hex/helpers/debugging.hpp ================================================ #pragma once #include #include #if defined(DEBUG) #define DBG_DEFINE_DEBUG_VARIABLE(type, name) \ static type name; \ hex::dbg::impl::drawDebugVariable(name, WOLV_STRINGIFY(name)); #else #define DBG_DEFINE_DEBUG_VARIABLE(type, name) \ static_assert(false, "Debug variables are only intended for use during development."); #endif namespace hex::trace { struct StackTraceResult; } namespace hex::dbg { namespace impl { bool &getDebugWindowState(); template static void drawDebugVariable(T &variable, std::string_view name) { if (!getDebugWindowState()) return; if (ImGui::Begin("Debug Variables", &getDebugWindowState(), ImGuiWindowFlags_AlwaysAutoResize)) { using Type = std::remove_cvref_t; if constexpr (std::same_as) { ImGui::Checkbox(name.data(), &variable); } else if constexpr (std::integral || std::floating_point) { ImGui::DragScalar(name.data(), ImGuiExt::getImGuiDataType(), &variable); } else if constexpr (std::same_as) { ImGui::DragFloat2(name.data(), &variable.x); } else if constexpr (std::same_as) { ImGui::InputText(name.data(), variable); } else if constexpr (std::same_as) { ImGui::ColorEdit4(name.data(), &variable.Value.x, ImGuiColorEditFlags_AlphaBar); } else { static_assert(hex::always_false::value, "Unsupported type"); } } ImGui::End(); } } bool debugModeEnabled(); void setDebugModeEnabled(bool enabled); void printStackTrace(const trace::StackTraceResult &stackTrace); } ================================================ FILE: lib/libimhex/include/hex/helpers/default_paths.hpp ================================================ #pragma once #include #include #include namespace hex::paths { namespace impl { class DefaultPath { protected: constexpr DefaultPath() = default; virtual ~DefaultPath() = default; public: DefaultPath(const DefaultPath&) = delete; DefaultPath(DefaultPath&&) = delete; DefaultPath& operator=(const DefaultPath&) = delete; DefaultPath& operator=(DefaultPath&&) = delete; virtual std::vector all() const = 0; virtual std::vector read() const; virtual std::vector write() const; }; class ConfigPath : public DefaultPath { public: explicit ConfigPath(std::fs::path postfix) : m_postfix(std::move(postfix)) {} std::vector all() const override; private: std::fs::path m_postfix; }; class DataPath : public DefaultPath { public: explicit DataPath(std::fs::path postfix) : m_postfix(std::move(postfix)) {} std::vector all() const override; std::vector write() const override; private: std::fs::path m_postfix; }; class PluginPath : public DefaultPath { public: explicit PluginPath(std::fs::path postfix) : m_postfix(std::move(postfix)) {} std::vector all() const override; private: std::fs::path m_postfix; }; } std::vector getDataPaths(bool includeSystemFolders); std::vector getConfigPaths(bool includeSystemFolders); const static inline impl::ConfigPath Config("config"); const static inline impl::ConfigPath Recent("recent"); const static inline impl::ConfigPath Updates("updates"); const static inline impl::PluginPath Libraries("lib"); const static inline impl::PluginPath Plugins("plugins"); const static inline impl::DataPath Patterns("patterns"); const static inline impl::DataPath PatternsInclude("includes"); const static inline impl::DataPath Magic("magic"); const static inline impl::DataPath Yara("yara"); const static inline impl::DataPath YaraAdvancedAnalysis("yara/advanced_analysis"); const static inline impl::DataPath Backups("backups"); const static inline impl::DataPath Resources("resources"); const static inline impl::DataPath Constants("constants"); const static inline impl::DataPath Encodings("encodings"); const static inline impl::DataPath Logs("logs"); const static inline impl::DataPath Scripts("scripts"); const static inline impl::DataPath Inspectors("scripts/inspectors"); const static inline impl::DataPath Themes("themes"); const static inline impl::DataPath Nodes("scripts/nodes"); const static inline impl::DataPath Layouts("layouts"); const static inline impl::DataPath Workspaces("workspaces"); const static inline impl::DataPath Disassemblers("disassemblers"); constexpr static inline std::array All = { &Config, &Recent, &Updates, &Libraries, &Plugins, &Patterns, &PatternsInclude, &Magic, &Yara, &YaraAdvancedAnalysis, &Backups, &Resources, &Constants, &Encodings, &Logs, &Scripts, &Inspectors, &Themes, &Nodes, &Layouts, &Workspaces, &Disassemblers }; } ================================================ FILE: lib/libimhex/include/hex/helpers/encoding_file.hpp ================================================ #pragma once #include #include #include #include #include #include namespace hex { class EncodingFile { public: enum class Type { Thingy }; EncodingFile(); EncodingFile(const EncodingFile &other); EncodingFile(EncodingFile &&other) noexcept; EncodingFile(Type type, const std::fs::path &path); EncodingFile(Type type, const std::string &content); EncodingFile& operator=(const EncodingFile &other); EncodingFile& operator=(EncodingFile &&other) noexcept; [[nodiscard]] std::pair getEncodingFor(std::span buffer) const; [[nodiscard]] u64 getEncodingLengthFor(std::span buffer) const; [[nodiscard]] u64 getShortestSequence() const { return m_shortestSequence; } [[nodiscard]] u64 getLongestSequence() const { return m_longestSequence; } [[nodiscard]] std::string decodeAll(std::span buffer) const; [[nodiscard]] bool valid() const { return m_valid; } [[nodiscard]] const std::string& getTableContent() const { return m_tableContent; } [[nodiscard]] const std::string& getName() const { return m_name; } private: void parse(const std::string &content); bool m_valid = false; std::string m_name; std::string m_tableContent; std::unique_ptr, std::string>>> m_mapping; u64 m_shortestSequence = std::numeric_limits::max(); u64 m_longestSequence = std::numeric_limits::min(); }; } ================================================ FILE: lib/libimhex/include/hex/helpers/fmt.hpp ================================================ #pragma once #include #include #include #include #include #if !defined(LIBWOLV_BUILTIN_UINT128) #include #endif ================================================ FILE: lib/libimhex/include/hex/helpers/fs.hpp ================================================ #pragma once #include #include #include #include #include #include EXPORT_MODULE namespace hex::fs { enum class DialogMode { Open, Save, Folder }; struct ItemFilter { // Human-friendly name std::string name; // Extensions that constitute this filter std::string spec; }; void setFileBrowserErrorCallback(const std::function &callback); bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath = {}, bool multiple = false); void openFileExternal(std::fs::path filePath); void openFolderExternal(std::fs::path dirPath); void openFolderWithSelectionExternal(std::fs::path selectedFilePath); bool isPathWritable(const std::fs::path &path); } ================================================ FILE: lib/libimhex/include/hex/helpers/http_requests.hpp ================================================ #pragma once #include #include #include #include #include #include #include #if defined(OS_WEB) #include #endif typedef void CURL; namespace hex { class HttpRequest { public: class ResultBase { public: ResultBase() = default; explicit ResultBase(u32 statusCode) : m_statusCode(statusCode), m_valid(true) { } [[nodiscard]] u32 getStatusCode() const { return m_statusCode; } [[nodiscard]] bool isSuccess() const { return this->getStatusCode() == 200; } [[nodiscard]] bool isValid() const { return m_valid; } private: u32 m_statusCode = 0; bool m_valid = false; }; template class Result : public ResultBase { public: Result() = default; Result(u32 statusCode, T data) : ResultBase(statusCode), m_data(std::move(data)) { } [[nodiscard]] const T& getData() const { return m_data; } private: T m_data; }; HttpRequest(std::string method, std::string url); ~HttpRequest(); HttpRequest(const HttpRequest&) = delete; HttpRequest& operator=(const HttpRequest&) = delete; HttpRequest(HttpRequest &&other) noexcept; HttpRequest& operator=(HttpRequest &&other) noexcept; static void setProxyState(bool enabled); static void setProxyUrl(std::string proxy); void setMethod(std::string method) { m_method = std::move(method); } void setUrl(std::string url) { m_url = std::move(url); } void addHeader(std::string key, std::string value) { m_headers[std::move(key)] = std::move(value); } void setBody(std::string body) { m_body = std::move(body); } void setTimeout(u32 timeout) { m_timeout = timeout; } float getProgress() const { return m_progress; } void cancel() { m_canceled = true; } template std::future> downloadFile(const std::fs::path &path); std::future>> downloadFile(); template std::future> uploadFile(const std::fs::path &path, const std::string &mimeName = "filename"); template std::future> uploadFile(std::vector data, const std::string &mimeName = "filename", const std::fs::path &fileName = "data.bin"); template std::future> execute(); static std::string urlEncode(const std::string &input); static std::string urlDecode(const std::string &input); void setProgress(float progress) { m_progress = progress; } bool isCanceled() const { return m_canceled; } static size_t writeToVector(void *contents, size_t size, size_t nmemb, void *userdata); static size_t writeToFile(void *contents, size_t size, size_t nmemb, void *userdata); template Result executeImpl(std::vector &data); private: static void checkProxyErrors(); void setDefaultConfig(); private: #if defined(OS_WEB) emscripten_fetch_attr_t m_attr; #else CURL *m_curl; #endif std::mutex m_transmissionMutex; std::string m_method; std::string m_url; std::string m_body; std::promise> m_promise; std::map m_headers; u32 m_timeout = 1000; std::atomic m_progress = 0.0F; std::atomic m_canceled = false; }; } #if defined(OS_WEB) #include #else #include #endif ================================================ FILE: lib/libimhex/include/hex/helpers/http_requests_emscripten.hpp ================================================ #pragma once #if defined(OS_WEB) #include #include namespace hex { template std::future> HttpRequest::downloadFile(const std::fs::path &path) { return std::async(std::launch::async, [this, path] { std::vector response; // Execute the request auto result = this->executeImpl(response); // Write the result to the file wolv::io::File file(path, wolv::io::File::Mode::Create); file.writeBuffer(reinterpret_cast(result.getData().data()), result.getData().size()); return result; }); } template std::future> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) { std::ignore = path; std::ignore = mimeName; throw std::logic_error("Not implemented"); } template std::future> HttpRequest::uploadFile(std::vector data, const std::string &mimeName, const std::fs::path &fileName) { std::ignore = data; std::ignore = mimeName; std::ignore = fileName; throw std::logic_error("Not implemented"); } template std::future> HttpRequest::execute() { return std::async(std::launch::async, [this] { std::vector responseData; return this->executeImpl(responseData); }); } template HttpRequest::Result HttpRequest::executeImpl(std::vector &data) { strcpy(m_attr.requestMethod, m_method.c_str()); m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; if (!m_body.empty()) { m_attr.requestData = m_body.c_str(); m_attr.requestDataSize = m_body.size(); } std::vector headers; for (auto it = m_headers.begin(); it != m_headers.end(); it++) { headers.push_back(it->first.c_str()); headers.push_back(it->second.c_str()); } headers.push_back(nullptr); m_attr.requestHeaders = headers.data(); // Send request emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str()); data.resize(fetch->numBytes); std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin()); return Result(fetch->status, { data.begin(), data.end() }); } } #endif ================================================ FILE: lib/libimhex/include/hex/helpers/http_requests_native.hpp ================================================ #pragma once #if !defined(OS_WEB) #include #include #include #include #include #include namespace hex { namespace impl { void setWriteFunctions(CURL *curl, wolv::io::File &file); void setWriteFunctions(CURL *curl, std::vector &data); void setupFileUpload(CURL *curl, wolv::io::File &file, const std::string &fileName, const std::string &mimeName); void setupFileUpload(CURL *curl, const std::vector &data, const std::fs::path &fileName, const std::string &mimeName); int executeCurl(CURL *curl, const std::string &url, const std::string &method, const std::string &body, std::map &headers); long getStatusCode(CURL *curl); std::string getStatusText(int result); } template std::future> HttpRequest::downloadFile(const std::fs::path &path) { return std::async(std::launch::async, [this, path] { std::vector response; wolv::io::File file(path, wolv::io::File::Mode::Create); impl::setWriteFunctions(m_curl, file); return this->executeImpl(response); }); } template std::future> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) { return std::async(std::launch::async, [this, path, mimeName]{ auto fileName = wolv::util::toUTF8String(path.filename()); wolv::io::File file(path, wolv::io::File::Mode::Read); impl::setupFileUpload(m_curl, file, fileName, mimeName); std::vector responseData; impl::setWriteFunctions(m_curl, responseData); return this->executeImpl(responseData); }); } template std::future> HttpRequest::uploadFile(std::vector data, const std::string &mimeName, const std::fs::path &fileName) { return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{ impl::setupFileUpload(m_curl, data, fileName, mimeName); std::vector responseData; impl::setWriteFunctions(m_curl, responseData); return this->executeImpl(responseData); }); } template std::future> HttpRequest::execute() { return std::async(std::launch::async, [this] { std::vector responseData; impl::setWriteFunctions(m_curl, responseData); return this->executeImpl(responseData); }); } template HttpRequest::Result HttpRequest::executeImpl(std::vector &data) { setDefaultConfig(); std::scoped_lock lock(m_transmissionMutex); if (auto result = impl::executeCurl(m_curl, m_url, m_method, m_body, m_headers); result != 0) { log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, m_url, u32(result), impl::getStatusText(result)); checkProxyErrors(); } return Result(impl::getStatusCode(m_curl), { data.begin(), data.end() }); } } #endif ================================================ FILE: lib/libimhex/include/hex/helpers/keys.hpp ================================================ #pragma once #if defined(__cplusplus) enum class Keys { #else enum Keys { #endif Invalid, Space, Apostrophe, Comma, Minus, Period, Slash, Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Semicolon, Equals, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, LeftBracket, Backslash, RightBracket, GraveAccent, World1, World2, Escape, Enter, Tab, Backspace, Insert, Delete, Right, Left, Down, Up, PageUp, PageDown, Home, End, CapsLock, ScrollLock, NumLock, PrintScreen, Pause, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, KeyPad0, KeyPad1, KeyPad2, KeyPad3, KeyPad4, KeyPad5, KeyPad6, KeyPad7, KeyPad8, KeyPad9, KeyPadDecimal, KeyPadDivide, KeyPadMultiply, KeyPadSubtract, KeyPadAdd, KeyPadEnter, KeyPadEqual, Menu }; enum Keys scanCodeToKey(int scanCode); int keyToScanCode(enum Keys key); ================================================ FILE: lib/libimhex/include/hex/helpers/literals.hpp ================================================ #pragma once namespace hex::literals { /* Byte literals */ constexpr static unsigned long long operator""_Bytes(unsigned long long bytes) noexcept { return bytes; } constexpr static unsigned long long operator""_KiB(unsigned long long kiB) noexcept { return operator""_Bytes(kiB * 1024); } constexpr static unsigned long long operator""_MiB(unsigned long long MiB) noexcept { return operator""_KiB(MiB * 1024); } constexpr static unsigned long long operator""_GiB(unsigned long long GiB) noexcept { return operator""_MiB(GiB * 1024); } } ================================================ FILE: lib/libimhex/include/hex/helpers/logger.hpp ================================================ #pragma once #include #include #include #include #include EXPORT_MODULE namespace hex::log { namespace impl { [[nodiscard]] FILE *getDestination(); [[nodiscard]] wolv::io::File& getFile(); [[nodiscard]] bool isRedirected(); [[maybe_unused]] void redirectToFile(); [[maybe_unused]] void enableColorPrinting(); [[nodiscard]] bool isLoggingSuspended(); [[nodiscard]] bool isDebugLoggingEnabled(); void lockLoggerMutex(); void unlockLoggerMutex(); struct LogEntry { std::string_view project; std::string_view level; std::string message; }; const std::vector& getLogEntries(); void addLogEntry(std::string_view project, std::string_view level, std::string message); [[maybe_unused]] void printPrefix(FILE *dest, fmt::text_style ts, std::string_view level, std::string_view projectName); template [[maybe_unused]] void print(fmt::text_style ts, std::string_view level, fmt::format_string fmt, Args && ... args) { if (isLoggingSuspended()) [[unlikely]] return; lockLoggerMutex(); ON_SCOPE_EXIT { unlockLoggerMutex(); }; auto dest = getDestination(); try { printPrefix(dest, ts, level, IMHEX_PROJECT_NAME); auto message = fmt::format(fmt, std::forward(args)...); fmt::print(dest, "{}\n", message); std::fflush(dest); addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message)); } catch (const std::exception&) { /* Ignore any exceptions, we can't do anything anyway */ } } namespace color { fmt::color debug(); fmt::color info(); fmt::color warn(); fmt::color error(); fmt::color fatal(); } } void suspendLogging(); void resumeLogging(); void enableDebugLogging(); template [[maybe_unused]] void debug(fmt::format_string fmt, Args && ... args) { if (impl::isDebugLoggingEnabled()) [[unlikely]] { impl::print(fg(impl::color::debug()) | fmt::emphasis::bold, "[DEBUG]", fmt, std::forward(args)...); } else { impl::addLogEntry(IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt, std::forward(args)...)); } } template [[maybe_unused]] void info(fmt::format_string fmt, Args && ... args) { impl::print(fg(impl::color::info()) | fmt::emphasis::bold, "[INFO] ", fmt, std::forward(args)...); } template [[maybe_unused]] void warn(fmt::format_string fmt, Args && ... args) { impl::print(fg(impl::color::warn()) | fmt::emphasis::bold, "[WARN] ", fmt, std::forward(args)...); } template [[maybe_unused]] void error(fmt::format_string fmt, Args && ... args) { impl::print(fg(impl::color::error()) | fmt::emphasis::bold, "[ERROR]", fmt, std::forward(args)...); } template [[maybe_unused]] void fatal(fmt::format_string fmt, Args && ... args) { impl::print(fg(impl::color::fatal()) | fmt::emphasis::bold, "[FATAL]", fmt, std::forward(args)...); } template [[maybe_unused]] void print(fmt::format_string fmt, Args && ... args) { impl::lockLoggerMutex(); ON_SCOPE_EXIT { impl::unlockLoggerMutex(); }; try { auto dest = impl::getDestination(); fmt::print(dest, fmt, std::forward(args)...); std::fflush(dest); } catch (const std::exception&) { /* Ignore any exceptions, we can't do anything anyway */ } } template [[maybe_unused]] void println(fmt::format_string fmt, Args && ... args) { impl::lockLoggerMutex(); ON_SCOPE_EXIT { impl::unlockLoggerMutex(); }; try { auto dest = impl::getDestination(); fmt::print(dest, fmt, std::forward(args)...); fmt::print("\n"); std::fflush(dest); } catch (const std::exception&) { /* Ignore any exceptions, we can't do anything anyway */ } } } ================================================ FILE: lib/libimhex/include/hex/helpers/magic.hpp ================================================ #pragma once #include #include #include #include #include #include namespace hex::prv { class Provider; } namespace hex::magic { using namespace hex::literals; bool compile(); std::string getDescription(const std::vector &data, bool firstEntryOnly = false); std::string getDescription(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false); std::string getMIMEType(const std::vector &data, bool firstEntryOnly = false); std::string getMIMEType(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false); std::string getExtensions(const std::vector &data, bool firstEntryOnly = false); std::string getExtensions(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false); std::string getAppleCreatorType(const std::vector &data, bool firstEntryOnly = false); std::string getAppleCreatorType(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false); bool isValidMIMEType(const std::string &mimeType); struct FoundPattern { std::fs::path patternFilePath; std::string author; std::string description; std::optional mimeType; std::optional magicOffset; }; std::vector findViablePatterns(prv::Provider *provider, Task* task = nullptr); } ================================================ FILE: lib/libimhex/include/hex/helpers/menu_items.hpp ================================================ #pragma once #include namespace hex::menu { void enableNativeMenuBar(bool enabled); bool isNativeMenuBarUsed(); bool beginMainMenuBar(); void endMainMenuBar(); bool beginMenu(const char *label, bool enabled = true); void endMenu(); bool beginTaskBarMenu(); void endTaskBarMenu(); bool beginMenuEx(const char* label, const char* icon, bool enabled = true); bool menuItem(const char *label, const Shortcut &shortcut = Shortcut::None, bool selected = false, bool enabled = true); bool menuItem(const char *label, const Shortcut &shortcut, bool *selected, bool enabled = true); bool menuItemEx(const char *label, const char *icon, const Shortcut &shortcut = Shortcut::None, bool selected = false, bool enabled = true); bool menuItemEx(const char *label, const char *icon, const Shortcut &shortcut, bool *selected, bool enabled = true); void menuSeparator(); } ================================================ FILE: lib/libimhex/include/hex/helpers/opengl.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include "imgui.h" namespace hex::gl { namespace impl { template GLuint getType() { if constexpr (std::is_same_v) return GL_FLOAT; else if constexpr (std::is_same_v) return GL_UNSIGNED_BYTE; else if constexpr (std::is_same_v) return GL_UNSIGNED_SHORT; else if constexpr (std::is_same_v) return GL_UNSIGNED_INT; else { static_assert(hex::always_false::value, "Unsupported type"); return 0; } } } template class Vector { public: Vector() = default; Vector(const T val) { for (size_t i = 0; i < Size; i++) m_data[i] = val; } Vector(std::array data) : m_data(data) { } Vector(Vector &&other) noexcept : m_data(std::move(other.m_data)) { } Vector(const Vector &other) : m_data(other.m_data) { } T &operator[](size_t index) { return m_data[index]; } const T &operator[](size_t index) const { return m_data[index]; } std::array &asArray() { return m_data; } T *data() { return m_data.data(); } const T *data() const { return m_data.data(); } [[nodiscard]] size_t size() const { return m_data.size(); } auto operator=(const Vector& other) { for (size_t i = 0; i < Size; i++) m_data[i] = other[i]; return *this; } auto operator+=(const Vector& other) { for (size_t i = 0; i < Size; i++) m_data[i] += other.m_data[i]; return *this; } auto operator+=(const T scalar) { for (size_t i = 0; i < Size; i++) m_data[i] += scalar; return *this; } auto operator-=(const Vector &other) { for (size_t i = 0; i < Size; i++) m_data[i] -= other.m_data[i]; return *this; } auto operator-=(const T scalar) { for (size_t i = 0; i < Size; i++) m_data[i] -= scalar; return *this; } Vector operator*=(const T scalar) { for (size_t i = 0; i < Size; i++) m_data[i] *= scalar; return *this; } auto operator*(const T scalar) { auto copy = *this; for (size_t i = 0; i < Size; i++) copy[i] *= scalar; return copy; } auto operator+(const Vector& other) { auto copy = *this; for (size_t i = 0; i < Size; i++) copy[i] += other[i]; return copy; } auto operator-(const Vector& other) { auto copy = *this; for (size_t i = 0; i < Size; i++) copy[i] -= other[i]; return copy; } auto dot(const Vector& other) { T result = 0; for (size_t i = 0; i < Size; i++) result += m_data[i] * other[i]; return result; } auto cross(const Vector& other) { static_assert(Size == 3, "Cross product is only defined for 3D vectors"); return Vector({m_data[1] * other[2] - m_data[2] * other[1], m_data[2] * other[0] - m_data[0] * other[2], m_data[0] * other[1] - m_data[1] * other[0]}); } auto magnitude() { return std::sqrt(this->dot(*this)); } auto normalize() { auto copy = *this; auto length = copy.magnitude(); for (size_t i = 0; i < Size; i++) copy[i] /= length; return copy; } auto operator==(const Vector& other) { for (size_t i = 0; i < Size; i++) if (m_data[i] != other[i]) return false; return true; } private: std::array m_data = { }; }; template class Matrix { public: Matrix(const T &init) { for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) mat[i * Columns + j] = init; } Matrix(const Matrix &A) { mat = A.mat; } virtual ~Matrix() {} size_t getRows() const { return Rows; } size_t getColumns() const { return Columns; } T *data() { return this->mat.data(); } const T *data() const { return this->mat.data(); } T &getElement(int row,int col) { return this->mat[row * Columns + col]; } Vector getColumn(int col) { Vector result; for (size_t i = 0; i < Rows; i++) result[i] = this->mat[i * Columns + col]; return result; } Vector getRow(int row) { Vector result; for (size_t i = 0; i < Columns; i++) result[i] = this->mat[row * Columns+i]; return result; } void updateRow(int row, const Vector &values) { for (size_t i = 0; i < Columns; i++) this->mat[row * Columns + i] = values[i]; } void updateColumn(int col, const Vector &values) { for (size_t i = 0; i < Rows; i++) this->mat[i * Columns + col] = values[i]; } void updateElement(int row, int col, T value) { this->mat[row * Columns + col] = value; } T &operator()(const unsigned &row, const unsigned &col) { return this->mat[row * Columns + col]; } const T &operator()(const unsigned &row, const unsigned &col) const { return this->mat[row * Columns + col]; } Matrix& operator=(const Matrix& A) { if (&A == this) return *this; for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) mat[i*Columns+j] = A(i, j); return *this; } Matrix operator+(const Matrix& A) { Matrix result(0.0); for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) result(i, j) = this->mat[i * Columns + j] + A(i, j); return result; } Matrix operator-(const Matrix& A) { Matrix result(0.0); for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) result(i, j) = this->mat[i * Columns + j] - A(i, j); return result; } static Matrix identity() { Matrix I(0); for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) if(i == j) I.updateElement(i, j, 1); return I; } Matrix transpose() { Matrix t(0); for (size_t i = 0; i < Columns; i++) for (size_t j = 0; j < Rows; j++) t.updateElement(i, j, this->mat[j * Rows + i]); return t; } private: std::array mat; }; template Matrix operator*(const Matrix &A, const Matrix &B) { Matrix result(0.0); for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) for (size_t k = 0; k < OtherDimension; k++) result(i, j) += A(i,k) * B(k, j); return result; } template Matrix operator*(const Vector &a, const Vector &b) { Matrix result(0); for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) result.updateElement(i, j, a[i] * b[j]); return result; } template Vector operator*(const Matrix &A, const Vector &b) { Vector result(0); for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) result[i] += A(i, j) * b[j]; return result; } template Vector operator*(const Vector &b, const Matrix &A) { Vector result(0); for (size_t i = 0; i < Rows; i++) for (size_t j = 0; j < Columns; j++) result[j] += b[i] * A(i, j); return result; } // Convert horizontal (Xh) vertical (Yv) and spin (Zs) angles to a rotation matrix. // Xh: Horizontal rotation, also known as heading or yaw. // Yv: Vertical rotation, also known as pitch or elevation. // Zs: Spin rotation, also known as intrinsic rotation, roll or bank. // Each column of the rotation matrix represents left, up and forward axis. // Angles of rotation are lowercase (x,y,z) in radians and the rotation matrix is uppercase (X,Y,Z). // S = sin, C = cos // The order of rotation is Yaw->Pitch->Roll (Zs*Yv*Xh) // Zs Yv Xh // | Cz -Sz 0 0| |Cy 0 Sy 0| |1 0 0 0| | Cz -Sz 0 0| | Cy Sy*Sx Sy*Cx 0| // | Sz Cz 0 0|*| 0 1 0 0|*|0 Cx -Sx 0| = | Sz Cz 0 0|*| 0 Cx -Sx 0| // | 0 0 1 0| |-Sy 0 Cy 0| |0 Sx Cx 0| | 0 0 1 0| |-Sy Sx*Cy Cx*Cy 0| // | 0 0 0 1| | 0 0 0 1| |0 0 0 1| | 0 0 0 1| | 0 0 0 1| // Left Up Forward // | Cz*Cy Cz*Sy*Sx-Sz*Cx Sz*Sx+Cz*Sy*Cx 0| // | Sz*Cy Sz*Sy*Sx+Cz*Cx Cz*Sy*Sx-Sz*Cx 0| // |-Sy*Cx Cy*Cx Sy 0| // | 0 0 0 1| // The order of rotation is Pitch->Yaw->Roll (Zs*Xh*Yv) // Zs Xh Yv // | Cz -Sz 0 0| |1 0 0 0| |Cy 0 Sy 0| | Cz -Sz 0 0| | Cy 0 Sy 0| // | Sz Cz 0 0|*|0 Cx -Sx 0|*| 0 1 0 0| = | Sz Cz 0 0|*| Sx*Sy Cx -Sx*Cy 0|= // | 0 0 1 0| |0 Sx Cx 0| |-Sy 0 Cy 0| | 0 0 1 0| |-Cx*Sy Sx Cx*Cy 0| // | 0 0 0 1| |0 0 0 1| | 0 0 0 1| | 0 0 0 1| | 0 0 0 1| // Left Up Forward // | Cz*Cy-Sz*Sx*Sy -Sz*Cx Cz*Sy+Sz*Sx*Cy 0| // | Sz*Cy+Cz*Sx*Sy Cz*Cx Sz*Sy-Cz*Sx*Cy 0| // |-Cx*Sy Sx Cx*Cy 0| // | 0 0 0 1| // The order of rotation is Roll->Pitch->Yaw (Xh*Yv*Zs) // Xh Yv Zs // |1 0 0 0| | Cy 0 Sy 0| |Cz -Sz 0 0| |1 0 0 0| | Cy*Cz -Cy*Sz Sy 0| // |0 Cx -Sx 0|*| 0 1 0 0|*|Sz Cz 0 0| = |0 Cx -Sx 0|*| Sz Cz 0 0| // |0 Sx Cx 0| |-Sy 0 Cy 0| | 0 0 1 0| |0 Sx Cx 0| |-Sy*Cz Sy*Sz Cy 0| // |0 0 0 1| | 0 0 0 1| | 0 0 0 1| |0 0 0 1| | 0 0 0 1| // Left Up Forward // | Cy*Cz -Cy*Sz Sy 0| // =| Sx*Sy*Cz+Cx*Sz -Sx*Sy*Sz+Cx*Cz -Sx*Cy 0| // |-Cx*Sy*Cz+Sx*Sz Cx*Sy*Sz+Sx*Cz Cx*Cy 0| // | 0 0 0 1| // just write final answer from here on // The order of rotation is Pitch->Roll->Yaw (Xh*Zs*Yv) // Left Up Forward // |Cz*Cy -Sz Cz*Sy 0| //Xh*Zs*Yv=|Cx*Cy*Sz+Sx*Sy Cx*Cz Cx*Sz*Sy-Cy*Sx 0| // |Cy*Sx*Sz-Cx*Sy Cz*Sx Sx*Sz*Sy+Cx*Cy 0| // |0 0 0 1| // The order of rotation is Roll->Yaw->Pitch (Yv*Xh*Zs) // Left Up Forward // |Cy*Cz+Sy*Sx*Sz Cz*Sy*Sx-Cy*Sz Cx*Sy 0| //Yv*Xh*Zs=|Cx*Sz Cx*Cz -Sx 0| // |Cy*Sx*Sz-Cz*Sy Cy*Cz*Sx+Sy*Sz Cy*Cx 0| // |0 0 0 1| // The order of rotation is Yaw->Roll->Pitch (Yv*Zs*Xh) // Left Up Forward // |Cy*Cz Sy*Sx-Cy*Cx*Sz Cx*Sy+Cy*Sz*Sx 0| //Yv*Zs*Xh= |Sz Cz*Cx -Cz*Sx 0| // |-Cz*Sy Cy*Sx+Cx*Sy*Sz Cy*Cx-Sy*Sz*Sx 0| // |0 0 0 1| enum RotationSequence { XYZ, XZY, YXZ, YZX, ZXY, ZYX }; template Matrix getRotationMatrix(Vector ypr, bool radians, RotationSequence rotationSequence) { Matrix rotation(0); T Sx, Cx, Sy, Cy, Sz, Cz; Vector angles = ypr; if (!radians) angles *= std::numbers::pi_v / 180; Sx = -sin(angles[0]); Cx = cos(angles[0]); Sy = -sin(angles[1]); Cy = cos(angles[1]); Sz = -sin(angles[2]); Cz = cos(angles[2]); switch (rotationSequence) { case ZXY: // | Cz*Cy-Sz*Sx*Sy -Sz*Cx Cz*Sy+Sz*Sx*Cy 0| // | Sz*Cy+Cz*Sx*Sy Cz*Cx Sz*Sy-Cz*Sx*Cy 0| // |-Cx*Sy Sx Cx*Cy 0| // | 0 0 0 1| rotation.updateElement(0, 0, Cz * Cy - Sz * Sx * Sy); rotation.updateElement(0, 1, -Sz * Cx); rotation.updateElement(0, 2, Cz * Sy + Sz * Sx * Cy); rotation.updateElement(1, 0, Sz * Cy + Cz * Sx * Sy); rotation.updateElement(1, 1, Cz * Cx); rotation.updateElement(1, 2, Sz * Sy - Cz * Sx * Cy); rotation.updateElement(2, 0, -Cx * Sy); rotation.updateElement(2, 1, Sx); rotation.updateElement(2, 2, Cx * Cy); break; case ZYX: // | Cz*Cy Cz*Sy*Sx-Sz*Cx Sz*Sx+Cz*Sy*Cx 0| // | Sz*Cy Sz*Sy*Sx+Cz*Cx Sz*Sy*Cx-Cz*Sx 0| // |-Sy Cy*Sx Cy*Cx 0| // | 0 0 0 1| rotation.updateElement(0, 0, Cz * Cy); rotation.updateElement(0, 1, Sx * Sy * Cz - Sz * Cx); rotation.updateElement(0, 2, Sz * Sx + Cz * Sy * Cx); rotation.updateElement(1, 0, Sz * Cy); rotation.updateElement(1, 1, Sz * Sy * Sx + Cz * Cx); rotation.updateElement(1, 2, Sz * Sy * Cx - Cz * Sx); rotation.updateElement(2, 0, -Sy); rotation.updateElement(2, 1, Cy * Sx); rotation.updateElement(2, 2, Cy*Cx); break; case XYZ: // | Cy*Cz -Cy*Sz Sy 0| // =| Sx*Sy*Cz+Cx*Sz -Sx*Sy*Sz+Cx*Cz -Sx*Cy 0| // |-Cx*Sy*Cz+Sx*Sz Cx*Sy*Sz+Sx*Cz Cx*Cy 0| // | 0 0 0 1| rotation.updateElement(0, 0, Cy * Cz); rotation.updateElement(0, 1, -Cy * Sz); rotation.updateElement(0, 2, Sy); rotation.updateElement(1, 0, Sx * Sy * Cz + Cx * Sz); rotation.updateElement(1, 1, -Sx * Sy * Sz + Cx * Cz); rotation.updateElement(1, 2, -Sx * Cy); rotation.updateElement(2, 0, -Cx * Sy * Cz + Sx * Sz); rotation.updateElement(2, 1, Cx * Sy * Sz + Sx * Cz); rotation.updateElement(2, 2, Cx * Cy); break; case XZY: // |Cz*Cy -Sz Cz*Sy 0| //Xh*Zs*Yv=|Cx*Cy*Sz+Sx*Sy Cx*Cz Cx*Sz*Sy-Cy*Sx 0| // |Cy*Sx*Sz-Cx*Sy Cz*Sx Sx*Sz*Sy+Cx*Cy 0| // |0 0 0 1| rotation.updateElement(0, 0, Cy * Cz); rotation.updateElement(0, 1, -Sz); rotation.updateElement(0, 2, Cz * Sy); rotation.updateElement(1, 0, Cx * Cy * Sz + Sx * Sy); rotation.updateElement(1, 1, Cx * Cz); rotation.updateElement(1, 2, Cx * Sy * Sz - Sx * Cy); rotation.updateElement(2, 0, Sx * Cy * Sz - Cx * Sy); rotation.updateElement(2, 1, Sx * Cz); rotation.updateElement(2, 2, Sx * Sy * Sz + Cx * Cy); break; case YXZ: // |Cy*Cz+Sy*Sx*Sz Cz*Sy*Sx-Cy*Sz Cx*Sy 0| //Yv*Xh*Zs=|Cx*Sz Cx*Cz -Sx 0| // |Cy*Sx*Sz-Cz*Sy Cy*Cz*Sx+Sy*Sz Cy*Cx 0| // |0 0 0 1| rotation.updateElement(0, 0, Cy*Cz+Sy*Sx*Sz ); rotation.updateElement(0, 1, Cz*Sy*Sx-Cy*Sz); rotation.updateElement(0, 2, Sy*Cx); rotation.updateElement(1, 0, Cx*Sz); rotation.updateElement(1, 1, Cx*Cz); rotation.updateElement(1, 2, -Sx); rotation.updateElement(2, 0, Cy*Sx*Sz-Cz*Sy); rotation.updateElement(2, 1, Cy*Cz*Sx+Sy*Sz); rotation.updateElement(2, 2, Cy*Cx); break; case YZX: // |Cy*Cz Sy*Sx-Cy*Cx*Sz Cx*Sy+Cy*Sz*Sx 0| //Yv*Zs*Xh= |Sz Cz*Cx -Cz*Sx 0| // |-Cz*Sy Cy*Sx+Cx*Sy*Sz Cy*Cx-Sy*Sz*Sx 0| // |0 0 0 1| rotation.updateElement(0, 0, Cy*Cz); rotation.updateElement(0, 1, Sy*Sx-Cy*Cx*Sz); rotation.updateElement(0, 2, Cx*Sy+Cy*Sz*Sx); rotation.updateElement(1, 0, Sz); rotation.updateElement(1, 1, Cz*Cx); rotation.updateElement(1, 2, -Cz*Sx); rotation.updateElement(2, 0, -Cz*Sy); rotation.updateElement(2, 1, Cy*Sx+Cx*Sy*Sz); rotation.updateElement(2, 2, Cy*Cx-Sy*Sz*Sx); break; } rotation.updateElement(3, 3, 1); return rotation; } template Matrix getRotationMatrixFromVectorAngle(Vector rotationVector, bool radians) { Vector rotationVector3 = {{rotationVector[0], rotationVector[1], rotationVector[2]}}; T theta = rotationVector3.magnitude(); if (!radians) theta *= std::numbers::pi / 180; Vector axis = rotationVector3; if (theta != 0) axis = axis.normalize(); Matrix rotation = Matrix::identity(); T S = sin(theta); T C = cos(theta); T OMC = 1 - C; T a00 = axis[0] * axis[0] * OMC; T a01 = axis[0] * axis[1] * OMC; T a02 = axis[0] * axis[2] * OMC; T a10 = axis[1] * axis[0] * OMC; T a11 = axis[1] * axis[1] * OMC; T a12 = axis[1] * axis[2] * OMC; T a20 = axis[2] * axis[0] * OMC; T a21 = axis[2] * axis[1] * OMC; T a22 = axis[2] * axis[2] * OMC; T a0S = axis[0] * S; T a1S = axis[1] * S; T a2S = axis[2] * S; rotation.updateElement(0, 0, C + a00); rotation.updateElement(0, 1, a01 - a2S); rotation.updateElement(0, 2, a02 + a1S); rotation.updateElement(1, 0, a10 + a2S); rotation.updateElement(1, 1, C + a11); rotation.updateElement(1, 2, a12 - a0S); rotation.updateElement(2, 0, a20 - a1S); rotation.updateElement(2, 1, a21 + a0S); rotation.updateElement(2, 2, C + a22); return rotation; } enum class MatrixElements { r00, r01, r02, r10, r11, r12, r20, r21, r22, }; template T findValue(Vector ypr, MatrixElements matrixElement, RotationSequence rotationSequence) { T Sx, Cx, Sy, Cy, Sz, Cz; Vector angles = ypr; Sx = sin(angles[0]); Cx = cos(angles[0]); Sy = sin(angles[1]); Cy = cos(angles[1]); Sz = sin(angles[2]); Cz = cos(angles[2]); switch (rotationSequence) { case ZXY: switch (matrixElement) { case MatrixElements::r00: return Cz * Cy - Sz * Sx * Sy; case MatrixElements::r01: return -Sz * Cx; case MatrixElements::r02: return Cz * Sy + Sz * Sx * Cy; case MatrixElements::r10: return Sz * Cy + Cz * Sx * Sy; case MatrixElements::r11: return Cz * Cx; case MatrixElements::r12: return Sz * Sy - Cz * Sx * Cy; case MatrixElements::r20: return -Cx * Sy; case MatrixElements::r21: return Sx; case MatrixElements::r22: return Cx * Cy; } break; case ZYX: switch (matrixElement) { case MatrixElements::r00: return Cz * Cy; case MatrixElements::r01: return Sx * Sy * Cz + Cx * Sz; case MatrixElements::r02: return -Cx * Sy * Cz + Sx * Sz; case MatrixElements::r10: return Cz * Sy; case MatrixElements::r11: return Sx * Sy * Sz - Cx * Cz; case MatrixElements::r12: return Cx * Sy * Sz + Sx * Cz; case MatrixElements::r20: return -Sy; case MatrixElements::r21: return Cy * Sx; case MatrixElements::r22: return Cy * Cx; } break; case XYZ: switch (matrixElement) { case MatrixElements::r00: return Cy * Cz; case MatrixElements::r01: return -Cy * Sz; case MatrixElements::r02: return Sy; case MatrixElements::r10: return Sx * Sy * Cz + Cx * Sz; case MatrixElements::r11: return -Sx * Sy * Sz + Cx * Cz; case MatrixElements::r12: return -Sx * Cy; case MatrixElements::r20: return -Cx * Sy * Cz + Sx * Sz; case MatrixElements::r21: return Cx * Sy * Sz + Sx * Cz; case MatrixElements::r22: return Cx * Cy; } break; case XZY: switch (matrixElement) { case MatrixElements::r00: return Cy * Cz; case MatrixElements::r01: return -Sz; case MatrixElements::r02: return Cz * Sy; case MatrixElements::r10: return Cx * Cy * Sz + Sx * Sy; case MatrixElements::r11: return Cx * Cz; case MatrixElements::r12: return Cx * Sy * Sz - Sx * Cy; case MatrixElements::r20: return Sx * Cy * Sz - Cx * Sy; case MatrixElements::r21: return Sx * Cz; case MatrixElements::r22: return Sx * Sy * Sz + Cx * Cy; } break; case YXZ: switch (matrixElement) { case MatrixElements::r00: return Cy * Cz + Sy * Sx * Sz; case MatrixElements::r01: return Cz * Sy * Sx - Cy * Sz; case MatrixElements::r02: return Cx * Sy; case MatrixElements::r10: return Cx * Sz; case MatrixElements::r11: return Cx * Cz; case MatrixElements::r12: return -Sx; case MatrixElements::r20: return -Cz * Sy + Cy * Sx * Sz; case MatrixElements::r21: return Cy * Cz * Sx + Sy * Sz; case MatrixElements::r22: return Cy * Cx; } break; case YZX: switch (matrixElement) { case MatrixElements::r00: return Cy * Cz; case MatrixElements::r01: return Sy * Sx - Cy * Cx * Sz; case MatrixElements::r02: return Cx * Sy + Cy * Sz * Sx; case MatrixElements::r10: return Sz; case MatrixElements::r11: return Cx * Cz; case MatrixElements::r12: return -Cz * Sx; case MatrixElements::r20: return -Cz * Sy; case MatrixElements::r21: return Cy * Sx + Cx * Sy * Sz; case MatrixElements::r22: return Cy * Cx - Sy * Sz * Sx; } break; } return 0; } template Matrix getTransformMatrix(Vector xyz, Vector ypr, bool radians) { Matrix transform( 0); Matrix rotation = getRotationMatrix(ypr, radians); for(int i=0; i<3; i++) for(int j=0; j<3; j++) transform.updateElement(i, j, rotation.getElement(i, j)); transform.updateElement(0,3, xyz[0]); transform.updateElement(1,3, xyz[1]); transform.updateElement(2,3, xyz[2]); transform.updateElement(3,3, 1); return transform; } template Vector getTranslationVector(Matrix transform_matrix) { Vector xyz; xyz.push_back(transform_matrix.getElement(0,3)); xyz.push_back(transform_matrix.getElement(1,3)); xyz.push_back(transform_matrix.getElement(2,3)); return xyz; } template Vector getYprVector(Matrix transform_matrix) { Vector result; Matrix rotation(0); for(int i=0; i<3; i++) for(int j=0; j<3; j++) rotation.updateElement(i, j, transform_matrix.getElement(i, j)); T sy = sqrt(rotation.getElement(0,0) * rotation.getElement(0,0) + rotation.getElement(1,0) * rotation.getElement(1,0) ); bool singular = sy < 1e-6; T x, y, z; if (!singular) { x = atan2(rotation.getElement(1,0), rotation.getElement(0,0)); y = atan2(-rotation.getElement(2,0), sy); z = atan2(rotation.getElement(2,1), rotation.getElement(2,2)); } else { x = 0; y = atan2(-rotation.getElement(2,0), sy); z = atan2(-rotation.getElement(1,2), rotation.getElement(1,1)); } result.push_back(x); result.push_back(y); result.push_back(z); return result; } Matrix GetPerspectiveMatrix( float viewWidth, float viewHeight, float nearVal, float farVal, bool actionType = false); Matrix GetOrthographicMatrix( float viewWidth, float viewHeight, float nearVal, float farVal, bool actionType = false); template static Matrix GetObliqueMatrix( T width, T height,T nearVal,T farVal, bool actionType = false) { int sign =1; if (actionType) sign=-1; Matrix result(0); result.updateElement(0,0,sign * nearVal/width); result.updateElement(1,1, sign * nearVal/height); result.updateElement(2,2,sign * (farVal + nearVal)/( farVal - nearVal )); result.updateElement(3,2,sign * 2*farVal * nearVal/( farVal - nearVal )); result.updateElement(2,3,-sign); return result; } class Shader { public: Shader() = default; Shader(std::string_view vertexSource, std::string_view fragmentSource); ~Shader(); Shader(const Shader&) = delete; Shader(Shader&& other) noexcept; Shader& operator=(const Shader&) = delete; Shader& operator=(Shader&& other) noexcept; void bind() const; void unbind() const; bool isValid() const { return m_program != 0; } void setUniform(std::string_view name, const int &value); void setUniform(std::string_view name, const float &value); bool hasUniform(std::string_view name); template void setUniform(std::string_view name, const Vector &value) { if constexpr (N == 2) glUniform2f(getUniformLocation(name), value[0], value[1]); else if constexpr (N == 3) glUniform3f(getUniformLocation(name), value[0], value[1], value[2]); else if constexpr (N == 4) glUniform4f(getUniformLocation(name), value[0], value[1], value[2],value[3]); } template void setUniform(std::string_view name, Matrix &value){ glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, value.data()); } private: void compile(GLuint shader, std::string_view source) const; GLint getUniformLocation(std::string_view name); private: GLuint m_program = 0; std::map m_uniforms; }; enum class BufferType { Vertex = GL_ARRAY_BUFFER, Index = GL_ELEMENT_ARRAY_BUFFER }; template class Buffer { public: Buffer() = default; Buffer(BufferType type, std::span data); ~Buffer(); Buffer(const Buffer&) = delete; Buffer(Buffer&& other) noexcept; Buffer& operator=(const Buffer&) = delete; Buffer& operator=(Buffer&& other) noexcept; void bind() const; void unbind() const; void draw(unsigned primitive) const; size_t getSize() const; void update(std::span data); private: GLuint m_buffer = 0; size_t m_size = 0; GLuint m_type = 0; }; extern template class Buffer; extern template class Buffer; extern template class Buffer; extern template class Buffer; class VertexArray { public: VertexArray(); ~VertexArray(); VertexArray(const VertexArray&) = delete; VertexArray(VertexArray&& other) noexcept; VertexArray& operator=(const VertexArray&) = delete; VertexArray& operator=(VertexArray&& other) noexcept; template void addBuffer(u32 index, const Buffer &buffer, u32 size = 3) const { glEnableVertexAttribArray(index); buffer.bind(); glVertexAttribPointer(index, size, gl::impl::getType(), GL_FALSE, size * sizeof(T), nullptr); buffer.unbind(); } void bind() const; void unbind() const; private: GLuint m_array = 0; }; class Texture { public: Texture(u32 width, u32 height); ~Texture(); Texture(const Texture&) = delete; Texture(Texture&& other) noexcept; Texture& operator=(const Texture&) = delete; Texture& operator=(Texture&& other) noexcept; void bind() const; void unbind() const; GLuint getTexture() const; u32 getWidth() const; u32 getHeight() const; GLuint release(); private: GLuint m_texture; u32 m_width, m_height; }; class FrameBuffer { public: FrameBuffer(u32 width, u32 height); ~FrameBuffer(); FrameBuffer(const FrameBuffer&) = delete; FrameBuffer(FrameBuffer&& other) noexcept; FrameBuffer& operator=(const FrameBuffer&) = delete; FrameBuffer& operator=(FrameBuffer&& other) noexcept; void bind() const; void unbind() const; void attachTexture(const Texture &texture) const; private: GLuint m_frameBuffer = 0, m_renderBuffer = 0; }; class AxesVectors { public: AxesVectors(); const std::vector& getVertices() const { return m_vertices; } const std::vector& getColors() const { return m_colors; } const std::vector& getIndices() const { return m_indices; } private: std::vector m_vertices; std::vector m_colors; std::vector m_indices; }; class AxesBuffers { public: AxesBuffers(const VertexArray& axesVertexArray, const AxesVectors &axesVectors); const gl::Buffer& getVertices() const { return m_vertices; } const gl::Buffer& getColors() const { return m_colors; } const gl::Buffer& getIndices() const { return m_indices; } private: gl::Buffer m_vertices; gl::Buffer m_colors; gl::Buffer m_indices; }; class GridVectors { public: GridVectors(int sliceCount); u32 getSlices() const { return m_slices; } const std::vector& getVertices() const { return m_vertices; } const std::vector& getColors() const { return m_colors; } const std::vector& getIndices() const { return m_indices; } private: u32 m_slices; std::vector m_vertices; std::vector m_colors; std::vector m_indices; }; class GridBuffers { public: GridBuffers(const VertexArray &gridVertexArray, const GridVectors &gridVectors); const gl::Buffer& getVertices() const { return m_vertices; } const gl::Buffer& getColors() const { return m_colors; } const gl::Buffer& getIndices() const { return m_indices; } private: gl::Buffer m_vertices; gl::Buffer m_colors; gl::Buffer m_indices; }; class LightSourceVectors { public: LightSourceVectors(int res); void moveTo(const Vector &position); const std::vector& getVertices() const { return m_vertices; } const std::vector& getNormals() const { return m_normals; } const std::vector& getColors() const { return m_colors; } const std::vector& getIndices() const { return m_indices; } void setColor(float r, float g, float b) { for (u32 i = 4; i < m_colors.size(); i += 4) { m_colors[i - 4] = r; m_colors[i - 3] = g; m_colors[i - 2] = b; m_colors[i - 1] = 1.0F; } } private: int m_resolution; float m_radius; std::vector m_vertices; std::vector m_normals; std::vector m_colors; std::vector m_indices; }; class LightSourceBuffers { public: LightSourceBuffers(const VertexArray &sourceVertexArray, const LightSourceVectors &sourceVectors); void moveVertices(const VertexArray &sourceVertexArray, const LightSourceVectors& sourceVectors); void updateColors(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors); const gl::Buffer& getVertices() const { return m_vertices; } const gl::Buffer& getNormals() const { return m_normals; } const gl::Buffer& getColors() const { return m_colors; } const gl::Buffer& getIndices() const { return m_indices; } private: gl::Buffer m_vertices; gl::Buffer m_normals; gl::Buffer m_colors; gl::Buffer m_indices; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/patches.hpp ================================================ #pragma once #include #include #include #include namespace hex { namespace prv { class Provider; } enum class IPSError { AddressOutOfRange, PatchTooLarge, InvalidPatchHeader, InvalidPatchFormat, MissingEOF }; enum class PatchKind { IPS, IPS32 }; class Patches { public: Patches() = default; Patches(std::map &&patches) : m_patches(std::move(patches)) {} static wolv::util::Expected fromProvider(hex::prv::Provider *provider); static wolv::util::Expected fromIPSPatch(const std::vector &ipsPatch); static wolv::util::Expected fromIPS32Patch(const std::vector &ipsPatch); wolv::util::Expected, IPSError> toIPSPatch() const; wolv::util::Expected, IPSError> toIPS32Patch() const; const auto& get() const { return m_patches; } auto& get() { return m_patches; } private: std::map m_patches; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/scaling.hpp ================================================ #pragma once #include namespace hex { [[nodiscard]] float operator""_scaled(long double value); [[nodiscard]] float operator""_scaled(unsigned long long value); [[nodiscard]] ImVec2 scaled(const ImVec2 &vector); [[nodiscard]] ImVec2 scaled(float x, float y); } ================================================ FILE: lib/libimhex/include/hex/helpers/semantic_version.hpp ================================================ #pragma once #include #include #include #include EXPORT_MODULE namespace hex { class SemanticVersion { public: SemanticVersion() = default; SemanticVersion(u32 major, u32 minor, u32 patch); SemanticVersion(std::string version); SemanticVersion(std::string_view version); SemanticVersion(const char *version); std::strong_ordering operator<=>(const SemanticVersion &) const; bool operator==(const SemanticVersion &other) const; u32 major() const; u32 minor() const; u32 patch() const; bool nightly() const; const std::string& buildType() const; bool isValid() const; std::string get(bool withBuildType = true) const; private: std::vector m_parts; std::string m_buildType; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/tar.hpp ================================================ #pragma once #include #include #include struct mtar_t; namespace hex { class Tar { public: enum class Mode { Read, Write, Create }; Tar() = default; Tar(const std::fs::path &path, Mode mode); ~Tar(); Tar(const Tar&) = delete; Tar(Tar&&) noexcept; Tar &operator=(Tar &&other) noexcept; void close(); /** * @brief get the error string explaining the error that occurred when opening the file. * This error is a combination of the tar error and the native file open error */ std::string getOpenErrorString() const; [[nodiscard]] std::vector readVector(const std::fs::path &path) const; [[nodiscard]] std::string readString(const std::fs::path &path) const; void writeVector(const std::fs::path &path, const std::vector &data) const; void writeString(const std::fs::path &path, const std::string &data) const; [[nodiscard]] std::vector listEntries(const std::fs::path &basePath = "/") const; [[nodiscard]] bool contains(const std::fs::path &path) const; void extract(const std::fs::path &path, const std::fs::path &outputPath) const; void extractAll(const std::fs::path &outputPath) const; [[nodiscard]] bool isValid() const { return m_valid; } private: std::unique_ptr m_ctx; std::fs::path m_path; bool m_valid = false; // These will be updated when the constructor is called int m_tarOpenErrno = 0; int m_fileOpenErrno = 0; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/types.hpp ================================================ #pragma once #include #include #include #include #include using namespace wolv::unsigned_integers; using namespace wolv::signed_integers; using color_t = u32; namespace hex { struct Region { u64 address; u64 size; [[nodiscard]] constexpr bool isWithin(const Region &other) const { if (*this == Invalid() || other == Invalid()) return false; if (this->getStartAddress() >= other.getStartAddress() && this->getEndAddress() <= other.getEndAddress()) return true; return false; } [[nodiscard]] constexpr bool overlaps(const Region &other) const { if (*this == Invalid() || other == Invalid()) return false; if (this->getEndAddress() >= other.getStartAddress() && this->getStartAddress() <= other.getEndAddress()) return true; return false; } [[nodiscard]] constexpr u64 getStartAddress() const { return this->address; } [[nodiscard]] constexpr u64 getEndAddress() const { if (this->size == 0) return this->address; else return this->address + this->size - 1; } [[nodiscard]] constexpr size_t getSize() const { return this->size; } [[nodiscard]] constexpr bool operator==(const Region &other) const { return this->address == other.address && this->size == other.size; } constexpr static Region Invalid() { return { 0, 0 }; } constexpr bool operator<(const Region &other) const { return this->address < other.address; } }; template concept Pointer = std::is_pointer_v; template struct NonNull { NonNull(T ptr) : pointer(ptr) { } NonNull(std::nullptr_t) = delete; NonNull(std::integral auto) = delete; NonNull(bool) = delete; [[nodiscard]] T get() const { return pointer; } [[nodiscard]] T operator->() const { return pointer; } [[nodiscard]] std::remove_pointer_t operator*() const { return *pointer; } [[nodiscard]] operator T() const { return pointer; } T pointer; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/udp_server.hpp ================================================ #pragma once #include #include #include #include #include namespace hex { class UDPServer { public: using Callback = std::function data)>; UDPServer() = default; UDPServer(u16 port, Callback callback); ~UDPServer(); UDPServer(const UDPServer&) = delete; UDPServer& operator=(const UDPServer&) = delete; UDPServer(UDPServer &&other) noexcept { m_port = other.m_port; m_callback = std::move(other.m_callback); m_thread = std::move(other.m_thread); m_running = other.m_running.load(); other.m_running = false; m_socketFd = other.m_socketFd; other.m_socketFd = -1; } UDPServer& operator=(UDPServer &&other) noexcept { if (this != &other) { m_port = other.m_port; m_callback = std::move(other.m_callback); m_thread = std::move(other.m_thread); m_running = other.m_running.load(); other.m_running = false; m_socketFd = other.m_socketFd; other.m_socketFd = -1; } return *this; } void start(); void stop(); [[nodiscard]] u16 getPort() const { return m_port; } private: void run(); u16 m_port = 0; Callback m_callback; std::thread m_thread; std::atomic m_running; int m_socketFd = -1; }; } ================================================ FILE: lib/libimhex/include/hex/helpers/utils.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(OS_MACOS) #include #elif defined(OS_LINUX) #include #endif #include #include namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif template [[nodiscard]] std::vector> sampleChannels(const std::vector &data, size_t count, size_t channels) { if (channels == 0) return {}; size_t signalLength = std::max(1.0, double(data.size()) / channels); size_t stride = std::max(1.0, double(signalLength) / count); std::vector> result; result.resize(channels); for (size_t i = 0; i < channels; i++) { result[i].reserve(count); } result.reserve(count); for (size_t i = 0; i < data.size(); i += stride) { for (size_t j = 0; j < channels; j++) { result[j].push_back(data[i + j]); } } return result; } template [[nodiscard]] std::vector sampleData(const std::vector &data, size_t count) { size_t stride = std::max(1.0, double(data.size()) / count); std::vector result; result.reserve(count); for (size_t i = 0; i < data.size(); i += stride) { result.push_back(data[i]); } return result; } template [[nodiscard]] std::vector operator|(const std::vector &lhs, const std::vector &rhs) { std::vector result; std::copy(lhs.begin(), lhs.end(), std::back_inserter(result)); std::copy(rhs.begin(), rhs.end(), std::back_inserter(result)); return result; } [[nodiscard]] std::string to_string(u128 value); [[nodiscard]] std::string to_string(i128 value); [[nodiscard]] std::string toLower(std::string string); [[nodiscard]] std::string toUpper(std::string string); [[nodiscard]] std::vector parseHexString(std::string string); [[nodiscard]] std::optional parseBinaryString(const std::string &string); [[nodiscard]] std::string toByteString(u64 bytes); [[nodiscard]] std::string makePrintable(u8 c); void startProgram(const std::vector &command); int executeCommand(const std::string &command); std::optional executeCommandWithOutput(const std::string &command); void executeCommandDetach(const std::string &command); void openWebpage(std::string url); extern "C" void registerFont(const char *fontName, const char *fontPath); const std::map& getFonts(); [[nodiscard]] std::string encodeByteString(const std::vector &bytes); [[nodiscard]] std::vector decodeByteString(const std::string &string); [[nodiscard]] std::wstring utf8ToUtf16(const std::string& utf8); [[nodiscard]] std::string utf16ToUtf8(const std::wstring& utf16); [[nodiscard]] constexpr u64 extract(u8 from, u8 to, const auto &value) { if (from < to) std::swap(from, to); using ValueType = std::remove_cvref_t; ValueType mask = (std::numeric_limits::max() >> (((sizeof(value) * 8) - 1) - (from - to))) << to; return u64((value & mask) >> to); } [[nodiscard]] inline u64 extract(u32 from, u32 to, const std::vector &bytes) { u8 index = 0; while (from > 32 && to > 32) { from -= 8; to -= 8; index++; } u64 value = 0; std::memcpy(&value, &bytes[index], std::min(sizeof(value), bytes.size() - index)); u64 mask = (std::numeric_limits::max() >> (64 - (from + 1))); return (value & mask) >> to; } [[nodiscard]] constexpr i128 signExtend(size_t numBits, i128 value) { i128 mask = 1ULL << (numBits - 1); return (value ^ mask) - mask; } template [[nodiscard]] constexpr T swapBitOrder(size_t numBits, T value) { T result = 0x00; for (size_t bit = 0; bit < numBits; bit++) { result <<= 1; result |= (value & (1 << bit)) != 0; } return result; } [[nodiscard]] constexpr size_t strnlen(const char *s, size_t n) { size_t i = 0; while (i < n && s[i] != '\x00') i++; return i; } template struct SizeTypeImpl { }; template<> struct SizeTypeImpl<1> { using Type = u8; }; template<> struct SizeTypeImpl<2> { using Type = u16; }; template<> struct SizeTypeImpl<4> { using Type = u32; }; template<> struct SizeTypeImpl<8> { using Type = u64; }; template<> struct SizeTypeImpl<16> { using Type = u128; }; template using SizeType = typename SizeTypeImpl::Type; template [[nodiscard]] constexpr T changeEndianness(const T &value, size_t size, std::endian endian) { if (endian == std::endian::native) return value; size = std::min(size, sizeof(T)); std::array data = { 0 }; std::memcpy(&data[0], &value, size); for (uint32_t i = 0; i < size / 2; i++) { std::swap(data[i], data[size - 1 - i]); } T result = { }; std::memcpy(&result, &data[0], size); return result; } template [[nodiscard]] constexpr T changeEndianness(const T &value, std::endian endian) { return changeEndianness(value, sizeof(value), endian); } [[nodiscard]] constexpr u128 bitmask(u8 bits) { return u128(-1) >> (128 - bits); } template [[nodiscard]] constexpr T bit_width(T x) noexcept { return std::numeric_limits::digits - std::countl_zero(x); } template [[nodiscard]] constexpr T bit_ceil(T x) noexcept { if (x <= 1u) return T(1); return T(1) << bit_width(T(x - 1)); } template [[nodiscard]] auto powi(T base, U exp) { using ResultType = decltype(T{} * U{}); if (exp < 0) return ResultType(0); ResultType result = 1; while (exp != 0) { if ((exp & 0b1) == 0b1) result *= base; exp >>= 1; base *= base; } return result; } template void moveToVector(std::vector &buffer, T &&first, Args &&...rest) { buffer.push_back(std::move(first)); if constexpr (sizeof...(rest) > 0) moveToVector(buffer, std::move(rest)...); } template [[nodiscard]] std::vector moveToVector(T &&first, Args &&...rest) { std::vector result; moveToVector(result, T(std::move(first)), std::move(rest)...); return result; } [[nodiscard]] std::string toEngineeringString(double value); [[nodiscard]] inline std::vector parseByteString(const std::string &string) { auto byteString = std::string(string); std::erase(byteString, ' '); if ((byteString.length() % 2) != 0) return {}; std::vector result; for (u32 i = 0; i < byteString.length(); i += 2) { if (!std::isxdigit(byteString[i]) || !std::isxdigit(byteString[i + 1])) return {}; auto value = wolv::util::from_chars(byteString.substr(i, 2), 16); if (!value.has_value()) return {}; result.push_back(*value); } return result; } [[nodiscard]] std::string toBinaryString(std::unsigned_integral auto number) { if (number == 0) return "0"; std::string result; for (i16 bit = hex::bit_width(number) - 1; bit >= 0; bit -= 1) result += (number & (0b1LLU << bit)) == 0 ? '0' : '1'; return result; } template [[nodiscard]] constexpr float customFloatToFloat32(u32 value) { static_assert(ExponentBits <= 8, "ExponentBits must be less than 8"); static_assert(ExponentBits + MantissaBits + 1 <= 32, "Format doesn't fit into a 32-bit float"); const u32 sign = value >> (ExponentBits + MantissaBits); const u32 exponent = (value >> MantissaBits) & ((1u << ExponentBits) - 1); u32 mantissa = value & ((1u << MantissaBits) - 1); // Calculate the bias for the input format and IEEE-754 float32 i32 inputBias = (1 << (ExponentBits - 1)) - 1; i32 float32Bias = 127; u32 result = 0; if (exponent == 0) { if (mantissa == 0) { // Zero result = sign << 31; } else { // Subnormal int shift = 0; while ((mantissa & (1u << MantissaBits)) == 0) { mantissa <<= 1; shift++; } mantissa &= ((1u << MantissaBits) - 1); // clear implicit bit int adjustedExp = float32Bias - inputBias - shift + 1; result = (sign << 31) | (adjustedExp << 23) | (mantissa << (23 - MantissaBits)); } } else if (exponent == ((1u << ExponentBits) - 1)) { // Inf or NaN result = (sign << 31) | (0xFF << 23) | (mantissa << (23 - MantissaBits)); } else { // Normalized number int adjustedExp = exponent - inputBias + float32Bias; result = (sign << 31) | (adjustedExp << 23) | (mantissa << (23 - MantissaBits)); } float floatResult; std::memcpy(&floatResult, &result, sizeof(float)); return floatResult; } [[nodiscard]] constexpr float float16ToFloat32(u16 float16) { return customFloatToFloat32<5, 10>(float16); } [[nodiscard]] inline bool equalsIgnoreCase(std::string_view left, std::string_view right) { return std::equal(left.begin(), left.end(), right.begin(), right.end(), [](char a, char b) { return tolower(a) == tolower(b); }); } [[nodiscard]] inline bool containsIgnoreCase(std::string_view a, std::string_view b) { auto iter = std::search(a.begin(), a.end(), b.begin(), b.end(), [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }); return iter != a.end(); } template [[nodiscard]] T get_or(const std::variant &variant, T alt) { const T *value = std::get_if(&variant); if (value == nullptr) return alt; else return *value; } template [[nodiscard]] T alignTo(T value, T alignment) { T remainder = value % alignment; return remainder != 0 ? value + (alignment - remainder) : value; } [[nodiscard]] std::optional hexCharToValue(char c); [[nodiscard]] bool isProcessElevated(); [[nodiscard]] std::optional getEnvironmentVariable(const std::string &env); [[nodiscard]] std::string limitStringLength(const std::string &string, size_t maxLength, bool fromBothEnds = true); [[nodiscard]] std::optional getInitialFilePath(); [[nodiscard]] std::string generateHexView(u64 offset, u64 size, prv::Provider *provider); [[nodiscard]] std::string generateHexView(u64 offset, const std::vector &data); [[nodiscard]] std::string formatSystemError(i32 error); /** * Gets the shared library handle for a given pointer * @param symbol Pointer to any function or variable in the shared library * @return The module handle * @warning Important! Calling this function on functions defined in other modules will return the handle of the current module! * This is because you're not actually passing a pointer to the function in the other module but rather a pointer to a thunk * that is defined in the current module. */ [[nodiscard]] void* getContainingModule(void* symbol); [[nodiscard]] std::optional blendColors(const std::optional &a, const std::optional &b); std::optional parseTime(std::string_view format, const std::string &timeString); std::optional getOSLanguage(); void showErrorMessageBox(const std::string &message); void showToastMessage(const std::string &title, const std::string &message); } ================================================ FILE: lib/libimhex/include/hex/helpers/utils_linux.hpp ================================================ #pragma once #if defined(OS_LINUX) namespace hex { void executeCmd(const std::vector &argsVector); } #endif ================================================ FILE: lib/libimhex/include/hex/helpers/utils_macos.hpp ================================================ #pragma once #include #if defined(OS_MACOS) #if !defined(HEX_MODULE_EXPORT) struct GLFWwindow; #endif extern "C" { void errorMessageMacos(const char *message); void openWebpageMacos(const char *url); bool isMacosSystemDarkModeEnabled(); bool isMacosFullScreenModeEnabled(GLFWwindow *window); float getBackingScaleFactor(); void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode); void enumerateFontsMacos(); void macosHandleTitlebarDoubleClickGesture(GLFWwindow *window); void macosSetWindowMovable(GLFWwindow *window, bool movable); bool macosIsWindowBeingResizedByUser(GLFWwindow *window); void macosMarkContentEdited(GLFWwindow *window, bool edited = true); void macosGetKey(Keys key, int *output); bool macosIsMainInstance(); void macosSendMessageToMainInstance(const unsigned char *data, size_t size); void macosInstallEventListener(); void toastMessageMacos(const char *title, const char *message); void macosSetupDockMenu(void); } #endif ================================================ FILE: lib/libimhex/include/hex/mcp/client.hpp ================================================ #pragma once #include namespace hex::mcp { class Client { public: Client() = default; ~Client() = default; int run(std::istream &input, std::ostream &output); }; } ================================================ FILE: lib/libimhex/include/hex/mcp/server.hpp ================================================ #pragma once #include #include #include #include namespace hex::mcp { class JsonRpc { public: explicit JsonRpc(std::string request) : m_request(std::move(request)){ } struct MethodNotFoundException : std::exception {}; struct InvalidParametersException : std::exception {}; enum class ErrorCode: i16 { ParseError = -32700, InvalidRequest = -32600, MethodNotFound = -32601, InvalidParams = -32602, InternalError = -32603, }; using Callback = std::function; std::optional execute(const Callback &callback); void setError(ErrorCode code, std::string message); private: std::optional handleMessage(const nlohmann::json &request, const Callback &callback); std::optional handleBatchedMessages(const nlohmann::json &request, const Callback &callback); nlohmann::json createDefaultMessage(); nlohmann::json createErrorMessage(ErrorCode code, const std::string &message); nlohmann::json createResponseMessage(const nlohmann::json &result); private: std::string m_request; std::optional m_id; struct Error { ErrorCode code; std::string message; }; std::optional m_error; }; struct TextContent { std::string text; operator nlohmann::json() const { nlohmann::json result; result["content"] = nlohmann::json::array({ nlohmann::json::object({ { "type", "text" }, { "text", text } }) }); return result; } }; struct StructuredContent { std::string text; nlohmann::json data; operator nlohmann::json() const { nlohmann::json result; result["content"] = nlohmann::json::array({ nlohmann::json::object({ { "type", "text" }, { "text", text } }) }); result["structuredContent"] = data; return result; } }; class Server { public: constexpr static auto McpInternalPort = 19743; Server(); ~Server(); void listen(); void shutdown(); void disconnect(); bool isConnected(); void addPrimitive(std::string type, std::string_view capabilities, std::function function); struct ClientInfo { std::string name; std::string version; std::string protocolVersion; }; const ClientInfo& getClientInfo() const { return m_clientInfo; } private: nlohmann::json handleInitialize(const nlohmann::json ¶ms); void handleNotifications(const std::string &method, const nlohmann::json ¶ms); struct Primitive { nlohmann::json capabilities; std::function function; }; std::map> m_primitives; wolv::net::SocketServer m_server; bool m_connected = false; ClientInfo m_clientInfo; }; } ================================================ FILE: lib/libimhex/include/hex/plugin.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) #include #define PLUGIN_ENTRY_POINT extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; } #else #define PLUGIN_ENTRY_POINT #endif namespace { struct PluginFunctionHelperInstantiation {}; } template struct PluginFeatureFunctionHelper { static void* getFeatures(); }; template struct PluginSubCommandsFunctionHelper { static void* getSubCommands(); }; template void* PluginFeatureFunctionHelper::getFeatures() { return nullptr; } template void* PluginSubCommandsFunctionHelper::getSubCommands() { return nullptr; } [[maybe_unused]] static auto& getFeaturesImpl() { static hex::AutoReset> features; return *features; } #if defined (IMHEX_STATIC_LINK_PLUGINS) #define IMHEX_PLUGIN_VISIBILITY_PREFIX static #else #if defined(_MSC_VER) #define IMHEX_PLUGIN_VISIBILITY_PREFIX extern "C" __declspec(dllexport) #else #define IMHEX_PLUGIN_VISIBILITY_PREFIX extern "C" [[gnu::visibility("default")]] #endif #endif #define IMHEX_FEATURE_ENABLED(feature) WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(IMHEX_PLUGIN_, IMHEX_PLUGIN_NAME), _FEATURE_), feature) #define IMHEX_DEFINE_PLUGIN_FEATURES() IMHEX_DEFINE_PLUGIN_FEATURES_IMPL() #define IMHEX_DEFINE_PLUGIN_FEATURES_IMPL() \ template<> \ struct PluginFeatureFunctionHelper { \ static void* getFeatures(); \ }; \ void* PluginFeatureFunctionHelper::getFeatures() { \ return &getFeaturesImpl(); \ } \ static auto initFeatures = [] { getFeaturesImpl() = std::vector({ IMHEX_PLUGIN_FEATURES_CONTENT }); return 0; }() #define IMHEX_PLUGIN_FEATURES ::getFeaturesImpl() /** * This macro is used to define all the required entry points for a plugin. * Name, Author and Description will be displayed in the plugin list on the Welcome screen. */ #define IMHEX_PLUGIN_SETUP(name, author, description) \ IMHEX_PLUGIN_SETUP_IMPL(name, author, description, nullptr) #define IMHEX_LIBRARY_SETUP(name) \ IMHEX_LIBRARY_SETUP_IMPL(name) #define IMHEX_PLUGIN_SETUP_BUILTIN(name, author, description) \ IMHEX_PLUGIN_VISIBILITY_PREFIX bool isBuiltinPlugin() { return true; } \ IMHEX_PLUGIN_SETUP_IMPL(name, author, description, isBuiltinPlugin) #define IMHEX_LIBRARY_SETUP_IMPL(name) \ IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)(); \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME)() { return name; } \ IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(setImGuiContext_, IMHEX_PLUGIN_NAME)(ImGuiContext *ctx) { \ ImGui::SetCurrentContext(ctx); \ GImGui = ctx; \ } \ extern "C" void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \ hex::PluginManager::addPlugin(name, hex::PluginFunctions { \ nullptr, \ WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME), \ nullptr, \ WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME), \ nullptr, \ nullptr, \ nullptr, \ WOLV_TOKEN_CONCAT(setImGuiContext_, IMHEX_PLUGIN_NAME), \ nullptr, \ nullptr, \ nullptr \ }); \ } \ PLUGIN_ENTRY_POINT \ IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)() #define IMHEX_PLUGIN_SETUP_IMPL(name, author, description, builtinPluginFunc) \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; } \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; } \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; } \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; } \ IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \ ImGui::SetCurrentContext(ctx); \ GImGui = ctx; \ } \ IMHEX_DEFINE_PLUGIN_FEATURES(); \ IMHEX_PLUGIN_VISIBILITY_PREFIX void* getFeatures() { \ return PluginFeatureFunctionHelper::getFeatures(); \ } \ IMHEX_PLUGIN_VISIBILITY_PREFIX void* getSubCommands() { \ return PluginSubCommandsFunctionHelper::getSubCommands(); \ } \ IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin(); \ extern "C" void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \ hex::PluginManager::addPlugin(name, hex::PluginFunctions { \ initializePlugin, \ nullptr, \ getPluginName, \ nullptr, \ getPluginAuthor, \ getPluginDescription, \ getCompatibleVersion, \ setImGuiContext, \ nullptr, \ getSubCommands, \ getFeatures, \ builtinPluginFunc \ }); \ } \ PLUGIN_ENTRY_POINT \ IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin() /** * This macro is used to define subcommands defined by the plugin * A subcommand consists of a key, a description, and a callback * The key is what the first argument to ImHex should be, prefixed by `--` * For example, if the key if `help`, ImHex should be started with `--help` as its first argument to trigger the subcommand * when the subcommand is triggerred, it's callback will be executed. The callback is executed BEFORE most of ImHex initialization * so to do anything meaningful, you should subscribe to an event (like EventImHexStartupFinished) and run your code there. */ #define IMHEX_PLUGIN_SUBCOMMANDS() IMHEX_PLUGIN_SUBCOMMANDS_IMPL() #define IMHEX_PLUGIN_SUBCOMMANDS_IMPL() \ extern std::vector g_subCommands; \ template<> \ struct PluginSubCommandsFunctionHelper { \ static void* getSubCommands(); \ }; \ void* PluginSubCommandsFunctionHelper::getSubCommands() { \ return &g_subCommands; \ } \ std::vector g_subCommands ================================================ FILE: lib/libimhex/include/hex/providers/buffered_reader.hpp ================================================ #pragma once #include #include #include namespace hex::prv { using namespace hex::literals; inline void providerReaderFunction(Provider *provider, void *buffer, u64 address, size_t size) { provider->read(address, buffer, size); } class ProviderReader : public wolv::io::BufferedReader { public: using BufferedReader::BufferedReader; explicit ProviderReader(Provider *provider, size_t bufferSize = 0x100000) : BufferedReader(provider, provider->getActualSize(), bufferSize) { this->setEndAddress(provider->getBaseAddress() + provider->getActualSize() - 1); this->seek(provider->getBaseAddress()); } }; } ================================================ FILE: lib/libimhex/include/hex/providers/cached_provider.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace hex::prv { /** * @brief A base class for providers that want to cache data in memory. * Thread-safe for concurrent reads/writes. Reads are cached in memory. * Subclasses must implement readFromSource and writeToSource. */ class CachedProvider : public Provider { public: CachedProvider(size_t cacheBlockSize = 4096, size_t maxBlocks = 1024); ~CachedProvider() override; OpenResult open() override; void close() override; void readRaw(u64 offset, void *buffer, size_t size) override; void writeRaw(u64 offset, const void *buffer, size_t size) override; void resizeRaw(u64 newSize) override; u64 getActualSize() const override; protected: virtual void readFromSource(uint64_t offset, void* buffer, size_t size) = 0; virtual void writeToSource(uint64_t offset, const void* buffer, size_t size) = 0; virtual void resizeSource(uint64_t newSize) { std::ignore = newSize; } virtual u64 getSourceSize() const = 0; void clearCache(); struct Block { uint64_t index; std::vector data; bool dirty = false; }; size_t m_cacheBlockSize; size_t m_maxBlocks; mutable std::shared_mutex m_cacheMutex; std::vector> m_cache; mutable u64 m_cachedSize = 0; constexpr u64 calcBlockIndex(u64 offset) const { return offset / m_cacheBlockSize; } constexpr size_t calcBlockOffset(u64 offset) const { return offset % m_cacheBlockSize; } void evictIfNeeded(); }; } ================================================ FILE: lib/libimhex/include/hex/providers/memory_provider.hpp ================================================ #pragma once #include namespace hex::prv { /** * This is a simple mock provider that can be used to pass in-memory data to APIs that require a provider. * It's NOT a provider that can be loaded by the user. */ class MemoryProvider : public hex::prv::Provider { public: MemoryProvider() = default; explicit MemoryProvider(std::vector data, std::string name = "") : m_data(std::move(data)), m_name(std::move(name)) { } ~MemoryProvider() override = default; MemoryProvider(const MemoryProvider&) = delete; MemoryProvider& operator=(const MemoryProvider&) = delete; MemoryProvider(MemoryProvider &&provider) noexcept = default; MemoryProvider& operator=(MemoryProvider &&provider) noexcept = default; [[nodiscard]] bool isAvailable() const override { return true; } [[nodiscard]] bool isReadable() const override { return true; } [[nodiscard]] bool isWritable() const override { return true; } [[nodiscard]] bool isResizable() const override { return true; } [[nodiscard]] bool isSavable() const override { return m_name.empty(); } [[nodiscard]] bool isSavableAsRecent() const override { return false; } [[nodiscard]] OpenResult open() override; void close() override { } void readRaw(u64 offset, void *buffer, size_t size) override; void writeRaw(u64 offset, const void *buffer, size_t size) override; [[nodiscard]] u64 getActualSize() const override { return m_data.size(); } void resizeRaw(u64 newSize) override; [[nodiscard]] std::string getName() const override { return m_name; } [[nodiscard]] UnlocalizedString getTypeName() const override { return "MemoryProvider"; } [[nodiscard]] const char* getIcon() const override { return ""; } private: void renameFile(); private: std::vector m_data; std::string m_name; }; } ================================================ FILE: lib/libimhex/include/hex/providers/overlay.hpp ================================================ #pragma once #include #include namespace hex::prv { class Overlay { public: Overlay() = default; void setAddress(u64 address) { m_address = address; } [[nodiscard]] u64 getAddress() const { return m_address; } [[nodiscard]] u64 getSize() const { return m_data.size(); } [[nodiscard]] std::vector &getData() { return m_data; } private: u64 m_address = 0; std::vector m_data; }; } ================================================ FILE: lib/libimhex/include/hex/providers/provider.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include namespace hex::prv { /** * @brief Interface for providers that need to draw a config interface when being created */ class IProviderLoadInterface { public: virtual ~IProviderLoadInterface() = default; virtual bool drawLoadInterface() = 0; }; /** * @brief Interface for providers that want to provide a custom sidebar interface */ class IProviderSidebarInterface { public: virtual ~IProviderSidebarInterface() = default; virtual void drawSidebarInterface() = 0; }; /** * @brief Interface for providers that need to show a file picker dialog when being created */ class IProviderFilePicker { public: virtual ~IProviderFilePicker() = default; virtual bool handleFilePicker() = 0; }; /** * @brief Interface for providers that want to display custom menu items in the provider context menu */ class IProviderMenuItems { public: struct MenuEntry { std::string name; const char *icon; std::function callback; }; virtual ~IProviderMenuItems() = default; virtual std::vector getMenuEntries() = 0; }; /** * @brief Interface for providers that want to show some extra information in the information view */ class IProviderDataDescription { public: struct Description { std::string name; std::string value; }; virtual ~IProviderDataDescription() = default; [[nodiscard]] virtual std::vector getDataDescription() const = 0; }; class IProviderDataBackupable { public: explicit IProviderDataBackupable(Provider *provider); virtual ~IProviderDataBackupable() = default; void createBackupIfNeeded(const std::fs::path &inputFilePath); private: Provider *m_provider = nullptr; bool m_backupCreated = false; bool m_shouldCreateBackups = true; u64 m_maxSize; std::string m_backupExtension; }; /** * @brief Represent the data source for a tab in the UI */ class Provider { public: constexpr static u64 MaxPageSize = 0xFFFF'FFFF'FFFF'FFFF; class OpenResult { public: OpenResult() : m_result(std::monostate{}) {} [[nodiscard]] static OpenResult failure(std::string errorMessage) { OpenResult result; result.m_result = std::move(errorMessage); return result; } [[nodiscard]] static OpenResult warning(std::string warningMessage) { OpenResult result; result.m_result = std::move(warningMessage); result.m_warning = true; return result; } [[nodiscard]] static OpenResult redirect(Provider *provider) { OpenResult result; result.m_result = provider; return result; } [[nodiscard]] bool isSuccess() const { return std::holds_alternative(m_result); } [[nodiscard]] bool isFailure() const { return std::holds_alternative(m_result) && !m_warning; } [[nodiscard]] bool isWarning() const { return std::holds_alternative(m_result) && m_warning; } [[nodiscard]] bool isRedirecting() const { return std::holds_alternative(m_result); } [[nodiscard]] Provider* getRedirectProvider() const { if (std::holds_alternative(m_result)) { return std::get(m_result); } return nullptr; } [[nodiscard]] std::string_view getErrorMessage() const { if (std::holds_alternative(m_result)) { return std::get(m_result); } return ""; } private: std::variant m_result; bool m_warning = false; }; Provider(); virtual ~Provider(); Provider(const Provider&) = delete; Provider& operator=(const Provider&) = delete; Provider(Provider &&provider) noexcept = default; Provider& operator=(Provider &&provider) noexcept = default; /** * @brief Opens this provider * @note The return value of this function allows to ensure the provider is available, * so calling Provider::isAvailable() just after a call to open() that returned true is redundant. * @note This is not related to the EventProviderOpened event * @return true if the provider was opened successfully, else false */ [[nodiscard]] virtual OpenResult open() = 0; /** * @brief Closes this provider * @note This function is called when the user requests for a provider to be closed, e.g. by closing a tab. * In general, this function should close the underlying data source but leave the provider in a state where * it can be opened again later by calling the open() function again. */ virtual void close() = 0; /** * @brief Checks if this provider is open and can be used to access data * @return Generally, if the open() function succeeded and the data source was successfully opened, this * function should return true */ [[nodiscard]] virtual bool isAvailable() const = 0; /** * @brief Checks if the data in this provider can be read * @return True if the provider is readable, false otherwise */ [[nodiscard]] virtual bool isReadable() const = 0; /** * @brief Controls if the user can write data to this specific provider. * This may be false for e.g. a file opened in read-only */ [[nodiscard]] virtual bool isWritable() const = 0; /** * @brief Controls if the user can resize this provider * @return True if the provider is resizable, false otherwise */ [[nodiscard]] virtual bool isResizable() const = 0; /** * @brief Controls whether the provider can be saved ("saved", not "saved as") * This is mainly used by providers that aren't buffered, and so don't need to be saved * This function will usually return false for providers that aren't writable, but this isn't guaranted */ [[nodiscard]] virtual bool isSavable() const = 0; /** * @brief Controls whether we can dump data from this provider (e.g. "save as", or "export -> .."). * Typically disabled for process with sparse data, like the Process memory provider * where the virtual address space is several TiBs large. * Default implementation returns true. */ [[nodiscard]] virtual bool isDumpable() const; /** * @brief Controls whether this provider can be saved as a recent entry * Typically used for providers that do not retain data, e.g. the memory provider */ [[nodiscard]] virtual bool isSavableAsRecent() const { return true; } /** * @brief Read data from this provider, applying overlays and patches * @param offset offset to start reading the data * @param buffer buffer to write read data * @param size number of bytes to read * @param overlays apply overlays and patches is true. Same as readRaw() if false */ virtual void read(u64 offset, void *buffer, size_t size, bool overlays = true); /** * @brief Write data to the patches of this provider. Will not directly modify provider. * @param offset offset to start writing the data * @param buffer buffer to take data to write from * @param size number of bytes to write */ virtual void write(u64 offset, const void *buffer, size_t size); /** * @brief Read data from this provider, without applying overlays and patches * @param offset offset to start reading the data * @param buffer buffer to write read data * @param size number of bytes to read */ virtual void readRaw(u64 offset, void *buffer, size_t size) = 0; /** * @brief Write data directly to this provider * @param offset offset to start writing the data * @param buffer buffer to take data to write from * @param size number of bytes to write */ virtual void writeRaw(u64 offset, const void *buffer, size_t size) = 0; /** * @brief Get the full size of the data in this provider * @return The size of the entire available data of this provider */ [[nodiscard]] virtual u64 getActualSize() const = 0; /** * @brief Gets the type name of this provider * @note This is mainly used to be stored in project files and recents to be able to later on * recreate this exact provider type. This needs to be unique across all providers, this is usually something * like "hex.builtin.provider.mem_file" or "hex.builtin.provider.file" * @return The provider's type name */ [[nodiscard]] virtual UnlocalizedString getTypeName() const = 0; /** * @brief Gets a human-readable representation of the current provider * @note This is mainly used to display the provider in the UI. For example, the file provider * will return the file name here * @return The name of the current provider */ [[nodiscard]] virtual std::string getName() const = 0; /** * @brief Gets the icon of this provider * @return The icon string */ [[nodiscard]] virtual const char* getIcon() const = 0; bool resize(u64 newSize); void insert(u64 offset, u64 size); void remove(u64 offset, u64 size); virtual void resizeRaw(u64 newSize) { std::ignore = newSize; } virtual void insertRaw(u64 offset, u64 size); virtual void removeRaw(u64 offset, u64 size); virtual void save(); virtual void saveAs(const std::fs::path &path); [[nodiscard]] Overlay *newOverlay(); void deleteOverlay(Overlay *overlay); void applyOverlays(u64 offset, void *buffer, size_t size) const; [[nodiscard]] const std::list> &getOverlays() const; [[nodiscard]] u64 getPageSize() const; void setPageSize(u64 pageSize); [[nodiscard]] u32 getPageCount() const; [[nodiscard]] u32 getCurrentPage() const; void setCurrentPage(u32 page); virtual void setBaseAddress(u64 address); [[nodiscard]] virtual u64 getBaseAddress() const; [[nodiscard]] virtual u64 getCurrentPageAddress() const; [[nodiscard]] virtual u64 getSize() const; [[nodiscard]] virtual std::optional getPageOfAddress(u64 address) const; [[nodiscard]] virtual std::variant queryInformation(const std::string &category, const std::string &argument); virtual void undo(); virtual void redo(); [[nodiscard]] virtual bool canUndo() const; [[nodiscard]] virtual bool canRedo() const; [[nodiscard]] u32 getID() const; void setID(u32 id); [[nodiscard]] virtual nlohmann::json storeSettings(nlohmann::json settings) const; virtual void loadSettings(const nlohmann::json &settings); void markDirty(bool dirty = true) { m_dirty = dirty; } [[nodiscard]] bool isDirty() const { return m_dirty; } [[nodiscard]] virtual std::pair getRegionValidity(u64 address) const; void skipLoadInterface() { m_skipLoadInterface = true; } [[nodiscard]] bool shouldSkipLoadInterface() const { return m_skipLoadInterface; } template T> bool addUndoableOperation(auto && ... args) { return m_undoRedoStack.add(std::forward(args)...); } [[nodiscard]] virtual undo::Stack& getUndoStack() { return m_undoRedoStack; } protected: u32 m_currPage = 0; u64 m_baseAddress = 0; undo::Stack m_undoRedoStack; std::list> m_overlays; u32 m_id; /** * @brief true if there is any data that needs to be saved */ bool m_dirty = false; /** * @brief Control if provider initialization should be skipped. * Initialization may be asking the user for information related to the provider, * e.g. a process ID for the process memory provider * this is used mainly when restoring a provider with already known initialization information * for example when loading a project or loading a provider from the "recent" lsit */ bool m_skipLoadInterface = false; u64 m_pageSize = MaxPageSize; }; } ================================================ FILE: lib/libimhex/include/hex/providers/provider_data.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace hex { #if !defined(HEX_MODULE_EXPORT) namespace prv { class Provider; } #endif template class PerProvider { public: PerProvider() { this->onCreate(); } PerProvider(const PerProvider&) = delete; PerProvider(PerProvider&&) = delete; PerProvider& operator=(const PerProvider&) = delete; PerProvider& operator=(PerProvider &&) = delete; ~PerProvider() { this->onDestroy(); } T* operator->() { return &this->get(); } const T* operator->() const { return &this->get(); } T& get(const prv::Provider *provider = ImHexApi::Provider::get()) { if (provider == nullptr) [[unlikely]] throw std::invalid_argument("PerProvider::get called with nullptr"); return m_data[provider]; } const T& get(const prv::Provider *provider = ImHexApi::Provider::get()) const { if (provider == nullptr) [[unlikely]] throw std::invalid_argument("PerProvider::get called with nullptr"); return m_data.at(provider); } void set(const T &data, const prv::Provider *provider = ImHexApi::Provider::get()) { if (provider == nullptr) [[unlikely]] throw std::invalid_argument("PerProvider::set called with nullptr"); m_data[provider] = data; } void set(T &&data, const prv::Provider *provider = ImHexApi::Provider::get()) { if (provider == nullptr) [[unlikely]] throw std::invalid_argument("PerProvider::set called with nullptr"); m_data[provider] = std::move(data); } T& operator*() { return this->get(); } const T& operator*() const { return this->get(); } PerProvider& operator=(const T &data) { this->set(data); return *this; } PerProvider& operator=(T &&data) { this->set(std::move(data)); return *this; } operator T&() { return this->get(); } auto all() { return m_data | std::views::values; } void setOnCreateCallback(std::function callback) { m_onCreateCallback = std::move(callback); } void setOnDestroyCallback(std::function callback) { m_onDestroyCallback = std::move(callback); } private: void onCreate() { EventProviderOpened::subscribe(this, [this](prv::Provider *provider) { auto [it, inserted] = m_data.emplace(provider, T()); auto &[key, value] = *it; if (m_onCreateCallback) m_onCreateCallback(provider, value); }); EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){ if (auto it = m_data.find(provider); it != m_data.end()) { if (m_onDestroyCallback) m_onDestroyCallback(provider, m_data.at(provider)); m_data.erase(it); } }); EventImHexClosing::subscribe(this, [this] { m_data.clear(); }); // Moves the data of this PerProvider instance from one provider to another MovePerProviderData::subscribe(this, [this](prv::Provider *from, prv::Provider *to) { // Get the value from the old provider, (removes it from the map) auto node = m_data.extract(from); // Ensure the value existed if (node.empty()) return; // Delete the value from the new provider, that we want to replace m_data.erase(to); // Re-insert it with the key of the new provider node.key() = to; m_data.insert(std::move(node)); }); } void onDestroy() { EventProviderOpened::unsubscribe(this); EventProviderDeleted::unsubscribe(this); EventImHexClosing::unsubscribe(this); MovePerProviderData::unsubscribe(this); } private: std::map m_data; std::function m_onCreateCallback, m_onDestroyCallback; }; } ================================================ FILE: lib/libimhex/include/hex/providers/undo_redo/operations/operation.hpp ================================================ #pragma once #include #include #include namespace hex::prv { class Provider; } namespace hex::prv::undo { class Operation : public ICloneable { public: ~Operation() override = default; virtual void undo(Provider *provider) = 0; virtual void redo(Provider *provider) = 0; [[nodiscard]] virtual Region getRegion() const = 0; [[nodiscard]] virtual std::string format() const = 0; [[nodiscard]] virtual std::vector formatContent() const { return { }; } [[nodiscard]] virtual bool shouldHighlight() const { return true; } }; } ================================================ FILE: lib/libimhex/include/hex/providers/undo_redo/operations/operation_group.hpp ================================================ #pragma once #include #include #include #include namespace hex::prv::undo { class OperationGroup : public Operation { public: explicit OperationGroup(UnlocalizedString unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {} OperationGroup(const OperationGroup &other) { for (const auto &operation : other.m_operations) m_operations.emplace_back(operation->clone()); } void undo(Provider *provider) override { for (auto &operation : m_operations) operation->undo(provider); } void redo(Provider *provider) override { for (auto &operation : m_operations) operation->redo(provider); } void addOperation(std::unique_ptr &&newOperation) { auto newRegion = newOperation->getRegion(); if (newRegion.getStartAddress() < m_startAddress) m_startAddress = newRegion.getStartAddress(); if (newRegion.getEndAddress() > m_endAddress) m_endAddress = newRegion.getEndAddress(); if (m_formattedContent.size() <= 10) m_formattedContent.emplace_back(newOperation->format()); else m_formattedContent.back() = fmt::format("[{}x] ...", (m_operations.size() - 10) + 1); m_operations.emplace_back(std::move(newOperation)); } [[nodiscard]] std::string format() const override { return fmt::format("{}", Lang(m_unlocalizedName)); } [[nodiscard]] Region getRegion() const override { return Region { m_startAddress, (m_endAddress - m_startAddress) + 1 }; } std::unique_ptr clone() const override { return std::make_unique(*this); } std::vector formatContent() const override { return m_formattedContent; } private: UnlocalizedString m_unlocalizedName; std::vector> m_operations; u64 m_startAddress = std::numeric_limits::max(); u64 m_endAddress = std::numeric_limits::min(); std::vector m_formattedContent; }; } ================================================ FILE: lib/libimhex/include/hex/providers/undo_redo/stack.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace hex::prv { class Provider; } namespace hex::prv::undo { using Patches = std::map; class Stack { public: explicit Stack(Provider *provider); void undo(u32 count = 1); void redo(u32 count = 1); void groupOperations(u32 count, const UnlocalizedString &unlocalizedName); void apply(const Stack &otherStack); void reapply(); [[nodiscard]] bool canUndo() const; [[nodiscard]] bool canRedo() const; template T> bool add(auto && ... args) { auto result = this->add(std::make_unique(std::forward(args)...)); return result; } bool add(std::unique_ptr &&operation); static std::recursive_mutex& getMutex(); const std::vector> &getAppliedOperations() const { return m_undoStack; } const std::vector> &getUndoneOperations() const { return m_redoStack; } void reset() { m_undoStack.clear(); m_redoStack.clear(); } private: [[nodiscard]] Operation* getLastOperation() const { return m_undoStack.back().get(); } private: std::vector> m_undoStack, m_redoStack; Provider *m_provider; }; } ================================================ FILE: lib/libimhex/include/hex/subcommands/subcommands.hpp ================================================ #pragma once #include #include #include namespace hex::subcommands { /** * @brief Internal method - takes all the arguments ImHex received from the command line, * and determine which subcommands to run, with which arguments. * In some cases, the subcommand or this function directly might exit the program * (e.g. --help, or when forwarding providers to open to another instance) * and so this function might not return */ void processArguments(const std::vector &args); /** * @brief Forward the given command to the main instance (might be this instance) * The callback will be executed after EventImHexStartupFinished */ void forwardSubCommand(const std::string &cmdName, const std::vector &args); using ForwardCommandHandler = std::function &)>; /** * @brief Register the handler for this specific command name */ void registerSubCommand(const std::string &cmdName, const ForwardCommandHandler &handler); } ================================================ FILE: lib/libimhex/include/hex/test/test_provider.hpp ================================================ #pragma once #include #include namespace hex::test { using namespace hex::prv; class TestProvider : public prv::Provider { public: explicit TestProvider(std::vector *data) { this->setData(data); } ~TestProvider() override = default; [[nodiscard]] bool isAvailable() const override { return true; } [[nodiscard]] bool isReadable() const override { return true; } [[nodiscard]] bool isWritable() const override { return false; } [[nodiscard]] bool isResizable() const override { return false; } [[nodiscard]] bool isSavable() const override { return false; } void setData(std::vector *data) { m_data = data; } [[nodiscard]] std::string getName() const override { return ""; } [[nodiscard]] const char* getIcon() const override { return ""; } void readRaw(u64 offset, void *buffer, size_t size) override { if (offset + size > m_data->size()) return; std::memcpy(buffer, m_data->data() + offset, size); } void writeRaw(u64 offset, const void *buffer, size_t size) override { if (offset + size > m_data->size()) return; std::memcpy(m_data->data() + offset, buffer, size); } [[nodiscard]] u64 getActualSize() const override { return m_data->size(); } [[nodiscard]] UnlocalizedString getTypeName() const override { return "hex.test.provider.test"; } OpenResult open() override { return {}; } void close() override { } nlohmann::json storeSettings(nlohmann::json) const override { return {}; } void loadSettings(const nlohmann::json &) override {}; private: std::vector *m_data = nullptr; }; } ================================================ FILE: lib/libimhex/include/hex/test/tests.hpp ================================================ #pragma once #include #include #include #include #include #include #if defined(IMGUI_TEST_ENGINE) #include #include #include #include #endif #include #include #include #include #define TEST_SEQUENCE(...) static auto WOLV_ANONYMOUS_VARIABLE(TEST_SEQUENCE) = ::hex::test::TestSequenceExecutor(__VA_ARGS__) + []() -> int #define TEST_FAIL() return EXIT_FAILURE #define TEST_SUCCESS() return EXIT_SUCCESS #define FAILING true #define TEST_ASSERT(x, ...) \ do { \ auto ret = (x); \ if (!ret) { \ hex::log::error("Test assert '{}' failed {} at {}:{}", \ #x, \ fmt::format("" __VA_ARGS__), \ __FILE__, \ __LINE__); \ return EXIT_FAILURE; \ } \ } while (0) #define INIT_PLUGIN(name) \ if (!hex::test::initPluginImpl(name)) TEST_FAIL(); #if defined(IMGUI_TEST_ENGINE) #define IMGUI_TEST_SEQUENCE(category, name, ctx) \ static auto WOLV_ANONYMOUS_VARIABLE(TEST_SEQUENCE) = \ ::hex::test::ImGuiTestSequenceExecutor(category, name, std::source_location::current()) + \ [](ImGuiTestContext *ctx) -> void #else #define IMGUI_TEST_SEQUENCE(...) static auto WOLV_ANONYMOUS_VARIABLE(TEST_SEQUENCE) = []() -> int #endif namespace hex::test { using Function = int(*)(); struct Test { Function function; bool shouldFail; }; class Tests { public: static int addTest(const std::string &name, Function func, bool shouldFail) noexcept; static std::map &get() noexcept; }; template class TestSequence { public: TestSequence(const std::string &name, F func, bool shouldFail) noexcept { Tests::addTest(name, func, shouldFail); } TestSequence &operator=(TestSequence &&) = delete; }; struct TestSequenceExecutor { explicit TestSequenceExecutor(std::string name, bool shouldFail = false) noexcept : m_name(std::move(name)), m_shouldFail(shouldFail) { } [[nodiscard]] const auto &getName() const noexcept { return m_name; } [[nodiscard]] bool shouldFail() const noexcept { return m_shouldFail; } private: std::string m_name; bool m_shouldFail; }; template TestSequence operator+(const TestSequenceExecutor &executor, F &&f) noexcept { return TestSequence(executor.getName(), std::forward(f), executor.shouldFail()); } #if defined(IMGUI_TEST_ENGINE) template class ImGuiTestSequence { public: ImGuiTestSequence(const std::string &category, const std::string &name, std::source_location sourceLocation, F func) noexcept { log::info("Registering ImGui Test"); EventRegisterImGuiTests::subscribe([=](ImGuiTestEngine *engine) { auto test = ImGuiTestEngine_RegisterTest(engine, category.c_str(), name.c_str(), sourceLocation.file_name(), sourceLocation.line()); test->TestFunc = func; }); } ImGuiTestSequence &operator=(ImGuiTestSequence &&) = delete; }; struct ImGuiTestSequenceExecutor { explicit ImGuiTestSequenceExecutor(std::string category, std::string name, std::source_location sourceLocation) noexcept : m_category(std::move(category)), m_name(std::move(name)), m_sourceLocation(sourceLocation) { } [[nodiscard]] const auto &getCategory() const noexcept { return m_category; } [[nodiscard]] const auto &getName() const noexcept { return m_name; } [[nodiscard]] const auto &getSourceLocation() const noexcept { return m_sourceLocation; } private: std::string m_category, m_name; std::source_location m_sourceLocation; }; template ImGuiTestSequence operator+(const ImGuiTestSequenceExecutor &executor, F &&f) noexcept { return ImGuiTestSequence(executor.getCategory(), executor.getName(), executor.getSourceLocation(), std::forward(f)); } #endif bool initPluginImpl(std::string name); } ================================================ FILE: lib/libimhex/include/hex/ui/banner.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace hex { namespace impl { class BannerBase { public: BannerBase(ImColor color) : m_color(color) {} virtual ~BannerBase() = default; virtual void draw() { drawContent(); } virtual void drawContent() = 0; [[nodiscard]] static std::list> &getOpenBanners(); [[nodiscard]] const ImColor& getColor() const { return m_color; } void close() { m_shouldClose = true; } [[nodiscard]] bool shouldClose() const { return m_shouldClose; } protected: static std::mutex& getMutex(); bool m_shouldClose = false; ImColor m_color; }; } template class Banner : public impl::BannerBase { public: using impl::BannerBase::BannerBase; template static void open(Args && ... args) { std::lock_guard lock(getMutex()); auto toast = std::make_unique(std::forward(args)...); getOpenBanners().emplace_back(std::move(toast)); } }; } ================================================ FILE: lib/libimhex/include/hex/ui/imgui_imhex_extensions.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include enum ImGuiCustomCol : int { ImGuiCustomCol_DescButton, ImGuiCustomCol_DescButtonHovered, ImGuiCustomCol_DescButtonActive, ImGuiCustomCol_ToolbarGray, ImGuiCustomCol_ToolbarRed, ImGuiCustomCol_ToolbarYellow, ImGuiCustomCol_ToolbarGreen, ImGuiCustomCol_ToolbarBlue, ImGuiCustomCol_ToolbarPurple, ImGuiCustomCol_ToolbarBrown, ImGuiCustomCol_LoggerDebug, ImGuiCustomCol_LoggerInfo, ImGuiCustomCol_LoggerWarning, ImGuiCustomCol_LoggerError, ImGuiCustomCol_LoggerFatal, ImGuiCustomCol_AchievementUnlocked, ImGuiCustomCol_FindHighlight, ImGuiCustomCol_DiffAdded, ImGuiCustomCol_DiffRemoved, ImGuiCustomCol_DiffChanged, ImGuiCustomCol_AdvancedEncodingASCII, ImGuiCustomCol_AdvancedEncodingSingleChar, ImGuiCustomCol_AdvancedEncodingMultiChar, ImGuiCustomCol_AdvancedEncodingUnknown, ImGuiCustomCol_Highlight, ImGuiCustomCol_Patches, ImGuiCustomCol_PatternSelected, ImGuiCustomCol_IEEEToolSign, ImGuiCustomCol_IEEEToolExp, ImGuiCustomCol_IEEEToolMantissa, ImGuiCustomCol_BlurBackground, ImGuiCustomCol_COUNT }; enum ImGuiCustomStyle { ImGuiCustomStyle_WindowBlur, ImGuiCustomStyle_COUNT }; namespace ImGuiExt { class Texture { public: enum class Filter : int { Linear, Nearest }; Texture() = default; Texture(const Texture&) = delete; Texture(Texture&& other) noexcept; [[nodiscard]] static Texture fromImage(const ImU8 *buffer, int size, Filter filter = Filter::Nearest); [[nodiscard]] static Texture fromImage(std::span buffer, Filter filter = Filter::Nearest); [[nodiscard]] static Texture fromImage(const char *path, Filter filter = Filter::Nearest); [[nodiscard]] static Texture fromImage(const std::fs::path &path, Filter filter = Filter::Nearest); [[nodiscard]] static Texture fromGLTexture(unsigned int texture, int width, int height); [[nodiscard]] static Texture fromBitmap(const ImU8 *buffer, int size, int width, int height, Filter filter = Filter::Nearest); [[nodiscard]] static Texture fromBitmap(std::span buffer, int width, int height, Filter filter = Filter::Nearest); [[nodiscard]] static Texture fromSVG(const char *path, int width = 0, int height = 0, Filter filter = Filter::Nearest); [[nodiscard]] static Texture fromSVG(const std::fs::path &path, int width = 0, int height = 0, Filter filter = Filter::Nearest); [[nodiscard]] static Texture fromSVG(std::span buffer, int width = 0, int height = 0, Filter filter = Filter::Nearest); ~Texture(); Texture& operator=(const Texture&) = delete; Texture& operator=(Texture&& other) noexcept; [[nodiscard]] constexpr bool isValid() const noexcept { return m_textureId != 0; } [[nodiscard]] operator ImTextureRef() const noexcept { return m_textureId; } [[nodiscard]] operator ImTextureID() const noexcept { return m_textureId; } [[nodiscard]] ImVec2 getSize() const noexcept { return ImVec2(m_width, m_height); } [[nodiscard]] constexpr float getAspectRatio() const noexcept { if (m_height == 0) return 1.0F; return float(m_width) / float(m_height); } [[nodiscard]] std::vector toBytes() const noexcept; void reset(); private: ImTextureID m_textureId = 0; int m_width = 0, m_height = 0; }; float GetTextWrapPos(); int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data); bool IconHyperlink(const char *icon, const char *label, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); bool Hyperlink(const char *label, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); bool BulletHyperlink(const char *label, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); bool DescriptionButton(const char *label, const char *description, const char *icon, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); bool DescriptionButtonProgress(const char *label, const char *description, const char *icon, float fraction, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); void HelpHover(const char *text, const char *icon = "(?)", ImU32 iconColor = ImGui::GetColorU32(ImGuiCol_ButtonActive)); void UnderlinedText(const char *label, ImColor color = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0)); void UnderwavedText(const char *label, ImColor textColor = ImGui::GetStyleColorVec4(ImGuiCol_Text), ImColor lineColor = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0)); void TextSpinner(const char *label); void Header(const char *label, bool firstEntry = false); void HeaderColored(const char *label, ImColor color, bool firstEntry); bool InfoTooltip(const char *text = "",bool = false); bool TitleBarButton(const char *label, ImVec2 size_arg); bool ToolBarButton(const char *symbol, ImVec4 color); bool IconButton(const char *symbol, ImVec4 color, ImVec2 size_arg = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0)); bool InputPrefix(const char* label, const char *prefix, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputIntegerPrefix(const char* label, const char *prefix, void *value, ImGuiDataType type, const char *format, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputHexadecimal(const char* label, u32 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputHexadecimal(const char* label, u64 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool SliderBytes(const char *label, u64 *value, u64 min, u64 max, u64 stepSize = 1, ImGuiSliderFlags flags = ImGuiSliderFlags_None); inline bool HasSecondPassed() { return static_cast(ImGui::GetTime() * 100) % 100 <= static_cast(ImGui::GetIO().DeltaTime * 100); } void OpenPopupInWindow(const char *window_name, const char *popup_name); void DisableWindowResize(ImGuiDir dir); struct ImHexCustomData { ImVec4 Colors[ImGuiCustomCol_COUNT]; struct Styles { float WindowBlur = 0.0F; float PopupWindowAlpha = 0.0F; // Alpha used by Popup tool windows when the user is not hovering over them } styles; }; ImU32 GetCustomColorU32(ImGuiCustomCol idx, float alpha_mul = 1.0F); ImVec4 GetCustomColorVec4(ImGuiCustomCol idx, float alpha_mul = 1.0F); inline ImHexCustomData::Styles& GetCustomStyle() { auto &customData = *static_cast(ImGui::GetIO().UserData); return customData.styles; } float GetCustomStyleFloat(ImGuiCustomStyle idx); ImVec2 GetCustomStyleVec2(ImGuiCustomStyle idx); void StyleCustomColorsDark(); void StyleCustomColorsLight(); void StyleCustomColorsClassic(); void ProgressBar(float fraction, ImVec2 size_value = ImVec2(0, 0), float yOffset = 0.0F); [[nodiscard]] bool IsDarkBackground(const ImColor& bgColor); void TextFormatted(std::string_view fmt, auto &&...args) { if constexpr (sizeof...(args) == 0) { ImGui::TextUnformatted(fmt.data(), fmt.data() + fmt.size()); } else { const auto string = fmt::format(fmt::runtime(fmt), std::forward(args)...); ImGui::TextUnformatted(string.c_str(), string.c_str() + string.size()); } } void TextFormattedSelectable(std::string_view fmt, auto &&...args) { auto text = fmt::format(fmt::runtime(fmt), std::forward(args)...); ImGui::PushID(text.c_str()); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2()); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4()); ImGui::PushItemWidth(ImGui::CalcTextSize(text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2); ImGui::InputText("##", const_cast(text.c_str()), text.size() + 1, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll); ImGui::PopItemWidth(); ImGui::PopStyleColor(); ImGui::PopStyleVar(2); ImGui::PopID(); } void TextFormattedColored(ImColor color, std::string_view fmt, auto &&...args) { ImGui::PushStyleColor(ImGuiCol_Text, color.Value); ImGuiExt::TextFormatted(fmt, std::forward(args)...); ImGui::PopStyleColor(); } void TextFormattedReadableColor(ImColor backgroundColor, std::string_view fmt, auto &&...args) { ImGui::PushStyleColor(ImGuiCol_Text, IsDarkBackground(backgroundColor) ? 0xFFFFFFFF : 0xFF000000); ImGuiExt::TextFormatted(fmt, std::forward(args)...); ImGui::PopStyleColor(); } void TextFormattedDisabled(std::string_view fmt, auto &&...args) { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); ImGuiExt::TextFormatted(fmt, std::forward(args)...); ImGui::PopStyleColor(); } void TextFormattedWrapped(std::string_view fmt, auto &&...args) { const bool need_backup = ImGuiExt::GetTextWrapPos() < 0.0F; // Keep existing wrap position if one is already set if (need_backup) ImGui::PushTextWrapPos(0.0F); ImGuiExt::TextFormatted(fmt, std::forward(args)...); if (need_backup) ImGui::PopTextWrapPos(); } void TextFormattedWrappedSelectable(std::string_view fmt, auto &&...args) { using namespace hex; // Manually wrap text, using the letter M (generally the widest character in non-monospaced fonts) to calculate the character width to use. auto text = wolv::util::trim(wolv::util::wrapMonospacedString( fmt::format(fmt::runtime(fmt), std::forward(args)...), ImGui::CalcTextSize("M").x, std::max(100_scaled, ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ScrollbarSize - ImGui::GetStyle().FrameBorderSize) )); auto textSize = ImGui::CalcTextSize(text.c_str()); ImGui::PushID(text.c_str()); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2()); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4()); ImGui::PushItemWidth(textSize.x + ImGui::GetStyle().FramePadding.x * 2); ImGui::InputTextMultiline( "##", const_cast(text.c_str()), text.size() + 1, ImVec2(0, textSize.y), ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll ); ImGui::PopItemWidth(); ImGui::PopStyleColor(); ImGui::PopStyleVar(2); ImGui::PopID(); } void TextUnformattedCentered(const char *text); void TextFormattedCentered(std::string_view fmt, auto &&...args) { auto text = fmt::format(fmt::runtime(fmt), std::forward(args)...); TextUnformattedCentered(text.c_str()); } void TextFormattedCenteredHorizontal(std::string_view fmt, auto &&...args) { auto text = fmt::format(fmt::runtime(fmt), std::forward(args)...); auto availableSpace = ImGui::GetContentRegionAvail(); auto textSize = ImGui::CalcTextSize(text.c_str(), nullptr, false, availableSpace.x * 0.75F); ImGui::SetCursorPosX(((availableSpace - textSize) / 2.0F).x); ImGui::PushTextWrapPos(availableSpace.x * 0.75F); ImGuiExt::TextFormattedWrapped("{}", text); ImGui::PopTextWrapPos(); } bool InputTextIcon(const char* label, const char *icon, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputTextIconHint(const char* label, const char *icon, const char *hint, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputScalarCallback(const char* label, ImGuiDataType data_type, void* p_data, const char* format, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); void HideTooltip(); bool BitCheckbox(const char* label, bool* v); bool DimmedButton(const char* label, ImVec2 size = ImVec2(0, 0), ImGuiButtonFlags flags = ImGuiButtonFlags_None); bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0)); bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0)); bool DimmedIconToggle(const char *icon, bool *v); bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v); bool DimmedArrowButton(const char *id, ImGuiDir dir, ImVec2 size = ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight())); void TextOverlay(const char *text, ImVec2 pos, float maxWidth = -1); bool BeginBox(); void EndBox(); bool BeginSubWindow(const char *label, bool *collapsed = nullptr, ImVec2 size = ImVec2(0, 0), ImGuiChildFlags flags = ImGuiChildFlags_None); void EndSubWindow(); void ConfirmButtons(const char *textLeft, const char *textRight, const auto &leftButtonCallback, const auto &rightButtonCallback) { auto width = ImGui::GetWindowWidth(); ImGui::SetCursorPosX(width / 9); if (ImGui::Button(textLeft, ImVec2(width / 3, 0))) leftButtonCallback(); ImGui::SameLine(); ImGui::SetCursorPosX(width / 9 * 5); if (ImGui::Button(textRight, ImVec2(width / 3, 0))) rightButtonCallback(); } bool VSliderAngle(const char* label, ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags); bool InputFilePicker(const char *label, std::fs::path &path, const std::vector &validExtensions); bool ToggleSwitch(const char *label, bool *v); bool ToggleSwitch(const char *label, bool v); bool PopupTitleBarButton(const char* label, bool p_enabled); void PopupTitleBarText(const char* text); template constexpr ImGuiDataType getImGuiDataType() { if constexpr (std::same_as) return ImGuiDataType_U8; else if constexpr (std::same_as) return ImGuiDataType_U16; else if constexpr (std::same_as) return ImGuiDataType_U32; else if constexpr (std::same_as) return ImGuiDataType_U64; else if constexpr (std::same_as) return ImGuiDataType_S8; else if constexpr (std::same_as) return ImGuiDataType_S16; else if constexpr (std::same_as) return ImGuiDataType_S32; else if constexpr (std::same_as) return ImGuiDataType_S64; else if constexpr (std::same_as) return ImGuiDataType_Float; else if constexpr (std::same_as) return ImGuiDataType_Double; else static_assert(hex::always_false::value, "Invalid data type!"); } template constexpr const char *getFormatLengthSpecifier() { if constexpr (std::same_as) return "hh"; else if constexpr (std::same_as) return "h"; else if constexpr (std::same_as) return "l"; else if constexpr (std::same_as) return "ll"; else if constexpr (std::same_as) return "hh"; else if constexpr (std::same_as) return "h"; else if constexpr (std::same_as) return "l"; else if constexpr (std::same_as) return "ll"; else static_assert(hex::always_false::value, "Invalid data type!"); } struct ImGuiTestEngine { ImGuiTestEngine() = delete; static void setEnabled(bool enabled); [[nodiscard]] static bool isEnabled(); }; } // these functions are exception because they just allow conversion from string to char*, they do not really add anything namespace ImGui { bool InputText(const char* label, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputText(const char *label, std::u8string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputTextMultiline(const char* label, std::string &buffer, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputTextWithHint(const char *label, const char *hint, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); } ================================================ FILE: lib/libimhex/include/hex/ui/popup.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include namespace hex { namespace impl { class PopupBase { public: explicit PopupBase(UnlocalizedString unlocalizedName, bool closeButton, bool modal) : m_unlocalizedName(std::move(unlocalizedName)), m_closeButton(closeButton), m_modal(modal) { } virtual ~PopupBase() = default; virtual void drawContent() = 0; [[nodiscard]] virtual ImGuiWindowFlags getFlags() const { return ImGuiWindowFlags_None; } [[nodiscard]] virtual ImVec2 getMinSize() const { return { 0, 0 }; } [[nodiscard]] virtual ImVec2 getMaxSize() const { return { 0, 0 }; } [[nodiscard]] static std::vector> &getOpenPopups(); [[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { return m_unlocalizedName; } [[nodiscard]] bool hasCloseButton() const { return m_closeButton; } [[nodiscard]] bool isModal() const { return m_modal; } void close() { m_close = true; } [[nodiscard]] bool shouldClose() const { return m_close; } protected: static std::mutex& getMutex(); private: UnlocalizedString m_unlocalizedName; bool m_closeButton, m_modal; std::atomic m_close = false; }; } template class Popup : public impl::PopupBase { protected: explicit Popup(UnlocalizedString unlocalizedName, bool closeButton = true, bool modal = true) : PopupBase(std::move(unlocalizedName), closeButton, modal) { } public: template static void open(Args && ... args) { std::lock_guard lock(getMutex()); auto popup = std::make_unique(std::forward(args)...); getOpenPopups().emplace_back(std::move(popup)); } }; } ================================================ FILE: lib/libimhex/include/hex/ui/toast.hpp ================================================ #pragma once #include #include #include #include #include #include namespace hex { namespace impl { class ToastBase { public: ToastBase(ImColor color) : m_color(color) {} virtual ~ToastBase() = default; virtual void draw() { drawContent(); } virtual void drawContent() = 0; [[nodiscard]] static std::list> &getQueuedToasts(); [[nodiscard]] const ImColor& getColor() const { return m_color; } void setAppearTime(double appearTime) { m_appearTime = appearTime; } [[nodiscard]] double getAppearTime() const { return m_appearTime; } constexpr static double VisibilityTime = 4.0; protected: static std::mutex& getMutex(); double m_appearTime = 0; ImColor m_color; }; } template class Toast : public impl::ToastBase { public: using impl::ToastBase::ToastBase; template static void open(Args && ... args) { TaskManager::doLater([=] { auto toast = std::make_unique(args...); getQueuedToasts().emplace_back(std::move(toast)); }); } }; } ================================================ FILE: lib/libimhex/include/hex/ui/view.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include namespace hex { class View { explicit View(UnlocalizedString unlocalizedName, const char *icon); public: virtual ~View() = default; /** * @brief Draws the view * @note Do not override this method. Override drawContent() instead */ virtual void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) = 0; /** * @brief Draws the content of the view */ virtual void drawContent() = 0; /** * @brief Draws content that should always be visible, even if the view is not open */ virtual void drawAlwaysVisibleContent() { } /** * @brief Whether or not the view window should be drawn * @return True if the view window should be drawn, false otherwise */ [[nodiscard]] virtual bool shouldDraw() const; /** * @brief Whether or not the entire view should be processed * If this returns false, the view will not be drawn and no shortcuts will be handled. This includes things * drawn in the drawAlwaysVisibleContent() function. * @return True if the view should be processed, false otherwise */ [[nodiscard]] virtual bool shouldProcess() const; /** * @brief Whether or not the view should have an entry in the view menu * @return True if the view should have an entry in the view menu, false otherwise */ [[nodiscard]] virtual bool hasViewMenuItemEntry() const; /** * @brief Gets the minimum size of the view window * @return The minimum size of the view window */ [[nodiscard]] virtual ImVec2 getMinSize() const; /** * @brief Gets the maximum size of the view window * @return The maximum size of the view window */ [[nodiscard]] virtual ImVec2 getMaxSize() const; /** * @brief Gets additional window flags for the view window * @return Additional window flags for the view window */ [[nodiscard]] virtual ImGuiWindowFlags getWindowFlags() const; /** * @brief Returns a view whose menu items should be additionally visible when this view is focused * @return */ [[nodiscard]] virtual View* getMenuItemInheritView() const { return nullptr; } [[nodiscard]] const char *getIcon() const { return m_icon; } [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const; [[nodiscard]] std::string getName() const; [[nodiscard]] virtual bool shouldDefaultFocus() const { return false; } [[nodiscard]] virtual bool shouldStoreWindowState() const { return true; } [[nodiscard]] bool &getWindowOpenState(); [[nodiscard]] const bool &getWindowOpenState() const; [[nodiscard]] bool isFocused() const { return m_focused; } [[nodiscard]] static std::string toWindowName(const UnlocalizedString &unlocalizedName); [[nodiscard]] static const View* getLastFocusedView(); static void discardNavigationRequests(); void bringToFront(); [[nodiscard]] bool didWindowJustOpen(); void setWindowJustOpened(bool state); [[nodiscard]] bool didWindowJustClose(); void setWindowJustClosed(bool state); void trackViewState(); void setFocused(bool focused); protected: /** * @brief Called when this view is opened (i.e. made visible). */ virtual void onOpen() {} /** * @brief Called when this view is closed (i.e. made invisible). */ virtual void onClose() {} public: class Window; class Special; class Floating; class Scrolling; class Modal; class FullScreen; private: UnlocalizedString m_unlocalizedViewName; bool m_windowOpen = false, m_prevWindowOpen = false; std::map m_shortcuts; bool m_windowJustOpened = false, m_windowJustClosed = false; const char *m_icon; bool m_focused = false; friend class ShortcutManager; }; /** * @brief A view that draws a regular window. This should be the default for most views */ class View::Window : public View { public: explicit Window(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {} [[nodiscard]] ImGuiWindow *getFocusedSubWindow() const { return m_focusedSubWindow; } /** * @brief Draws help text for the view */ virtual void drawHelpText() = 0; void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) override; [[nodiscard]] virtual bool allowScroll() const { return false; } private: void handleFocusRestoration(); private: ImGuiWindow *m_focusedSubWindow{nullptr}; }; /** * @brief A view that doesn't handle any window creation and just draws its content. * This should be used if you intend to draw your own special window */ class View::Special : public View { public: explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {} void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final; }; /** * @brief A view that draws a floating window. These are the same as regular windows but cannot be docked */ class View::Floating : public View::Window { public: explicit Floating(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {} void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final; [[nodiscard]] bool shouldStoreWindowState() const override { return false; } }; /** * @brief A view that draws all its content at once without any scrolling being done by the window itself */ class View::Scrolling : public View::Window { public: explicit Scrolling(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {} void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final; [[nodiscard]] bool allowScroll() const final { return true; } }; /** * @brief A view that draws a modal window. The window will always be drawn on top and will block input to other windows */ class View::Modal : public View { public: explicit Modal(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {} void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final; [[nodiscard]] virtual bool hasCloseButton() const { return true; } [[nodiscard]] bool shouldStoreWindowState() const override { return false; } }; class View::FullScreen : public View { public: explicit FullScreen() : View("FullScreen", "") {} void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final; }; } ================================================ FILE: lib/libimhex/include/hex/ui/widgets.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace hex::ui { template class SearchableWidget { public: SearchableWidget(const std::function &comparator) : m_comparator(comparator) { } const std::vector &draw(const auto &entries) { if (m_filteredEntries.empty() && m_searchBuffer.empty()) { for (auto &entry : entries) m_filteredEntries.push_back(&entry); } if (ImGui::InputText("##search", m_searchBuffer)) { m_pendingUpdate = true; } if (m_pendingUpdate && !m_updateTask.isRunning()) { m_pendingUpdate = false; m_filteredEntries.clear(); m_filteredEntries.reserve(entries.size()); m_updateTask = TaskManager::createBackgroundTask("Searching", [this, &entries, searchBuffer = m_searchBuffer](Task&) { for (auto &entry : entries) { if (searchBuffer.empty() || m_comparator(searchBuffer, entry)) m_filteredEntries.push_back(&entry); } }); } return m_filteredEntries; } void reset() { m_filteredEntries.clear(); } private: std::atomic m_pendingUpdate = false; TaskHolder m_updateTask; std::string m_searchBuffer; std::vector m_filteredEntries; std::function m_comparator; }; } ================================================ FILE: lib/libimhex/include/hex.cppm ================================================ module; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include export module hex; #define HEX_MODULE_EXPORT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ================================================ FILE: lib/libimhex/include/hex.hpp ================================================ #pragma once #include #if defined(HEX_MODULE_EXPORT) #define EXPORT_MODULE export #else #define EXPORT_MODULE #endif ================================================ FILE: lib/libimhex/source/api/achievement_manager.cpp ================================================ #include #include #include #include #include #if defined(OS_WEB) #include #endif namespace hex { static AutoReset>>> s_achievements; const std::unordered_map>> &AchievementManager::getAchievements() { return *s_achievements; } static AutoReset>> s_nodeCategoryStorage; std::unordered_map>& getAchievementNodesMutable(bool rebuild) { if (!s_nodeCategoryStorage->empty() || !rebuild) return s_nodeCategoryStorage; s_nodeCategoryStorage->clear(); // Add all achievements to the node storage for (auto &[categoryName, achievements] : AchievementManager::getAchievements()) { auto &nodes = (*s_nodeCategoryStorage)[categoryName]; for (auto &[achievementName, achievement] : achievements) { nodes.emplace_back(achievement.get()); } } return s_nodeCategoryStorage; } const std::unordered_map>& AchievementManager::getAchievementNodes(bool rebuild) { return getAchievementNodesMutable(rebuild); } static AutoReset>> s_startNodes; const std::unordered_map>& AchievementManager::getAchievementStartNodes(bool rebuild) { if (!s_startNodes->empty() || !rebuild) return s_startNodes; auto &nodeCategoryStorage = getAchievementNodesMutable(rebuild); s_startNodes->clear(); // Add all parents and children to the nodes for (auto &[categoryName, achievements] : nodeCategoryStorage) { for (auto &achievementNode : achievements) { for (auto &requirement : achievementNode.achievement->getRequirements()) { for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) { auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) { return node.achievement->getUnlocalizedName() == requirement; }); if (iter != requirementAchievements.end()) { achievementNode.parents.emplace_back(&*iter); iter->children.emplace_back(&achievementNode); } } } for (auto &requirement : achievementNode.achievement->getVisibilityRequirements()) { for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) { auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) { return node.achievement->getUnlocalizedName() == requirement; }); if (iter != requirementAchievements.end()) { achievementNode.visibilityParents.emplace_back(&*iter); } } } } } for (auto &[categoryName, achievements] : nodeCategoryStorage) { for (auto &achievementNode : achievements) { if (!achievementNode.hasParents()) { (*s_startNodes)[categoryName].emplace_back(&achievementNode); } for (const auto &parent : achievementNode.parents) { if (parent->achievement->getUnlocalizedCategory() != achievementNode.achievement->getUnlocalizedCategory()) (*s_startNodes)[categoryName].emplace_back(&achievementNode); } } } return s_startNodes; } void AchievementManager::unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName) { auto &categories = getAchievements(); auto categoryIter = categories.find(unlocalizedCategory); if (categoryIter == categories.end()) { return; } auto &[categoryName, achievements] = *categoryIter; const auto achievementIter = achievements.find(unlocalizedName); if (achievementIter == achievements.end()) { return; } const auto &nodes = getAchievementNodes(); if (!nodes.contains(categoryName)) return; for (const auto &node : nodes.at(categoryName)) { auto &achievement = node.achievement; if (achievement->getUnlocalizedCategory() != unlocalizedCategory) { continue; } if (achievement->getUnlocalizedName() != unlocalizedName) { continue; } if (node.achievement->isUnlocked()) { return; } for (const auto &requirement : node.parents) { if (!requirement->achievement->isUnlocked()) { return; } } achievement->setUnlocked(true); if (achievement->isUnlocked()) EventAchievementUnlocked::post(*achievement); return; } } void AchievementManager::clearTemporary() { auto &categories = *s_achievements; for (auto &[categoryName, achievements] : categories) { std::erase_if(achievements, [](auto &data) { auto &[achievementName, achievement] = data; return achievement->isTemporary(); }); } std::erase_if(categories, [](auto &data) { auto &[categoryName, achievements] = data; return achievements.empty(); }); s_startNodes->clear(); s_nodeCategoryStorage->clear(); } std::pair AchievementManager::getProgress() { u32 unlocked = 0; u32 total = 0; for (auto &[categoryName, achievements] : getAchievements()) { for (auto &[achievementName, achievement] : achievements) { total += 1; if (achievement->isUnlocked()) { unlocked += 1; } } } return { unlocked, total }; } void AchievementManager::achievementAdded() { s_startNodes->clear(); s_nodeCategoryStorage->clear(); } Achievement &AchievementManager::addAchievementImpl(std::unique_ptr &&newAchievement) { const auto &category = newAchievement->getUnlocalizedCategory(); const auto &name = newAchievement->getUnlocalizedName(); auto [categoryIter, categoryInserted] = s_achievements->insert({ category, std::unordered_map>{} }); auto &[categoryKey, achievements] = *categoryIter; auto [achievementIter, achievementInserted] = achievements.emplace(name, std::move(newAchievement)); auto &[achievementKey, achievement] = *achievementIter; achievementAdded(); return *achievement; } constexpr static auto AchievementsFile = "achievements.json"; bool AchievementManager::s_initialized = false; void AchievementManager::loadProgress() { if (s_initialized) return; for (const auto &directory : paths::Config.read()) { auto path = directory / AchievementsFile; if (!wolv::io::fs::exists(path)) { continue; } wolv::io::File file(path, wolv::io::File::Mode::Read); if (!file.isValid()) { continue; } try { #if defined(OS_WEB) auto data = (char *) MAIN_THREAD_EM_ASM_INT({ let data = localStorage.getItem("achievements"); return data ? stringToNewUTF8(data) : null; }); #else auto data = file.readString(); #endif auto json = nlohmann::json::parse(data); for (const auto &[categoryName, achievements] : getAchievements()) { for (const auto &[achievementName, achievement] : achievements) { try { const auto &progress = json[categoryName][achievementName]; if (progress.is_null()) continue; achievement->setProgress(progress); } catch (const std::exception &e) { log::warn("Failed to load achievement progress for '{}::{}': {}", categoryName.get(), achievementName.get(), e.what()); } } } s_initialized = true; } catch (const std::exception &e) { log::error("Failed to load achievements: {}", e.what()); } } } void AchievementManager::storeProgress() { if (!s_initialized) loadProgress(); nlohmann::json json; for (const auto &[categoryName, achievements] : getAchievements()) { json[categoryName] = nlohmann::json::object(); for (const auto &[achievementName, achievement] : achievements) { json[categoryName][achievementName] = achievement->getProgress(); } } if (json.empty()) return; #if defined(OS_WEB) auto data = json.dump(); MAIN_THREAD_EM_ASM({ localStorage.setItem("achievements", UTF8ToString($0)); }, data.c_str()); #else for (const auto &directory : paths::Config.write()) { auto path = directory / AchievementsFile; wolv::io::File file(path, wolv::io::File::Mode::Create); if (!file.isValid()) continue; file.writeString(json.dump(4)); break; } #endif } } ================================================ FILE: lib/libimhex/source/api/content_registry.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(OS_WEB) #include #endif #include #include #include #include namespace hex { namespace ContentRegistry::Settings { [[maybe_unused]] constexpr auto SettingsFile = "settings.json"; namespace impl { struct OnChange { u64 id; OnChangeCallback callback; }; struct OnSave { u64 id; OnSaveCallback callback; }; static AutoReset>>> s_onChangeCallbacks; static AutoReset> s_onSaveCallbacks; static void runAllOnChangeCallbacks() { for (const auto &[category, rest] : *impl::s_onChangeCallbacks) { for (const auto &[name, callbacks] : rest) { for (const auto &[id, callback] : callbacks) { try { callback(getSetting(category, name, {})); } catch (const std::exception &e) { log::error("Failed to load setting [{} / {}]: {}", category, name, e.what()); } } } } } void runOnChangeHandlers(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value) { if (auto categoryIt = s_onChangeCallbacks->find(unlocalizedCategory); categoryIt != s_onChangeCallbacks->end()) { if (auto nameIt = categoryIt->second.find(unlocalizedName); nameIt != categoryIt->second.end()) { for (const auto &[id, callback] : nameIt->second) { try { callback(value); } catch (const nlohmann::json::exception &e) { log::error("Failed to run onChange handler for setting {}/{}: {}", unlocalizedCategory.get(), unlocalizedName.get(), e.what()); } } } } } static AutoReset s_settings; const nlohmann::json& getSettingsData() { return s_settings; } nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue) { auto &settings = *s_settings; if (!settings.contains(unlocalizedCategory)) settings[unlocalizedCategory] = {}; if (!settings[unlocalizedCategory].contains(unlocalizedName)) settings[unlocalizedCategory][unlocalizedName] = defaultValue; if (settings[unlocalizedCategory][unlocalizedName].is_null()) settings[unlocalizedCategory][unlocalizedName] = defaultValue; return settings[unlocalizedCategory][unlocalizedName]; } #if defined(OS_WEB) void load() { char *data = (char *) MAIN_THREAD_EM_ASM_INT({ let data = localStorage.getItem("config"); return data ? stringToNewUTF8(data) : null; }); if (data == nullptr) { store(); } else { s_settings = nlohmann::json::parse(data); } runAllOnChangeCallbacks(); } void store() { if (!s_settings.isValid()) return; const auto &settingsData = *s_settings; // During a crash settings can be empty, causing them to be overwritten. if (settingsData.empty()) { return; } const auto result = settingsData.dump(4); if (result.empty()) { return; } MAIN_THREAD_EM_ASM({ localStorage.setItem("config", UTF8ToString($0)); }, result.c_str()); } void clear() { MAIN_THREAD_EM_ASM({ localStorage.removeItem("config"); }); } #else void load() { bool loaded = false; for (const auto &dir : paths::Config.read()) { wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Read); if (file.isValid()) { s_settings = nlohmann::json::parse(file.readString()); loaded = true; break; } } if (!loaded) store(); runAllOnChangeCallbacks(); } void store() { thread_local bool isRunningCallbacks = false; if (isRunningCallbacks) return; isRunningCallbacks = true; for (const auto &[id, callback] : *s_onSaveCallbacks) { try { callback(); } catch (const std::exception &e) { log::error("Failed to run onSave handler for setting: {}", e.what()); } } isRunningCallbacks = false; if (!s_settings.isValid()) return; const auto &settingsData = *s_settings; // During a crash settings can be empty, causing them to be overwritten. if (s_settings->empty()) { return; } const auto result = settingsData.dump(4); if (result.empty()) { return; } for (const auto &dir : paths::Config.write()) { wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Create); if (file.isValid()) { file.writeString(result); break; } } } void clear() { for (const auto &dir : paths::Config.write()) { wolv::io::fs::remove(dir / SettingsFile); } } #endif template static T* insertOrGetEntry(std::vector &vector, const UnlocalizedString &unlocalizedName) { T *foundEntry = nullptr; for (auto &entry : vector) { if (entry.unlocalizedName == unlocalizedName) { foundEntry = &entry; break; } } if (foundEntry == nullptr) { if (unlocalizedName.empty()) foundEntry = &*vector.emplace(vector.begin(), unlocalizedName); else foundEntry = &vector.emplace_back(unlocalizedName); } return foundEntry; } static AutoReset> s_categories; const std::vector& getSettings() { return *s_categories; } Widgets::Widget* add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, std::unique_ptr &&widget) { const auto category = insertOrGetEntry(*s_categories, unlocalizedCategory); const auto subCategory = insertOrGetEntry(category->subCategories, unlocalizedSubCategory); const auto entry = insertOrGetEntry(subCategory->entries, unlocalizedName); entry->widget = std::move(widget); if (entry->widget != nullptr) { onChange(unlocalizedCategory, unlocalizedName, [widget = entry->widget.get(), unlocalizedCategory, unlocalizedName](const SettingsValue &) { auto defaultValue = widget->store(); try { widget->load(ContentRegistry::Settings::impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue)); widget->onChanged(); } catch (const std::exception &e) { log::error("Failed to load setting [{} / {}]: {}", unlocalizedCategory.get(), unlocalizedName.get(), e.what()); ContentRegistry::Settings::impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue) = defaultValue; } }); runOnChangeHandlers(unlocalizedCategory, unlocalizedName, getSetting(unlocalizedCategory, unlocalizedName, entry->widget->store())); } return entry->widget.get(); } void printSettingReadError(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json::exception& e) { hex::log::error("Failed to read setting {}/{}: {}", unlocalizedCategory.get(), unlocalizedName.get(), e.what()); } } void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription) { const auto category = insertOrGetEntry(*impl::s_categories, unlocalizedCategory); category->unlocalizedDescription = unlocalizedDescription; } u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback) { static u64 id = 1; (*impl::s_onChangeCallbacks)[unlocalizedCategory][unlocalizedName].emplace_back(id, callback); auto result = id; id += 1; return result; } void removeOnChangeHandler(u64 id) { bool done = false; auto categoryIt = impl::s_onChangeCallbacks->begin(); for (; categoryIt != impl::s_onChangeCallbacks->end(); ++categoryIt) { auto nameIt = categoryIt->second.begin(); for (; nameIt != categoryIt->second.end(); ++nameIt) { done = std::erase_if(nameIt->second, [id](const impl::OnChange &entry) { return entry.id == id; }) > 0; if (done) break; } if (done) { if (nameIt->second.empty()) categoryIt->second.erase(nameIt); break; } } if (done) { if (categoryIt->second.empty()) impl::s_onChangeCallbacks->erase(categoryIt); } } u64 onSave(const OnSaveCallback &callback) { static u64 id = 1; impl::s_onSaveCallbacks->emplace_back(id, callback); auto result = id; id += 1; return result; } namespace Widgets { bool Checkbox::draw(const std::string &name) { return ImGui::Checkbox(name.c_str(), &m_value); } void Checkbox::load(const nlohmann::json &data) { if (data.is_number()) { m_value = data.get() != 0; } else if (data.is_boolean()) { m_value = data.get(); } else { log::warn("Invalid data type loaded from settings for checkbox!"); } } nlohmann::json Checkbox::store() { return m_value; } bool SliderInteger::draw(const std::string &name) { return ImGui::SliderInt(name.c_str(), &m_value, m_min, m_max); } void SliderInteger::load(const nlohmann::json &data) { if (data.is_number_integer()) { m_value = data.get(); } else { log::warn("Invalid data type loaded from settings for slider!"); } } nlohmann::json SliderInteger::store() { return m_value; } bool SliderFloat::draw(const std::string &name) { return ImGui::SliderFloat(name.c_str(), &m_value, m_min, m_max); } void SliderFloat::load(const nlohmann::json &data) { if (data.is_number()) { m_value = data.get(); } else { log::warn("Invalid data type loaded from settings for slider!"); } } nlohmann::json SliderFloat::store() { return m_value; } bool SliderDataSize::draw(const std::string &name) { return ImGuiExt::SliderBytes(name.c_str(), &m_value, m_min, m_max, m_stepSize); } void SliderDataSize::load(const nlohmann::json &data) { if (data.is_number_integer()) { m_value = data.get(); } else { log::warn("Invalid data type loaded from settings for slider!"); } } nlohmann::json SliderDataSize::store() { return m_value; } ColorPicker::ColorPicker(ImColor defaultColor, ImGuiColorEditFlags flags) { m_defaultValue = m_value = { defaultColor.Value.x, defaultColor.Value.y, defaultColor.Value.z, defaultColor.Value.w }; m_flags = flags; } static bool areColorsEquals(const std::array &a, const std::array &b) { return std::abs(a[0] - b[0]) < 0.005f && std::abs(a[1] - b[1]) < 0.005f && std::abs(a[2] - b[2]) < 0.005f && std::abs(a[3] - b[3]) < 0.005f; } bool ColorPicker::draw(const std::string &name) { ImGui::PushID(name.c_str()); auto result = ImGui::ColorEdit4("##color_picker", m_value.data(), ImGuiColorEditFlags_NoInputs | m_flags); ImGui::SameLine(); ImGui::BeginDisabled(areColorsEquals(m_value, m_defaultValue)); if (ImGuiExt::DimmedButton("\xee\xab\xa2", ImGui::GetStyle().FramePadding * 2 + ImVec2(ImGui::GetTextLineHeight(), ImGui::GetTextLineHeight()))) { m_value = m_defaultValue; result = true; } ImGui::EndDisabled(); ImGui::SameLine(); ImGui::TextUnformatted(name.c_str()); ImGui::PopID(); return result; } void ColorPicker::load(const nlohmann::json &data) { if (data.is_number()) { const ImColor color(data.get()); m_value = { color.Value.x, color.Value.y, color.Value.z, color.Value.w }; } else { log::warn("Invalid data type loaded from settings for color picker!"); } } nlohmann::json ColorPicker::store() { const ImColor color(m_value[0], m_value[1], m_value[2], m_value[3]); return static_cast(color); } ImColor ColorPicker::getColor() const { return { m_value[0], m_value[1], m_value[2], m_value[3] }; } bool DropDown::draw(const std::string &name) { auto preview = ""; if (static_cast(m_value) < m_items.size()) preview = m_items[m_value].get().c_str(); bool changed = false; if (ImGui::BeginCombo(name.c_str(), Lang(preview))) { int index = 0; for (const auto &item : m_items) { const bool selected = index == m_value; if (ImGui::Selectable(Lang(item), selected)) { m_value = index; changed = true; } if (selected) ImGui::SetItemDefaultFocus(); index += 1; } ImGui::EndCombo(); } return changed; } void DropDown::load(const nlohmann::json &data) { m_value = 0; int defaultItemIndex = 0; int index = 0; for (const auto &item : m_settingsValues) { if (item == m_defaultItem) defaultItemIndex = index; if (item == data) { m_value = index; return; } index += 1; } m_value = defaultItemIndex; } nlohmann::json DropDown::store() { if (m_value == -1) return m_defaultItem; if (static_cast(m_value) >= m_items.size()) return m_defaultItem; return m_settingsValues[m_value]; } const nlohmann::json& DropDown::getValue() const { return m_settingsValues[m_value]; } bool TextBox::draw(const std::string &name) { return ImGui::InputText(name.c_str(), m_value); } void TextBox::load(const nlohmann::json &data) { if (data.is_string()) { m_value = data.get(); } else { log::warn("Invalid data type loaded from settings for text box!"); } } nlohmann::json TextBox::store() { return m_value; } bool FilePicker::draw(const std::string &name) { bool changed = false; auto pathString = wolv::util::toUTF8String(m_path); if (ImGui::InputText("##font_path", pathString)) { changed = true; } ImGui::SameLine(); if (ImGuiExt::IconButton("...", ImGui::GetStyleColorVec4(ImGuiCol_Text))) { changed = fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } }, [&](const std::fs::path &path) { pathString = wolv::util::toUTF8String(path); }); } ImGui::SameLine(); ImGuiExt::TextFormatted("{}", name); if (changed) { m_path = pathString; } return changed; } void FilePicker::load(const nlohmann::json &data) { if (data.is_string()) { m_path = data.get(); } else { log::warn("Invalid data type loaded from settings for file picker!"); } } nlohmann::json FilePicker::store() { return m_path; } bool Label::draw(const std::string& name) { ImGui::NewLine(); ImGui::TextUnformatted(name.c_str()); return false; } bool Spacer::draw(const std::string& name) { std::ignore = name; ImGui::NewLine(); return false; } } } namespace ContentRegistry::CommandPalette { namespace impl { static AutoReset> s_entries; const std::vector& getEntries() { return *s_entries; } static AutoReset> s_handlers; const std::vector& getHandlers() { return *s_handlers; } static AutoReset> s_displayedContent; std::optional& getDisplayedContent() { return *s_displayedContent; } } void add(Type type, const std::string &command, const UnlocalizedString &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) { log::debug("Registered new command palette command: {}", command); impl::s_entries->push_back(impl::Entry { .type=type, .command=command, .unlocalizedDescription=unlocalizedDescription, .displayCallback=displayCallback, .executeCallback=executeCallback }); } void addHandler(Type type, const std::string &command, const impl::QueryCallback &queryCallback, const impl::DisplayCallback &displayCallback) { log::debug("Registered new command palette command handler: {}", command); impl::s_handlers->push_back(impl::Handler { .type=type, .command=command, .queryCallback=queryCallback, .displayCallback=displayCallback }); } void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback) { impl::s_displayedContent = impl::ContentDisplay { .showSearchBox=true, .callback=displayCallback }; } void openWithContent(const impl::ContentDisplayCallback &displayCallback) { RequestOpenCommandPalette::post(); impl::s_displayedContent = impl::ContentDisplay { .showSearchBox=false, .callback=displayCallback }; } } namespace ContentRegistry::PatternLanguage { namespace impl { static AutoReset> s_visualizers; const std::map& getVisualizers() { return *s_visualizers; } static AutoReset> s_inlineVisualizers; const std::map& getInlineVisualizers() { return *s_inlineVisualizers; } static AutoReset> s_pragmas; const std::map& getPragmas() { return *s_pragmas; } static AutoReset> s_functions; const std::vector& getFunctions() { return *s_functions; } static AutoReset> s_types; const std::vector& getTypes() { return *s_types; } } static std::string getFunctionName(const pl::api::Namespace &ns, const std::string &name) { std::string functionName; for (auto &scope : ns) functionName += scope + "::"; functionName += name; return functionName; } pl::PatternLanguage& getRuntime() { static PerProvider runtime; AT_FIRST_TIME { runtime.setOnCreateCallback([](prv::Provider *provider, pl::PatternLanguage &runtime) { configureRuntime(runtime, provider); }); }; return *runtime; } std::mutex& getRuntimeLock() { static std::mutex runtimeLock; return runtimeLock; } void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider) { runtime.reset(); if (provider != nullptr) { runtime.setDataSource(provider->getBaseAddress(), provider->getActualSize(), [provider](u64 offset, u8 *buffer, size_t size) { provider->read(offset, buffer, size); }, [provider](u64 offset, const u8 *buffer, size_t size) { if (provider->isWritable()) provider->write(offset, buffer, size); } ); } runtime.setIncludePaths(paths::PatternsInclude.read() | paths::Patterns.read()); for (const auto &[ns, name, paramCount, callback, dangerous] : impl::getFunctions()) { if (dangerous) runtime.addDangerousFunction(ns, name, paramCount, callback); else runtime.addFunction(ns, name, paramCount, callback); } for (const auto &[ns, name, paramCount, callback] : impl::getTypes()) { runtime.addType(ns, name, paramCount, callback); } for (const auto &[name, callback] : impl::getPragmas()) { runtime.addPragma(name, callback); } runtime.addDefine("__IMHEX__"); runtime.addDefine("__IMHEX_VERSION__", ImHexApi::System::getImHexVersion().get()); } void addPragma(const std::string &name, const pl::api::PragmaHandler &handler) { log::debug("Registered new pattern language pragma: {}", name); (*impl::s_pragmas)[name] = handler; } void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func) { log::debug("Registered new pattern language function: {}", getFunctionName(ns, name)); impl::s_functions->push_back({ ns, name, parameterCount, func, false }); } void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func) { log::debug("Registered new dangerous pattern language function: {}", getFunctionName(ns, name)); impl::s_functions->push_back({ ns, name, parameterCount, func, true }); } void addType(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::TypeCallback &func) { log::debug("Registered new pattern language type: {}", getFunctionName(ns, name)); impl::s_types->push_back({ ns, name, parameterCount, func }); } void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount) { log::debug("Registered new pattern visualizer function: {}", name); (*impl::s_visualizers)[name] = impl::Visualizer { .parameterCount=parameterCount, .callback=function }; } void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount) { log::debug("Registered new inline pattern visualizer function: {}", name); (*impl::s_inlineVisualizers)[name] = impl::Visualizer { .parameterCount=parameterCount, .callback=function }; } } namespace ContentRegistry::Views { namespace impl { static AutoReset>> s_views; const std::map>& getEntries() { return *s_views; } void add(std::unique_ptr &&view) { log::debug("Registered new view: {}", view->getUnlocalizedName().get()); s_views->emplace(view->getUnlocalizedName(), std::move(view)); } static AutoReset> s_fullscreenView; const std::unique_ptr& getFullScreenView() { return *s_fullscreenView; } void setFullScreenView(std::unique_ptr &&view) { s_fullscreenView = std::move(view); } } View* getViewByName(const UnlocalizedString &unlocalizedName) { auto &views = *impl::s_views; if (views.contains(unlocalizedName)) return views[unlocalizedName].get(); else return nullptr; } View* getFocusedView() { for (const auto &[unlocalizedName, view] : *impl::s_views) { if (view->isFocused()) return view.get(); } return nullptr; } } namespace ContentRegistry::Tools { namespace impl { static AutoReset> s_tools; const std::vector& getEntries() { return *s_tools; } } void add(const UnlocalizedString &unlocalizedName, const char *icon, const impl::Callback &function) { log::debug("Registered new tool: {}", unlocalizedName.get()); impl::s_tools->emplace_back(impl::Entry { .unlocalizedName=unlocalizedName, .icon=icon, .function=function }); } } namespace ContentRegistry::DataInspector { namespace impl { static AutoReset> s_entries; const std::vector& getEntries() { return *s_entries; } } namespace EditWidget { std::optional> TextInput::draw(std::string &value, std::endian endian) { if (ImGui::InputText("##InspectorLineEditing", value, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { return getBytes(value, endian); } return std::nullopt; } } void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional editingFunction) { log::debug("Registered new data inspector format: {}", unlocalizedName.get()); impl::s_entries->push_back({ unlocalizedName, requiredSize, requiredSize, std::move(displayGeneratorFunction), std::move(editingFunction) }); } void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional editingFunction) { log::debug("Registered new data inspector format: {}", unlocalizedName.get()); impl::s_entries->push_back({ unlocalizedName, requiredSize, maxSize, std::move(displayGeneratorFunction), std::move(editingFunction) }); } void drawMenuItems(const std::function &function) { if (ImGui::BeginPopup("##DataInspectorRowContextMenu")) { function(); ImGui::Separator(); ImGui::EndPopup(); } } } namespace ContentRegistry::DataProcessor { namespace impl { static AutoReset> s_nodes; const std::vector& getEntries() { return *s_nodes; } void add(const Entry &entry) { log::debug("Registered new data processor node type: [{}]: {}", entry.unlocalizedCategory.get(), entry.unlocalizedName.get()); s_nodes->push_back(entry); } } void addSeparator() { impl::s_nodes->push_back({ "", "", [] { return nullptr; } }); } } namespace ContentRegistry::UserInterface { namespace impl { static AutoReset> s_mainMenuItems; const std::multimap& getMainMenuItems() { return *s_mainMenuItems; } static AutoReset> s_menuItems; const std::multimap& getMenuItems() { return *s_menuItems; } static AutoReset> s_toolbarMenuItems; const std::vector& getToolbarMenuItems() { return s_toolbarMenuItems; } std::multimap& getMenuItemsMutable() { return *s_menuItems; } static AutoReset> s_welcomeScreenEntries; const std::vector& getWelcomeScreenEntries() { return *s_welcomeScreenEntries; } static AutoReset> s_footerItems; const std::vector& getFooterItems() { return *s_footerItems; } static AutoReset> s_toolbarItems; const std::vector& getToolbarItems() { return *s_toolbarItems; } static AutoReset> s_sidebarItems; const std::vector& getSidebarItems() { return *s_sidebarItems; } static AutoReset> s_titlebarButtons; const std::vector& getTitlebarButtons() { return *s_titlebarButtons; } static AutoReset> s_welcomeScreenQuickSettingsToggles; const std::vector& getWelcomeScreenQuickSettingsToggles() { return *s_welcomeScreenQuickSettingsToggles; } } void registerMainMenuItem(const UnlocalizedString &unlocalizedName, u32 priority) { log::debug("Registered new main menu item: {}", unlocalizedName.get()); impl::s_mainMenuItems->insert({ priority, { unlocalizedName } }); } void addMenuItem(const std::vector &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) { addMenuItem(unlocalizedMainMenuNames, "", priority, shortcut, function, enabledCallback, selectedCallback, view); } void addMenuItem(const std::vector &unlocalizedMainMenuNames, const Icon &icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) { addMenuItem(unlocalizedMainMenuNames, icon, priority, shortcut, function, enabledCallback, []{ return false; }, view); } void addMenuItem(const std::vector &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) { addMenuItem(unlocalizedMainMenuNames, "", priority, shortcut, function, enabledCallback, []{ return false; }, view); } void addMenuItem(const std::vector &unlocalizedMainMenuNames, const Icon &icon, u32 priority, Shortcut shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) { log::debug("Added new menu item to menu {} with priority {}", unlocalizedMainMenuNames[0].get(), priority); Icon coloredIcon = icon; if (coloredIcon.color == 0x00) coloredIcon.color = ImGuiCustomCol_ToolbarGray; impl::s_menuItems->insert({ priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon=coloredIcon, .shortcut=shortcut, .view=view, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=selectedCallback, .toolbarIndex=-1 } }); if (shortcut != Shortcut::None) { if (view != nullptr && !shortcut.isLocal()) { shortcut += CurrentView; } if (shortcut.isLocal() && view != nullptr) ShortcutManager::addShortcut(view, shortcut, unlocalizedMainMenuNames, function, enabledCallback); else ShortcutManager::addGlobalShortcut(shortcut, unlocalizedMainMenuNames, function, enabledCallback); } } void addMenuItemSubMenu(std::vector unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view, bool showOnWelcomeScreen) { addMenuItemSubMenu(std::move(unlocalizedMainMenuNames), "", priority, function, enabledCallback, view, showOnWelcomeScreen); } void addMenuItemSubMenu(std::vector unlocalizedMainMenuNames, const char *icon, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view, bool showOnWelcomeScreen) { log::debug("Added new menu item sub menu to menu {} with priority {}", unlocalizedMainMenuNames[0].get(), priority); unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue); impl::s_menuItems->insert({ priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon=icon, .shortcut=showOnWelcomeScreen ? Shortcut({ ShowOnWelcomeScreen }) : Shortcut::None, .view=view, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 } }); } void addMenuItemSeparator(std::vector unlocalizedMainMenuNames, u32 priority, View *view) { unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue); impl::s_menuItems->insert({ priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon="", .shortcut=Shortcut::None, .view=view, .callback=[]{}, .enabledCallback=[]{ return true; }, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 } }); } void addTaskBarMenuItem(std::vector unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) { log::debug("Added new taskbar menu item to menu {} ", unlocalizedMainMenuNames[0].get()); unlocalizedMainMenuNames.insert(unlocalizedMainMenuNames.begin(), impl::TaskBarMenuValue); impl::s_menuItems->insert({ priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon="", .shortcut=Shortcut::None, .view=nullptr, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 } }); } void addWelcomeScreenEntry(const impl::DrawCallback &function) { impl::s_welcomeScreenEntries->push_back(function); } void addFooterItem(const impl::DrawCallback &function) { impl::s_footerItems->push_back(function); } void addToolbarItem(const impl::DrawCallback &function) { impl::s_toolbarItems->push_back(function); } void addMenuItemToToolbar(const std::vector& unlocalizedNames, ImGuiCustomCol color) { const auto maxIndex = std::ranges::max_element(impl::getMenuItems(), [](const auto &a, const auto &b) { return a.second.toolbarIndex < b.second.toolbarIndex; })->second.toolbarIndex; for (auto &[priority, menuItem] : *impl::s_menuItems) { if (menuItem.unlocalizedNames == unlocalizedNames) { menuItem.toolbarIndex = maxIndex + 1; menuItem.icon.color = color; updateToolbarItems(); break; } } } struct MenuItemSorter { bool operator()(const auto *a, const auto *b) const { return a->toolbarIndex < b->toolbarIndex; } }; void updateToolbarItems() { std::set menuItems; for (auto &[priority, menuItem] : impl::getMenuItemsMutable()) { if (menuItem.toolbarIndex != -1) { menuItems.insert(&menuItem); } } impl::s_toolbarMenuItems->clear(); for (auto menuItem : menuItems) { impl::s_toolbarMenuItems->push_back(menuItem); } } void addSidebarItem(const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback) { impl::s_sidebarItems->push_back({ icon, function, enabledCallback }); } void addTitleBarButton(const std::string &icon, ImGuiCustomCol color, const UnlocalizedString &unlocalizedTooltip, const impl::ClickCallback &function) { impl::s_titlebarButtons->push_back({ icon, color, unlocalizedTooltip, function }); } void addWelcomeScreenQuickSettingsToggle(const std::string &icon, const UnlocalizedString &unlocalizedTooltip, bool defaultState, const impl::ToggleCallback &function) { impl::s_welcomeScreenQuickSettingsToggles->push_back({ icon, icon, unlocalizedTooltip, function, defaultState }); } void addWelcomeScreenQuickSettingsToggle(const std::string &onIcon, const std::string &offIcon, const UnlocalizedString &unlocalizedTooltip, bool defaultState, const impl::ToggleCallback &function) { impl::s_welcomeScreenQuickSettingsToggles->push_back({ onIcon, offIcon, unlocalizedTooltip, function, defaultState }); } } namespace ContentRegistry::Provider::impl { void add(const std::string &typeName, ProviderCreationFunction creationFunction) { (void)RequestCreateProvider::subscribe([expectedName = typeName, creationFunction](const std::string &name, bool skipLoadInterface, bool selectProvider, std::shared_ptr *provider) { if (name != expectedName) return; auto newProvider = creationFunction(); if (provider != nullptr) { *provider = newProvider; ImHexApi::Provider::add(std::move(newProvider), skipLoadInterface, selectProvider); } }); } static AutoReset> s_providerNames; const std::vector& getEntries() { return *s_providerNames; } void addProviderName(const UnlocalizedString &unlocalizedName, const char *icon) { log::debug("Registered new provider: {}", unlocalizedName.get()); s_providerNames->emplace_back(unlocalizedName, icon); } } namespace ContentRegistry::DataFormatter { namespace impl { static AutoReset> s_exportMenuEntries; const std::vector& getExportMenuEntries() { return *s_exportMenuEntries; } static AutoReset> s_findExportEntries; const std::vector& getFindExporterEntries() { return *s_findExportEntries; } } void addExportMenuEntry(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) { log::debug("Registered new data formatter: {}", unlocalizedName.get()); impl::s_exportMenuEntries->push_back({ unlocalizedName, callback }); } void addFindExportFormatter(const UnlocalizedString &unlocalizedName, const std::string &fileExtension, const impl::FindExporterCallback &callback) { log::debug("Registered new export formatter: {}", unlocalizedName.get()); impl::s_findExportEntries->push_back({ unlocalizedName, fileExtension, callback }); } } namespace ContentRegistry::FileTypeHandler { namespace impl { static AutoReset> s_entries; const std::vector& getEntries() { return *s_entries; } } void add(const std::vector &extensions, const impl::Callback &callback) { for (const auto &extension : extensions) log::debug("Registered new data handler for extensions: {}", extension); impl::s_entries->push_back({ extensions, callback }); } } namespace ContentRegistry::HexEditor { int DataVisualizer::DefaultTextInputFlags() { return ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_AlwaysOverwrite; } bool DataVisualizer::drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const { struct UserData { u8 *data; i32 maxChars; bool editingDone; }; UserData userData = { .data = data, .maxChars = this->getMaxCharsPerCell(), .editingDone = false }; ImGui::PushID(reinterpret_cast(address)); ImGuiExt::InputScalarCallback("##editing_input", dataType, data, format, flags | DefaultTextInputFlags() | ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) -> int { auto &userData = *static_cast(data->UserData); if (data->CursorPos >= userData.maxChars) userData.editingDone = true; data->Buf[userData.maxChars] = 0x00; return 0; }, &userData); ImGui::PopID(); return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Escape); } bool DataVisualizer::drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const { struct UserData { std::string *data; i32 maxChars; bool editingDone; }; UserData userData = { .data = &data, .maxChars = this->getMaxCharsPerCell(), .editingDone = false }; ImGui::PushID(reinterpret_cast(address)); ImGui::InputText("##editing_input", data.data(), data.size() + 1, flags | DefaultTextInputFlags() | ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) -> int { auto &userData = *static_cast(data->UserData); userData.data->resize(data->BufSize); if (data->BufTextLen >= userData.maxChars) userData.editingDone = true; data->Buf[userData.maxChars] = 0x00; return 0; }, &userData); ImGui::PopID(); return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Escape); } namespace impl { static AutoReset>> s_visualizers; const std::vector>& getVisualizers() { return *s_visualizers; } static AutoReset>> s_miniMapVisualizers; const std::vector>& getMiniMapVisualizers() { return *s_miniMapVisualizers; } void addDataVisualizer(std::shared_ptr &&visualizer) { s_visualizers->emplace_back(std::move(visualizer)); } } std::shared_ptr getVisualizerByName(const UnlocalizedString &unlocalizedName) { for (const auto &visualizer : impl::getVisualizers()) { if (visualizer->getUnlocalizedName() == unlocalizedName) return visualizer; } return nullptr; } void addMiniMapVisualizer(UnlocalizedString unlocalizedName, MiniMapVisualizer::Callback callback) { impl::s_miniMapVisualizers->emplace_back(std::make_shared(std::move(unlocalizedName), std::move(callback))); } } namespace ContentRegistry::Diffing::impl { static AutoReset>> s_algorithms; const std::vector>& getAlgorithms() { return *s_algorithms; } void addAlgorithm(std::unique_ptr &&hash) { s_algorithms->emplace_back(std::move(hash)); } } namespace ContentRegistry::Hashes::impl { static AutoReset>> s_hashes; const std::vector>& getHashes() { return *s_hashes; } void add(std::unique_ptr &&hash) { s_hashes->emplace_back(std::move(hash)); } } namespace ContentRegistry::BackgroundServices { namespace impl { class Service { public: Service(UnlocalizedString unlocalizedName, std::jthread thread) : m_unlocalizedName(std::move(unlocalizedName)), m_thread(std::move(thread)) { } Service(const Service&) = delete; Service(Service &&) = default; ~Service() { m_thread.request_stop(); if (m_thread.joinable()) m_thread.join(); } Service& operator=(const Service&) = delete; Service& operator=(Service &&) = default; [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; } [[nodiscard]] const std::jthread& getThread() const { return m_thread; } private: UnlocalizedString m_unlocalizedName; std::jthread m_thread; }; static AutoReset> s_services; const std::vector& getServices() { return *s_services; } void stopServices() { s_services->clear(); } } void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) { log::debug("Registered new background service: {}", unlocalizedName.get()); impl::s_services->emplace_back( unlocalizedName, std::jthread([=](const std::stop_token &stopToken){ TaskManager::setCurrentThreadName(Lang(unlocalizedName)); while (!stopToken.stop_requested()) { callback(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } }) ); } } namespace ContentRegistry::CommunicationInterface { namespace impl { static AutoReset> s_endpoints; const std::map& getNetworkEndpoints() { return *s_endpoints; } } void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback) { log::debug("Registered new network endpoint: {}", endpoint); impl::s_endpoints->insert({ endpoint, callback }); } } namespace ContentRegistry::MCP { namespace impl { std::unique_ptr& getMcpServerInstance() { static std::unique_ptr server; if (server == nullptr) server = std::make_unique(); return server; } static bool s_mcpEnabled = false; void setEnabled(bool enabled) { s_mcpEnabled = enabled; } } bool isEnabled() { return impl::s_mcpEnabled; } bool isConnected() { return impl::getMcpServerInstance()->isConnected(); } void registerTool(std::string_view capabilities, std::function function) { impl::getMcpServerInstance()->addPrimitive("tools", capabilities, function); } } namespace ContentRegistry::Experiments { namespace impl { static AutoReset> s_experiments; const std::map& getExperiments() { return *s_experiments; } } void addExperiment(const std::string &experimentName, const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) { auto &experiments = *impl::s_experiments; if (experiments.contains(experimentName)) { log::error("Experiment with name '{}' already exists!", experimentName); return; } experiments[experimentName] = impl::Experiment { .unlocalizedName = unlocalizedName, .unlocalizedDescription = unlocalizedDescription, .enabled = false }; } void enableExperiement(const std::string &experimentName, bool enabled) { auto &experiments = *impl::s_experiments; if (!experiments.contains(experimentName)) { log::error("Experiment with name '{}' does not exist!", experimentName); return; } experiments[experimentName].enabled = enabled; } [[nodiscard]] bool isExperimentEnabled(const std::string &experimentName) { auto &experiments = *impl::s_experiments; if (!experiments.contains(experimentName)) { log::error("Experiment with name '{}' does not exist!", experimentName); return false; } return experiments[experimentName].enabled; } } namespace ContentRegistry::Reports { namespace impl { static AutoReset> s_generators; const std::vector& getGenerators() { return *s_generators; } } void addReportProvider(impl::Callback callback) { impl::s_generators->push_back(impl::ReportGenerator { std::move(callback ) }); } } namespace ContentRegistry::DataInformation { void InformationSection::load(const nlohmann::json &data) { m_enabled = data.value("enabled", true); } [[nodiscard]] nlohmann::json InformationSection::store() { nlohmann::json data; data["enabled"] = m_enabled.load(); return data; } namespace impl { static AutoReset> s_informationSectionConstructors; const std::vector& getInformationSectionConstructors() { return *s_informationSectionConstructors; } void addInformationSectionCreator(const CreateCallback &callback) { s_informationSectionConstructors->emplace_back(callback); } } } namespace ContentRegistry::Disassemblers::impl { static AutoReset> s_architectures; void addArchitectureCreator(impl::CreatorFunction function) { const auto arch = function(); (*s_architectures)[arch->getName()] = std::move(function); } const std::map& getArchitectures() { return *s_architectures; } } } ================================================ FILE: lib/libimhex/source/api/event_manager.cpp ================================================ #include #include namespace hex { std::multimap& EventManager::getTokenStore() { static std::multimap tokenStore; return tokenStore; } EventManager::EventList& EventManager::getEvents() { static EventManager::EventList events; return events; } std::recursive_mutex& EventManager::getEventMutex() { static std::recursive_mutex mutex; return mutex; } bool EventManager::isAlreadyRegistered(void *token, impl::EventId id) { if (getTokenStore().contains(token)) { auto&& [begin, end] = getTokenStore().equal_range(token); return std::any_of(begin, end, [&](auto &item) { return item.second->first == id; }); } return false; } void EventManager::unsubscribe(void *token, impl::EventId id) { auto &tokenStore = getTokenStore(); auto iter = std::ranges::find_if(tokenStore, [&](auto &item) { return item.first == token && item.second->first == id; }); if (iter != tokenStore.end()) { getEvents().erase(iter->second); tokenStore.erase(iter); } } } ================================================ FILE: lib/libimhex/source/api/imhex_api.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(OS_WINDOWS) #include #include #else #include #include #endif #if defined(OS_WEB) #include #elif defined(OS_MACOS) extern "C" { void macosRegisterFont(const unsigned char *data, size_t size); } #endif namespace hex { namespace ImHexApi::HexEditor { Highlighting::Highlighting(Region region, color_t color) : m_region(region), m_color(color) { } Tooltip::Tooltip(Region region, std::string value, color_t color) : m_region(region), m_value(std::move(value)), m_color(color) { } namespace impl { static AutoReset> s_backgroundHighlights; const std::map& getBackgroundHighlights() { return *s_backgroundHighlights; } static AutoReset> s_backgroundHighlightingFunctions; const std::map& getBackgroundHighlightingFunctions() { return *s_backgroundHighlightingFunctions; } static AutoReset> s_foregroundHighlights; const std::map& getForegroundHighlights() { return *s_foregroundHighlights; } static AutoReset> s_foregroundHighlightingFunctions; const std::map& getForegroundHighlightingFunctions() { return *s_foregroundHighlightingFunctions; } static AutoReset> s_tooltips; const std::map& getTooltips() { return *s_tooltips; } static AutoReset> s_tooltipFunctions; const std::map& getTooltipFunctions() { return *s_tooltipFunctions; } static AutoReset> s_hoveringFunctions; const std::map& getHoveringFunctions() { return *s_hoveringFunctions; } static AutoReset> s_currentSelection; void setCurrentSelection(const std::optional ®ion) { if (region == Region::Invalid()) { clearSelection(); } else { *s_currentSelection = region; } } static PerProvider> s_hoveredRegion; void setHoveredRegion(const prv::Provider *provider, const Region ®ion) { if (provider == nullptr) return; if (region == Region::Invalid()) s_hoveredRegion.get(provider).reset(); else s_hoveredRegion.get(provider) = region; } } u32 addBackgroundHighlight(const Region ®ion, color_t color) { static u32 id = 0; id++; impl::s_backgroundHighlights->insert({ id, Highlighting { region, color } }); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); return id; } void removeBackgroundHighlight(u32 id) { impl::s_backgroundHighlights->erase(id); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) { static u32 id = 0; id++; impl::s_backgroundHighlightingFunctions->insert({ id, function }); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); return id; } void removeBackgroundHighlightingProvider(u32 id) { impl::s_backgroundHighlightingFunctions->erase(id); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } u32 addForegroundHighlight(const Region ®ion, color_t color) { static u32 id = 0; id++; impl::s_foregroundHighlights->insert({ id, Highlighting { region, color } }); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); return id; } void removeForegroundHighlight(u32 id) { impl::s_foregroundHighlights->erase(id); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) { static u32 id = 0; id++; impl::s_foregroundHighlightingFunctions->insert({ id, function }); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); return id; } void removeForegroundHighlightingProvider(u32 id) { impl::s_foregroundHighlightingFunctions->erase(id); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } u32 addHoverHighlightProvider(const impl::HoveringFunction &function) { static u32 id = 0; id++; impl::s_hoveringFunctions->insert({ id, function }); return id; } void removeHoverHighlightProvider(u32 id) { impl::s_hoveringFunctions->erase(id); } static u32 s_tooltipId = 0; u32 addTooltip(Region region, std::string value, color_t color) { s_tooltipId++; impl::s_tooltips->insert({ s_tooltipId, { region, std::move(value), color } }); return s_tooltipId; } void removeTooltip(u32 id) { impl::s_tooltips->erase(id); } static u32 s_tooltipFunctionId; u32 addTooltipProvider(TooltipFunction function) { s_tooltipFunctionId++; impl::s_tooltipFunctions->insert({ s_tooltipFunctionId, std::move(function) }); return s_tooltipFunctionId; } void removeTooltipProvider(u32 id) { impl::s_tooltipFunctions->erase(id); } bool isSelectionValid() { auto selection = getSelection(); return selection.has_value() && selection->provider != nullptr; } std::optional getSelection() { return impl::s_currentSelection; } void clearSelection() { impl::s_currentSelection->reset(); } void setSelection(const Region ®ion, prv::Provider *provider) { setSelection(ProviderRegion { region, provider == nullptr ? Provider::get() : provider }); } void setSelection(const ProviderRegion ®ion) { RequestHexEditorSelectionChange::post(region); } void setSelection(u64 address, size_t size, prv::Provider *provider) { setSelection({ { .address=address, .size=size }, provider == nullptr ? Provider::get() : provider }); } void addVirtualFile(const std::string &path, std::vector data, Region region) { RequestAddVirtualFile::post(path, std::move(data), region); } const std::optional& getHoveredRegion(const prv::Provider *provider) { return impl::s_hoveredRegion.get(provider); } } namespace ImHexApi::Bookmarks { u64 add(Region region, const std::string &name, const std::string &comment, u32 color) { u64 id = 0; RequestAddBookmark::post(region, name, comment, color, &id); return id; } u64 add(u64 address, size_t size, const std::string &name, const std::string &comment, u32 color) { return add(Region { .address=address, .size=size }, name, comment, color); } void remove(u64 id) { RequestRemoveBookmark::post(id); } } namespace ImHexApi::Provider { static i64 s_currentProvider = -1; static AutoReset>> s_providers; static AutoReset>> s_providersToRemove; namespace impl { static std::set s_closingProviders; void resetClosingProvider() { s_closingProviders.clear(); } std::set getClosingProviders() { return s_closingProviders; } static std::recursive_mutex s_providerMutex; } prv::Provider *get() { if (!ImHexApi::Provider::isValid()) return nullptr; return (*s_providers)[s_currentProvider].get(); } std::vector getProviders() { std::vector result; result.reserve(s_providers->size()); for (const auto &provider : *s_providers) result.push_back(provider.get()); return result; } void setCurrentProvider(i64 index) { std::scoped_lock lock(impl::s_providerMutex); if (TaskManager::getRunningTaskCount() > 0) return; if (std::cmp_less(index, s_providers->size()) && s_currentProvider != index) { auto oldProvider = get(); s_currentProvider = index; EventProviderChanged::post(oldProvider, get()); } RequestUpdateWindowTitle::post(); } void setCurrentProvider(NonNull provider) { std::scoped_lock lock(impl::s_providerMutex); if (TaskManager::getRunningTaskCount() > 0) return; const auto providers = getProviders(); auto it = std::ranges::find(providers, provider.get()); auto index = std::distance(providers.begin(), it); setCurrentProvider(index); } i64 getCurrentProviderIndex() { return s_currentProvider; } bool isValid() { return !s_providers->empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers->size()); } void markDirty() { const auto provider = get(); if (!provider->isDirty()) { provider->markDirty(); } EventProviderDirtied::post(provider); } void resetDirty() { for (const auto &provider : *s_providers) provider->markDirty(false); } bool isDirty() { return std::ranges::any_of(*s_providers, [](const auto &provider) { return provider->isDirty(); }); } void add(std::shared_ptr &&provider, bool skipLoadInterface, bool select) { std::scoped_lock lock(impl::s_providerMutex); if (TaskManager::getRunningTaskCount() > 0) return; if (skipLoadInterface) provider->skipLoadInterface(); EventProviderCreated::post(provider); s_providers->emplace_back(std::move(provider)); if (select || s_providers->size() == 1) setCurrentProvider(s_providers->size() - 1); } void remove(prv::Provider *provider, bool noQuestions) { std::scoped_lock lock(impl::s_providerMutex); if (provider == nullptr) return; if (TaskManager::getRunningTaskCount() > 0) return; if (!noQuestions) { impl::s_closingProviders.insert(provider); bool shouldClose = true; EventProviderClosing::post(provider, &shouldClose); if (!shouldClose) return; } const auto it = std::ranges::find_if(*s_providers, [provider](const auto &p) { return p.get() == provider; }); if (it == s_providers->end()) return; if (!s_providers->empty()) { if (it == s_providers->begin()) { // If the first provider is being closed, select the one that's the first one now setCurrentProvider(0); if (s_providers->size() > 1) EventProviderChanged::post(s_providers->at(0).get(), s_providers->at(1).get()); } else if (std::distance(s_providers->begin(), it) == s_currentProvider) { // If the current provider is being closed, select the one that's before it setCurrentProvider(s_currentProvider - 1); } else { // If any other provider is being closed, find the current provider in the list again and select it again const auto currentProvider = get(); const auto currentIt = std::ranges::find_if(*s_providers, [currentProvider](const auto &p) { return p.get() == currentProvider; }); if (currentIt != s_providers->end()) { auto newIndex = std::distance(s_providers->begin(), currentIt); if (s_currentProvider == newIndex && newIndex != 0) newIndex -= 1; setCurrentProvider(newIndex); } else { // If the current provider is not in the list anymore, select the first one setCurrentProvider(0); } } } static std::mutex eraseMutex; // Move provider over to a list of providers to delete eraseMutex.lock(); auto providerToRemove = it->get(); (*s_providersToRemove)[providerToRemove] = std::move(*it); eraseMutex.unlock(); // Remove left over references from the main provider list s_providers->erase(it); impl::s_closingProviders.erase(provider); if (s_currentProvider >= i64(s_providers->size()) && !s_providers->empty()) setCurrentProvider(s_providers->size() - 1); if (s_providers->empty()) EventProviderChanged::post(provider, nullptr); EventProviderClosed::post(providerToRemove); RequestUpdateWindowTitle::post(); // Do the destruction of the provider in the background once all tasks have finished TaskManager::runWhenTasksFinished([providerToRemove] { EventProviderDeleted::post(providerToRemove); TaskManager::createBackgroundTask("Closing Provider", [providerToRemove](Task &) { eraseMutex.lock(); auto provider = std::move((*s_providersToRemove)[providerToRemove]); s_providersToRemove->erase(providerToRemove); eraseMutex.unlock(); provider->close(); }); }); } std::shared_ptr createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) { std::shared_ptr result = nullptr; RequestCreateProvider::post(unlocalizedName, skipLoadInterface, select, &result); return result; } void openProvider(std::shared_ptr provider) { RequestOpenProvider::post(provider); } } namespace ImHexApi::System { namespace impl { // Default to true means we forward to ourselves by default static bool s_isMainInstance = true; void setMainInstanceStatus(bool status) { s_isMainInstance = status; } static ImVec2 s_mainWindowPos; static ImVec2 s_mainWindowSize; void setMainWindowPosition(i32 x, i32 y) { s_mainWindowPos = ImVec2(float(x), float(y)); } void setMainWindowSize(u32 width, u32 height) { s_mainWindowSize = ImVec2(float(width), float(height)); } static ImGuiID s_mainDockSpaceId; void setMainDockSpaceId(ImGuiID id) { s_mainDockSpaceId = id; } static GLFWwindow *s_mainWindowHandle; void setMainWindowHandle(GLFWwindow *window) { s_mainWindowHandle = window; } static bool s_mainWindowFocused = false; void setMainWindowFocusState(bool focused) { s_mainWindowFocused = focused; } static float s_globalScale = 1.0; void setGlobalScale(float scale) { s_globalScale = scale; } static float s_nativeScale = 1.0; void setNativeScale(float scale) { s_nativeScale = scale; } static bool s_borderlessWindowMode; void setBorderlessWindowMode(bool enabled) { s_borderlessWindowMode = enabled; } static bool s_multiWindowMode = false; void setMultiWindowMode(bool enabled) { s_multiWindowMode = enabled; } static std::optional s_initialWindowProperties; void setInitialWindowProperties(InitialWindowProperties properties) { s_initialWindowProperties = properties; } static AutoReset s_gpuVendor; void setGPUVendor(const std::string &vendor) { s_gpuVendor = vendor; } static AutoReset s_glRenderer; void setGLRenderer(const std::string &renderer) { s_glRenderer = renderer; } static SemanticVersion s_openGLVersion; void setGLVersion(SemanticVersion version) { s_openGLVersion = version; } static AutoReset> s_initArguments; void addInitArgument(const std::string &key, const std::string &value) { static std::mutex initArgumentsMutex; std::scoped_lock lock(initArgumentsMutex); (*s_initArguments)[key] = value; } static double s_lastFrameTime; void setLastFrameTime(double time) { s_lastFrameTime = time; } static bool s_windowResizable = true; bool isWindowResizable() { return s_windowResizable; } static auto& getAutoResetObjects() { static std::set autoResetObjects; return autoResetObjects; } void addAutoResetObject(hex::impl::AutoResetBase *object) { getAutoResetObjects().insert(object); } void removeAutoResetObject(hex::impl::AutoResetBase *object) { getAutoResetObjects().erase(object); } void cleanup() { for (const auto &object : getAutoResetObjects()) object->reset(); } static bool s_frameRateUnlockRequested = false; bool frameRateUnlockRequested() { return s_frameRateUnlockRequested; } void resetFrameRateUnlockRequested() { s_frameRateUnlockRequested = false; } } bool isMainInstance() { return impl::s_isMainInstance; } void closeImHex(bool noQuestions) { RequestCloseImHex::post(noQuestions); } void restartImHex() { RequestRestartImHex::post(); RequestCloseImHex::post(false); } void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress) { EventSetTaskBarIconState::post(u32(state), u32(type), progress); } static float s_targetFPS = 14.0F; float getTargetFPS() { return s_targetFPS; } void setTargetFPS(float fps) { s_targetFPS = fps; } float getGlobalScale() { return impl::s_globalScale; } float getNativeScale() { return impl::s_nativeScale; } float getBackingScaleFactor() { #if defined(OS_WINDOWS) return 1.0F; #elif defined(OS_MACOS) return ::getBackingScaleFactor(); #elif defined(OS_LINUX) const auto sessionType = hex::getEnvironmentVariable("XDG_SESSION_TYPE"); if (!sessionType.has_value() || sessionType == "x11") return 1.0F; else { int windowW, windowH; int displayW, displayH; glfwGetWindowSize(getMainWindowHandle(), &windowW, &windowH); glfwGetFramebufferSize(getMainWindowHandle(), &displayW, &displayH); return (windowW > 0) ? float(displayW) / windowW : 1.0f; } #elif defined(OS_WEB) return emscripten_get_device_pixel_ratio(); #else return 1.0F; #endif } ImVec2 getMainWindowPosition() { if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != ImGuiConfigFlags_None) return impl::s_mainWindowPos; else return { 0, 0 }; } ImVec2 getMainWindowSize() { return impl::s_mainWindowSize; } ImGuiID getMainDockSpaceId() { return impl::s_mainDockSpaceId; } GLFWwindow* getMainWindowHandle() { return impl::s_mainWindowHandle; } bool isMainWindowFocused() { return impl::s_mainWindowFocused; } bool isBorderlessWindowModeEnabled() { return impl::s_borderlessWindowMode; } bool isMultiWindowModeEnabled() { return impl::s_multiWindowMode; } std::optional getInitialWindowProperties() { return impl::s_initialWindowProperties; } void* getLibImHexModuleHandle() { return hex::getContainingModule(reinterpret_cast(&getLibImHexModuleHandle)); } void addMigrationRoutine(SemanticVersion migrationVersion, std::function function) { EventImHexUpdated::subscribe([migrationVersion, function](const SemanticVersion &oldVersion, const SemanticVersion &newVersion) { if (oldVersion < migrationVersion && newVersion >= migrationVersion) { function(); } }); } const std::map& getInitArguments() { return *impl::s_initArguments; } std::string getInitArgument(const std::string &key) { if (impl::s_initArguments->contains(key)) return impl::s_initArguments->at(key); else return ""; } static bool s_systemThemeDetection; void enableSystemThemeDetection(bool enabled) { s_systemThemeDetection = enabled; EventOSThemeChanged::post(); } bool usesSystemThemeDetection() { return s_systemThemeDetection; } static AutoReset> s_additionalFolderPaths; const std::vector& getAdditionalFolderPaths() { return *s_additionalFolderPaths; } void setAdditionalFolderPaths(const std::vector &paths) { s_additionalFolderPaths = paths; } const std::string &getGPUVendor() { return impl::s_gpuVendor; } const std::string &getGLRenderer() { return impl::s_glRenderer; } const SemanticVersion& getGLVersion() { return impl::s_openGLVersion; } bool isCorporateEnvironment() { #if defined(OS_WINDOWS) { DSROLE_PRIMARY_DOMAIN_INFO_BASIC * info; if ((DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *)&info) == ERROR_SUCCESS) && (info != nullptr)) { bool result = std::wstring(info->DomainNameFlat) != L"WORKGROUP"; DsRoleFreeMemory(info); return result; } else { DWORD size = 128; char buffer[128]; ::GetComputerNameExA(ComputerNameDnsDomain, buffer, &size); return size > 0; } } #else return false; #endif } bool isPortableVersion() { static std::optional portable; if (portable.has_value()) return portable.value(); if (const auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) { const auto flagFile = executablePath->parent_path() / "PORTABLE"; portable = wolv::io::fs::exists(flagFile) && wolv::io::fs::isRegularFile(flagFile); } else { portable = false; } return portable.value(); } std::string getOSName() { #if defined(OS_WINDOWS) return "Windows"; #elif defined(OS_LINUX) #if defined(OS_FREEBSD) return "FreeBSD"; #else return "Linux"; #endif #elif defined(OS_MACOS) return "macOS"; #elif defined(OS_WEB) return "Web"; #else return "Unknown"; #endif } std::string getOSVersion() { #if defined(OS_WINDOWS) OSVERSIONINFOA info; info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); ::GetVersionExA(&info); return fmt::format("{}.{}.{}", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber); #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB) struct utsname details = { }; if (uname(&details) != 0) { return "Unknown"; } return std::string(details.release) + " " + std::string(details.version); #else return "Unknown"; #endif } std::string getArchitecture() { #if defined(OS_WINDOWS) SYSTEM_INFO info; ::GetNativeSystemInfo(&info); switch (info.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64"; case PROCESSOR_ARCHITECTURE_ARM: return "ARM"; case PROCESSOR_ARCHITECTURE_ARM64: return "ARM64"; case PROCESSOR_ARCHITECTURE_IA64: return "IA64"; case PROCESSOR_ARCHITECTURE_INTEL: return "x86"; default: return "Unknown"; } #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB) struct utsname details = { }; if (uname(&details) != 0) { return "Unknown"; } return { details.machine }; #else return "Unknown"; #endif } std::optional getLinuxDistro() { wolv::io::File file("/etc/os-release", wolv::io::File::Mode::Read); std::string name; std::string version; auto fileContent = file.readString(); for (const auto &line : wolv::util::splitString(fileContent, "\n")) { if (line.find("PRETTY_NAME=") != std::string::npos) { name = line.substr(line.find("=") + 1); std::erase(name, '\"'); } else if (line.find("VERSION_ID=") != std::string::npos) { version = line.substr(line.find("=") + 1); std::erase(version, '\"'); } } return { { .name=name, .version=version } }; } const SemanticVersion& getImHexVersion() { #if defined(IMHEX_VERSION) static auto version = SemanticVersion(IMHEX_VERSION); return version; #else static auto version = SemanticVersion(); return version; #endif } std::string getCommitHash(bool longHash) { #if defined(GIT_COMMIT_HASH_LONG) if (longHash) { return GIT_COMMIT_HASH_LONG; } else { return std::string(GIT_COMMIT_HASH_LONG).substr(0, 7); } #else std::ignore = longHash; return "Unknown"; #endif } std::string getCommitBranch() { #if defined(GIT_BRANCH) return GIT_BRANCH; #else return "Unknown"; #endif } std::optional getBuildTime() { #if defined(IMHEX_BUILD_DATE) return hex::parseTime("%Y-%m-%dT%H:%M:%SZ", IMHEX_BUILD_DATE); #else return std::nullopt; #endif } bool isDebugBuild() { #if defined DEBUG return true; #else return false; #endif } bool isNightlyBuild() { const static bool isNightly = getImHexVersion().nightly(); return isNightly; } std::optional checkForUpdate() { #if defined(OS_WEB) return std::nullopt; #else if (ImHexApi::System::isNightlyBuild()) { HttpRequest request("GET", GitHubApiURL + std::string("/releases/tags/nightly")); request.setTimeout(10000); // Query the GitHub API for the latest release version auto response = request.execute().get(); if (response.getStatusCode() != 200) return std::nullopt; nlohmann::json releases; try { releases = nlohmann::json::parse(response.getData()); } catch (const std::exception &) { return std::nullopt; } // Check if the response is valid if (!releases.contains("assets") || !releases["assets"].is_array()) return std::nullopt; const auto firstAsset = releases["assets"].front(); if (!firstAsset.is_object() || !firstAsset.contains("updated_at")) return std::nullopt; const auto nightlyUpdateTime = hex::parseTime("%Y-%m-%dT%H:%M:%SZ", firstAsset["updated_at"].get()); const auto imhexBuildTime = ImHexApi::System::getBuildTime(); // Give a bit of time leniency for the update time check // We're comparing here the binary build time to the release upload time. If we were to strictly compare // upload time to be greater than current build time, the check would always be true since the CI // takes a few minutes after the build to actually upload the artifact. // TODO: Is there maybe a better way to handle this without downloading the artifact just to check the build time? if (nightlyUpdateTime.has_value() && imhexBuildTime.has_value() && *nightlyUpdateTime > *imhexBuildTime + std::chrono::hours(1)) { return "Nightly"; } } else { HttpRequest request("GET", GitHubApiURL + std::string("/releases/latest")); // Query the GitHub API for the latest release version auto response = request.execute().get(); if (response.getStatusCode() != 200) return std::nullopt; nlohmann::json releases; try { releases = nlohmann::json::parse(response.getData()); } catch (const std::exception &) { return std::nullopt; } // Check if the response is valid if (!releases.contains("tag_name") || !releases["tag_name"].is_string()) return std::nullopt; // Convert the current version string to a format that can be compared to the latest release auto currVersion = "v" + ImHexApi::System::getImHexVersion().get(false); // Get the latest release version string auto latestVersion = releases["tag_name"].get(); // Check if the latest release is different from the current version if (latestVersion != currVersion) return latestVersion; } return std::nullopt; #endif } bool updateImHex(UpdateType updateType) { #if defined(OS_WEB) switch (updateType) { case UpdateType::Stable: EM_ASM({ window.location.href = window.location.origin; }); break; case UpdateType::Nightly: EM_ASM({ window.location.href = window.location.origin + "/nightly"; }); break; } return true; #else // Get the path of the updater executable std::fs::path executablePath; for (const auto &entry : std::fs::directory_iterator(wolv::io::fs::getExecutablePath()->parent_path())) { if (entry.path().filename().string().starts_with("imhex-updater")) { executablePath = entry.path(); break; } } if (executablePath.empty() || !wolv::io::fs::exists(executablePath)) return false; std::string updateTypeString; switch (updateType) { case UpdateType::Stable: updateTypeString = "stable"; break; case UpdateType::Nightly: updateTypeString = "nightly"; break; } EventImHexClosing::subscribe([executablePath, updateTypeString] { hex::startProgram({ wolv::util::toUTF8String(executablePath), updateTypeString }); }); ImHexApi::System::closeImHex(); return true; #endif } void addStartupTask(const std::string &name, bool async, const std::function &function) { RequestAddInitTask::post(name, async, function); } double getLastFrameTime() { return impl::s_lastFrameTime; } void setWindowResizable(bool resizable) { glfwSetWindowAttrib(impl::s_mainWindowHandle, GLFW_RESIZABLE, int(resizable)); impl::s_windowResizable = resizable; } void unlockFrameRate() { impl::s_frameRateUnlockRequested = true; } void setPostProcessingShader(const std::string &vertexShader, const std::string &fragmentShader) { RequestSetPostProcessingShader::post(vertexShader, fragmentShader); } } namespace ImHexApi::Messaging { namespace impl { static AutoReset> s_handlers; const std::map& getHandlers() { return *s_handlers; } void runHandler(const std::string &eventName, const std::vector &args) { const auto& handlers = getHandlers(); const auto matchHandler = handlers.find(eventName); if (matchHandler == handlers.end()) { log::error("Forward event handler {} not found", eventName); } else { matchHandler->second(args); } } } void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler) { log::debug("Registered new forward event handler: {}", eventName); impl::s_handlers->insert({ eventName, handler }); } } namespace ImHexApi::Fonts { namespace impl { static AutoReset> s_fonts; const std::vector& getMergeFonts() { return *s_fonts; } static AutoReset> s_fontDefinitions; std::map& getFontDefinitions() { return *s_fontDefinitions; } static AutoReset s_defaultFont; } Font::Font(UnlocalizedString fontName) : m_fontName(std::move(fontName)) { if (impl::s_defaultFont == nullptr) impl::s_defaultFont = this; } void Font::push(float size) const { push(size, getFont(m_fontName).regular); } void Font::pushBold(float size) const { push(size, getFont(m_fontName).bold); } void Font::pushItalic(float size) const { push(size, getFont(m_fontName).italic); } void Font::push(float size, ImFont* font) const { if (font != nullptr) { if (size <= 0.0F) { size = font->LegacySize; if (font->Sources[0]->PixelSnapH) size *= System::getGlobalScale(); else size *= std::floor(System::getGlobalScale()); } else { size *= ImGui::GetCurrentContext()->FontSizeBase; } } // If no font has been loaded, revert back to the default font to // prevent an assertion failure in ImGui const auto *ctx = ImGui::GetCurrentContext(); if (font == nullptr && ctx->Font == nullptr) [[unlikely]] { font = ImGui::GetDefaultFont(); } ImGui::PushFont(font, size); } void Font::pop() const { ImGui::PopFont(); } Font::operator ImFont*() const { return getFont(m_fontName).regular; } void registerMergeFont(const std::string &name, const std::span &data, Offset offset, std::optional fontSizeMultiplier) { impl::s_fonts->emplace_back( name, data, offset, fontSizeMultiplier ); #if defined(OS_MACOS) macosRegisterFont(data.data(), data.size_bytes()); #endif } void registerFont(const Font& font) { (*impl::s_fontDefinitions)[font.getUnlocalizedName()] = {}; } FontDefinition getFont(const UnlocalizedString &fontName) { auto it = impl::s_fontDefinitions->find(fontName); if (it == impl::s_fontDefinitions->end()) { const auto defaultFont = ImGui::GetDefaultFont(); return { .regular=defaultFont, .bold=defaultFont, .italic=defaultFont }; } else return it->second; } void setDefaultFont(const Font& font) { impl::s_defaultFont = &font; } const Font& getDefaultFont() { if (*impl::s_defaultFont == nullptr) { static Font emptyFont(""); return emptyFont; } return **impl::s_defaultFont; } float getDpi() { auto dpi = ImHexApi::System::getNativeScale() * 96.0F; return dpi ? dpi : 96.0F; } float pixelsToPoints(float pixels) { return pixels * (72.0 / getDpi()); } float pointsToPixels(float points) { return points / (72.0 / getDpi()); } } } ================================================ FILE: lib/libimhex/source/api/layout_manager.cpp ================================================ #include #include #include #include #include #include #include #include namespace hex { namespace { AutoReset> s_layoutPathToLoad; AutoReset> s_layoutStringToLoad; AutoReset> s_layouts; AutoReset> s_loadCallbacks; AutoReset> s_storeCallbacks; bool s_layoutLocked = false; } void LayoutManager::load(const std::fs::path &path) { s_layoutPathToLoad = path; } void LayoutManager::loadFromString(const std::string &content) { s_layoutStringToLoad = content; } void LayoutManager::save(const std::string &name) { auto fileName = name; fileName = wolv::util::replaceStrings(fileName, " ", "_"); std::ranges::transform(fileName, fileName.begin(), tolower); fileName += ".hexlyt"; for (const auto &path : paths::Layouts.write()) { size_t outSize = 0; const char* iniData = ImGui::SaveIniSettingsToMemory(&outSize); std::fs::path layoutPath = path / fileName; wolv::io::File file = wolv::io::File(layoutPath, wolv::io::File::Mode::Write); if (!file.isValid()) { log::warn("Failed to save layout '{}'. Could not open file '{}', continuing with next path", name, wolv::util::toUTF8String(layoutPath)); continue; } const size_t written = file.writeBuffer(reinterpret_cast(iniData), outSize); if (written != outSize) { log::warn("Failed to save layout '{}'. Could not write file '{}', continuing with next path", name, wolv::util::toUTF8String(layoutPath)); continue; } log::info("Layout '{}' saved to '{}'", name, wolv::util::toUTF8String(layoutPath)); LayoutManager::reload(); return; } log::error("Failed to save layout '{}'. No writable path found", name); } std::string LayoutManager::saveToString() { return ImGui::SaveIniSettingsToMemory(); } const std::vector& LayoutManager::getLayouts() { return s_layouts; } void LayoutManager::removeLayout(const std::string& name) { for (const auto &layout : *s_layouts) { if (layout.name == name) { if (wolv::io::fs::remove(layout.path)) { log::info("Removed layout '{}'", name); } else { log::error("Failed to remove layout '{}'", name); } } } LayoutManager::reload(); } void LayoutManager::closeAllViews() { for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries()) view->getWindowOpenState() = false; } void LayoutManager::process() { if (s_layoutPathToLoad->has_value()) { LayoutManager::closeAllViews(); wolv::io::File file(**s_layoutPathToLoad, wolv::io::File::Mode::Read); s_layoutStringToLoad = file.readString(); s_layoutPathToLoad->reset(); } if (s_layoutStringToLoad->has_value()) { LayoutManager::closeAllViews(); ImGui::LoadIniSettingsFromMemory((*s_layoutStringToLoad)->c_str()); s_layoutStringToLoad->reset(); log::info("Loaded new Layout"); } } void LayoutManager::reload() { s_layouts->clear(); for (const auto &directory : paths::Layouts.read()) { for (const auto &entry : std::fs::directory_iterator(directory)) { const auto &path = entry.path(); if (path.extension() != ".hexlyt") continue; auto name = path.stem().string(); name = wolv::util::replaceStrings(name, "_", " "); for (size_t i = 0; i < name.size(); i++) { if (i == 0 || name[i - 1] == '_') name[i] = char(std::toupper(name[i])); } s_layouts->push_back({ name, path }); } } } void LayoutManager::reset() { s_layoutPathToLoad->reset(); s_layoutStringToLoad->reset(); s_layouts->clear(); } bool LayoutManager::isLayoutLocked() { return s_layoutLocked; } void LayoutManager::lockLayout(bool locked) { log::debug("Layout {}", locked ? "locked" : "unlocked"); s_layoutLocked = locked; } void LayoutManager::registerLoadCallback(const LoadCallback &callback) { s_loadCallbacks->push_back(callback); } void LayoutManager::registerStoreCallback(const StoreCallback &callback) { s_storeCallbacks->push_back(callback); } void LayoutManager::onLoad(std::string_view line) { for (const auto &callback : *s_loadCallbacks) callback(line); } void LayoutManager::onStore(ImGuiTextBuffer *buffer) { for (const auto &callback : *s_storeCallbacks) callback(buffer); } } ================================================ FILE: lib/libimhex/source/api/localization_manager.cpp ================================================ #include #include #include #include #include #include #include namespace hex { namespace LocalizationManager { constexpr static auto FallbackLanguageId = "en-US"; namespace { AutoReset> s_languageDefinitions; AutoReset> s_localizations; AutoReset s_selectedLanguageId; } void addLanguages(const std::string_view &languageList, std::function callback) { const auto json = nlohmann::json::parse(languageList); for (const auto &item : json) { if (!item.contains("code") || !item.contains("path")) { log::error("Invalid language definition: {}", item.dump(4)); continue; } auto &definition = (*s_languageDefinitions)[item["code"].get()]; if (definition.id.empty()) { definition.id = item["code"].get(); } if (definition.name.empty() && item.contains("name")) { definition.name = item["name"].get(); } if (definition.nativeName.empty() && item.contains("native_name")) { definition.nativeName = item["native_name"].get(); } if (definition.fallbackLanguageId.empty() && item.contains("fallback")) { definition.fallbackLanguageId = item["fallback"].get(); } if (item.contains("hidden") && item["hidden"].get()) { definition.hidden = true; } const auto path = item["path"].get(); definition.languageFilePaths.emplace_back(PathEntry{ .path=path, .callback=callback }); } } static LanguageId findBestLanguageMatch(LanguageId languageId) { if (s_languageDefinitions->contains(languageId)) return languageId; if (const auto pos = languageId.find('_'); pos != std::string::npos) { // Turn language Ids like "en_US" into "en-US" languageId[pos] = '-'; } if (const auto pos = languageId.find('-'); pos != std::string::npos) { // Try to find a match with the language code without region languageId = languageId.substr(0, pos); for (const auto &definition : *s_languageDefinitions) { if (definition.first.starts_with(languageId) || definition.first.starts_with(toLower(languageId))) { return definition.first; } } } // Fall back to English if no better match was found return "en-US"; } static void populateLocalization(LanguageId languageId, std::unordered_map &localizations) { if (languageId.empty()) return; languageId = findBestLanguageMatch(languageId); if (const auto it = s_languageDefinitions->find(languageId); it == s_languageDefinitions->end()) { log::error("No language definition found for language: {}", languageId); if (languageId != FallbackLanguageId) populateLocalization(FallbackLanguageId, localizations); } else { const auto &definition = it->second; for (const auto &path : definition.languageFilePaths) { try { const auto translation = path.callback(path.path); const auto json = nlohmann::json::parse(translation); for (const auto &entry : json.items()) { auto value = entry.value().get(); // Skip empty values if (value.empty()) continue; // Handle references to files if (value.starts_with("#@")) { try { value = path.callback(value.substr(2)); } catch (std::exception &e) { log::error("Failed to load localization file reference '{}': {}", entry.key(), e.what()); continue; } } localizations.try_emplace(LangConst::hash(entry.key()), std::move(value)); } } catch (std::exception &e) { log::error("Failed to load localization file '{}': {}", path.path, e.what()); } } populateLocalization(definition.fallbackLanguageId, localizations); } } void setLanguage(const LanguageId &languageId) { if (languageId == "native") { setLanguage(hex::getOSLanguage().value_or(FallbackLanguageId)); s_selectedLanguageId = languageId; return; } if (*s_selectedLanguageId == languageId) return; s_localizations->clear(); s_selectedLanguageId = languageId; populateLocalization(languageId, s_localizations); } [[nodiscard]] const std::string& getSelectedLanguageId() { return *s_selectedLanguageId; } [[nodiscard]] const std::string& get(const LanguageId &languageId, const UnlocalizedString &unlocalizedString) { static AutoReset currentLanguageId; static AutoReset> loadedLocalization; static std::mutex mutex; std::scoped_lock lock(mutex); if (*currentLanguageId != languageId) { currentLanguageId = languageId; loadedLocalization->clear(); populateLocalization(languageId, *loadedLocalization); } return (*loadedLocalization)[LangConst::hash(unlocalizedString.get())]; } const std::map& getLanguageDefinitions() { return *s_languageDefinitions; } const LanguageDefinition& getLanguageDefinition(const LanguageId &languageId) { const auto bestMatch = findBestLanguageMatch(languageId); const auto &result = (*s_languageDefinitions)[bestMatch]; if (!dbg::debugModeEnabled()) { if (result.hidden) return getLanguageDefinition(result.fallbackLanguageId); } return result; } } static AutoReset> s_unlocalizedNames; Lang::Lang(std::string_view unlocalizedString) : m_entryHash(LangConst::hash(unlocalizedString)) { if (!s_unlocalizedNames->contains(m_entryHash)) [[unlikely]] { s_unlocalizedNames->emplace(m_entryHash, unlocalizedString); } } Lang::Lang(const char *unlocalizedString) : Lang(std::string_view(unlocalizedString)) { } Lang::Lang(const std::string &unlocalizedString) : Lang(std::string_view(unlocalizedString)) { } Lang::Lang(const LangConst &localizedString) : m_entryHash(localizedString.m_entryHash) { if (!s_unlocalizedNames->contains(m_entryHash)) [[unlikely]] { s_unlocalizedNames->emplace(m_entryHash, localizedString.m_unlocalizedString); } } Lang::Lang(const UnlocalizedString &unlocalizedString) : Lang(unlocalizedString.get()) { } Lang::operator std::string() const { return get(); } Lang::operator std::string_view() const { return get(); } Lang::operator const char *() const { return get(); } const char *Lang::get() const { const auto &lang = *LocalizationManager::s_localizations; const auto it = lang.find(m_entryHash); if (it == lang.end()) { if (auto unlocalizedIt = s_unlocalizedNames->find(m_entryHash); unlocalizedIt != s_unlocalizedNames->end()) { return unlocalizedIt->second.c_str(); } else { return ""; } } else { return it->second.c_str(); } } LangConst::operator std::string() const { return get(); } LangConst::operator std::string_view() const { return get(); } LangConst::operator const char *() const { return get(); } const char *LangConst::get() const { const auto &lang = *LocalizationManager::s_localizations; const auto it = lang.find(m_entryHash); if (it == lang.end()) { return m_unlocalizedString; } else { return it->second.c_str(); } } } ================================================ FILE: lib/libimhex/source/api/plugin_manager.cpp ================================================ #include #include #include #include #include #include #include #include #include #if defined(OS_WINDOWS) #include #else #include #endif namespace hex { static uintptr_t loadLibrary(const std::fs::path &path) { #if defined(OS_WINDOWS) auto handle = uintptr_t(LoadLibraryW(path.c_str())); if (handle == uintptr_t(INVALID_HANDLE_VALUE) || handle == 0) { log::error("Loading library '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), hex::formatSystemError(::GetLastError())); return 0; } return handle; #else const auto pathString = wolv::util::toUTF8String(path); auto handle = uintptr_t(dlopen(pathString.c_str(), RTLD_NOLOAD)); if (handle == 0) handle = uintptr_t(dlopen(pathString.c_str(), RTLD_NOW | RTLD_GLOBAL)); if (handle == 0) { log::error("Loading library '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror()); return 0; } return handle; #endif } static void unloadLibrary(uintptr_t handle, const std::fs::path &path) { #if defined(OS_WINDOWS) if (handle != 0) { if (FreeLibrary(HMODULE(handle)) == FALSE) { log::error("Error when unloading library '{}': {}!", wolv::util::toUTF8String(path.filename()), hex::formatSystemError(::GetLastError())); } } #else if (handle != 0) { if (dlclose(reinterpret_cast(handle)) != 0) { log::error("Error when unloading library '{}': {}!", path.filename().string(), dlerror()); } } #endif } Plugin::Plugin(const std::fs::path &path) : m_path(path) { log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename())); m_handle = loadLibrary(path); if (m_handle == 0) return; const auto fileName = path.stem().string(); m_functions.initializePluginFunction = getPluginFunction("initializePlugin"); m_functions.initializeLibraryFunction = getPluginFunction(fmt::format("initializeLibrary_{}", fileName)); m_functions.getPluginNameFunction = getPluginFunction("getPluginName"); m_functions.getLibraryNameFunction = getPluginFunction(fmt::format("getLibraryName_{}", fileName)); m_functions.getPluginAuthorFunction = getPluginFunction("getPluginAuthor"); m_functions.getPluginDescriptionFunction = getPluginFunction("getPluginDescription"); m_functions.getCompatibleVersionFunction = getPluginFunction("getCompatibleVersion"); m_functions.setImGuiContextFunction = getPluginFunction("setImGuiContext"); m_functions.setImGuiContextLibraryFunction = getPluginFunction(fmt::format("setImGuiContext_{}", fileName)); m_functions.getSubCommandsFunction = getPluginFunction("getSubCommands"); m_functions.getFeaturesFunction = getPluginFunction("getFeatures"); m_functions.isBuiltinPluginFunction = getPluginFunction("isBuiltinPlugin"); } Plugin::Plugin(const std::string &name, const hex::PluginFunctions &functions) : m_path(name), m_addedManually(true), m_functions(functions) { } Plugin::Plugin(Plugin &&other) noexcept { m_handle = other.m_handle; other.m_handle = 0; m_path = std::move(other.m_path); m_addedManually = other.m_addedManually; m_functions = other.m_functions; other.m_functions = {}; m_enabled = other.m_enabled; m_initialized = other.m_initialized; } Plugin& Plugin::operator=(Plugin &&other) noexcept { m_handle = other.m_handle; other.m_handle = 0; m_path = std::move(other.m_path); m_addedManually = other.m_addedManually; m_functions = other.m_functions; other.m_functions = {}; m_enabled = other.m_enabled; m_initialized = other.m_initialized; return *this; } Plugin::~Plugin() { unloadLibrary(m_handle, m_path); } bool Plugin::initializePlugin() const { const auto pluginName = wolv::util::toUTF8String(m_path.filename()); if (this->isLibraryPlugin()) { m_functions.initializeLibraryFunction(); log::info("Library '{}' initialized successfully", pluginName); m_initialized = true; return true; } if (!m_enabled) return true; const auto requestedVersion = getCompatibleVersion(); const auto imhexVersion = ImHexApi::System::getImHexVersion().get(); if (!imhexVersion.starts_with(requestedVersion)) { if (requestedVersion.empty()) { log::warn("Plugin '{}' did not specify a compatible version, assuming it is compatible with the current version of ImHex.", wolv::util::toUTF8String(m_path.filename())); } else { log::error("Refused to load plugin '{}' which was built for a different version of ImHex: '{}'", wolv::util::toUTF8String(m_path.filename()), requestedVersion); return false; } } if (m_functions.initializePluginFunction != nullptr) { try { m_functions.initializePluginFunction(); } catch (const std::exception &e) { log::error("Plugin '{}' threw an exception on init: {}", pluginName, e.what()); return false; } catch (...) { log::error("Plugin '{}' threw an exception on init", pluginName); return false; } } else { log::error("Plugin '{}' does not have a proper entrypoint", pluginName); return false; } log::info("Plugin '{}' initialized successfully", pluginName); m_initialized = true; return true; } std::string Plugin::getPluginName() const { if (m_functions.getPluginNameFunction != nullptr) { return m_functions.getPluginNameFunction(); } else { if (this->isLibraryPlugin()) return m_functions.getLibraryNameFunction(); else return fmt::format("Unknown Plugin @ 0x{0:016X}", m_handle); } } std::string Plugin::getPluginAuthor() const { if (m_functions.getPluginAuthorFunction != nullptr) return m_functions.getPluginAuthorFunction(); else return "Unknown"; } std::string Plugin::getPluginDescription() const { if (m_functions.getPluginDescriptionFunction != nullptr) return m_functions.getPluginDescriptionFunction(); else return ""; } std::string Plugin::getCompatibleVersion() const { if (m_functions.getCompatibleVersionFunction != nullptr) return m_functions.getCompatibleVersionFunction(); else return ""; } void Plugin::setImGuiContext(ImGuiContext *ctx) const { if (m_functions.setImGuiContextFunction != nullptr) m_functions.setImGuiContextFunction(ctx); } const std::fs::path &Plugin::getPath() const { return m_path; } bool Plugin::isLoaded() const { return m_handle != 0; } bool Plugin::isValid() const { return isLoaded() || m_functions.initializeLibraryFunction != nullptr || m_functions.initializePluginFunction != nullptr; } bool Plugin::isInitialized() const { return m_initialized; } bool Plugin::isBuiltinPlugin() const { return m_functions.isBuiltinPluginFunction != nullptr && m_functions.isBuiltinPluginFunction(); } std::span Plugin::getSubCommands() const { if (m_functions.getSubCommandsFunction != nullptr) { const auto result = m_functions.getSubCommandsFunction(); if (result == nullptr) return { }; return *static_cast*>(result); } else { return { }; } } std::span Plugin::getFeatures() const { if (m_functions.getFeaturesFunction != nullptr) { const auto result = m_functions.getFeaturesFunction(); if (result == nullptr) return { }; return *static_cast*>(result); } else { return { }; } } bool Plugin::isLibraryPlugin() const { return m_functions.initializeLibraryFunction != nullptr && m_functions.initializePluginFunction == nullptr; } bool Plugin::wasAddedManually() const { return m_addedManually; } void* Plugin::getPluginFunction(const std::string &symbol) const { #if defined(OS_WINDOWS) return reinterpret_cast(GetProcAddress(HMODULE(m_handle), symbol.c_str())); #else return dlsym(reinterpret_cast(m_handle), symbol.c_str()); #endif } void Plugin::setEnabled(bool enabled) { m_enabled = enabled; } AutoReset> PluginManager::s_pluginPaths, PluginManager::s_pluginLoadPaths; void PluginManager::addLoadPath(const std::fs::path& path) { s_pluginLoadPaths->emplace_back(path); } bool PluginManager::load() { bool success = true; for (const auto &loadPath : getPluginLoadPaths()) success = PluginManager::load(loadPath) && success; return success; } bool PluginManager::load(const std::fs::path &pluginFolder) { if (!wolv::io::fs::exists(pluginFolder)) return false; s_pluginPaths->push_back(pluginFolder); // Load library plugins first for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) { if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexpluglib") { if (!isPluginLoaded(pluginPath.path())) { getPluginsMutable().emplace_back(pluginPath.path()); } } } // Load regular plugins afterward for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) { if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug") { if (!isPluginLoaded(pluginPath.path())) { getPluginsMutable().emplace_back(pluginPath.path()); } } } std::erase_if(getPluginsMutable(), [](const Plugin &plugin) { return !plugin.isValid(); }); // Move the built-in plugins to the front of the list so they get initialized first getPluginsMutable().sort([](const Plugin &a, const Plugin &b) { if (a.isBuiltinPlugin() && !b.isBuiltinPlugin()) { return true; } else if (!a.isBuiltinPlugin() && b.isBuiltinPlugin()) { return false; } else { return a.getPluginName() < b.getPluginName(); } }); return true; } AutoReset> PluginManager::s_loadedLibraries; bool PluginManager::loadLibraries() { bool success = true; for (const auto &loadPath : paths::Libraries.read()) success = PluginManager::loadLibraries(loadPath) && success; return success; } bool PluginManager::loadLibraries(const std::fs::path& libraryFolder) { bool success = true; for (const auto &entry : std::fs::directory_iterator(libraryFolder)) { if (!(entry.path().extension() == ".dll" || entry.path().extension() == ".so" || entry.path().extension() == ".dylib")) continue; auto handle = loadLibrary(entry); if (handle == 0) { success = false; } PluginManager::s_loadedLibraries->push_back(handle); } return success; } void PluginManager::initializeNewPlugins() { for (const auto &plugin : getPlugins()) { if (!plugin.isInitialized()) std::ignore = plugin.initializePlugin(); } } void PluginManager::unload() { s_pluginPaths->clear(); // Unload plugins in reverse order auto &plugins = getPluginsMutable(); std::list savedPlugins; while (!plugins.empty()) { if (plugins.back().wasAddedManually()) savedPlugins.emplace_front(std::move(plugins.back())); plugins.pop_back(); } while (!s_loadedLibraries->empty()) { unloadLibrary(s_loadedLibraries->back(), ""); s_loadedLibraries->pop_back(); } getPluginsMutable() = std::move(savedPlugins); } void PluginManager::addPlugin(const std::string &name, hex::PluginFunctions functions) { getPluginsMutable().emplace_back(name, functions); } const std::list& PluginManager::getPlugins() { return getPluginsMutable(); } std::list& PluginManager::getPluginsMutable() { static std::list plugins; return plugins; } Plugin* PluginManager::getPlugin(const std::string &name) { for (auto &plugin : getPluginsMutable()) { if (plugin.getPluginName() == name) return &plugin; } return nullptr; } const std::vector& PluginManager::getPluginPaths() { return s_pluginPaths; } const std::vector& PluginManager::getPluginLoadPaths() { return s_pluginLoadPaths; } bool PluginManager::isPluginLoaded(const std::fs::path &path) { return std::ranges::any_of(getPlugins(), [&path](const Plugin &plugin) { return plugin.getPath().filename() == path.filename(); }); } void PluginManager::setPluginEnabled(const Plugin &plugin, bool enabled) { auto &plugins = getPluginsMutable(); auto it = std::ranges::find_if(plugins, [&plugin](const Plugin &p) { return &plugin == &p; }); if (it != plugins.end()) { it->setEnabled(enabled); } } } ================================================ FILE: lib/libimhex/source/api/project_file_manager.cpp ================================================ #include #include #include namespace hex { namespace { AutoReset> s_handlers; AutoReset> s_providerHandlers; AutoReset s_currProjectPath; AutoReset> s_loadProjectFunction; AutoReset, bool)>> s_storeProjectFunction; } void ProjectFile::setProjectFunctions( const std::function &loadFun, const std::function, bool)> &storeFun ) { s_loadProjectFunction = loadFun; s_storeProjectFunction = storeFun; } bool ProjectFile::load(const std::fs::path &filePath) { return (*s_loadProjectFunction)(filePath); } bool ProjectFile::store(std::optional filePath, bool updateLocation) { return (*s_storeProjectFunction)(std::move(filePath), updateLocation); } bool ProjectFile::hasPath() { return !s_currProjectPath->empty(); } void ProjectFile::clearPath() { s_currProjectPath->clear(); } std::fs::path ProjectFile::getPath() { return *s_currProjectPath; } void ProjectFile::setPath(const std::fs::path &path) { s_currProjectPath = path; } void ProjectFile::registerHandler(const Handler &handler) { s_handlers->push_back(handler); } void ProjectFile::registerPerProviderHandler(const ProviderHandler &handler) { s_providerHandlers->push_back(handler); } const std::vector& ProjectFile::getHandlers() { return s_handlers; } const std::vector& ProjectFile::getProviderHandlers() { return s_providerHandlers; } } ================================================ FILE: lib/libimhex/source/api/shortcut_manager.cpp ================================================ #include #include #include #include #include #include #include #include namespace hex { namespace { AutoReset> s_globalShortcuts; std::atomic s_paused; std::optional s_prevShortcut; bool s_macOSMode = false; AutoReset> s_lastShortcutMainMenu; } Shortcut operator+(const Key &lhs, const Key &rhs) { Shortcut result; result.m_keys = { lhs, rhs }; return result; } Shortcut::Shortcut(Keys key) : m_keys({ key }) { } Shortcut::Shortcut(std::set keys) : m_keys(std::move(keys)) { } Shortcut Shortcut::operator+(const Key &other) const { Shortcut result = *this; result.m_keys.insert(other); return result; } Shortcut& Shortcut::operator+=(const Key &other) { m_keys.insert(other); return *this; } bool Shortcut::operator<(const Shortcut &other) const { return m_keys < other.m_keys; } bool Shortcut::operator==(const Shortcut &other) const { return m_keys == other.m_keys; } bool Shortcut::isLocal() const { return m_keys.contains(CurrentView); } const std::set& Shortcut::getKeys() const { return m_keys; } bool Shortcut::has(Key key) const { return m_keys.contains(key); } bool Shortcut::matches(const Shortcut& other) const { auto left = this->m_keys; auto right = other.m_keys; left.erase(CurrentView); left.erase(AllowWhileTyping); right.erase(CurrentView); right.erase(AllowWhileTyping); return left == right; } std::string Shortcut::toString() const { std::string result; const auto CTRL_NAME = s_macOSMode ? "⌃" : "CTRL"; const auto ALT_NAME = s_macOSMode ? "⌥" : "ALT"; const auto SHIFT_NAME = s_macOSMode ? "⇧" : "SHIFT"; const auto SUPER_NAME = s_macOSMode ? "⌘" : "SUPER"; const auto Concatination = s_macOSMode ? " " : " + "; auto keys = m_keys; if (keys.erase(CTRL) > 0 || (!s_macOSMode && keys.erase(CTRLCMD) > 0)) { result += CTRL_NAME; result += Concatination; } if (keys.erase(ALT) > 0) { result += ALT_NAME; result += Concatination; } if (keys.erase(SHIFT) > 0) { result += SHIFT_NAME; result += Concatination; } if (keys.erase(SUPER) > 0 || (s_macOSMode && keys.erase(CTRLCMD) > 0)) { result += SUPER_NAME; result += Concatination; } keys.erase(CurrentView); for (const auto &key : keys) { switch (Keys(key.getKeyCode())) { case Keys::Space: result += "⎵"; break; case Keys::Apostrophe: result += "'"; break; case Keys::Comma: result += ","; break; case Keys::Minus: result += "-"; break; case Keys::Period: result += "."; break; case Keys::Slash: result += "/"; break; case Keys::Num0: result += "0"; break; case Keys::Num1: result += "1"; break; case Keys::Num2: result += "2"; break; case Keys::Num3: result += "3"; break; case Keys::Num4: result += "4"; break; case Keys::Num5: result += "5"; break; case Keys::Num6: result += "6"; break; case Keys::Num7: result += "7"; break; case Keys::Num8: result += "8"; break; case Keys::Num9: result += "9"; break; case Keys::Semicolon: result += ";"; break; case Keys::Equals: result += "="; break; case Keys::A: result += "A"; break; case Keys::B: result += "B"; break; case Keys::C: result += "C"; break; case Keys::D: result += "D"; break; case Keys::E: result += "E"; break; case Keys::F: result += "F"; break; case Keys::G: result += "G"; break; case Keys::H: result += "H"; break; case Keys::I: result += "I"; break; case Keys::J: result += "J"; break; case Keys::K: result += "K"; break; case Keys::L: result += "L"; break; case Keys::M: result += "M"; break; case Keys::N: result += "N"; break; case Keys::O: result += "O"; break; case Keys::P: result += "P"; break; case Keys::Q: result += "Q"; break; case Keys::R: result += "R"; break; case Keys::S: result += "S"; break; case Keys::T: result += "T"; break; case Keys::U: result += "U"; break; case Keys::V: result += "V"; break; case Keys::W: result += "W"; break; case Keys::X: result += "X"; break; case Keys::Y: result += "Y"; break; case Keys::Z: result += "Z"; break; case Keys::LeftBracket: result += "["; break; case Keys::Backslash: result += "\\"; break; case Keys::RightBracket: result += "]"; break; case Keys::GraveAccent: result += "`"; break; case Keys::World1: result += "WORLD1"; break; case Keys::World2: result += "WORLD2"; break; case Keys::Escape: result += "ESC"; break; case Keys::Enter: result += "⏎"; break; case Keys::Tab: result += "⇥"; break; case Keys::Backspace: result += "⌫"; break; case Keys::Insert: result += "INSERT"; break; case Keys::Delete: result += "DELETE"; break; case Keys::Right: result += "RIGHT"; break; case Keys::Left: result += "LEFT"; break; case Keys::Down: result += "DOWN"; break; case Keys::Up: result += "UP"; break; case Keys::PageUp: result += "PAGEUP"; break; case Keys::PageDown: result += "PAGEDOWN"; break; case Keys::Home: result += "HOME"; break; case Keys::End: result += "END"; break; case Keys::CapsLock: result += "⇪"; break; case Keys::ScrollLock: result += "SCROLLLOCK"; break; case Keys::NumLock: result += "NUMLOCK"; break; case Keys::PrintScreen: result += "PRINTSCREEN"; break; case Keys::Pause: result += "PAUSE"; break; case Keys::F1: result += "F1"; break; case Keys::F2: result += "F2"; break; case Keys::F3: result += "F3"; break; case Keys::F4: result += "F4"; break; case Keys::F5: result += "F5"; break; case Keys::F6: result += "F6"; break; case Keys::F7: result += "F7"; break; case Keys::F8: result += "F8"; break; case Keys::F9: result += "F9"; break; case Keys::F10: result += "F10"; break; case Keys::F11: result += "F11"; break; case Keys::F12: result += "F12"; break; case Keys::F13: result += "F13"; break; case Keys::F14: result += "F14"; break; case Keys::F15: result += "F15"; break; case Keys::F16: result += "F16"; break; case Keys::F17: result += "F17"; break; case Keys::F18: result += "F18"; break; case Keys::F19: result += "F19"; break; case Keys::F20: result += "F20"; break; case Keys::F21: result += "F21"; break; case Keys::F22: result += "F22"; break; case Keys::F23: result += "F23"; break; case Keys::F24: result += "F24"; break; case Keys::F25: result += "F25"; break; case Keys::KeyPad0: result += "KP0"; break; case Keys::KeyPad1: result += "KP1"; break; case Keys::KeyPad2: result += "KP2"; break; case Keys::KeyPad3: result += "KP3"; break; case Keys::KeyPad4: result += "KP4"; break; case Keys::KeyPad5: result += "KP5"; break; case Keys::KeyPad6: result += "KP6"; break; case Keys::KeyPad7: result += "KP7"; break; case Keys::KeyPad8: result += "KP8"; break; case Keys::KeyPad9: result += "KP9"; break; case Keys::KeyPadDecimal: result += "KPDECIMAL"; break; case Keys::KeyPadDivide: result += "KPDIVIDE"; break; case Keys::KeyPadMultiply: result += "KPMULTIPLY"; break; case Keys::KeyPadSubtract: result += "KPSUBTRACT"; break; case Keys::KeyPadAdd: result += "KPADD"; break; case Keys::KeyPadEnter: result += "KPENTER"; break; case Keys::KeyPadEqual: result += "KPEQUAL"; break; case Keys::Menu: result += "MENU"; break; default: continue; } result += Concatination; } if (result.ends_with(Concatination)) result = result.substr(0, result.size() - strlen(Concatination)); return result; } KeyEquivalent Shortcut::toKeyEquivalent() const { #if defined(OS_MACOS) if (*this == None) return { }; KeyEquivalent result = {}; result.valid = true; for (const auto &key : m_keys) { switch (key.getKeyCode()) { case CTRL.getKeyCode(): result.ctrl = true; break; case SHIFT.getKeyCode(): result.shift = true; break; case ALT.getKeyCode(): result.opt = true; break; case SUPER.getKeyCode(): case CTRLCMD.getKeyCode(): result.cmd = true; break; case CurrentView.getKeyCode(): case AllowWhileTyping.getKeyCode(): case ShowOnWelcomeScreen.getKeyCode(): // Ignore flags that are only used internally by the ShortcutManager break; default: macosGetKey(Keys(key.getKeyCode()), &result.key); break; } } return result; #else return { }; #endif } void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::vector &unlocalizedName, const std::function &callback, const EnabledCallback &enabledCallback) { log::debug("Adding global shortcut {} for {}", shortcut.toString(), unlocalizedName.back().get()); auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { .shortcut=shortcut, .unlocalizedName=unlocalizedName, .callback=callback, .enabledCallback=enabledCallback } }); if (!inserted) log::error("Failed to add shortcut!"); } void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function &callback, const EnabledCallback &enabledCallback) { log::debug("Adding global shortcut {} for {}", shortcut.toString(), unlocalizedName.get()); auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { .shortcut=shortcut, .unlocalizedName={ unlocalizedName }, .callback=callback, .enabledCallback=enabledCallback } }); if (!inserted) log::error("Failed to add shortcut!"); } void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const std::vector &unlocalizedName, const std::function &callback, const EnabledCallback &enabledCallback) { log::debug("Adding shortcut {} for {}", shortcut.toString(), unlocalizedName.back().get()); auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { .shortcut=shortcut, .unlocalizedName=unlocalizedName, .callback=callback, .enabledCallback=enabledCallback } }); if (!inserted) log::error("Failed to add shortcut!"); } void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function &callback, const EnabledCallback &enabledCallback) { log::debug("Adding shortcut {} for {}", shortcut.toString(), unlocalizedName.get()); auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { .shortcut=shortcut, .unlocalizedName={ unlocalizedName }, .callback=callback, .enabledCallback=enabledCallback } }); if (!inserted) log::error("Failed to add shortcut!"); } static Shortcut getShortcut(bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) { Shortcut pressedShortcut; if (ctrl) pressedShortcut += s_macOSMode ? CTRL : CTRLCMD; if (alt) pressedShortcut += ALT; if (shift) pressedShortcut += SHIFT; if (super) pressedShortcut += s_macOSMode ? CTRLCMD : SUPER; if (focused) pressedShortcut += CurrentView; pressedShortcut += scanCodeToKey(keyCode); return pressedShortcut; } static bool processShortcut(Shortcut shortcut, const std::map &shortcuts) { if (s_paused) return true; if (ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId)) return true; auto it = shortcuts.end(); if (ImGui::GetIO().WantTextInput) { it = shortcuts.find(shortcut + AllowWhileTyping); } else { it = shortcuts.find(shortcut); if (it == shortcuts.end()) it = shortcuts.find(shortcut + AllowWhileTyping); } if (it != shortcuts.end()) { const auto &[foundShortcut, entry] = *it; if (entry.enabledCallback()) { entry.callback(); if (!entry.unlocalizedName.empty()) { s_lastShortcutMainMenu = entry.unlocalizedName.front(); } return true; } } return false; } bool ShortcutManager::runShortcut(const Shortcut &shortcut, const View *view) { if (view == nullptr) return processShortcut(shortcut, s_globalShortcuts); else return processShortcut(shortcut, view->m_shortcuts); } void ShortcutManager::process(const View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) { const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode); if (keyCode != 0) s_prevShortcut = Shortcut(pressedShortcut.getKeys()); std::set processedViews; while (true) { if (runShortcut(pressedShortcut, currentView)) { break; } processedViews.insert(currentView); currentView = currentView->getMenuItemInheritView(); if (currentView == nullptr || processedViews.contains(currentView)) break; } } void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) { const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode); if (keyCode != 0) s_prevShortcut = Shortcut(pressedShortcut.getKeys()); runShortcut(pressedShortcut); } std::optional ShortcutManager::getLastActivatedMenu() { return *s_lastShortcutMainMenu; } void ShortcutManager::resetLastActivatedMenu() { s_lastShortcutMainMenu->reset(); } void ShortcutManager::clearShortcuts() { s_globalShortcuts->clear(); } Shortcut ShortcutManager::getShortcutByName(const std::vector &unlocalizedName, const View *view) { if (view != nullptr) { for (const auto &[shortcut, entry] : view->m_shortcuts) { if (entry.unlocalizedName == unlocalizedName) { return entry.shortcut; } } } else { for (const auto &[shortcut, entry] : *s_globalShortcuts) { if (entry.unlocalizedName == unlocalizedName) { return entry.shortcut; } } } return Shortcut::None; } void ShortcutManager::resumeShortcuts() { s_paused = false; } void ShortcutManager::pauseShortcuts() { s_paused = true; s_prevShortcut.reset(); } std::optional ShortcutManager::getPreviousShortcut() { return s_prevShortcut; } std::vector ShortcutManager::getGlobalShortcuts() { std::vector result; for (auto &[shortcut, entry] : *s_globalShortcuts) result.push_back(entry); return result; } std::vector ShortcutManager::getViewShortcuts(const View *view) { std::vector result; result.reserve(view->m_shortcuts.size()); for (auto &[shortcut, entry] : view->m_shortcuts) result.push_back(entry); return result; } static bool updateShortcutImpl(const Shortcut &oldShortcut, const Shortcut &newShortcut, std::map &shortcuts) { if (shortcuts.contains(oldShortcut)) { if (shortcuts.contains(newShortcut)) return false; shortcuts[newShortcut] = shortcuts[oldShortcut]; shortcuts[newShortcut].shortcut = newShortcut; shortcuts.erase(oldShortcut); } return true; } bool ShortcutManager::updateShortcut(const Shortcut &oldShortcut, Shortcut newShortcut, View *view) { if (oldShortcut.matches(newShortcut)) return true; if (oldShortcut.has(AllowWhileTyping)) newShortcut += AllowWhileTyping; bool result; if (view != nullptr) { result = updateShortcutImpl(oldShortcut + CurrentView, newShortcut + CurrentView , view->m_shortcuts); } else { result = updateShortcutImpl(oldShortcut, newShortcut, s_globalShortcuts); } if (result) { for (auto &[priority, menuItem] : ContentRegistry::UserInterface::impl::getMenuItemsMutable()) { if (menuItem.view == view && menuItem.shortcut == oldShortcut) { menuItem.shortcut = newShortcut; break; } } } return result; } void ShortcutManager::enableMacOSMode() { s_macOSMode = true; } } ================================================ FILE: lib/libimhex/source/api/task_manager.cpp ================================================ #include #include #include #include #include #include #include #include #include #if defined(OS_WINDOWS) #include #include #include #else #include #endif namespace { struct SourceLocationWrapper { std::source_location location; bool operator==(const SourceLocationWrapper &other) const { return location.file_name() == other.location.file_name() && location.function_name() == other.location.function_name() && location.column() == other.location.column() && location.line() == other.location.line(); } }; } template<> struct std::hash { std::size_t operator()(const SourceLocationWrapper& s) const noexcept { auto h1 = std::hash{}(s.location.file_name()); auto h2 = std::hash{}(s.location.function_name()); auto h3 = std::hash{}(s.location.column()); auto h4 = std::hash{}(s.location.line()); return (h1 << 0) ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3); } }; namespace hex { namespace { std::recursive_mutex s_deferredCallsMutex, s_tasksFinishedMutex; std::list> s_tasks, s_taskQueue; std::list> s_deferredCalls; std::unordered_map> s_onceDeferredCalls; std::list> s_tasksFinishedCallbacks; std::list> s_taskCompletionCallbacks; std::mutex s_queueMutex; std::condition_variable s_jobCondVar; std::vector s_workers; thread_local std::array s_currentThreadName; thread_local Task* s_currentTask = nullptr; std::thread::id s_mainThreadId; } Task::Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, bool blocking, std::function function) : m_unlocalizedName(std::move(unlocalizedName)), m_maxValue(maxValue), m_function(std::move(function)), m_background(background), m_blocking(blocking) { } Task::Task(hex::Task &&other) noexcept { { std::scoped_lock thisLock(m_mutex); std::scoped_lock otherLock(other.m_mutex); m_function = std::move(other.m_function); m_unlocalizedName = std::move(other.m_unlocalizedName); } m_maxValue = u64(other.m_maxValue); m_currValue = u64(other.m_currValue); if (other.m_finished.test()) m_finished.test_and_set(); if (other.m_hadException.test()) m_hadException.test_and_set(); if (other.m_interrupted.test()) m_interrupted.test_and_set(); m_finished.notify_all(); m_hadException.notify_all(); m_interrupted.notify_all(); m_shouldInterrupt = bool(other.m_shouldInterrupt); } Task::~Task() { if (!this->isFinished()) this->interrupt(); } void Task::update(u64 value) { // Update the current progress value of the task m_currValue.store(value, std::memory_order_relaxed); // Check if the task has been interrupted by the main thread and if yes, // throw an exception that is generally not caught by the task if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]] throw TaskInterruptor(); } void Task::update() const { if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]] throw TaskInterruptor(); } void Task::increment() { m_currValue.fetch_add(1, std::memory_order_relaxed); if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]] throw TaskInterruptor(); } void Task::setMaxValue(u64 value) { m_maxValue = value; } void Task::interrupt() { m_shouldInterrupt = true; // Call the interrupt callback on the current thread if one is set if (m_interruptCallback) m_interruptCallback(); } void Task::setInterruptCallback(std::function callback) { m_interruptCallback = std::move(callback); } bool Task::isBackgroundTask() const { return m_background; } bool Task::isBlocking() const { return m_blocking; } bool Task::isFinished() const { return m_finished.test(); } bool Task::hadException() const { return m_hadException.test(); } bool Task::shouldInterrupt() const { return m_shouldInterrupt; } bool Task::wasInterrupted() const { return m_interrupted.test(); } void Task::clearException() { m_hadException.clear(); } std::string Task::getExceptionMessage() const { std::scoped_lock lock(m_mutex); return m_exceptionMessage; } const UnlocalizedString &Task::getUnlocalizedName() { return m_unlocalizedName; } u64 Task::getValue() const { return m_currValue; } u64 Task::getMaxValue() const { return m_maxValue; } void Task::wait() const { m_finished.wait(false); } void Task::finish() { m_finished.test_and_set(); m_finished.notify_all(); } void Task::interruption() { m_interrupted.test_and_set(); m_interrupted.notify_all(); } void Task::exception(const char *message) { std::scoped_lock lock(m_mutex); // Store information about the caught exception m_exceptionMessage = message; m_hadException.test_and_set(); m_hadException.notify_all(); // Call the interrupt callback on the current thread if one is set if (m_interruptCallback) m_interruptCallback(); } bool TaskHolder::isRunning() const { const auto &task = m_task.lock(); if (!task) return false; return !task->isFinished(); } bool TaskHolder::hadException() const { const auto &task = m_task.lock(); if (!task) return false; return task->hadException(); } bool TaskHolder::shouldInterrupt() const { const auto &task = m_task.lock(); if (!task) return false; return task->shouldInterrupt(); } bool TaskHolder::wasInterrupted() const { const auto &task = m_task.lock(); if (!task) return false; return task->wasInterrupted(); } void TaskHolder::interrupt() const { const auto &task = m_task.lock(); if (!task) return; task->interrupt(); } void TaskHolder::wait() const { const auto &task = m_task.lock(); if (!task) return; task->wait(); } u32 TaskHolder::getProgress() const { const auto &task = m_task.lock(); if (!task) return 0; // If the max value is 0, the task has no progress if (task->getMaxValue() == 0) return 0; // Calculate the progress of the task from 0 to 100 return u32((task->getValue() * 100) / task->getMaxValue()); } void TaskManager::init() { const auto threadCount = std::thread::hardware_concurrency(); log::debug("Initializing task manager thread pool with {} workers.", threadCount); // Create worker threads for (u32 i = 0; i < threadCount; i++) { s_workers.emplace_back([](const std::stop_token &stopToken) { while (true) { std::shared_ptr task; // Set the thread name to "Idle Task" while waiting for a task TaskManager::setCurrentThreadName("Idle Task"); { // Wait for a task to be added to the queue std::unique_lock lock(s_queueMutex); s_jobCondVar.wait(lock, [&] { return !s_taskQueue.empty() || stopToken.stop_requested(); }); // Check if the thread should exit if (stopToken.stop_requested()) break; // Grab the next task from the queue task = std::move(s_taskQueue.front()); s_taskQueue.pop_front(); s_currentTask = task.get(); } try { trace::enableExceptionCaptureForCurrentThread(); // Set the thread name to the name of the task TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName)); // Execute the task task->m_function(*task); log::debug("Task '{}' finished", task->m_unlocalizedName.get()); { std::scoped_lock lock(s_tasksFinishedMutex); for (const auto &callback : s_taskCompletionCallbacks) callback(*task); } } catch (const Task::TaskInterruptor &) { // Handle the task being interrupted by user request task->interruption(); } catch (const std::exception &e) { log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what()); dbg::printStackTrace(trace::getStackTrace()); // Handle the task throwing an uncaught exception task->exception(e.what()); } catch (...) { log::error("Exception in task '{}'", task->m_unlocalizedName.get()); dbg::printStackTrace(trace::getStackTrace()); // Handle the task throwing an uncaught exception of unknown type task->exception("Unknown Exception"); } trace::disableExceptionCaptureForCurrentThread(); s_currentTask = nullptr; task->finish(); } }); } } void TaskManager::exit() { // Interrupt all tasks for (const auto &task : s_tasks) { task->interrupt(); } // Ask worker threads to exit after finishing their task for (auto &thread : s_workers) thread.request_stop(); // Wake up all the idle worker threads so they can exit { std::unique_lock lock(s_queueMutex); s_jobCondVar.notify_all(); } // Wait for all worker threads to exit s_workers.clear(); s_tasks.clear(); s_taskQueue.clear(); s_deferredCalls.clear(); s_onceDeferredCalls.clear(); s_tasksFinishedCallbacks.clear(); s_taskCompletionCallbacks.clear(); } TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function function) { std::scoped_lock lock(s_queueMutex); // Construct new task auto task = std::make_shared(unlocalizedName, maxValue, background, blocking, std::move(function)); s_tasks.emplace_back(task); // Add task to the queue for the worker to pick up s_taskQueue.emplace_back(std::move(task)); s_jobCondVar.notify_one(); return TaskHolder(s_tasks.back()); } TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { log::debug("Creating task {}", unlocalizedName.get()); return createTask(unlocalizedName, maxValue, false, false, std::move(function)); } TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { log::debug("Creating task {}", unlocalizedName.get()); return createTask(unlocalizedName, maxValue, false, false, [function = std::move(function)](Task&) { function(); } ); } TaskHolder TaskManager::createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function) { log::debug("Creating background task {}", unlocalizedName.get()); return createTask(unlocalizedName, 0, true, false, std::move(function)); } TaskHolder TaskManager::createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function) { log::debug("Creating background task {}", unlocalizedName.get()); return createTask(unlocalizedName, 0, true, false, [function = std::move(function)](Task&) { function(); } ); } TaskHolder TaskManager::createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { log::debug("Creating blocking task {}", unlocalizedName.get()); return createTask(unlocalizedName, maxValue, true, true, std::move(function)); } TaskHolder TaskManager::createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { log::debug("Creating blocking task {}", unlocalizedName.get()); return createTask(unlocalizedName, maxValue, true, true, [function = std::move(function)](Task&) { function(); } ); } void TaskManager::collectGarbage() { { std::scoped_lock lock(s_queueMutex); std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); }); } if (s_tasks.empty()) { std::scoped_lock lock(s_deferredCallsMutex); for (const auto &call : s_tasksFinishedCallbacks) call(); s_tasksFinishedCallbacks.clear(); } } Task& TaskManager::getCurrentTask() { return *s_currentTask; } const std::list>& TaskManager::getRunningTasks() { return s_tasks; } size_t TaskManager::getRunningTaskCount() { std::scoped_lock lock(s_queueMutex); return std::ranges::count_if(s_tasks, [](const auto &task){ return !task->isBackgroundTask(); }); } size_t TaskManager::getRunningBackgroundTaskCount() { std::scoped_lock lock(s_queueMutex); return std::ranges::count_if(s_tasks, [](const auto &task){ return task->isBackgroundTask(); }); } size_t TaskManager::getRunningBlockingTaskCount() { std::scoped_lock lock(s_queueMutex); return std::ranges::count_if(s_tasks, [](const auto &task){ return task->isBlocking(); }); } void TaskManager::doLater(const std::function &function) { std::scoped_lock lock(s_deferredCallsMutex); s_deferredCalls.push_back(function); } void TaskManager::doLaterOnce(const std::function &function, std::source_location location) { std::scoped_lock lock(s_deferredCallsMutex); s_onceDeferredCalls[SourceLocationWrapper{ location }] = function; } void TaskManager::runDeferredCalls() { std::scoped_lock lock(s_deferredCallsMutex); while (!s_deferredCalls.empty()) { auto callback = s_deferredCalls.front(); s_deferredCalls.pop_front(); callback(); } while (!s_onceDeferredCalls.empty()) { auto node = s_onceDeferredCalls.extract(s_onceDeferredCalls.begin()); node.mapped()(); } } void TaskManager::runWhenTasksFinished(const std::function &function) { std::scoped_lock lock(s_tasksFinishedMutex); for (const auto &task : s_tasks) { task->interrupt(); } s_tasksFinishedCallbacks.push_back(function); } void TaskManager::setCurrentThreadName(const std::string &name) { std::ranges::fill(s_currentThreadName, '\0'); std::ranges::copy(name | std::views::take(255), s_currentThreadName.begin()); #if defined(OS_WINDOWS) using SetThreadDescriptionFunc = HRESULT(WINAPI*)(HANDLE hThread, PCWSTR lpThreadDescription); static auto setThreadDescription = reinterpret_cast( reinterpret_cast( ::GetProcAddress( ::GetModuleHandleW(L"Kernel32.dll"), "SetThreadDescription" ) ) ); if (setThreadDescription != nullptr) { const auto longName = hex::utf8ToUtf16(name); setThreadDescription(::GetCurrentThread(), longName.c_str()); } else { struct THREADNAME_INFO { DWORD dwType; LPCSTR szName; DWORD dwThreadID; DWORD dwFlags; }; THREADNAME_INFO info = { }; info.dwType = 0x1000; info.szName = name.c_str(); info.dwThreadID = ::GetCurrentThreadId(); info.dwFlags = 0; constexpr static DWORD MS_VC_EXCEPTION = 0x406D1388; RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast(&info)); } #elif defined(OS_LINUX) pthread_setname_np(pthread_self(), name.c_str()); #elif defined(OS_WEB) std::ignore = name; #elif defined(OS_MACOS) pthread_setname_np(name.c_str()); #endif } std::string_view TaskManager::getCurrentThreadName() { if (TaskManager::isMainThread()) return "Main"; else return s_currentThreadName.data(); } void TaskManager::setMainThreadId(std::thread::id threadId) { s_mainThreadId = threadId; } bool TaskManager::isMainThread() { return s_mainThreadId == std::this_thread::get_id(); } void TaskManager::addTaskCompletionCallback(const std::function &function) { std::scoped_lock lock(s_tasksFinishedMutex); for (const auto &task : s_tasks) { task->interrupt(); } s_taskCompletionCallbacks.push_back(function); } } ================================================ FILE: lib/libimhex/source/api/theme_manager.cpp ================================================ #include #include #include #include #include #include #include namespace hex { namespace { AutoReset> s_themes; AutoReset> s_themeHandlers; AutoReset> s_styleHandlers; AutoReset s_imageTheme; AutoReset s_currTheme; AutoReset> s_accentColor; std::recursive_mutex s_themeMutex; } void ThemeManager::reapplyCurrentTheme() { ThemeManager::changeTheme(s_currTheme); } void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function &getFunction, const std::function &setFunction) { std::unique_lock lock(s_themeMutex); (*s_themeHandlers)[name] = { .colorMap=colorMap, .getFunction=getFunction, .setFunction=setFunction }; } void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) { std::unique_lock lock(s_themeMutex); (*s_styleHandlers)[name] = { styleMap }; } void ThemeManager::addTheme(const std::string &content) { std::unique_lock lock(s_themeMutex); try { auto theme = nlohmann::json::parse(content); if (theme.contains("name") && theme.contains("colors")) { (*s_themes)[theme["name"].get()] = theme; } else { hex::log::error("Invalid theme file"); } } catch (const nlohmann::json::parse_error &e) { hex::log::error("Invalid theme file: {}", e.what()); } } std::optional ThemeManager::parseColorString(const std::string &colorString) { if (colorString == "auto") return ImVec4(0, 0, 0, -1); if (colorString.length() != 9 || colorString[0] != '#') return std::nullopt; u32 color = 0; for (u32 i = 1; i < 9; i++) { color <<= 4; if (colorString[i] >= '0' && colorString[i] <= '9') color |= colorString[i] - '0'; else if (colorString[i] >= 'A' && colorString[i] <= 'F') color |= colorString[i] - 'A' + 10; else if (colorString[i] >= 'a' && colorString[i] <= 'f') color |= colorString[i] - 'a' + 10; else return std::nullopt; } if (color == 0x00000000) return ImVec4(0, 0, 0, -1); return ImColor(hex::changeEndianness(color, std::endian::big)); } nlohmann::json ThemeManager::exportCurrentTheme(const std::string &name) { nlohmann::json theme = { { "name", name }, { "image_theme", s_imageTheme }, { "colors", {} }, { "styles", {} }, { "base", s_currTheme } }; for (const auto &[type, handler] : *s_themeHandlers) { theme["colors"][type] = {}; for (const auto &[key, value] : handler.colorMap) { auto color = handler.getFunction(value); theme["colors"][type][key] = fmt::format("#{:08X}", hex::changeEndianness(u32(color), std::endian::big)); } } for (const auto &[type, handler] : *s_styleHandlers) { theme["styles"][type] = {}; for (const auto &[key, style] : handler.styleMap) { if (std::holds_alternative(style.value)) { theme["styles"][type][key] = *std::get(style.value); } else if (std::holds_alternative(style.value)) { theme["styles"][type][key] = { std::get(style.value)->x, std::get(style.value)->y }; } } } return theme; } void ThemeManager::changeTheme(std::string name) { std::unique_lock lock(s_themeMutex); if (!s_themes->contains(name)) { if (s_themes->empty()) { return; } else { const std::string &defaultTheme = s_themes->begin()->first; hex::log::error("Theme '{}' does not exist, using default theme '{}' instead!", name, defaultTheme); name = defaultTheme; } } const auto &theme = (*s_themes)[name]; if (theme.contains("base")) { if (theme["base"].is_string()) { if (theme["base"] != name) changeTheme(theme["base"].get()); } else { hex::log::error("Theme '{}' has invalid base theme!", name); } } if (theme.contains("colors") && !s_themeHandlers->empty()) { for (const auto&[type, content] : theme["colors"].items()) { if (!s_themeHandlers->contains(type)) { log::warn("No theme handler found for '{}'", type); continue; } const auto &handler = (*s_themeHandlers)[type]; for (const auto &[key, value] : content.items()) { if (!handler.colorMap.contains(key)) { log::warn("No color found for '{}.{}'", type, key); continue; } auto colorString = value.get(); bool accentableColor = false; if (colorString.starts_with("*")) { colorString = colorString.substr(1); accentableColor = true; } auto color = parseColorString(colorString); if (!color.has_value()) { log::warn("Invalid color '{}' for '{}.{}'", colorString, type, key); continue; } if (accentableColor && s_accentColor->has_value()) { float h, s, v; ImGui::ColorConvertRGBtoHSV(color->Value.x, color->Value.y, color->Value.z, h, s, v); h = s_accentColor->value(); ImGui::ColorConvertHSVtoRGB(h, s, v, color->Value.x, color->Value.y, color->Value.z); } (*s_themeHandlers)[type].setFunction((*s_themeHandlers)[type].colorMap.at(key), color.value()); } } } if (theme.contains("styles") && !s_styleHandlers->empty()) { for (const auto&[type, content] : theme["styles"].items()) { if (!s_styleHandlers->contains(type)) { log::warn("No style handler found for '{}'", type); continue; } auto &handler = (*s_styleHandlers)[type]; for (const auto &[key, value] : content.items()) { if (!handler.styleMap.contains(key)) continue; auto &style = handler.styleMap.at(key); const float scale = style.needsScaling ? 1_scaled : 1.0F; if (value.is_number_float()) { if (const auto newValue = std::get_if(&style.value); newValue != nullptr && *newValue != nullptr) **newValue = value.get() * scale; else log::warn("Style variable '{}' was of type ImVec2 but a float was expected.", name); } else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) { if (const auto newValue = std::get_if(&style.value); newValue != nullptr && *newValue != nullptr) **newValue = ImVec2(value[0].get() * scale, value[1].get() * scale); else log::warn("Style variable '{}' was of type float but a ImVec2 was expected.", name); } else { hex::log::error("Theme '{}' has invalid style value for '{}.{}'!", name, type, key); } } } } if (theme.contains("image_theme")) { if (theme["image_theme"].is_string()) { s_imageTheme = theme["image_theme"].get(); } else { hex::log::error("Theme '{}' has invalid image theme!", name); s_imageTheme = "dark"; } } else { s_imageTheme = "dark"; } s_currTheme = name; EventThemeChanged::post(); } const std::string &ThemeManager::getImageTheme() { return s_imageTheme; } std::vector ThemeManager::getThemeNames() { std::vector themeNames; for (const auto &[name, theme] : *s_themes) themeNames.push_back(name); return themeNames; } void ThemeManager::reset() { std::unique_lock lock(s_themeMutex); s_themes->clear(); s_styleHandlers->clear(); s_themeHandlers->clear(); s_imageTheme->clear(); s_currTheme->clear(); } void ThemeManager::setAccentColor(const ImColor &color) { float h, s, v; ImGui::ColorConvertRGBtoHSV(color.Value.x, color.Value.y, color.Value.z, h, s, v); s_accentColor = h; reapplyCurrentTheme(); } const std::map &ThemeManager::getThemeHandlers() { return s_themeHandlers; } const std::map &ThemeManager::getStyleHandlers() { return s_styleHandlers; } } ================================================ FILE: lib/libimhex/source/api/tutorial_manager.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include namespace hex { namespace { AutoReset> s_tutorials; auto s_currentTutorial = s_tutorials->end(); AutoReset> s_highlights; AutoReset>> s_highlightDisplays; AutoReset> s_interactiveHelpDisplays; AutoReset>> s_interactiveHelpItems; ImRect s_hoveredRect; ImGuiID s_hoveredId; ImGuiID s_activeHelpId; bool s_helpHoverActive = false; AutoReset(const std::string &)>> s_renderer; class IDStack { public: IDStack() { idStack.push_back(0); } void add(const char *string) { const ImGuiID seed = idStack.back(); const ImGuiID id = ImHashStr(string, 0, seed); idStack.push_back(id); } void add(const std::string &string) { const ImGuiID seed = idStack.back(); const ImGuiID id = ImHashStr(string.c_str(), string.length(), seed); idStack.push_back(id); } void add(const void *pointer) { const ImGuiID seed = idStack.back(); const ImGuiID id = ImHashData((const void*) &pointer, sizeof(pointer), seed); idStack.push_back(id); } void add(int value) { const ImGuiID seed = idStack.back(); const ImGuiID id = ImHashData(&value, sizeof(value), seed); idStack.push_back(id); } ImGuiID get() { return idStack.back(); } private: ImVector idStack; }; ImGuiID calculateId(const auto &ids) { IDStack idStack; for (const auto &id : ids) { std::visit(wolv::util::overloaded { [&idStack](const Lang &id) { idStack.add(id.get()); }, [&idStack](const auto &id) { idStack.add(id); } }, id); } return idStack.get(); } } void TutorialManager::init() { if (*s_renderer == nullptr) { *s_renderer = [](const std::string &message) { return [message] { ImGui::PushTextWrapPos(300_scaled); ImGui::TextUnformatted(message.c_str()); ImGui::PopTextWrapPos(); ImGui::NewLine(); }; }; } } void TutorialManager::postElementRendered(ImGuiID id, const ImRect &boundingBox) { if (!ImGui::IsRectVisible(boundingBox.Min, boundingBox.Max)) return; { const auto element = hex::s_highlights->find(id); if (element != hex::s_highlights->end()) { hex::s_highlightDisplays->emplace_back(boundingBox, element->second); const auto window = ImGui::GetCurrentWindow(); if (window != nullptr && window->DockNode != nullptr && window->DockNode->TabBar != nullptr) window->DockNode->TabBar->NextSelectedTabId = window->TabId; } } { const auto element = s_interactiveHelpItems->find(id); if (element != s_interactiveHelpItems->end()) { (*s_interactiveHelpDisplays)[id] = boundingBox; } } if (id != 0 && boundingBox.Contains(ImGui::GetMousePos())) { if ((s_hoveredRect.GetArea() == 0 || boundingBox.GetArea() < s_hoveredRect.GetArea()) && s_interactiveHelpItems->contains(id)) { s_hoveredRect = boundingBox; s_hoveredId = id; } } } const std::map& TutorialManager::getTutorials() { return s_tutorials; } std::map::iterator TutorialManager::getCurrentTutorial() { return s_currentTutorial; } TutorialManager::Tutorial& TutorialManager::createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) { return s_tutorials->try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second; } void TutorialManager::startHelpHover() { TaskManager::doLater([]{ s_helpHoverActive = true; }); } void TutorialManager::addInteractiveHelpText(std::initializer_list> &&ids, UnlocalizedString unlocalizedString) { auto id = calculateId(ids); s_interactiveHelpItems->emplace(id, [text = std::move(unlocalizedString)]{ log::info("{}", Lang(text).get()); }); } void TutorialManager::addInteractiveHelpLink(std::initializer_list> &&ids, std::string link) { auto id = calculateId(ids); s_interactiveHelpItems->emplace(id, [link = std::move(link)]{ hex::openWebpage(link); }); } void TutorialManager::setLastItemInteractiveHelpPopup(std::function callback) { auto id = ImGui::GetItemID(); if (!s_interactiveHelpItems->contains(id)) { s_interactiveHelpItems->emplace(id, [id]{ s_activeHelpId = id; }); } if (id == s_activeHelpId) { ImGui::SetNextWindowSize(scaled({ 400, 0 })); if (ImGui::BeginTooltip()) { callback(); ImGui::EndTooltip(); } if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) || ImGui::IsKeyPressed(ImGuiKey_Escape)) s_activeHelpId = 0; } } void TutorialManager::setLastItemInteractiveHelpLink(std::string link) { auto id = ImGui::GetItemID(); if (s_interactiveHelpItems->contains(id)) return; s_interactiveHelpItems->emplace(id, [link = std::move(link)]{ hex::openWebpage(link); }); } void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) { s_currentTutorial = s_tutorials->find(unlocalizedName); if (s_currentTutorial == s_tutorials->end()) return; s_currentTutorial->second.start(); } void TutorialManager::stopCurrentTutorial() { s_currentTutorial = s_tutorials->end(); } void TutorialManager::drawHighlights() { if (s_helpHoverActive) { const auto &drawList = ImGui::GetForegroundDrawList(ImGui::GetMainViewport()); drawList->AddText(ImGui::GetMousePos() + scaled({ 10, -5, }), ImGui::GetColorU32(ImGuiCol_Text), "?"); for (const auto &[id, boundingBox] : *s_interactiveHelpDisplays) { drawList->AddRect( boundingBox.Min - ImVec2(5, 5), boundingBox.Max + ImVec2(5, 5), ImGui::GetColorU32(ImGuiCol_PlotHistogram), 5.0F, ImDrawFlags_None, 2.0F ); } s_interactiveHelpDisplays->clear(); const bool mouseClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left); if (s_hoveredId != 0) { drawList->AddRectFilled(s_hoveredRect.Min, s_hoveredRect.Max, 0x30FFFFFF); if (mouseClicked) { auto it = s_interactiveHelpItems->find(s_hoveredId); if (it != s_interactiveHelpItems->end()) { it->second(); } } s_hoveredId = 0; s_hoveredRect = {}; } if (mouseClicked || ImGui::IsKeyPressed(ImGuiKey_Escape)) { s_helpHoverActive = false; } // Discard mouse click so it doesn't activate clicked item ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] = false; ImGui::GetIO().MouseReleased[ImGuiMouseButton_Left] = false; ImGui::GetIO().MouseClicked[ImGuiMouseButton_Left] = false; } for (const auto &[rect, unlocalizedText] : *s_highlightDisplays) { const auto drawList = ImGui::GetForegroundDrawList(); drawList->PushClipRectFullScreen(); { auto highlightColor = ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_Highlight); highlightColor.w *= ImSin(ImGui::GetTime() * 6.0F) / 4.0F + 0.75F; ImHexApi::System::unlockFrameRate(); drawList->AddRect(rect.Min - ImVec2(5, 5), rect.Max + ImVec2(5, 5), ImColor(highlightColor), 5.0F, ImDrawFlags_None, 2.0F); } { if (!unlocalizedText.empty()) { const auto mainWindowPos = ImHexApi::System::getMainWindowPosition(); const auto mainWindowSize = ImHexApi::System::getMainWindowSize(); const auto margin = ImGui::GetStyle().WindowPadding; ImVec2 windowPos = { rect.Min.x + 20_scaled, rect.Max.y + 10_scaled }; ImVec2 windowSize = { std::max(rect.Max.x - rect.Min.x - 40_scaled, 300_scaled), 0 }; const char* text = Lang(unlocalizedText); const auto textSize = ImGui::CalcTextSize(text, nullptr, false, windowSize.x - margin.x * 2); windowSize.y = textSize.y + margin.y * 2; if (windowPos.y + windowSize.y > mainWindowPos.y + mainWindowSize.y) windowPos.y = rect.Min.y - windowSize.y - 15_scaled; if (windowPos.y < mainWindowPos.y) windowPos.y = rect.Min.y + 10_scaled; ImGui::SetNextWindowPos(windowPos); ImGui::SetNextWindowSize(windowSize); ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); if (ImGui::Begin(unlocalizedText.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize)) { ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead()); ImGuiExt::TextFormattedWrapped("{}", text); } ImGui::End(); } } drawList->PopClipRect(); } s_highlightDisplays->clear(); } void TutorialManager::drawMessageBox(std::optional message) { const auto windowStart = ImHexApi::System::getMainWindowPosition() + scaled({ 10, 10 }); const auto windowEnd = ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize() - scaled({ 10, 10 }); ImVec2 position = ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize() / 2.0F; ImVec2 pivot = { 0.5F, 0.5F }; if (!message.has_value()) { message = Tutorial::Step::Message { .position=Position::None, .unlocalizedTitle="", .unlocalizedMessage="", .allowSkip=false }; } if (message->position == Position::None) { message->position = Position::Bottom | Position::Right; } if ((message->position & Position::Top) == Position::Top) { position.y = windowStart.y; pivot.y = 0.0F; } if ((message->position & Position::Bottom) == Position::Bottom) { position.y = windowEnd.y; pivot.y = 1.0F; } if ((message->position & Position::Left) == Position::Left) { position.x = windowStart.x; pivot.x = 0.0F; } if ((message->position & Position::Right) == Position::Right) { position.x = windowEnd.x; pivot.x = 1.0F; } ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot); ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); ImGui::SetNextWindowSize(ImVec2(300_scaled, 0)); bool open = true; if (ImGui::Begin(message->unlocalizedTitle.empty() ? "##TutorialMessage" : Lang(message->unlocalizedTitle), &open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoFocusOnAppearing)) { ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead()); auto &step = s_currentTutorial->second.m_currentStep; if (!message->unlocalizedMessage.empty()) { step->m_drawFunction(); ImGui::NewLine(); ImGui::NewLine(); } ImGui::BeginDisabled(step == s_currentTutorial->second.m_steps.begin()); if (ImGuiExt::DimmedArrowButton("Backwards", ImGuiDir_Left)) { s_currentTutorial->second.m_currentStep->advance(-1); } ImGui::EndDisabled(); ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetWindowWidth() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.x); ImGui::BeginDisabled(!message->allowSkip && step == s_currentTutorial->second.m_latestStep); if (ImGuiExt::DimmedArrowButton("Forwards", ImGuiDir_Right)) { step->advance(1); } ImGui::EndDisabled(); } ImGui::End(); if (!open) { stopCurrentTutorial(); } } void TutorialManager::drawTutorial() { drawHighlights(); if (s_currentTutorial == s_tutorials->end()) return; const auto ¤tStep = s_currentTutorial->second.m_currentStep; if (currentStep == s_currentTutorial->second.m_steps.end()) return; const auto &message = currentStep->m_message; drawMessageBox(message); } void TutorialManager::reset() { s_tutorials->clear(); s_currentTutorial = s_tutorials->end(); s_highlights->clear(); s_highlightDisplays->clear(); } void TutorialManager::setRenderer(std::function renderer) { s_renderer = std::move(renderer); } TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() { auto &newStep = m_steps.emplace_back(this); m_currentStep = m_steps.end(); m_latestStep = m_currentStep; return newStep; } void TutorialManager::Tutorial::start() { m_currentStep = m_steps.begin(); m_latestStep = m_currentStep; if (m_currentStep == m_steps.end()) return; m_currentStep->addHighlights(); if (m_currentStep->m_message.has_value()) m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_currentStep->m_message->unlocalizedMessage)); } void TutorialManager::Tutorial::Step::addHighlights() const { if (m_onAppear) m_onAppear(); for (const auto &[text, ids] : m_highlights) { s_highlights->emplace(calculateId(ids), text); } } void TutorialManager::Tutorial::Step::removeHighlights() const { for (const auto &[text, ids] : m_highlights) { s_highlights->erase(calculateId(ids)); } } void TutorialManager::Tutorial::Step::advance(i32 steps) const { m_parent->m_currentStep->removeHighlights(); if (m_parent->m_currentStep == m_parent->m_latestStep && steps > 0) std::advance(m_parent->m_latestStep, steps); std::advance(m_parent->m_currentStep, steps); if (m_parent->m_currentStep != m_parent->m_steps.end()) { m_parent->m_currentStep->addHighlights(); if (m_message.has_value()) m_parent->m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_parent->m_currentStep->m_message->unlocalizedMessage)); } else s_currentTutorial = s_tutorials->end(); } TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::addHighlight(const UnlocalizedString &unlocalizedText, std::initializer_list>&& ids) { m_highlights.emplace_back( unlocalizedText, ids ); return *this; } TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::addHighlight(std::initializer_list>&& ids) { return this->addHighlight("", std::forward(ids)); } TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::setMessage(const UnlocalizedString &unlocalizedTitle, const UnlocalizedString &unlocalizedMessage, Position position) { m_message = Message { .position=position, .unlocalizedTitle=unlocalizedTitle, .unlocalizedMessage=unlocalizedMessage, .allowSkip=false }; return *this; } TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::allowSkip() { if (m_message.has_value()) { m_message->allowSkip = true; } else { m_message = Message { .position=Position::Bottom | Position::Right, .unlocalizedTitle="", .unlocalizedMessage="", .allowSkip=true }; } return *this; } TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::onAppear(std::function callback) { m_onAppear = std::move(callback); return *this; } TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::onComplete(std::function callback) { m_onComplete = std::move(callback); return *this; } bool TutorialManager::Tutorial::Step::isCurrent() const { const auto ¤tStep = m_parent->m_currentStep; if (currentStep == m_parent->m_steps.end()) return false; return &*currentStep == this; } void TutorialManager::Tutorial::Step::complete() const { if (this->isCurrent()) { this->advance(); if (m_onComplete) { TaskManager::doLater([this] { m_onComplete(); }); } } } } ================================================ FILE: lib/libimhex/source/api/workspace_manager.cpp ================================================ #include #include #include #include #include #include #include #include #include namespace hex { static AutoReset> s_workspaces; static auto s_currentWorkspace = s_workspaces->end(); static auto s_previousWorkspace = s_workspaces->end(); static auto s_workspaceToRemove = s_workspaces->end(); void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) { s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace { .layout = layout.empty() ? LayoutManager::saveToString() : layout, .path = {}, .builtin = false }).first; for (const auto &workspaceFolder : paths::Workspaces.write()) { const auto workspacePath = workspaceFolder / (name + ".hexws"); if (exportToFile(workspacePath)) { s_currentWorkspace->second.path = workspacePath; break; } } } void WorkspaceManager::switchWorkspace(const std::string& name) { const auto newWorkspace = s_workspaces->find(name); if (newWorkspace != s_workspaces->end()) { s_currentWorkspace = newWorkspace; log::info("Switching to workspace '{}'", name); } } void WorkspaceManager::importFromFile(const std::fs::path& path) { if (std::ranges::any_of(*s_workspaces, [path](const auto &pair) { return pair.second.path == path; })) { return; } wolv::io::File file(path, wolv::io::File::Mode::Read); if (!file.isValid()) { log::error("Failed to load workspace from file '{}'", path.string()); file.remove(); return; } auto content = file.readString(); try { auto json = nlohmann::json::parse(content.begin(), content.end()); const std::string name = json["name"]; std::string layout = json["layout"]; const bool builtin = json.value("builtin", false); (*s_workspaces)[name] = Workspace { .layout = std::move(layout), .path = path, .builtin = builtin }; } catch (nlohmann::json::exception &e) { log::error("Failed to load workspace from file '{}': {}", path.string(), e.what()); file.remove(); } } bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName, bool builtin) { if (path.empty()) { if (s_currentWorkspace == s_workspaces->end()) { return false; } path = s_currentWorkspace->second.path; } if (workspaceName.empty()) { workspaceName = s_currentWorkspace->first; } wolv::io::File file(path, wolv::io::File::Mode::Create); if (!file.isValid()) { return false; } const auto layoutString = LayoutManager::saveToString(); if (auto it = s_workspaces->find(workspaceName); it != s_workspaces->end()) { it->second.layout = layoutString; } nlohmann::json json; json["name"] = workspaceName; json["layout"] = layoutString; json["builtin"] = builtin; file.writeString(json.dump(4)); return true; } void WorkspaceManager::removeWorkspace(const std::string& name) { bool deletedCurrentWorkspace = false; for (const auto &[workspaceName, workspace] : *s_workspaces) { if (workspaceName == name) { log::info("Removing workspace file '{}'", wolv::util::toUTF8String(workspace.path)); if (wolv::io::fs::remove(workspace.path)) { log::info("Removed workspace '{}'", name); if (workspaceName == s_currentWorkspace->first) { deletedCurrentWorkspace = true; } } else { log::error("Failed to remove workspace '{}'", name); } } } WorkspaceManager::reload(); if (deletedCurrentWorkspace && !s_workspaces->empty()) { s_currentWorkspace = s_workspaces->begin(); } } void WorkspaceManager::process() { if (s_previousWorkspace != s_currentWorkspace) { log::debug("Updating workspace"); if (s_previousWorkspace != s_workspaces->end()) { auto newWorkspace = s_currentWorkspace; s_currentWorkspace = s_previousWorkspace; exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first, s_previousWorkspace->second.builtin); s_currentWorkspace = newWorkspace; } LayoutManager::closeAllViews(); ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str()); s_previousWorkspace = s_currentWorkspace; if (s_workspaceToRemove != s_workspaces->end()) { s_workspaces->erase(s_workspaceToRemove); s_workspaceToRemove = s_workspaces->end(); } } } void WorkspaceManager::reset() { s_workspaces->clear(); s_currentWorkspace = s_workspaces->end(); s_previousWorkspace = s_workspaces->end(); } void WorkspaceManager::reload() { WorkspaceManager::reset(); // Explicitly only search paths that are writable so the workspaces can be modified for (const auto &defaultPath : paths::Workspaces.write()) { for (const auto &entry : std::fs::directory_iterator(defaultPath)) { if (!entry.is_regular_file()) { continue; } const auto &path = entry.path(); if (path.extension() != ".hexws") { continue; } WorkspaceManager::importFromFile(path); } } } const std::map& WorkspaceManager::getWorkspaces() { return *s_workspaces; } const std::map::iterator& WorkspaceManager::getCurrentWorkspace() { return s_currentWorkspace; } } ================================================ FILE: lib/libimhex/source/data_processor/attribute.cpp ================================================ #include namespace hex::dp { int Attribute::s_idCounter = 1; Attribute::Attribute(IOType ioType, Type type, UnlocalizedString unlocalizedName) : m_id(s_idCounter++), m_ioType(ioType), m_type(type), m_unlocalizedName(std::move(unlocalizedName)) { } Attribute::~Attribute() { for (auto &[linkId, attr] : this->getConnectedAttributes()) attr->removeConnectedAttribute(linkId); } void Attribute::setIdCounter(int id) { if (id > s_idCounter) s_idCounter = id; } } ================================================ FILE: lib/libimhex/source/data_processor/link.cpp ================================================ #include namespace hex::dp { int Link::s_idCounter = 1; Link::Link(int from, int to) : m_id(s_idCounter++), m_from(from), m_to(to) { } void Link::setIdCounter(int id) { if (id > s_idCounter) s_idCounter = id; } } ================================================ FILE: lib/libimhex/source/data_processor/node.cpp ================================================ #include #include #include #include namespace hex::dp { int Node::s_idCounter = 1; static std::atomic_bool s_interrupted; Node::Node(UnlocalizedString unlocalizedTitle, std::vector attributes) : m_id(s_idCounter++), m_unlocalizedTitle(std::move(unlocalizedTitle)), m_attributes(std::move(attributes)) { for (auto &attr : m_attributes) attr.setParentNode(this); } void Node::draw() { this->drawNode(); } const std::vector& Node::getBufferOnInput(u32 index) { auto attribute = this->getConnectedInputAttribute(index); if (attribute == nullptr) throwNodeError(fmt::format("Nothing connected to input '{0}'", Lang(m_attributes[index].getUnlocalizedName()))); if (attribute->getType() != Attribute::Type::Buffer) throwNodeError("Tried to read buffer from non-buffer attribute"); markInputProcessed(index); attribute->getParentNode()->process(); unmarkInputProcessed(index); auto &outputData = attribute->getOutputData(); return outputData; } const i128& Node::getIntegerOnInput(u32 index) { auto attribute = this->getConnectedInputAttribute(index); auto &outputData = [&]() -> std::vector&{ if (attribute != nullptr) { if (attribute->getType() != Attribute::Type::Integer) throwNodeError("Tried to read integer from non-integer attribute"); markInputProcessed(index); attribute->getParentNode()->process(); unmarkInputProcessed(index); return attribute->getOutputData(); } else { return this->getAttribute(index).getOutputData(); } }(); if (outputData.empty()) throwNodeError("No data available at connected attribute"); if (outputData.size() < sizeof(i128)) throwNodeError("Not enough data provided for integer"); return *reinterpret_cast(outputData.data()); } const double& Node::getFloatOnInput(u32 index) { auto attribute = this->getConnectedInputAttribute(index); auto &outputData = [&]() -> std::vector&{ if (attribute != nullptr) { if (attribute->getType() != Attribute::Type::Float) throwNodeError("Tried to read integer from non-float attribute"); markInputProcessed(index); attribute->getParentNode()->process(); unmarkInputProcessed(index); return attribute->getOutputData(); } else { return this->getAttribute(index).getOutputData(); } }(); if (outputData.empty()) throwNodeError("No data available at connected attribute"); if (outputData.size() < sizeof(double)) throwNodeError("Not enough data provided for float"); return *reinterpret_cast(outputData.data()); } void Node::setBufferOnOutput(u32 index, std::span data) { if (index >= this->getAttributes().size()) throwNodeError("Attribute index out of bounds!"); auto &attribute = this->getAttributes()[index]; if (attribute.getIOType() != Attribute::IOType::Out) throwNodeError("Tried to set output data of an input attribute!"); if (attribute.getType() != Attribute::Type::Buffer) throwNodeError("Tried to set buffer on non-buffer attribute!"); attribute.getOutputData() = { data.begin(), data.end() }; } void Node::setIntegerOnOutput(u32 index, i128 integer) { if (index >= this->getAttributes().size()) throwNodeError("Attribute index out of bounds!"); auto &attribute = this->getAttributes()[index]; if (attribute.getIOType() != Attribute::IOType::Out) throwNodeError("Tried to set output data of an input attribute!"); if (attribute.getType() != Attribute::Type::Integer) throwNodeError("Tried to set integer on non-integer attribute!"); std::vector buffer(sizeof(integer), 0); std::memcpy(buffer.data(), &integer, sizeof(integer)); attribute.getOutputData() = buffer; } void Node::setFloatOnOutput(u32 index, double floatingPoint) { if (index >= this->getAttributes().size()) throwNodeError("Attribute index out of bounds!"); auto &attribute = this->getAttributes()[index]; if (attribute.getIOType() != Attribute::IOType::Out) throwNodeError("Tried to set output data of an input attribute!"); if (attribute.getType() != Attribute::Type::Float) throwNodeError("Tried to set float on non-float attribute!"); std::vector buffer(sizeof(floatingPoint), 0); std::memcpy(buffer.data(), &floatingPoint, sizeof(floatingPoint)); attribute.getOutputData() = buffer; } void Node::setOverlayData(u64 address, const std::vector &data) { if (m_overlay == nullptr) throwNodeError("Tried setting overlay data on a node that's not the end of a chain!"); m_overlay->setAddress(address); m_overlay->getData() = data; } [[noreturn]] void Node::throwNodeError(const std::string &msg) { throw NodeError(this, msg); } void Node::setAttributes(std::vector attributes) { m_attributes = std::move(attributes); for (auto &attr : m_attributes) attr.setParentNode(this); } void Node::setIdCounter(int id) { if (id > s_idCounter) s_idCounter = id; } Attribute& Node::getAttribute(u32 index) { if (index >= this->getAttributes().size()) throw std::runtime_error("Attribute index out of bounds!"); return this->getAttributes()[index]; } Attribute *Node::getConnectedInputAttribute(u32 index) { const auto &connectedAttribute = this->getAttribute(index).getConnectedAttributes(); if (connectedAttribute.empty()) return nullptr; return connectedAttribute.begin()->second; } void Node::markInputProcessed(u32 index) { const auto &[iter, inserted] = m_processedInputs.insert(index); if (!inserted) throwNodeError("Recursion detected!"); if (s_interrupted) { s_interrupted = false; throwNodeError("Execution interrupted!"); } } void Node::unmarkInputProcessed(u32 index) { m_processedInputs.erase(index); } void Node::interrupt() { s_interrupted = true; } } ================================================ FILE: lib/libimhex/source/helpers/binary_pattern.cpp ================================================ #include #include namespace hex { namespace { void skipWhitespace(std::string_view &string) { while (!string.empty()) { if (!std::isspace(string.front())) break; string = string.substr(1); } } std::vector parseValueExpression(std::string_view &string) { string = string.substr(1); // Parse bit size number u64 bitSize = 0; std::endian endian = std::endian::little; while (!string.empty() && std::isdigit(string.front())) { bitSize *= 10; bitSize += string.front() - '0'; string = string.substr(1); skipWhitespace(string); } if (string.starts_with("le")) { endian = std::endian::little; string = string.substr(2); } else if (string.starts_with("be")) { endian = std::endian::big; string = string.substr(2); } if (bitSize > 64 || bitSize % 8 != 0) return { }; if (string.empty() || string.front() != '(') return { }; string = string.substr(1); i128 value = 0x00; bool negative = false; for (u32 i = 0; !string.empty(); i++) { const char c = string.front(); if (c == ')') break; if (i == 0 && c == '-') negative = true; else if (i == 0 && c == '+') continue; else if (std::isdigit(c)) value = value * 10 + (c - '0'); else return {}; string = string.substr(1); } if (negative) value = -value; if (string.empty() || string.front() != ')') return { }; string = string.substr(1); u128 resultValue = changeEndianness(value, bitSize / 8, endian); std::vector result; for (u32 bit = 0; bit < bitSize; bit += 8) { result.emplace_back( 0xFF, u8((resultValue >> bit) & hex::bitmask(8)) ); } return result; } std::vector parseBinaryPatternString(std::string_view string) { std::vector result; if (string.length() < 2) return { }; bool inString = false; while (!string.empty()) { BinaryPattern::Pattern pattern = { .mask=0, .value=0 }; if (string.starts_with("\"")) { inString = !inString; string = string.substr(1); continue; } else if (inString) { pattern = { .mask=0xFF, .value=u8(string.front()) }; string = string.substr(1); } else if (string.starts_with("u") || string.starts_with("s")) { auto newPatterns = parseValueExpression(string); if (newPatterns.empty()) return {}; std::ranges::move(newPatterns, std::back_inserter(result)); continue; } else if (string.starts_with("??")) { pattern = { .mask=0x00, .value=0x00 }; string = string.substr(2); } else if ((std::isxdigit(string.front()) || string.front() == '?') && string.length() >= 2) { const auto hex = string.substr(0, 2); for (const auto &c : hex) { pattern.mask <<= 4; pattern.value <<= 4; if (std::isxdigit(c)) { pattern.mask |= 0x0F; if (auto hexValue = hex::hexCharToValue(c); hexValue.has_value()) pattern.value |= hexValue.value(); else return { }; } else if (c != '?') { return { }; } } string = string.substr(2); } else if (std::isspace(string.front())) { string = string.substr(1); continue; } else { return { }; } result.push_back(pattern); } if (inString) return { }; return result; } } BinaryPattern::BinaryPattern(const std::string &pattern) : m_patterns(parseBinaryPatternString(pattern)) { } bool BinaryPattern::isValid() const { return !m_patterns.empty(); } bool BinaryPattern::matches(const std::vector &bytes) const { if (bytes.size() < m_patterns.size()) return false; for (u32 i = 0; i < m_patterns.size(); i++) { if (!this->matchesByte(bytes[i], i)) return false; } return true; } bool BinaryPattern::matchesByte(u8 byte, u32 offset) const { const auto &pattern = m_patterns[offset]; return (byte & pattern.mask) == pattern.value; } u64 BinaryPattern::getSize() const { return m_patterns.size(); } } ================================================ FILE: lib/libimhex/source/helpers/crypto.cpp ================================================ #include #include #include #include #include #include #include #if MBEDTLS_VERSION_MAJOR >= 4 // TODO: We'll need to migrate to the new eventually. For now, just include the old stuff again #define MBEDTLS_DECLARE_PRIVATE_IDENTIFIERS #include #include #include #include #include #include #else #include #include #include #include #include #include #endif #include #include #include #include #include namespace hex::crypt { using namespace std::placeholders; template Func> void processDataByChunks(prv::Provider *data, u64 offset, size_t size, Func func) { std::array buffer = { 0 }; for (size_t bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) { const auto readSize = std::min(buffer.size(), size - bufferOffset); data->read(offset + bufferOffset, buffer.data(), readSize); func(buffer.data(), readSize); } } template T reflect(T in, std::size_t bits) { T out {}; for (std::size_t i = 0; i < bits; i++) { out <<= 1; if (in & 0b1) out |= 1; in >>= 1; } return out; } template T reflect(T in) { if constexpr (sizeof(T) == 1) { T out { in }; out = ((out & 0xf0u) >> 4) | ((out & 0x0fu) << 4); out = ((out & 0xccu) >> 2) | ((out & 0x33u) << 2); out = ((out & 0xaau) >> 1) | ((out & 0x55u) << 1); return out; } else { return reflect(in, sizeof(T) * 8); } } template requires (std::has_single_bit(NumBits)) class Crc { // Use reflected algorithm, so we reflect only if refin / refout is FALSE // mask values, 0b1 << 64 is UB, so use 0b10 << 63 public: constexpr Crc(u64 polynomial, u64 init, u64 xorOut, bool reflectInput, bool reflectOutput) : m_init(init & ((0b10ull << (NumBits - 1)) - 1)), m_xorOut(xorOut & ((0b10ull << (NumBits - 1)) - 1)), m_reflectInput(reflectInput), m_reflectOutput(reflectOutput), m_table([polynomial] { auto reflectedPoly = reflect(polynomial & ((0b10ull << (NumBits - 1)) - 1), NumBits); std::array table = { 0 }; for (uint32_t i = 0; i < 256; i++) { uint64_t c = i; for (std::size_t j = 0; j < 8; j++) { if (c & 0b1) c = reflectedPoly ^ (c >> 1); else c >>= 1; } table[i] = c; } return table; }()) { reset(); } constexpr void reset() { m_value = reflect(m_init, NumBits); } constexpr void processBytes(const unsigned char *data, std::size_t size) { for (std::size_t i = 0; i < size; i++) { u8 byte; if (m_reflectInput) byte = data[i]; else byte = reflect(data[i]); m_value = m_table[(m_value ^ byte) & 0xFFL] ^ (m_value >> 8); } } [[nodiscard]] constexpr u64 checksum() const { if (m_reflectOutput) return m_value ^ m_xorOut; else return reflect(m_value, NumBits) ^ m_xorOut; } private: u64 m_value = 0x00; u64 m_init; u64 m_xorOut; bool m_reflectInput; bool m_reflectOutput; std::array m_table; }; template auto calcCrc(prv::Provider *data, u64 offset, std::size_t size, u32 polynomial, u32 init, u32 xorout, bool reflectIn, bool reflectOut) { using Crc = Crc; Crc crc(polynomial, init, xorout, reflectIn, reflectOut); processDataByChunks(data, offset, size, [&crc](auto && data, auto && size) { crc.processBytes(data, size); }); return crc.checksum(); } u8 crc8(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut) { return calcCrc<8>(data, offset, size, polynomial, init, xorOut, reflectIn, reflectOut); } u16 crc16(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut) { return calcCrc<16>(data, offset, size, polynomial, init, xorOut, reflectIn, reflectOut); } u32 crc32(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut) { return calcCrc<32>(data, offset, size, polynomial, init, xorOut, reflectIn, reflectOut); } std::array md5(prv::Provider *&data, u64 offset, size_t size) { std::array result = { 0 }; mbedtls_md5_context ctx; mbedtls_md5_init(&ctx); mbedtls_md5_starts(&ctx); processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_md5_update(&ctx, data, size); }); mbedtls_md5_finish(&ctx, result.data()); mbedtls_md5_free(&ctx); return result; } std::array md5(const std::vector &data) { std::array result = { 0 }; mbedtls_md5_context ctx; mbedtls_md5_init(&ctx); mbedtls_md5_starts(&ctx); mbedtls_md5_update(&ctx, data.data(), data.size()); mbedtls_md5_finish(&ctx, result.data()); mbedtls_md5_free(&ctx); return result; } std::array sha1(prv::Provider *&data, u64 offset, size_t size) { std::array result = { 0 }; mbedtls_sha1_context ctx; mbedtls_sha1_init(&ctx); mbedtls_sha1_starts(&ctx); processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha1_update(&ctx, data, size); }); mbedtls_sha1_finish(&ctx, result.data()); mbedtls_sha1_free(&ctx); return result; } std::array sha1(const std::vector &data) { std::array result = { 0 }; mbedtls_sha1_context ctx; mbedtls_sha1_init(&ctx); mbedtls_sha1_starts(&ctx); mbedtls_sha1_update(&ctx, data.data(), data.size()); mbedtls_sha1_finish(&ctx, result.data()); mbedtls_sha1_free(&ctx); return result; } std::array sha224(prv::Provider *&data, u64 offset, size_t size) { std::array result = { 0 }; mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); mbedtls_sha256_starts(&ctx, true); processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha256_update(&ctx, data, size); }); mbedtls_sha256_finish(&ctx, result.data()); mbedtls_sha256_free(&ctx); return result; } std::array sha224(const std::vector &data) { std::array result = { 0 }; mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); mbedtls_sha256_starts(&ctx, true); mbedtls_sha256_update(&ctx, data.data(), data.size()); mbedtls_sha256_finish(&ctx, result.data()); mbedtls_sha256_free(&ctx); return result; } std::array sha256(prv::Provider *&data, u64 offset, size_t size) { std::array result = { 0 }; mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); mbedtls_sha256_starts(&ctx, false); processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha256_update(&ctx, data, size); }); mbedtls_sha256_finish(&ctx, result.data()); mbedtls_sha256_free(&ctx); return result; } std::array sha256(const std::vector &data) { std::array result = { 0 }; mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); mbedtls_sha256_starts(&ctx, false); mbedtls_sha256_update(&ctx, data.data(), data.size()); mbedtls_sha256_finish(&ctx, result.data()); mbedtls_sha256_free(&ctx); return result; } std::array sha384(prv::Provider *&data, u64 offset, size_t size) { std::array result = { 0 }; mbedtls_sha512_context ctx; mbedtls_sha512_init(&ctx); mbedtls_sha512_starts(&ctx, true); processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha512_update(&ctx, data, size); }); mbedtls_sha512_finish(&ctx, result.data()); mbedtls_sha512_free(&ctx); return result; } std::array sha384(const std::vector &data) { std::array result = { 0 }; mbedtls_sha512_context ctx; mbedtls_sha512_init(&ctx); mbedtls_sha512_starts(&ctx, true); mbedtls_sha512_update(&ctx, data.data(), data.size()); mbedtls_sha512_finish(&ctx, result.data()); mbedtls_sha512_free(&ctx); return result; } std::array sha512(prv::Provider *&data, u64 offset, size_t size) { std::array result = { 0 }; mbedtls_sha512_context ctx; mbedtls_sha512_init(&ctx); mbedtls_sha512_starts(&ctx, false); processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha512_update(&ctx, data, size); }); mbedtls_sha512_finish(&ctx, result.data()); mbedtls_sha512_free(&ctx); return result; } std::array sha512(const std::vector &data) { std::array result = { 0 }; mbedtls_sha512_context ctx; mbedtls_sha512_init(&ctx); mbedtls_sha512_starts(&ctx, false); mbedtls_sha512_update(&ctx, data.data(), data.size()); mbedtls_sha512_finish(&ctx, result.data()); mbedtls_sha512_free(&ctx); return result; } std::vector decode64(const std::vector &input) { size_t written = 0; mbedtls_base64_decode(nullptr, 0, &written, input.data(), input.size()); std::vector output(written, 0x00); if (mbedtls_base64_decode(output.data(), output.size(), &written, input.data(), input.size())) return {}; output.resize(written); return output; } std::vector encode64(const std::vector &input) { size_t written = 0; mbedtls_base64_encode(nullptr, 0, &written, input.data(), input.size()); std::vector output(written, 0x00); if (mbedtls_base64_encode(output.data(), output.size(), &written, input.data(), input.size())) return {}; output.resize(written); return output; } std::vector decode16(const std::string &input) { std::vector output(input.length() / 2, 0x00); mbedtls_mpi ctx; mbedtls_mpi_init(&ctx); ON_SCOPE_EXIT { mbedtls_mpi_free(&ctx); }; // Read buffered constexpr static auto BufferSize = 0x100; for (size_t offset = 0; offset < input.size(); offset += BufferSize) { std::string inputPart = input.substr(offset, std::min(BufferSize, input.size() - offset)); if (mbedtls_mpi_read_string(&ctx, 16, inputPart.c_str())) return {}; if (mbedtls_mpi_write_binary(&ctx, output.data() + offset / 2, inputPart.size() / 2)) return {}; } return output; } std::string encode16(const std::vector &input) { if (input.empty()) return {}; std::string output(input.size() * 2, '\0'); for (size_t i = 0; i < input.size(); i++) { output[2 * i + 0] = "0123456789ABCDEF"[input[i] / 16]; output[2 * i + 1] = "0123456789ABCDEF"[input[i] % 16]; } return output; } template static T safeLeftShift(T t, u32 shift) { if (shift >= sizeof(t) * 8) { return 0; } else { return t << shift; } } template static T decodeLeb128(const std::vector &bytes) { T value = 0; u32 shift = 0; u8 b = 0; for (u8 byte : bytes) { b = byte; value |= safeLeftShift(static_cast(byte & 0x7F), shift); shift += 7; if ((byte & 0x80) == 0) { break; } } if constexpr(std::signed_integral) { if ((b & 0x40) != 0) { value |= safeLeftShift(~static_cast(0), shift); } } return value; } u128 decodeUleb128(const std::vector &bytes) { return decodeLeb128(bytes); } i128 decodeSleb128(const std::vector &bytes) { return decodeLeb128(bytes); } template static std::vector encodeLeb128(T value) { std::vector bytes; u8 byte; while (true) { byte = u8(value & 0x7F); value >>= 7; if constexpr(std::signed_integral) { if (value == 0 && (byte & 0x40) == 0) { break; } if (value == -1 && (byte & 0x40) != 0) { break; } } else { if (value == 0) { break; } } bytes.push_back(byte | 0x80); } bytes.push_back(byte); return bytes; } std::vector encodeUleb128(u128 value) { return encodeLeb128(value); } std::vector encodeSleb128(i128 value) { return encodeLeb128(value); } static wolv::util::Expected, int> aes(mbedtls_cipher_type_t type, mbedtls_operation_t operation, const std::vector &key, std::array nonce, std::array iv, const std::span &input) { if (input.empty()) return {}; if (key.size() > 256) return {}; mbedtls_cipher_context_t ctx; auto cipherInfo = mbedtls_cipher_info_from_type(type); if (cipherInfo == nullptr) return {}; int setupResult = mbedtls_cipher_setup(&ctx, cipherInfo); if (setupResult != 0) return wolv::util::Unexpected(setupResult); int setKeyResult = mbedtls_cipher_setkey(&ctx, key.data(), key.size() * 8, operation); if (setKeyResult != 0) return wolv::util::Unexpected(setKeyResult); std::array nonceCounter = { 0 }; auto mode = mbedtls_cipher_get_cipher_mode(&ctx); // if we are in ECB mode, we don't need to set the nonce if (mode != MBEDTLS_MODE_ECB) { std::ranges::copy(nonce, nonceCounter.begin()); std::ranges::copy(iv, nonceCounter.begin() + 8); } size_t outputSize = input.size() + mbedtls_cipher_get_block_size(&ctx); std::vector output(outputSize, 0x00); int cryptResult = 0; if (mode == MBEDTLS_MODE_ECB) { cryptResult = mbedtls_cipher_crypt(&ctx, nullptr, 0, input.data(), input.size(), output.data(), &outputSize); } else { cryptResult = mbedtls_cipher_crypt(&ctx, nonceCounter.data(), nonceCounter.size(), input.data(), input.size(), output.data(), &outputSize); } // free regardless of the result mbedtls_cipher_free(&ctx); if (cryptResult != 0) { return wolv::util::Unexpected(cryptResult); } output.resize(input.size()); return output; } wolv::util::Expected, int> aesDecrypt(AESMode mode, KeyLength keyLength, const std::vector &key, std::array nonce, std::array iv, const std::vector &input) { switch (keyLength) { case KeyLength::Key128Bits: if (key.size() != 128 / 8) return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_KEY_LENGTH); break; case KeyLength::Key192Bits: if (key.size() != 192 / 8) return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_KEY_LENGTH); break; case KeyLength::Key256Bits: if (key.size() != 256 / 8) return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_KEY_LENGTH); break; default: return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_KEY_LENGTH); } mbedtls_cipher_type_t type; switch (mode) { case AESMode::ECB: type = MBEDTLS_CIPHER_AES_128_ECB; break; case AESMode::CBC: type = MBEDTLS_CIPHER_AES_128_CBC; break; case AESMode::CFB128: type = MBEDTLS_CIPHER_AES_128_CFB128; break; case AESMode::CTR: type = MBEDTLS_CIPHER_AES_128_CTR; break; case AESMode::GCM: type = MBEDTLS_CIPHER_AES_128_GCM; break; case AESMode::CCM: type = MBEDTLS_CIPHER_AES_128_CCM; break; case AESMode::OFB: type = MBEDTLS_CIPHER_AES_128_OFB; break; case AESMode::XTS: type = MBEDTLS_CIPHER_AES_128_XTS; break; default: return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_MODE); } type = mbedtls_cipher_type_t(type + u8(keyLength)); return aes(type, MBEDTLS_DECRYPT, key, nonce, iv, input); } } ================================================ FILE: lib/libimhex/source/helpers/debugging.cpp ================================================ #include #include #include namespace hex::dbg { namespace impl { bool &getDebugWindowState() { static bool state = false; return state; } } static bool s_debugMode = false; bool debugModeEnabled() { return s_debugMode; } void setDebugModeEnabled(bool enabled) { s_debugMode = enabled; } [[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString) { log::error("Assertion failed: {} at {}:{} => {}", exprString, file, line, function); const auto stackTrace = trace::getStackTrace(); dbg::printStackTrace(stackTrace); std::abort(); } void printStackTrace(const trace::StackTraceResult &stackTrace) { log::fatal("Printing stacktrace using implementation '{}'", stackTrace.implementationName); for (const auto &stackFrame : stackTrace.stackFrames) { if (stackFrame.line == 0) log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function); else log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function); } } } ================================================ FILE: lib/libimhex/source/helpers/default_paths.cpp ================================================ #include #include #include #include #include #include #if defined(OS_WINDOWS) #include #include #elif defined(OS_LINUX) || defined(OS_WEB) #include # endif namespace hex::paths { std::vector getDataPaths(bool includeSystemFolders) { std::vector paths; std::set uniquePaths; const auto emplaceUniquePath = [&](const std::fs::path &path) { auto duplicate = uniquePaths.insert(path); if (duplicate.second) paths.emplace_back(path); }; #if defined(OS_WINDOWS) // In the portable Windows version, we just use the executable directory // Prevent the use of the AppData folder here if (!ImHexApi::System::isPortableVersion()) { PWSTR wAppDataPath = nullptr; if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath))) { emplaceUniquePath(wAppDataPath); CoTaskMemFree(wAppDataPath); } } #elif defined(OS_MACOS) emplaceUniquePath(wolv::io::fs::getApplicationSupportDirectoryPath() / "imhex"); #elif defined(OS_LINUX) || defined(OS_WEB) emplaceUniquePath(xdg::DataHomeDir()); auto dataDirs = xdg::DataDirs(); for (const auto &path : dataDirs) emplaceUniquePath(path); #endif #if defined(OS_MACOS) if (includeSystemFolders) { if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) { emplaceUniquePath(executablePath->parent_path()); } } #else uniquePaths.clear(); for (auto &path : paths) { path = path / "imhex"; uniquePaths.insert(path); } if (ImHexApi::System::isPortableVersion() || includeSystemFolders) { if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) emplaceUniquePath(executablePath->parent_path()); } #endif // Add additional data directories to the path auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths(); for (const auto &path : additionalDirs) emplaceUniquePath(path); // Add the project file directory to the path, if one is loaded if (ProjectFile::hasPath()) { emplaceUniquePath(ProjectFile::getPath().parent_path()); } return paths; } std::vector getConfigPaths(bool includeSystemFolders) { #if defined(OS_WINDOWS) return getDataPaths(includeSystemFolders); #elif defined(OS_MACOS) return getDataPaths(includeSystemFolders); #elif defined(OS_LINUX) || defined(OS_WEB) std::ignore = includeSystemFolders; return {xdg::ConfigHomeDir() / "imhex"}; #endif } static std::vector appendPath(std::vector paths, std::fs::path folder) { folder.make_preferred(); for (auto &path : paths) path = path / folder; return paths; } static std::vector getPluginPaths() { // If running from an AppImage, only allow loaded plugins from inside it #if defined(OS_LINUX) if(const char* appdir = std::getenv("APPDIR")) { // check for AppImage environment return {std::string(appdir) + "/usr/lib/imhex"}; } #endif std::vector paths = getDataPaths(true); // Add the system plugin directory to the path if one was provided at compile time #if defined(OS_LINUX) && defined(SYSTEM_PLUGINS_LOCATION) paths.emplace_back(SYSTEM_PLUGINS_LOCATION); #endif return paths; } namespace impl { std::vector DefaultPath::read() const { auto result = this->all(); std::erase_if(result, [](const auto &entryPath) { return !wolv::io::fs::isDirectory(entryPath); }); return result; } std::vector DefaultPath::write() const { auto result = this->read(); std::erase_if(result, [](const auto &entryPath) { return !hex::fs::isPathWritable(entryPath); }); return result; } std::vector ConfigPath::all() const { return appendPath(getConfigPaths(false), m_postfix); } std::vector DataPath::all() const { return appendPath(getDataPaths(true), m_postfix); } std::vector DataPath::write() const { auto result = appendPath(getDataPaths(false), m_postfix); std::erase_if(result, [](const auto &entryPath) { return !hex::fs::isPathWritable(entryPath); }); return result; } std::vector PluginPath::all() const { return appendPath(getPluginPaths(), m_postfix); } } } ================================================ FILE: lib/libimhex/source/helpers/encoding_file.cpp ================================================ #include #include #include #include #include namespace hex { EncodingFile::EncodingFile() : m_mapping(std::make_unique, std::string>>>()) { } EncodingFile::EncodingFile(const hex::EncodingFile &other) { m_mapping = std::make_unique, std::string>>>(*other.m_mapping); m_tableContent = other.m_tableContent; m_longestSequence = other.m_longestSequence; m_shortestSequence = other.m_shortestSequence; m_valid = other.m_valid; m_name = other.m_name; } EncodingFile::EncodingFile(EncodingFile &&other) noexcept { m_mapping = std::move(other.m_mapping); m_tableContent = std::move(other.m_tableContent); m_longestSequence = other.m_longestSequence; m_shortestSequence = other.m_shortestSequence; m_valid = other.m_valid; m_name = std::move(other.m_name); } EncodingFile::EncodingFile(Type type, const std::fs::path &path) : EncodingFile() { auto file = wolv::io::File(path, wolv::io::File::Mode::Read); switch (type) { case Type::Thingy: parse(file.readString()); break; default: return; } { m_name = path.stem().string(); m_name = wolv::util::replaceStrings(m_name, "_", " "); if (!m_name.empty()) m_name[0] = std::toupper(m_name[0]); } m_valid = true; } EncodingFile::EncodingFile(Type type, const std::string &content) : EncodingFile() { switch (type) { case Type::Thingy: parse(content); break; default: return; } m_name = "Unknown"; m_valid = true; } EncodingFile &EncodingFile::operator=(const hex::EncodingFile &other) { if(this == &other) { return *this; } m_mapping = std::make_unique, std::string>>>(*other.m_mapping); m_tableContent = other.m_tableContent; m_longestSequence = other.m_longestSequence; m_shortestSequence = other.m_shortestSequence; m_valid = other.m_valid; m_name = other.m_name; return *this; } EncodingFile &EncodingFile::operator=(EncodingFile &&other) noexcept { m_mapping = std::move(other.m_mapping); m_tableContent = std::move(other.m_tableContent); m_longestSequence = other.m_longestSequence; m_shortestSequence = other.m_shortestSequence; m_valid = other.m_valid; m_name = std::move(other.m_name); return *this; } std::pair EncodingFile::getEncodingFor(std::span buffer) const { for (const auto &[size, mapping] : std::ranges::reverse_view(*m_mapping)) { if (size > buffer.size()) continue; std::vector key(buffer.begin(), buffer.begin() + size); if (mapping.contains(key)) return { mapping.at(key), size }; } return { ".", 1 }; } u64 EncodingFile::getEncodingLengthFor(std::span buffer) const { for (const auto& [size, mapping] : std::ranges::reverse_view(*m_mapping)) { if (size > buffer.size()) continue; std::vector key(buffer.begin(), buffer.begin() + size); if (mapping.contains(key)) return size; } return 1; } std::string EncodingFile::decodeAll(std::span buffer) const { std::string result; while (!buffer.empty()) { const auto [character, size] = getEncodingFor(buffer); result += character; buffer = buffer.subspan(size); } return result; } void EncodingFile::parse(const std::string &content) { m_tableContent = content; for (const auto &line : wolv::util::splitString(m_tableContent, "\n")) { std::string from, to; { auto delimiterPos = line.find('='); if (delimiterPos >= line.length()) continue; from = line.substr(0, delimiterPos); to = line.substr(delimiterPos + 1); if (from.empty()) continue; } auto fromBytes = hex::parseByteString(from); if (fromBytes.empty()) continue; if (to.length() > 1) to = wolv::util::trim(to); if (to.empty()) to = " "; if (!m_mapping->contains(fromBytes.size())) m_mapping->insert({ fromBytes.size(), {} }); u64 keySize = fromBytes.size(); (*m_mapping)[keySize].insert({ std::move(fromBytes), to }); m_longestSequence = std::max(m_longestSequence, keySize); m_shortestSequence = std::min(m_shortestSequence, keySize); } } } ================================================ FILE: lib/libimhex/source/helpers/fs.cpp ================================================ #include #include #include #include #include #if defined(OS_WINDOWS) #include #include #include #elif defined(OS_LINUX) || defined(OS_WEB) #include # if defined(OS_FREEBSD) #include # endif #endif #if defined(OS_WEB) #include #else #include #include #endif #include #include #include #include #include #include namespace hex::fs { static AutoReset> s_fileBrowserErrorCallback; void setFileBrowserErrorCallback(const std::function &callback) { s_fileBrowserErrorCallback = callback; } // With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp void openFileExternal(std::fs::path filePath) { filePath.make_preferred(); // Make sure the file exists before trying to open it if (!wolv::io::fs::exists(filePath)) { return; } #if defined(OS_WINDOWS) std::ignore = ShellExecuteW(nullptr, L"open", filePath.c_str(), nullptr, nullptr, SW_SHOWNORMAL); #elif defined(OS_MACOS) std::ignore = system( fmt::format("open {}", wolv::util::toUTF8String(filePath)).c_str() ); #elif defined(OS_LINUX) executeCmd({"xdg-open", wolv::util::toUTF8String(filePath)}); #endif } void openFolderExternal(std::fs::path dirPath) { dirPath.make_preferred(); // Make sure the folder exists before trying to open it if (!wolv::io::fs::exists(dirPath)) { return; } #if defined(OS_WINDOWS) auto args = fmt::format(L"\"{}\"", dirPath.c_str()); ShellExecuteW(nullptr, L"open", L"explorer.exe", args.c_str(), nullptr, SW_SHOWNORMAL); #elif defined(OS_MACOS) std::ignore = system( fmt::format("open {}", wolv::util::toUTF8String(dirPath)).c_str() ); #elif defined(OS_LINUX) executeCmd({"xdg-open", wolv::util::toUTF8String(dirPath)}); #endif } void openFolderWithSelectionExternal(std::fs::path selectedFilePath) { selectedFilePath.make_preferred(); // Make sure the file exists before trying to open it if (!wolv::io::fs::exists(selectedFilePath)) { return; } #if defined(OS_WINDOWS) auto args = fmt::format(L"/select,\"{}\"", selectedFilePath.c_str()); ShellExecuteW(nullptr, L"open", L"explorer.exe", args.c_str(), nullptr, SW_SHOWNORMAL); #elif defined(OS_MACOS) std::ignore = system( fmt::format( R"(osascript -e 'tell application "Finder" to reveal POSIX file "{}"')", wolv::util::toUTF8String(selectedFilePath) ).c_str() ); system(R"(osascript -e 'tell application "Finder" to activate')"); #elif defined(OS_LINUX) // Fallback to only opening the folder for now // TODO actually select the file executeCmd({"xdg-open", wolv::util::toUTF8String(selectedFilePath.parent_path())}); #endif } #if defined(OS_WEB) std::function currentCallback; EMSCRIPTEN_KEEPALIVE extern "C" void fileBrowserCallback(char* path) { currentCallback(path); } EM_JS(int, callJs_saveFile, (const char *rawFilename), { let filename = UTF8ToString(rawFilename) || "file.bin"; FS.createPath("/", "savedFiles"); if (FS.analyzePath(filename).exists) { FS.unlink(filename); } // Call callback that will write the file Module._fileBrowserCallback(stringToNewUTF8("/savedFiles/" + filename)); let data; try { data = FS.readFile("/savedFiles/" + filename); } catch (e) { console.log(e); return; } const reader = Object.assign(new FileReader(), { onload: () => { // Show popup to user to download let saver = document.createElement('a'); saver.href = reader.result; saver.download = filename; saver.style = "display: none"; saver.click(); }, onerror: () => { throw new Error(reader.error); }, }); reader.readAsDataURL(new File([data], "", { type: "application/octet-stream" })); }); EM_JS(int, callJs_openFile, (bool multiple), { let selector = document.createElement("input"); selector.type = "file"; selector.style = "display: none"; if (multiple) { selector.multiple = true; } selector.onchange = () => { if (selector.files.length == 0) return; FS.createPath("/", "openedFiles"); for (let file of selector.files) { const fr = new FileReader(); fr.onload = () => { let folder = "/openedFiles/"+Math.random().toString(36).substring(2)+"/"; FS.createPath("/", folder); if (FS.analyzePath(folder+file.name).exists) { console.log(`Error: ${folder+file.name} already exist`); } else { FS.createDataFile(folder, file.name, fr.result, true, true); Module._fileBrowserCallback(stringToNewUTF8(folder+file.name)); } }; fr.readAsBinaryString(file); } }; selector.click(); }); bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath, bool multiple) { switch (mode) { case DialogMode::Open: { currentCallback = callback; callJs_openFile(multiple); break; } case DialogMode::Save: { currentCallback = callback; std::fs::path path; if (!defaultPath.empty()) path = std::fs::path(defaultPath).filename(); else if (!validExtensions.empty()) path = "file." + validExtensions[0].spec; std::fs::create_directory("/savedFiles"); callJs_saveFile(path.filename().string().c_str()); break; } case DialogMode::Folder: { throw std::logic_error("Selecting a folder is not implemented"); return false; } default: std::unreachable(); } return true; } #else bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath, bool multiple) { // Turn the content of the ItemFilter objects into something NFD understands std::vector validExtensionsNfd; validExtensionsNfd.reserve(validExtensions.size()); for (const auto &extension : validExtensions) { validExtensionsNfd.emplace_back(nfdfilteritem_t{ extension.name.c_str(), extension.spec.c_str() }); } // Clear errors from previous runs NFD::ClearError(); // Try to initialize NFD if (NFD::Init() != NFD_OKAY) { // Handle errors if initialization failed log::error("NFD init returned an error: {}", NFD::GetError()); if (*s_fileBrowserErrorCallback != nullptr) { const auto error = NFD::GetError(); (*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details"); } return false; } NFD::UniquePathU8 outPath; NFD::UniquePathSet outPaths; nfdresult_t result = NFD_ERROR; // Open the correct file dialog based on the mode switch (mode) { case DialogMode::Open: if (multiple) result = NFD::OpenDialogMultiple(outPaths, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str()); else result = NFD::OpenDialog(outPath, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str()); break; case DialogMode::Save: result = NFD::SaveDialog(outPath, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str()); break; case DialogMode::Folder: result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str()); break; } if (result == NFD_OKAY){ // Handle the path if the dialog was opened in single mode if (outPath != nullptr) { // Call the provided callback with the path callback(outPath.get()); } // Handle multiple paths if the dialog was opened in multiple mode if (outPaths != nullptr) { nfdpathsetsize_t numPaths = 0; if (NFD::PathSet::Count(outPaths, numPaths) == NFD_OKAY) { // Loop over all returned paths and call the callback with each of them for (size_t i = 0; i < numPaths; i++) { NFD::UniquePathSetPath path; if (NFD::PathSet::GetPath(outPaths, i, path) == NFD_OKAY) callback(path.get()); } } } } else if (result == NFD_ERROR) { // Handle errors that occurred during the file dialog call log::error("Requested file dialog returned an error: {}", NFD::GetError()); if (*s_fileBrowserErrorCallback != nullptr) { const auto error = NFD::GetError(); (*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details"); } } NFD::Quit(); return result == NFD_OKAY; } #endif bool isPathWritable(const std::fs::path &path) { constexpr static auto TestFileName = "__imhex__tmp__"; // Try to open the __imhex__tmp__ file in the given path // If one does exist already, try to delete it { wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Read); if (file.isValid()) { if (!file.remove()) return false; } } // Try to create a new file in the given path // If that fails, or the file cannot be deleted anymore afterward; the path is not writable wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create); const bool result = file.isValid(); if (!file.remove()) return false; return result; } std::fs::path toShortPath(const std::fs::path &path) { #if defined(OS_WINDOWS) // Get the size of the short path size_t size = GetShortPathNameW(path.c_str(), nullptr, 0); if (size == 0) return path; // Get the short path std::wstring newPath(size, 0x00); GetShortPathNameW(path.c_str(), newPath.data(), newPath.size()); newPath.pop_back(); return newPath; #else // Other supported platforms don't have short paths return path; #endif } } ================================================ FILE: lib/libimhex/source/helpers/http_requests.cpp ================================================ #include #include #include namespace hex { size_t HttpRequest::writeToVector(void *contents, size_t size, size_t nmemb, void *userdata) { auto &response = *static_cast*>(userdata); auto startSize = response.size(); response.resize(startSize + size * nmemb); std::memcpy(response.data() + startSize, contents, size * nmemb); return size * nmemb; } size_t HttpRequest::writeToFile(void *contents, size_t size, size_t nmemb, void *userdata) { auto &file = *static_cast(userdata); file.writeBuffer(static_cast(contents), size * nmemb); return size * nmemb; } std::string HttpRequest::urlEncode(const std::string &input) { std::string result; for (char c : input){ if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') result += c; else result += fmt::format("%02X", c); } return result; } std::string HttpRequest::urlDecode(const std::string &input) { std::string result; for (u32 i = 0; i < input.size(); i++){ if (input[i] != '%'){ if (input[i] == '+') result += ' '; else result += input[i]; } else { const auto hex = crypt::decode16(input.substr(i + 1, 2)); if (hex.empty()) return ""; result += char(hex[0]); i += 2; } } return result; } } ================================================ FILE: lib/libimhex/source/helpers/http_requests_emscripten.cpp ================================================ #if defined(OS_WEB) #include namespace hex { HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) { emscripten_fetch_attr_init(&m_attr); } HttpRequest::HttpRequest(HttpRequest &&other) noexcept { m_attr = other.m_attr; m_method = std::move(other.m_method); m_url = std::move(other.m_url); m_headers = std::move(other.m_headers); m_body = std::move(other.m_body); } HttpRequest& HttpRequest::operator=(HttpRequest &&other) noexcept { m_attr = other.m_attr; m_method = std::move(other.m_method); m_url = std::move(other.m_url); m_headers = std::move(other.m_headers); m_body = std::move(other.m_body); return *this; } HttpRequest::~HttpRequest() { } void HttpRequest::setDefaultConfig() { } std::future>> HttpRequest::downloadFile() { return std::async(std::launch::async, [this] { std::vector response; return this->executeImpl>(response); }); } void HttpRequest::setProxyUrl(std::string proxy) { std::ignore = proxy; } void HttpRequest::setProxyState(bool state) { std::ignore = state; } void HttpRequest::checkProxyErrors() { } } #endif ================================================ FILE: lib/libimhex/source/helpers/http_requests_native.cpp ================================================ #if !defined(OS_WEB) #include #include namespace hex { namespace { std::string s_proxyUrl; bool s_proxyState; } int progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow) { auto &request = *static_cast(contents); if (dlTotal > 0) request.setProgress(float(dlNow) / dlTotal); else if (ulTotal > 0) request.setProgress(float(ulNow) / ulTotal); else request.setProgress(0.0F); return request.isCanceled() ? CURLE_ABORTED_BY_CALLBACK : CURLE_OK; } HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) { AT_FIRST_TIME { curl_global_init(CURL_GLOBAL_ALL); }; AT_FINAL_CLEANUP { curl_global_cleanup(); }; m_curl = curl_easy_init(); } HttpRequest::~HttpRequest() { curl_easy_cleanup(m_curl); } HttpRequest::HttpRequest(HttpRequest &&other) noexcept { m_curl = other.m_curl; other.m_curl = nullptr; m_method = std::move(other.m_method); m_url = std::move(other.m_url); m_headers = std::move(other.m_headers); m_body = std::move(other.m_body); } HttpRequest& HttpRequest::operator=(HttpRequest &&other) noexcept { m_curl = other.m_curl; other.m_curl = nullptr; m_method = std::move(other.m_method); m_url = std::move(other.m_url); m_headers = std::move(other.m_headers); m_body = std::move(other.m_body); m_timeout = other.m_timeout; return *this; } void HttpRequest::setDefaultConfig() { curl_easy_setopt(m_curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); curl_easy_setopt(m_curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(m_curl, CURLOPT_USERAGENT, "ImHex/1.0"); curl_easy_setopt(m_curl, CURLOPT_DEFAULT_PROTOCOL, "https"); curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(m_curl, CURLOPT_TIMEOUT_MS, 0L); curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT_MS, m_timeout); curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(m_curl, CURLOPT_XFERINFODATA, this); curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, progressCallback); if (s_proxyState) curl_easy_setopt(m_curl, CURLOPT_PROXY, s_proxyUrl.c_str()); } std::future>> HttpRequest::downloadFile() { return std::async(std::launch::async, [this] { std::vector response; curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector); curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response); return this->executeImpl>(response); }); } void HttpRequest::setProxyUrl(std::string proxy) { s_proxyUrl = std::move(proxy); } void HttpRequest::setProxyState(bool enabled) { s_proxyState = enabled; } void HttpRequest::checkProxyErrors() { if (s_proxyState && !s_proxyUrl.empty()){ log::info("A custom proxy '{0}' is in use. Is it working correctly?", s_proxyUrl); } } namespace impl { void setWriteFunctions(CURL *curl, wolv::io::File &file) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpRequest::writeToFile); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file); } void setWriteFunctions(CURL *curl, std::vector &data) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpRequest::writeToVector); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); } void setupFileUpload(CURL *curl, const wolv::io::File &file, const std::string &fileName, const std::string &mimeName) { curl_mime *mime = curl_mime_init(curl); curl_mimepart *part = curl_mime_addpart(mime); curl_mime_data_cb(part, file.getSize(), [](char *buffer, size_t size, size_t nitems, void *arg) -> size_t { auto handle = static_cast(arg); return fread(buffer, size, nitems, handle); }, [](void *arg, curl_off_t offset, int origin) -> int { auto handle = static_cast(arg); if (fseek(handle, offset, origin) != 0) return CURL_SEEKFUNC_CANTSEEK; else return CURL_SEEKFUNC_OK; }, [](void *arg) { auto handle = static_cast(arg); fclose(handle); }, file.getHandle()); curl_mime_filename(part, fileName.c_str()); curl_mime_name(part, mimeName.c_str()); curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); } void setupFileUpload(CURL *curl, const std::vector &data, const std::fs::path &fileName, const std::string &mimeName) { curl_mime *mime = curl_mime_init(curl); curl_mimepart *part = curl_mime_addpart(mime); curl_mime_data(part, reinterpret_cast(data.data()), data.size()); auto fileNameStr = wolv::util::toUTF8String(fileName.filename()); curl_mime_filename(part, fileNameStr.c_str()); curl_mime_name(part, mimeName.c_str()); curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); } int executeCurl(CURL *curl, const std::string &url, const std::string &method, const std::string &body, std::map &headers) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str()); if (!body.empty()) { curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); } curl_slist *headersList = nullptr; headersList = curl_slist_append(headersList, "Cache-Control: no-cache"); ON_SCOPE_EXIT { curl_slist_free_all(headersList); }; for (auto &[key, value] : headers) { std::string header = fmt::format("{}: {}", key, value); headersList = curl_slist_append(headersList, header.c_str()); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headersList); auto result = curl_easy_perform(curl); if (result != CURLE_OK){ return result; } return 0; } long getStatusCode(CURL *curl) { long statusCode = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode); return statusCode; } std::string getStatusText(int result) { return curl_easy_strerror(CURLcode(result)); } } } #endif ================================================ FILE: lib/libimhex/source/helpers/imgui_hooks.cpp ================================================ #include #include #include #include #if !defined(IMGUI_TEST_ENGINE) void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) { hex::TutorialManager::postElementRendered(id, bb); } void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {} void ImGuiTestEngineHook_Log(ImGuiContext*, const char*, ...) {} const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext*, ImGuiID) { return nullptr; } #endif ================================================ FILE: lib/libimhex/source/helpers/keys.cpp ================================================ #include #include enum Keys scanCodeToKey(int scanCode) { switch (scanCode) { case GLFW_KEY_SPACE: return Keys::Space; case GLFW_KEY_APOSTROPHE: return Keys::Apostrophe; case GLFW_KEY_COMMA: return Keys::Comma; case GLFW_KEY_MINUS: return Keys::Minus; case GLFW_KEY_PERIOD: return Keys::Period; case GLFW_KEY_SLASH: return Keys::Slash; case GLFW_KEY_0: return Keys::Num0; case GLFW_KEY_1: return Keys::Num1; case GLFW_KEY_2: return Keys::Num2; case GLFW_KEY_3: return Keys::Num3; case GLFW_KEY_4: return Keys::Num4; case GLFW_KEY_5: return Keys::Num5; case GLFW_KEY_6: return Keys::Num6; case GLFW_KEY_7: return Keys::Num7; case GLFW_KEY_8: return Keys::Num8; case GLFW_KEY_9: return Keys::Num9; case GLFW_KEY_SEMICOLON: return Keys::Semicolon; case GLFW_KEY_EQUAL: return Keys::Equals; case GLFW_KEY_A: return Keys::A; case GLFW_KEY_B: return Keys::B; case GLFW_KEY_C: return Keys::C; case GLFW_KEY_D: return Keys::D; case GLFW_KEY_E: return Keys::E; case GLFW_KEY_F: return Keys::F; case GLFW_KEY_G: return Keys::G; case GLFW_KEY_H: return Keys::H; case GLFW_KEY_I: return Keys::I; case GLFW_KEY_J: return Keys::J; case GLFW_KEY_K: return Keys::K; case GLFW_KEY_L: return Keys::L; case GLFW_KEY_M: return Keys::M; case GLFW_KEY_N: return Keys::N; case GLFW_KEY_O: return Keys::O; case GLFW_KEY_P: return Keys::P; case GLFW_KEY_Q: return Keys::Q; case GLFW_KEY_R: return Keys::R; case GLFW_KEY_S: return Keys::S; case GLFW_KEY_T: return Keys::T; case GLFW_KEY_U: return Keys::U; case GLFW_KEY_V: return Keys::V; case GLFW_KEY_W: return Keys::W; case GLFW_KEY_X: return Keys::X; case GLFW_KEY_Y: return Keys::Y; case GLFW_KEY_Z: return Keys::Z; case GLFW_KEY_LEFT_BRACKET: return Keys::LeftBracket; case GLFW_KEY_BACKSLASH: return Keys::Backslash; case GLFW_KEY_RIGHT_BRACKET: return Keys::RightBracket; case GLFW_KEY_GRAVE_ACCENT: return Keys::GraveAccent; case GLFW_KEY_WORLD_1: return Keys::World1; case GLFW_KEY_WORLD_2: return Keys::World2; case GLFW_KEY_ESCAPE: return Keys::Escape; case GLFW_KEY_ENTER: return Keys::Enter; case GLFW_KEY_TAB: return Keys::Tab; case GLFW_KEY_BACKSPACE: return Keys::Backspace; case GLFW_KEY_INSERT: return Keys::Insert; case GLFW_KEY_DELETE: return Keys::Delete; case GLFW_KEY_RIGHT: return Keys::Right; case GLFW_KEY_LEFT: return Keys::Left; case GLFW_KEY_DOWN: return Keys::Down; case GLFW_KEY_UP: return Keys::Up; case GLFW_KEY_PAGE_UP: return Keys::PageUp; case GLFW_KEY_PAGE_DOWN: return Keys::PageDown; case GLFW_KEY_HOME: return Keys::Home; case GLFW_KEY_END: return Keys::End; case GLFW_KEY_CAPS_LOCK: return Keys::CapsLock; case GLFW_KEY_SCROLL_LOCK: return Keys::ScrollLock; case GLFW_KEY_NUM_LOCK: return Keys::NumLock; case GLFW_KEY_PRINT_SCREEN: return Keys::PrintScreen; case GLFW_KEY_PAUSE: return Keys::Pause; case GLFW_KEY_F1: return Keys::F1; case GLFW_KEY_F2: return Keys::F2; case GLFW_KEY_F3: return Keys::F3; case GLFW_KEY_F4: return Keys::F4; case GLFW_KEY_F5: return Keys::F5; case GLFW_KEY_F6: return Keys::F6; case GLFW_KEY_F7: return Keys::F7; case GLFW_KEY_F8: return Keys::F8; case GLFW_KEY_F9: return Keys::F9; case GLFW_KEY_F10: return Keys::F10; case GLFW_KEY_F11: return Keys::F11; case GLFW_KEY_F12: return Keys::F12; case GLFW_KEY_F13: return Keys::F13; case GLFW_KEY_F14: return Keys::F14; case GLFW_KEY_F15: return Keys::F15; case GLFW_KEY_F16: return Keys::F16; case GLFW_KEY_F17: return Keys::F17; case GLFW_KEY_F18: return Keys::F18; case GLFW_KEY_F19: return Keys::F19; case GLFW_KEY_F20: return Keys::F20; case GLFW_KEY_F21: return Keys::F21; case GLFW_KEY_F22: return Keys::F22; case GLFW_KEY_F23: return Keys::F23; case GLFW_KEY_F24: return Keys::F24; case GLFW_KEY_F25: return Keys::F25; case GLFW_KEY_KP_0: return Keys::KeyPad0; case GLFW_KEY_KP_1: return Keys::KeyPad1; case GLFW_KEY_KP_2: return Keys::KeyPad2; case GLFW_KEY_KP_3: return Keys::KeyPad3; case GLFW_KEY_KP_4: return Keys::KeyPad4; case GLFW_KEY_KP_5: return Keys::KeyPad5; case GLFW_KEY_KP_6: return Keys::KeyPad6; case GLFW_KEY_KP_7: return Keys::KeyPad7; case GLFW_KEY_KP_8: return Keys::KeyPad8; case GLFW_KEY_KP_9: return Keys::KeyPad9; case GLFW_KEY_KP_DECIMAL: return Keys::KeyPadDecimal; case GLFW_KEY_KP_DIVIDE: return Keys::KeyPadDivide; case GLFW_KEY_KP_MULTIPLY: return Keys::KeyPadMultiply; case GLFW_KEY_KP_SUBTRACT: return Keys::KeyPadSubtract; case GLFW_KEY_KP_ADD: return Keys::KeyPadAdd; case GLFW_KEY_KP_ENTER: return Keys::KeyPadEnter; case GLFW_KEY_KP_EQUAL: return Keys::KeyPadEqual; case GLFW_KEY_MENU: return Keys::Menu; default: return Keys(scanCode); } } int keyToScanCode(enum Keys key) { switch (key) { case Keys::Space: return GLFW_KEY_SPACE; case Keys::Apostrophe: return GLFW_KEY_APOSTROPHE; case Keys::Comma: return GLFW_KEY_COMMA; case Keys::Minus: return GLFW_KEY_MINUS; case Keys::Period: return GLFW_KEY_PERIOD; case Keys::Slash: return GLFW_KEY_SLASH; case Keys::Num0: return GLFW_KEY_0; case Keys::Num1: return GLFW_KEY_1; case Keys::Num2: return GLFW_KEY_2; case Keys::Num3: return GLFW_KEY_3; case Keys::Num4: return GLFW_KEY_4; case Keys::Num5: return GLFW_KEY_5; case Keys::Num6: return GLFW_KEY_6; case Keys::Num7: return GLFW_KEY_7; case Keys::Num8: return GLFW_KEY_8; case Keys::Num9: return GLFW_KEY_9; case Keys::Semicolon: return GLFW_KEY_SEMICOLON; case Keys::Equals: return GLFW_KEY_EQUAL; case Keys::A: return GLFW_KEY_A; case Keys::B: return GLFW_KEY_B; case Keys::C: return GLFW_KEY_C; case Keys::D: return GLFW_KEY_D; case Keys::E: return GLFW_KEY_E; case Keys::F: return GLFW_KEY_F; case Keys::G: return GLFW_KEY_G; case Keys::H: return GLFW_KEY_H; case Keys::I: return GLFW_KEY_I; case Keys::J: return GLFW_KEY_J; case Keys::K: return GLFW_KEY_K; case Keys::L: return GLFW_KEY_L; case Keys::M: return GLFW_KEY_M; case Keys::N: return GLFW_KEY_N; case Keys::O: return GLFW_KEY_O; case Keys::P: return GLFW_KEY_P; case Keys::Q: return GLFW_KEY_Q; case Keys::R: return GLFW_KEY_R; case Keys::S: return GLFW_KEY_S; case Keys::T: return GLFW_KEY_T; case Keys::U: return GLFW_KEY_U; case Keys::V: return GLFW_KEY_V; case Keys::W: return GLFW_KEY_W; case Keys::X: return GLFW_KEY_X; case Keys::Y: return GLFW_KEY_Y; case Keys::Z: return GLFW_KEY_Z; case Keys::LeftBracket: return GLFW_KEY_LEFT_BRACKET; case Keys::Backslash: return GLFW_KEY_BACKSLASH; case Keys::RightBracket: return GLFW_KEY_RIGHT_BRACKET; case Keys::GraveAccent: return GLFW_KEY_GRAVE_ACCENT; case Keys::World1: return GLFW_KEY_WORLD_1; case Keys::World2: return GLFW_KEY_WORLD_2; case Keys::Escape: return GLFW_KEY_ESCAPE; case Keys::Enter: return GLFW_KEY_ENTER; case Keys::Tab: return GLFW_KEY_TAB; case Keys::Backspace: return GLFW_KEY_BACKSPACE; case Keys::Insert: return GLFW_KEY_INSERT; case Keys::Delete: return GLFW_KEY_DELETE; case Keys::Right: return GLFW_KEY_RIGHT; case Keys::Left: return GLFW_KEY_LEFT; case Keys::Down: return GLFW_KEY_DOWN; case Keys::Up: return GLFW_KEY_UP; case Keys::PageUp: return GLFW_KEY_PAGE_UP; case Keys::PageDown: return GLFW_KEY_PAGE_DOWN; case Keys::Home: return GLFW_KEY_HOME; case Keys::End: return GLFW_KEY_END; case Keys::CapsLock: return GLFW_KEY_CAPS_LOCK; case Keys::ScrollLock: return GLFW_KEY_SCROLL_LOCK; case Keys::NumLock: return GLFW_KEY_NUM_LOCK; case Keys::PrintScreen: return GLFW_KEY_PRINT_SCREEN; case Keys::Pause: return GLFW_KEY_PAUSE; case Keys::F1: return GLFW_KEY_F1; case Keys::F2: return GLFW_KEY_F2; case Keys::F3: return GLFW_KEY_F3; case Keys::F4: return GLFW_KEY_F4; case Keys::F5: return GLFW_KEY_F5; case Keys::F6: return GLFW_KEY_F6; case Keys::F7: return GLFW_KEY_F7; case Keys::F8: return GLFW_KEY_F8; case Keys::F9: return GLFW_KEY_F9; case Keys::F10: return GLFW_KEY_F10; case Keys::F11: return GLFW_KEY_F11; case Keys::F12: return GLFW_KEY_F12; case Keys::F13: return GLFW_KEY_F13; case Keys::F14: return GLFW_KEY_F14; case Keys::F15: return GLFW_KEY_F15; case Keys::F16: return GLFW_KEY_F16; case Keys::F17: return GLFW_KEY_F17; case Keys::F18: return GLFW_KEY_F18; case Keys::F19: return GLFW_KEY_F19; case Keys::F20: return GLFW_KEY_F20; case Keys::F21: return GLFW_KEY_F21; case Keys::F22: return GLFW_KEY_F22; case Keys::F23: return GLFW_KEY_F23; case Keys::F24: return GLFW_KEY_F24; case Keys::F25: return GLFW_KEY_F25; case Keys::KeyPad0: return GLFW_KEY_KP_0; case Keys::KeyPad1: return GLFW_KEY_KP_1; case Keys::KeyPad2: return GLFW_KEY_KP_2; case Keys::KeyPad3: return GLFW_KEY_KP_3; case Keys::KeyPad4: return GLFW_KEY_KP_4; case Keys::KeyPad5: return GLFW_KEY_KP_5; case Keys::KeyPad6: return GLFW_KEY_KP_6; case Keys::KeyPad7: return GLFW_KEY_KP_7; case Keys::KeyPad8: return GLFW_KEY_KP_8; case Keys::KeyPad9: return GLFW_KEY_KP_9; case Keys::KeyPadDecimal: return GLFW_KEY_KP_DECIMAL; case Keys::KeyPadDivide: return GLFW_KEY_KP_DIVIDE; case Keys::KeyPadMultiply: return GLFW_KEY_KP_MULTIPLY; case Keys::KeyPadSubtract: return GLFW_KEY_KP_SUBTRACT; case Keys::KeyPadAdd: return GLFW_KEY_KP_ADD; case Keys::KeyPadEnter: return GLFW_KEY_KP_ENTER; case Keys::KeyPadEqual: return GLFW_KEY_KP_EQUAL; case Keys::Menu: return GLFW_KEY_MENU; default: return int(key); } } ================================================ FILE: lib/libimhex/source/helpers/logger.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #if defined(OS_WINDOWS) #include #endif namespace hex::log { namespace { wolv::io::File s_loggerFile; bool s_colorOutputEnabled = false; std::recursive_mutex s_loggerMutex; bool s_loggingSuspended = false; bool s_debugLoggingEnabled = false; } void suspendLogging() { s_loggingSuspended = true; } void resumeLogging() { s_loggingSuspended = false; } void enableDebugLogging() { s_debugLoggingEnabled = true; } namespace impl { void lockLoggerMutex() { s_loggerMutex.lock(); } void unlockLoggerMutex() { s_loggerMutex.unlock(); } bool isLoggingSuspended() { return s_loggingSuspended; } bool isDebugLoggingEnabled() { #if defined(DEBUG) return true; #else return s_debugLoggingEnabled; #endif } FILE *getDestination() { if (s_loggerFile.isValid()) return s_loggerFile.getHandle(); else return stdout; } wolv::io::File& getFile() { return s_loggerFile; } bool isRedirected() { return s_loggerFile.isValid(); } void redirectToFile() { if (s_loggerFile.isValid()) return; for (const auto &path : paths::Logs.all()) { wolv::io::fs::createDirectories(path); time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); s_loggerFile = wolv::io::File(path / fmt::format("{0:%Y%m%d_%H%M%S}.log", *std::localtime(&time)), wolv::io::File::Mode::Create); s_loggerFile.disableBuffering(); if (s_loggerFile.isValid()) { s_colorOutputEnabled = false; break; } } } void enableColorPrinting() { s_colorOutputEnabled = true; #if defined(OS_WINDOWS) auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole != INVALID_HANDLE_VALUE) { DWORD mode = 0; if (::GetConsoleMode(hConsole, &mode) == TRUE) { mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT; ::SetConsoleMode(hConsole, mode); } } #endif } static AutoReset> s_logEntries; const std::vector& getLogEntries() { return s_logEntries; } void addLogEntry(std::string_view project, std::string_view level, std::string message) { s_logEntries->emplace_back( project, level, std::move(message) ); } void printPrefix(FILE *dest, fmt::text_style ts, std::string_view level, std::string_view projectName) { const auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); const auto now = *std::localtime(&time); auto threadName = TaskManager::getCurrentThreadName(); if (threadName.empty()) [[unlikely]] { threadName = "???"; } constexpr static auto MaxTagLength = 25; const auto totalLength = std::min(static_cast(MaxTagLength), projectName.length() + (threadName.empty() ? 0 : 3 + threadName.length())); const auto remainingSpace = MaxTagLength - projectName.length() - 3; fmt::print(dest, "[{0:%H:%M:%S}] {1} [{2} | {3}] {4: <{5}} ", now, s_colorOutputEnabled ? fmt::format(ts, "{}", level) : level, projectName.substr(0, std::min(projectName.length(), static_cast(MaxTagLength))), threadName.substr(0, remainingSpace), "", MaxTagLength - totalLength ); } namespace color { fmt::color debug() { return fmt::color::medium_sea_green; } fmt::color info() { return fmt::color::steel_blue; } fmt::color warn() { return fmt::color::orange; } fmt::color error() { return fmt::color::indian_red; } fmt::color fatal() { return fmt::color::medium_purple; } } } } ================================================ FILE: lib/libimhex/source/helpers/macos_menu.m ================================================ #import #import #import struct KeyEquivalent { bool valid; bool ctrl, opt, cmd, shift; int key; }; const static int MenuBegin = 1; static NSInteger s_currTag = MenuBegin; static NSInteger s_selectedTag = -1; @interface MenuItemHandler : NSObject -(void) OnClick: (id) sender; @end @implementation MenuItemHandler -(void) OnClick: (id) sender { NSMenuItem* menu_item = sender; s_selectedTag = menu_item.tag; } @end static NSMenu* s_menuStack[1024]; static int s_menuStackSize = 0; static MenuItemHandler* s_menuItemHandler; static bool s_constructingMenu = false; static bool s_resetNeeded = true; void macosMenuBarInit(void) { s_menuStackSize = 0; s_menuStack[0] = NSApp.mainMenu; s_menuStackSize += 1; s_menuItemHandler = [[MenuItemHandler alloc] init]; } void macosClearMenu(void) { // Remove all items except the Application menu while (s_menuStack[0].itemArray.count > 2) { [s_menuStack[0] removeItemAtIndex:1]; } s_currTag = MenuBegin; } bool macosBeginMainMenuBar(void) { if (s_resetNeeded) { macosClearMenu(); s_resetNeeded = false; } return true; } void macosEndMainMenuBar(void) { s_constructingMenu = false; } static NSMutableArray* g_RegisteredIconFontDescriptors = nil; void macosRegisterFont(const unsigned char* fontBytes, size_t fontLength) { if (!fontBytes || fontLength == 0 || fontLength > 100 * 1024 * 1024) { // Max 100MB sanity check NSLog(@"Invalid font data: bytes=%p, length=%zu", fontBytes, fontLength); return; } // Initialize array on first use if (!g_RegisteredIconFontDescriptors) { g_RegisteredIconFontDescriptors = [[NSMutableArray alloc] init]; } // Create NSData - this will copy the bytes NSData *fontData = [NSData dataWithBytes:fontBytes length:fontLength]; if (!fontData) { NSLog(@"Failed to create NSData from font bytes"); return; } CFErrorRef error = NULL; CFArrayRef descriptors = CTFontManagerCreateFontDescriptorsFromData((__bridge CFDataRef)fontData); if (descriptors && CFArrayGetCount(descriptors) > 0) { // Register all descriptors from this font file CTFontManagerRegisterFontDescriptors(descriptors, kCTFontManagerScopeProcess, true, NULL); if (true) { // Store each descriptor for later use for (CFIndex i = 0; i < CFArrayGetCount(descriptors); i++) { CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descriptors, i); [g_RegisteredIconFontDescriptors addObject:(__bridge id)descriptor]; // Log the font name for debugging CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, 16.0, NULL); if (font) { CFRelease(font); } } } else { NSLog(@"Failed to register font descriptors"); } CFRelease(descriptors); } else { NSLog(@"Failed to create font descriptors from data (length: %zu)", fontLength); if (error) { NSLog(@"Error: %@", (__bridge NSError *)error); CFRelease(error); } } } static NSImage* imageFromIconFont(NSString* character, CGFloat size, NSColor* color) { if (!character || [character length] == 0) { return nil; } if (!g_RegisteredIconFontDescriptors || [g_RegisteredIconFontDescriptors count] == 0) { NSLog(@"No icon fonts registered"); return nil; } NSFont *font = nil; unichar unicode = [character characterAtIndex:0]; for (id descriptorObj in g_RegisteredIconFontDescriptors) { CTFontDescriptorRef descriptor = (__bridge CTFontDescriptorRef)descriptorObj; CTFontRef ctFont = CTFontCreateWithFontDescriptor(descriptor, size, NULL); if (ctFont) { CGGlyph glyph; UniChar unichar = unicode; if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) { font = (__bridge NSFont *)ctFont; break; } CFRelease(ctFont); } } if (!font) { NSLog(@"No font found with glyph for character U+%04X (string: '%@', length: %lu)", unicode, character, (unsigned long)[character length]); return nil; } NSDictionary *attributes = @{ NSFontAttributeName: font, NSForegroundColorAttributeName: color }; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:character attributes:attributes]; NSSize stringSize = [attrString size]; NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(size, size)]; [image lockFocus]; NSPoint drawPoint = NSMakePoint((size - stringSize.width) / 2.0, (size - stringSize.height) / 2.0); [attrString drawAtPoint:drawPoint]; [image unlockFocus]; [image setTemplate:YES]; return image; } bool macosBeginMenu(const char* label, const char *icon, bool enabled) { NSString* title = [NSString stringWithUTF8String:label]; // Search for menu item with the given name NSInteger menuIndex = [s_menuStack[s_menuStackSize - 1] indexOfItemWithTitle:title]; if (menuIndex == -1) { // Construct the content of the menu if it doesn't exist yet s_constructingMenu = true; NSMenu* newMenu = [[NSMenu alloc] init]; newMenu.autoenablesItems = false; newMenu.title = title; NSMenuItem* menuItem = [[NSMenuItem alloc] init]; menuItem.title = title; [menuItem setSubmenu:newMenu]; // Hide menus starting with '$' (used for special menus) if (label[0] == '$') { [menuItem setHidden:YES]; } if (icon != NULL) { NSString *iconString = [NSString stringWithUTF8String:icon]; NSImage* iconImage = imageFromIconFont(iconString, 16.0, [NSColor blackColor]); [menuItem setImage:iconImage]; } // Add the new menu to the end of the list menuIndex = [s_menuStack[s_menuStackSize - 1] numberOfItems]; if (s_menuStackSize == 1) menuIndex -= 1; [s_menuStack[s_menuStackSize - 1] insertItem:menuItem atIndex:menuIndex]; } NSMenuItem* menuItem = [s_menuStack[s_menuStackSize - 1] itemAtIndex:menuIndex]; if (menuItem != NULL) { menuItem.enabled = enabled; s_menuStack[s_menuStackSize] = menuItem.submenu; s_menuStackSize += 1; } return true; } void macosEndMenu(void) { s_menuStack[s_menuStackSize - 1] = NULL; s_menuStackSize -= 1; } bool macosMenuItem(const char* label, const char *icon, struct KeyEquivalent keyEquivalent, bool selected, bool enabled) { NSString* title = [NSString stringWithUTF8String:label]; if (s_constructingMenu) { NSMenuItem* menuItem = [[NSMenuItem alloc] init]; menuItem.title = title; menuItem.action = @selector(OnClick:); menuItem.target = s_menuItemHandler; if (icon != NULL) { NSString *iconString = [NSString stringWithUTF8String:icon]; NSImage* iconImage = imageFromIconFont(iconString, 16.0, [NSColor blackColor]); [menuItem setImage:iconImage]; } [menuItem setTag:s_currTag]; s_currTag += 1; // Setup the key equivalent, aka the shortcut if (keyEquivalent.valid) { NSInteger flags = 0x00; if (keyEquivalent.ctrl) flags |= NSEventModifierFlagControl; if (keyEquivalent.shift) flags |= NSEventModifierFlagShift; if (keyEquivalent.cmd) flags |= NSEventModifierFlagCommand; if (keyEquivalent.opt) flags |= NSEventModifierFlagOption; [menuItem setKeyEquivalentModifierMask:flags]; menuItem.keyEquivalent = [[NSString alloc] initWithCharacters:(const unichar *)&keyEquivalent.key length:1]; } [s_menuStack[s_menuStackSize - 1] addItem:menuItem]; } NSInteger menuIndex = [s_menuStack[s_menuStackSize - 1] indexOfItemWithTitle:title]; NSMenuItem* menuItem = NULL; if (menuIndex >= 0 && menuIndex < [s_menuStack[s_menuStackSize - 1] numberOfItems]) { menuItem = [s_menuStack[s_menuStackSize - 1] itemAtIndex:menuIndex]; if (menuItem != NULL) { if (s_constructingMenu == false) { if (![title isEqualToString:menuItem.title]) { s_resetNeeded = true; } } menuItem.enabled = enabled; menuItem.state = selected ? NSControlStateValueOn : NSControlStateValueOff; } if (enabled && menuItem != NULL) { if ([menuItem tag] == s_selectedTag) { s_selectedTag = -1; return true; } } } else { s_resetNeeded = true; } return false; } bool macosMenuItemSelect(const char* label, const char *icon, struct KeyEquivalent keyEquivalent, bool* selected, bool enabled) { if (macosMenuItem(label, icon, keyEquivalent, selected != NULL ? *selected : false, enabled)) { if (selected != NULL) *selected = !(*selected); return true; } return false; } void macosSeparator(void) { if (s_constructingMenu) { NSMenuItem* separator = [NSMenuItem separatorItem]; [s_menuStack[s_menuStackSize - 1] addItem:separator]; } } @interface NSObject (DockMenuAddition) - (NSMenu *)imhexApplicationDockMenu:(NSApplication *)sender; @end @implementation NSObject (DockMenuAddition) - (NSMenu *)cloneMenu:(NSMenu *)originalMenu { NSMenu *clonedMenu = [[NSMenu alloc] initWithTitle:[originalMenu title]]; for (NSMenuItem *item in [originalMenu itemArray]) { NSMenuItem *clonedItem = [self cloneMenuItem:item]; [clonedMenu addItem:clonedItem]; } return clonedMenu; } - (NSMenuItem *)cloneMenuItem:(NSMenuItem *)original { if ([original isSeparatorItem]) { return [NSMenuItem separatorItem]; } // Create new item with same properties NSMenuItem *clone = [[NSMenuItem alloc] initWithTitle:[original title] action:[original action] keyEquivalent:[original keyEquivalent]]; // Copy other properties [clone setTarget:[original target]]; [clone setEnabled:[original isEnabled]]; [clone setImage:[original image]]; [clone setTag:[original tag]]; [clone setRepresentedObject:[original representedObject]]; [clone setToolTip:[original toolTip]]; [clone setState:[original state]]; // Handle submenus recursively if ([original hasSubmenu]) { NSMenu *clonedSubmenu = [self cloneMenu:[original submenu]]; [clone setSubmenu:clonedSubmenu]; } return clone; } - (NSMenu *)imhexApplicationDockMenu:(NSApplication *)sender { NSMenu *dockMenu = [[NSMenu alloc] init]; NSInteger menuIndex = [s_menuStack[s_menuStackSize - 1] indexOfItemWithTitle:@"$TASKBAR$"]; if (menuIndex == -1) { return dockMenu; } NSMenuItem *fileMenuItem = [s_menuStack[s_menuStackSize - 1] itemAtIndex:menuIndex]; // Get the File submenu NSMenu *fileSubmenu = [fileMenuItem submenu]; if (fileSubmenu) { // Clone each item from the File submenu directly into the dock menu for (NSMenuItem *item in [fileSubmenu itemArray]) { NSMenuItem *clonedItem = [self cloneMenuItem:item]; [dockMenu addItem:clonedItem]; } } return dockMenu; } @end void macosSetupDockMenu(void) { @autoreleasepool { // Get GLFW's delegate class Class delegateClass = objc_getClass("GLFWApplicationDelegate"); if (delegateClass != nil) { // Get our custom implementation Method customMethod = class_getInstanceMethod([NSObject class], @selector(imhexApplicationDockMenu:)); // Add the method to GLFW's delegate class class_addMethod(delegateClass, @selector(applicationDockMenu:), method_getImplementation(customMethod), method_getTypeEncoding(customMethod)); } else { NSLog(@"ERROR: Could not find GLFWApplicationDelegate class"); } } } ================================================ FILE: lib/libimhex/source/helpers/magic.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) #include #else #include #endif #if defined(OS_WINDOWS) #define MAGIC_PATH_SEPARATOR ";" #else #define MAGIC_PATH_SEPARATOR ":" #endif namespace hex::magic { static std::optional getMagicFiles(bool sourceFiles = false) { std::string magicFiles; std::error_code error; for (const auto &dir : paths::Magic.read()) { for (const auto &entry : std::fs::directory_iterator(dir, error)) { auto path = std::fs::absolute(entry.path()); if (sourceFiles) { if (path.extension().empty() || entry.is_directory()) magicFiles += wolv::util::toUTF8String(path) + MAGIC_PATH_SEPARATOR; } else { if (path.extension() == ".mgc") magicFiles += wolv::util::toUTF8String(path) + MAGIC_PATH_SEPARATOR; } } } if (!magicFiles.empty()) magicFiles.pop_back(); if (error) return std::nullopt; else return magicFiles; } bool compile() { magic_t ctx = magic_open(MAGIC_CHECK); ON_SCOPE_EXIT { magic_close(ctx); }; auto magicFiles = getMagicFiles(true); if (!magicFiles.has_value()) return false; if (magicFiles->empty()) return true; std::array cwd = { }; if (getcwd(cwd.data(), cwd.size()) == nullptr) return false; std::optional magicFolder; for (const auto &dir : paths::Magic.write()) { if (std::fs::exists(dir) && fs::isPathWritable(dir)) { magicFolder = dir; break; } } if (!magicFolder.has_value()) { log::error("Could not find a writable magic folder"); return false; } if (chdir(wolv::util::toUTF8String(*magicFolder).c_str()) != 0) return false; auto result = magic_compile(ctx, magicFiles->c_str()) == 0; if (!result) { log::error("Failed to compile magic files \"{}\": {}", *magicFiles, magic_error(ctx)); } if (chdir(cwd.data()) != 0) return false; return result; } std::string getDescription(const std::vector &data, bool firstEntryOnly) { if (data.empty()) return ""; auto magicFiles = getMagicFiles(); if (magicFiles.has_value()) { magic_t ctx = magic_open(firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE); ON_SCOPE_EXIT { magic_close(ctx); }; if (magic_load(ctx, magicFiles->c_str()) == 0) { if (auto description = magic_buffer(ctx, data.data(), data.size()); description != nullptr) { auto result = wolv::util::replaceStrings(description, "\\012-", "\n-"); if (result.ends_with("- data")) result = result.substr(0, result.size() - 6); return result; } } } return ""; } std::string getDescription(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) { std::vector buffer(std::min(provider->getSize(), size), 0x00); provider->read(address, buffer.data(), buffer.size()); return getDescription(buffer, firstEntryOnly); } std::string getMIMEType(const std::vector &data, bool firstEntryOnly) { if (data.empty()) return ""; auto magicFiles = getMagicFiles(); if (magicFiles.has_value()) { magic_t ctx = magic_open(MAGIC_MIME_TYPE | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE)); ON_SCOPE_EXIT { magic_close(ctx); }; if (magic_load(ctx, magicFiles->c_str()) == 0) { if (auto mimeType = magic_buffer(ctx, data.data(), data.size()); mimeType != nullptr) { auto result = wolv::util::replaceStrings(mimeType, "\\012-", "\n-"); if (result.ends_with("- application/octet-stream")) result = result.substr(0, result.size() - 26); return result; } } } return ""; } std::string getMIMEType(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) { std::vector buffer(std::min(provider->getSize(), size), 0x00); provider->read(address, buffer.data(), buffer.size()); return getMIMEType(buffer, firstEntryOnly); } std::string getExtensions(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) { std::vector buffer(std::min(provider->getSize(), size), 0x00); provider->read(address, buffer.data(), buffer.size()); return getExtensions(buffer, firstEntryOnly); } std::string getExtensions(const std::vector &data, bool firstEntryOnly) { if (data.empty()) return ""; auto magicFiles = getMagicFiles(); if (magicFiles.has_value()) { magic_t ctx = magic_open(MAGIC_EXTENSION | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE)); ON_SCOPE_EXIT { magic_close(ctx); }; if (magic_load(ctx, magicFiles->c_str()) == 0) { if (auto extension = magic_buffer(ctx, data.data(), data.size()); extension != nullptr) { auto result = wolv::util::replaceStrings(extension, "\\012-", "\n-"); if (result.ends_with("- ???")) result = result.substr(0, result.size() - 5); return result; } } } return ""; } std::string getAppleCreatorType(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) { std::vector buffer(std::min(provider->getSize(), size), 0x00); provider->read(address, buffer.data(), buffer.size()); return getAppleCreatorType(buffer, firstEntryOnly); } std::string getAppleCreatorType(const std::vector &data, bool firstEntryOnly) { if (data.empty()) return ""; auto magicFiles = getMagicFiles(); if (magicFiles.has_value()) { magic_t ctx = magic_open(MAGIC_APPLE | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE)); ON_SCOPE_EXIT { magic_close(ctx); }; if (magic_load(ctx, magicFiles->c_str()) == 0) { if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr) return wolv::util::replaceStrings(result, "\\012-", "\n-"); } } return {}; } bool isValidMIMEType(const std::string &mimeType) { // MIME types always contain a slash if (!mimeType.contains("/")) return false; // The MIME type "application/octet-stream" is a fallback type for arbitrary binary data. // Specifying this in a pattern would make it get suggested for every single unknown binary that's being loaded. // We don't want that, so we ignore it here if (mimeType == "application/octet-stream") return false; return true; } std::vector findViablePatterns(prv::Provider *provider, Task* task) { std::vector result; pl::PatternLanguage runtime; ContentRegistry::PatternLanguage::configureRuntime(runtime, provider); bool foundCorrectType = false; auto mimeType = getMIMEType(provider, 0, 4_KiB, true); std::error_code errorCode; for (const auto &dir : paths::Patterns.read()) { for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) { if (task != nullptr) task->update(); foundCorrectType = false; if (!entry.is_regular_file()) continue; wolv::io::File file(entry.path(), wolv::io::File::Mode::Read); if (!file.isValid()) continue; std::string author, description; bool matchedMimeType = false; std::optional magicOffset; const auto pragmaValues = runtime.getPragmaValues(file.readString()); if (auto it = pragmaValues.find("author"); it != pragmaValues.end()) author = it->second; if (auto it = pragmaValues.find("description"); it != pragmaValues.end()) description = it->second; // Format: #pragma MIME type/subtype for (auto [it, itEnd] = pragmaValues.equal_range("MIME"); it != itEnd; ++it) { if (isValidMIMEType(it->second) && it->second == mimeType) { foundCorrectType = true; matchedMimeType = true; } } // Format: #pragma magic [ AA BB CC DD ] @ 0x12345678 for (auto [it, itEnd] = pragmaValues.equal_range("magic"); it != itEnd; ++it) { const auto pattern = [value = it->second]() mutable -> std::optional { value = wolv::util::trim(value); if (value.empty()) return std::nullopt; if (!value.starts_with('[')) return std::nullopt; value = value.substr(1); const auto end = value.find(']'); if (end == std::string::npos) return std::nullopt; value.resize(end); value = wolv::util::trim(value); return BinaryPattern(value); }(); const auto address = [provider, value = it->second]() mutable -> std::optional { value = wolv::util::trim(value); if (value.empty()) return std::nullopt; const auto start = value.find('@'); if (start == std::string::npos) return std::nullopt; value = value.substr(start + 1); value = wolv::util::trim(value); size_t end = 0; auto result = std::stoll(value, &end, 0); if (end != value.length()) return std::nullopt; if (result < 0) { const auto size = provider->getActualSize(); if (u64(-result) > size) { return std::nullopt; } return size + result; } else { return result; } }(); if (address && pattern) { std::vector bytes(pattern->getSize()); if (!bytes.empty()) { provider->read(*address, bytes.data(), bytes.size()); if (pattern->matches(bytes)) { foundCorrectType = true; magicOffset = address; } } } } if (foundCorrectType) { result.emplace_back( entry.path(), std::move(author), std::move(description), matchedMimeType ? std::make_optional(mimeType) : std::nullopt, magicOffset ); } runtime.reset(); } } return result; } } ================================================ FILE: lib/libimhex/source/helpers/opengl.cpp ================================================ #include #include "wolv/types.hpp" #include #include #include #include #include #include #include #include #include namespace hex::gl { Matrix GetOrthographicMatrix( float viewWidth, float viewHeight, float nearVal,float farVal, bool actionType) { int sign =1; if (actionType) sign=-1; //float left = leftRight.x; //float right = leftRight.y; //float down = upDown.x; //float up = upDown.y; Matrix result(0); //result.updateElement(0,0,sign /(right-left)) result.updateElement(0,0,sign / viewWidth); //result.updateElement(0,3,sign * (right + left)/(right - left)); //result.updateElement(1,1, sign /(up-down)); result.updateElement(1,1, sign / viewHeight); //result.updateElement(1,3, sign * (up + down)/(up - down)); result.updateElement(2,2,-sign * 2/(farVal - nearVal)); result.updateElement(3,2,-sign * (farVal + nearVal)/(farVal - nearVal)); result.updateElement(3,3,sign); return result; } Matrix GetPerspectiveMatrix( float viewWidth, float viewHeight, float nearVal,float farVal, bool actionType) { int sign =1; if (actionType) sign=-1; //float left = leftRight.x; //float right = leftRight.y; //float down = upDown.x; //float up = upDown.y; //T aspect=(right-left)/(top-bottom); //T f = nearVal/top; Matrix result(0); // T f = 1.0 / tan(fovy / 2.0); tan(fovy / 2.0) = top / near; fovy = 2 * atan2(top,near) //result.updateElement(0,0,sign * nearVal/(right-left)); //result.updateElement(1,1, sign * nearVal/(up-down)); result.updateElement(0,0,sign * nearVal/viewWidth); result.updateElement(1,1, sign * nearVal/viewHeight); result.updateElement(2,2,-sign * (farVal + nearVal)/(farVal - nearVal)); result.updateElement(3,2,-sign * 2*farVal*nearVal/(farVal - nearVal)); result.updateElement(2,3,-sign); return result; } Shader::Shader(std::string_view vertexSource, std::string_view fragmentSource) { if (vertexSource.empty() || fragmentSource.empty()) { return; } auto vertexShader = glCreateShader(GL_VERTEX_SHADER); this->compile(vertexShader, vertexSource); auto fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); this->compile(fragmentShader, fragmentSource); ON_SCOPE_EXIT { glDeleteShader(vertexShader); glDeleteShader(fragmentShader); }; m_program = glCreateProgram(); glAttachShader(m_program, vertexShader); glAttachShader(m_program, fragmentShader); glLinkProgram(m_program); int result = false; glGetProgramiv(m_program, GL_LINK_STATUS, &result); if (!result) { std::vector log(512); glGetProgramInfoLog(m_program, log.size(), nullptr, log.data()); log::error("Failed to link shader: {}", log.data()); glDeleteProgram(m_program); m_program = 0; } } Shader::~Shader() { if (m_program != 0) glDeleteProgram(m_program); } Shader::Shader(Shader &&other) noexcept { m_program = other.m_program; other.m_program = 0; } Shader& Shader::operator=(Shader &&other) noexcept { if (m_program != 0) glDeleteProgram(m_program); m_program = other.m_program; other.m_program = 0; return *this; } void Shader::bind() const { glUseProgram(m_program); } void Shader::unbind() const { glUseProgram(0); } void Shader::setUniform(std::string_view name, const int &value) { glUniform1i(getUniformLocation(name), value); } void Shader::setUniform(std::string_view name, const float &value) { glUniform1f(getUniformLocation(name), value); } bool Shader::hasUniform(std::string_view name) { return getUniformLocation(name) != -1; } GLint Shader::getUniformLocation(std::string_view name) { auto nameStr = std::string(name); auto uniform = m_uniforms.find(nameStr); if (uniform == m_uniforms.end()) { auto location = glGetUniformLocation(m_program, nameStr.data()); if (location == -1) { log::warn("Uniform '{}' not found in shader", name); m_uniforms[nameStr] = -1; return -1; } m_uniforms[nameStr] = location; uniform = m_uniforms.find(nameStr); } return uniform->second; } void Shader::compile(GLuint shader, std::string_view source) const { auto sourcePtr = source.data(); glShaderSource(shader, 1, &sourcePtr, nullptr); glCompileShader(shader); int result = false; glGetShaderiv(shader, GL_COMPILE_STATUS, &result); if (!result) { std::vector log(512); glGetShaderInfoLog(shader, log.size(), nullptr, log.data()); log::error("Failed to compile shader: {}", log.data()); } } template Buffer::Buffer(BufferType type, std::span data) : m_size(data.size()), m_type(GLuint(type)) { glGenBuffers(1, &m_buffer); glBindBuffer(m_type, m_buffer); glBufferData(m_type, data.size_bytes(), data.data(), GL_STATIC_DRAW); glBindBuffer(m_type, 0); } template Buffer::~Buffer() { glDeleteBuffers(1, &m_buffer); } template Buffer::Buffer(Buffer &&other) noexcept { m_buffer = other.m_buffer; m_size = other.m_size; m_type = other.m_type; other.m_buffer = 0; } template Buffer& Buffer::operator=(Buffer &&other) noexcept { m_buffer = other.m_buffer; m_size = other.m_size; m_type = other.m_type; other.m_buffer = 0; return *this; } template void Buffer::bind() const { glBindBuffer(m_type, m_buffer); } template void Buffer::unbind() const { glBindBuffer(m_type, 0); } template size_t Buffer::getSize() const { return m_size; } template void Buffer::draw(unsigned primitive) const { switch (m_type) { case GL_ARRAY_BUFFER: glDrawArrays(primitive, 0, m_size); break; case GL_ELEMENT_ARRAY_BUFFER: glDrawElements(primitive, m_size, impl::getType(), nullptr); break; } } template void Buffer::update(std::span data) { glBindBuffer(m_type, m_buffer); glBufferSubData(m_type, 0, data.size_bytes(), data.data()); glBindBuffer(m_type, 0); } template class Buffer; template class Buffer; template class Buffer; template class Buffer; VertexArray::VertexArray() { glGenVertexArrays(1, &m_array); } VertexArray::~VertexArray() { glDeleteVertexArrays(1, &m_array); } VertexArray::VertexArray(VertexArray &&other) noexcept { m_array = other.m_array; other.m_array = 0; } VertexArray& VertexArray::operator=(VertexArray &&other) noexcept { m_array = other.m_array; other.m_array = 0; return *this; } void VertexArray::bind() const { glBindVertexArray(m_array); } void VertexArray::unbind() const { glBindVertexArray(0); } Texture::Texture(u32 width, u32 height) : m_texture(0), m_width(width), m_height(height) { glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D, m_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); } Texture::~Texture() { if (m_texture != 0) glDeleteTextures(1, &m_texture); } Texture::Texture(Texture &&other) noexcept { m_texture = other.m_texture; other.m_texture = 0; m_width = other.m_width; m_height = other.m_height; } Texture& Texture::operator=(Texture &&other) noexcept { m_texture = other.m_texture; other.m_texture = 0; m_width = other.m_width; m_height = other.m_height; return *this; } void Texture::bind() const { glBindTexture(GL_TEXTURE_2D, m_texture); } void Texture::unbind() const { glBindTexture(GL_TEXTURE_2D, 0); } GLuint Texture::getTexture() const { return m_texture; } u32 Texture::getWidth() const { return m_width; } u32 Texture::getHeight() const { return m_height; } GLuint Texture::release() { auto copy = m_texture; m_texture = 0; return copy; } FrameBuffer::FrameBuffer(u32 width, u32 height) { glGenFramebuffers(1, &m_frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer); glGenRenderbuffers(1, &m_renderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); } FrameBuffer::~FrameBuffer() { glDeleteFramebuffers(1, &m_frameBuffer); glDeleteRenderbuffers(1, &m_renderBuffer); } FrameBuffer::FrameBuffer(FrameBuffer &&other) noexcept { m_frameBuffer = other.m_frameBuffer; other.m_frameBuffer = 0; m_renderBuffer = other.m_renderBuffer; other.m_renderBuffer = 0; } FrameBuffer& FrameBuffer::operator=(FrameBuffer &&other) noexcept { m_frameBuffer = other.m_frameBuffer; other.m_frameBuffer = 0; m_renderBuffer = other.m_renderBuffer; other.m_renderBuffer = 0; return *this; } void FrameBuffer::bind() const { glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer); } void FrameBuffer::unbind() const { glBindFramebuffer(GL_FRAMEBUFFER, 0); } void FrameBuffer::attachTexture(const Texture &texture) const { glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer); texture.bind(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.getTexture(), 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); } AxesVectors::AxesVectors() { m_vertices.resize(36); m_colors.resize(48); m_indices.resize(18); // Vertices are x,y,z. Colors are RGBA. Indices are for the ends of each segment // Entries with value zero are unneeded but kept to help keep track of location // x-axis //vertices[0]=0.0F; vertices[1]= 0.0F vertices[2] = 0.0F; // shaft base m_vertices[3] = 1.0F;//vertices[4]= 0.0F vertices[5] = 0.0F; // shaft tip m_vertices[6] = 0.9F; m_vertices[8] = 0.05F; // arrow base m_vertices[9] = 0.9F; m_vertices[11]=-0.05F; // arrow base // y-axis //vertices[12]=0.0F; vertices[13] = 0.0F; vertices[14]=0.0F;// shaft base m_vertices[16] = 1.0F;//vertices[17]=0.0F;// shaft tip m_vertices[18] = 0.05F; m_vertices[19] = 0.9F;//vertices[20]=0.0F;// arrow base m_vertices[21] =-0.05F; m_vertices[22] = 0.9F;//vertices[23]=0.0F;// arrow base // z-axis //vertices[24]=0.0F; vertices[25]=0.0F vertices[26] = 0.0F; // shaft base m_vertices[29] = 1.0F; // shaft tip m_vertices[30] = 0.05F; m_vertices[32] = 0.9F; // arrow base m_vertices[33] =-0.05F; m_vertices[35] = 0.9F; // arrow base // x-axis colors m_colors[0] = 0.7F; m_colors[3] = 1.0F; m_colors[4] = 0.7F; m_colors[7] = 1.0F; m_colors[8] = 0.7F; m_colors[11] = 1.0F; m_colors[12] = 0.7F; m_colors[15] = 1.0F; // y-axis colors m_colors[17] = 0.7F; m_colors[19] = 1.0F; m_colors[21] = 0.7F; m_colors[23] = 1.0F; m_colors[25] = 0.7F; m_colors[27] = 1.0F; m_colors[29] = 0.7F; m_colors[31] = 1.0F; // z-axis colors m_colors[34] = 0.7F; m_colors[35] = 1.0F; m_colors[38] = 0.7F; m_colors[39] = 1.0F; m_colors[42] = 0.7F; m_colors[43] = 1.0F; m_colors[46] = 0.7F; m_colors[47] = 1.0F; // indices for x m_indices[0] = 0; m_indices[1] = 1; m_indices[2] = 2; m_indices[3] = 1; m_indices[4] = 3; m_indices[5] = 1; // indices for y m_indices[6] = 4; m_indices[7] = 5; m_indices[8] = 6; m_indices[9] = 5; m_indices[10] = 7; m_indices[11] = 5; // indices for z m_indices[12] = 8; m_indices[13] = 9; m_indices[14] = 10; m_indices[15] = 9; m_indices[16] = 11; m_indices[17] = 9; } AxesBuffers::AxesBuffers(const VertexArray& axesVertexArray, const AxesVectors &axesVectors) { m_vertices = {}; m_colors = {}; m_indices = {}; axesVertexArray.bind(); m_vertices = gl::Buffer(gl::BufferType::Vertex, axesVectors.getVertices()); m_colors = gl::Buffer(gl::BufferType::Vertex, axesVectors.getColors()); m_indices = gl::Buffer(gl::BufferType::Index, axesVectors.getIndices()); axesVertexArray.addBuffer(0, m_vertices); axesVertexArray.addBuffer(1, m_colors, 4); m_vertices.unbind(); m_colors.unbind(); m_indices.unbind(); axesVertexArray.unbind(); } GridVectors::GridVectors(int sliceCount) { m_slices = sliceCount; m_vertices.resize((m_slices + 1) * (m_slices + 1) * 3); m_colors.resize((m_slices + 1) * (m_slices + 1) * 4); m_indices.resize(m_slices * m_slices * 6 + m_slices * 2); int k = 0; int l = 0; for (u32 j = 0; j <= m_slices; ++j) { float z = 2.0F * float(j) / float(m_slices) - 1.0F; for (u32 i = 0; i <= m_slices; ++i) { m_vertices[k ] = 2.0F * float(i) / float(m_slices) - 1.0F; m_vertices[k + 1] = 0.0F; m_vertices[k + 2] = z; k += 3; m_colors[l ] = 0.5F; m_colors[l + 1] = 0.5F; m_colors[l + 2] = 0.5F; m_colors[l + 3] = 0.3F; l += 4; } } k = 0; for (u32 j = 0; j < m_slices; ++j) { int row1 = j * (m_slices + 1); int row2 = (j + 1) * (m_slices + 1); for (u32 i = 0; i < m_slices; ++i) { m_indices[k ] = row1 + i; m_indices[k + 1] = row1 + i + 1; m_indices[k + 2] = row1 + i + 1; m_indices[k + 3] = row2 + i + 1; m_indices[k + 4] = row2 + i + 1; m_indices[k + 5] = row2 + i; k += 6; if (i == 0) { m_indices[k ] = row2 + i; m_indices[k + 1] = row1 + i; k += 2; } } } } GridBuffers::GridBuffers(const VertexArray& gridVertexArray, const GridVectors &gridVectors) { m_vertices = {}; m_colors = {}; m_indices = {}; gridVertexArray.bind(); m_vertices = gl::Buffer(gl::BufferType::Vertex, gridVectors.getVertices()); m_indices = gl::Buffer(gl::BufferType::Index, gridVectors.getIndices()); m_colors = gl::Buffer(gl::BufferType::Vertex, gridVectors.getColors()); gridVertexArray.addBuffer(0, m_vertices); gridVertexArray.addBuffer(1, m_colors,4); m_vertices.unbind(); m_colors.unbind(); m_indices.unbind(); gridVertexArray.unbind(); } hex::gl::LightSourceVectors::LightSourceVectors(int res) { m_resolution = res; auto res_sq = m_resolution * m_resolution; m_radius = 0.05f; m_vertices.resize((res_sq + 2) * 3); m_normals.resize((res_sq + 2) * 3); m_colors.resize((res_sq + 2) * 4); m_indices.resize(res_sq * 6); constexpr auto TwoPi = std::numbers::pi_v * 2.0F; constexpr auto HalfPi = std::numbers::pi_v / 2.0F; const auto dv = TwoPi / m_resolution; const auto du = std::numbers::pi_v / (m_resolution + 1); m_normals[0] = 0; m_normals[1] = 0; m_normals[2] = 1; m_vertices[0] = 0; m_vertices[1] = 0; m_vertices[2] = m_radius; m_colors[0] = 1.0; m_colors[1] = 1.0; m_colors[2] = 1.0; m_colors[3] = 1.0; // Vertical: pi/2 to -pi/2 for (int i = 0; i < m_resolution; i += 1) { float u = HalfPi - (i + 1) * du; float z = std::sin(u); float xy = std::cos(u); // Horizontal: 0 to 2pi for (int j = 0; j < m_resolution; j += 1) { float v = j * dv; float x = xy * std::cos(v); float y = xy * std::sin(v); i32 n = (i * m_resolution + j + 1) * 3; m_normals[n] = x; m_normals[n + 1] = y; m_normals[n + 2] = z; m_vertices[n] = m_radius * x; m_vertices[n + 1] = m_radius * y; m_vertices[n + 2] = m_radius * z; n = (i * m_resolution + j + 1) * 4; m_colors[n] = 1.0F; m_colors[n + 1] = 1.0F; m_colors[n + 2] = 1.0F; m_colors[n + 3] = 1.0F; } } i32 n = ((res_sq + 1) * 3); m_normals[n ] = 0; m_normals[n + 1] = 0; m_normals[n + 2] = -1; m_vertices[n ] = 0; m_vertices[n + 1] = 0; m_vertices[n + 2] = -m_radius; n = ((res_sq + 1) * 4); m_colors[n ] = 1.0; m_colors[n + 1] = 1.0; m_colors[n + 2] = 1.0; m_colors[n + 3] = 1.0; // that was the easy part, indices are a bit more complicated // and may need some explaining. The RxR grid slices the globe // into longitudes which are the vertical slices and latitudes // which are the horizontal slices. The latitudes are all full // circles except for the poles, so we don't count them as part // of the grid. That means that there are R+2 latitudes and R // longitudes.Between consecutive latitudes we have 2*R triangles. // Since we have R true latitudes there are R-1 spaces between them so // between the top and the bottom we have 2*R*(R-1) triangles. // the top and bottom have R triangles each, so we have a total of // 2*R*(R-1) + 2*R = 2*R*R triangles. Each triangle has 3 vertices, // so we have 6*R*R indices. // The North Pole is index 0 and the South Pole is index 6*res*res -1 // The first row of vertices is 1 to res, the second row is res+1 to 2*res etc. // First, the North Pole for (int i = 0; i < m_resolution; i += 1) { m_indices[i * 3] = 0; m_indices[i * 3 + 1] = i + 1; if (i == m_resolution - 1) m_indices[i * 3 + 2] = 1; else m_indices[i * 3 + 2] = (i + 2); } // Now the spaces between true latitudes for (int i = 0; i < m_resolution - 1; i += 1) { // k is the index of the first vertex of the i-th latitude i32 k = i * m_resolution + 1; // When we go a full circle we need to connect the last vertex to the first, so // we do R-1 first because their indices can be computed easily for (int j = 0; j < m_resolution - 1; j += 1) { // We store the indices of the array where the vertices were store // in the triplets that make the triangles. These triplets are stored in // an array that has indices itself which can be confusing. // l keeps track of the indices of the array that stores the triplets // each i brings 6R and each j 6. 3R from the North Pole. i32 l = (i * m_resolution + j) * 6 + 3 * m_resolution; m_indices[l ] = k + j; m_indices[l + 1] = k + j + m_resolution + 1; m_indices[l + 2] = k + j + 1; m_indices[l + 3] = k + j; m_indices[l + 4] = k + j + m_resolution; m_indices[l + 5] = k + j + m_resolution + 1; } // Now the last vertex of the i-th latitude is connected to the first i32 l = (( i + 1) * m_resolution - 1) * 6 + 3 * m_resolution; m_indices[l ] = k + m_resolution - 1; m_indices[l + 1] = k + m_resolution; m_indices[l + 2] = k; m_indices[l + 3] = k + m_resolution - 1; m_indices[l + 4] = k + 2 * m_resolution - 1; m_indices[l + 5] = k + m_resolution; } // Now the South Pole i32 k = (m_resolution-1) * m_resolution + 1; i32 l = 3 * m_resolution * ( 2 * m_resolution - 1); for (int i = 0; i < m_resolution; i += 1) { if (i == m_resolution -1) m_indices[l + i * 3] = k; else m_indices[l + i * 3] = k + i + 1; m_indices[l + i * 3 + 1] = k + i; m_indices[l + i * 3 + 2] = k + m_resolution; } } void LightSourceVectors::moveTo(const Vector &position) { auto vertexCount = m_vertices.size(); for (unsigned k = 0; k < vertexCount; k += 3) { m_vertices[k ] = m_radius * m_normals[k ] + position[0]; m_vertices[k + 1] = m_radius * m_normals[k + 1] + position[1]; m_vertices[k + 2] = m_radius * m_normals[k + 2] + position[2]; } } LightSourceBuffers::LightSourceBuffers(const VertexArray &sourceVertexArray, const LightSourceVectors &sourceVectors) { sourceVertexArray.bind(); m_vertices = gl::Buffer(gl::BufferType::Vertex, sourceVectors.getVertices()); m_indices = gl::Buffer(gl::BufferType::Index, sourceVectors.getIndices()); m_normals = gl::Buffer(gl::BufferType::Vertex, sourceVectors.getNormals()); m_colors = gl::Buffer(gl::BufferType::Vertex, sourceVectors.getColors()); sourceVertexArray.addBuffer(0, m_vertices); sourceVertexArray.addBuffer(1, m_normals); sourceVertexArray.addBuffer(2, m_colors, 4); m_vertices.unbind(); m_normals.unbind(); m_colors.unbind(); m_indices.unbind(); sourceVertexArray.unbind(); } void LightSourceBuffers::moveVertices(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors) { sourceVertexArray.bind(); m_vertices.update(sourceVectors.getVertices()); sourceVertexArray.addBuffer(0, m_vertices); sourceVertexArray.unbind(); } void LightSourceBuffers::updateColors(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors) { sourceVertexArray.bind(); m_colors.update(sourceVectors.getColors()); sourceVertexArray.addBuffer(2, m_colors, 4); sourceVertexArray.unbind(); } } ================================================ FILE: lib/libimhex/source/helpers/patches.cpp ================================================ #include #include #include #include #include namespace hex { namespace { class PatchesGenerator : public hex::prv::Provider { public: explicit PatchesGenerator() = default; ~PatchesGenerator() override = default; [[nodiscard]] bool isAvailable() const override { return true; } [[nodiscard]] bool isReadable() const override { return true; } [[nodiscard]] bool isWritable() const override { return true; } [[nodiscard]] bool isResizable() const override { return true; } [[nodiscard]] bool isSavable() const override { return false; } [[nodiscard]] bool isSavableAsRecent() const override { return false; } [[nodiscard]] OpenResult open() override { return {}; } void close() override { } void readRaw(u64 offset, void *buffer, size_t size) override { std::ignore = offset; std::ignore = buffer; std::ignore = size; } void writeRaw(u64 offset, const void *buffer, size_t size) override { for (u64 i = 0; i < size; i += 1) m_patches[offset] = static_cast(buffer)[i]; } [[nodiscard]] u64 getActualSize() const override { if (m_patches.empty()) return 0; else return m_patches.rbegin()->first; } void insertRaw(u64 offset, u64 size) override { std::vector> patchesToMove; for (auto &[address, value] : m_patches) { if (address > offset) patchesToMove.emplace_back(address, value); } for (const auto &[address, value] : patchesToMove) m_patches.erase(address); for (const auto &[address, value] : patchesToMove) m_patches.insert({ address + size, value }); } void removeRaw(u64 offset, u64 size) override { std::vector> patchesToMove; for (auto &[address, value] : m_patches) { if (address > offset) patchesToMove.emplace_back(address, value); } for (const auto &[address, value] : patchesToMove) m_patches.erase(address); for (const auto &[address, value] : patchesToMove) m_patches.insert({ address - size, value }); } [[nodiscard]] std::string getName() const override { return ""; } [[nodiscard]] const char* getIcon() const override { return ""; } [[nodiscard]] UnlocalizedString getTypeName() const override { return ""; } [[nodiscard]] const std::map& getPatches() const { return m_patches; } private: std::map m_patches; }; void pushStringBack(std::vector &buffer, const std::string &string) { std::ranges::copy(string, std::back_inserter(buffer)); } template void pushBytesBack(std::vector &buffer, T bytes) { buffer.resize(buffer.size() + sizeof(T)); std::memcpy((&buffer.back() - sizeof(T)) + 1, &bytes, sizeof(T)); } } wolv::util::Expected, IPSError> Patches::toIPSPatch() const { std::vector result; pushStringBack(result, "PATCH"); std::vector addresses; std::vector values; for (const auto &[address, value] : m_patches) { addresses.push_back(address); values.push_back(value); } std::optional startAddress; std::vector bytes; for (u32 i = 0; i < addresses.size(); i++) { if (!startAddress.has_value()) startAddress = addresses[i]; if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) { bytes.push_back(values[i]); } else { bytes.push_back(values[i]); if (bytes.size() > 0xFFFF) return wolv::util::Unexpected(IPSError::PatchTooLarge); if (startAddress > 0xFFFF'FFFF) return wolv::util::Unexpected(IPSError::AddressOutOfRange); u32 address = startAddress.value(); auto addressBytes = reinterpret_cast(&address); result.push_back(addressBytes[2]); result.push_back(addressBytes[1]); result.push_back(addressBytes[0]); pushBytesBack(result, changeEndianness(bytes.size(), std::endian::big)); for (auto byte : bytes) result.push_back(byte); bytes.clear(); startAddress = {}; } } pushStringBack(result, "EOF"); return result; } wolv::util::Expected, IPSError> Patches::toIPS32Patch() const { std::vector result; pushStringBack(result, "IPS32"); std::vector addresses; std::vector values; for (const auto &[address, value] : m_patches) { addresses.push_back(address); values.push_back(value); } std::optional startAddress; std::vector bytes; for (u32 i = 0; i < addresses.size(); i++) { if (!startAddress.has_value()) startAddress = addresses[i]; if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) { bytes.push_back(values[i]); } else { bytes.push_back(values[i]); if (bytes.size() > 0xFFFF) return wolv::util::Unexpected(IPSError::PatchTooLarge); if (startAddress > 0xFFFF'FFFF) return wolv::util::Unexpected(IPSError::AddressOutOfRange); u32 address = startAddress.value(); auto addressBytes = reinterpret_cast(&address); result.push_back(addressBytes[3]); result.push_back(addressBytes[2]); result.push_back(addressBytes[1]); result.push_back(addressBytes[0]); pushBytesBack(result, changeEndianness(bytes.size(), std::endian::big)); for (auto byte : bytes) result.push_back(byte); bytes.clear(); startAddress = {}; } } pushStringBack(result, "EEOF"); return result; } wolv::util::Expected Patches::fromProvider(hex::prv::Provider* provider) { PatchesGenerator generator; generator.getUndoStack().apply(provider->getUndoStack()); if (generator.getActualSize() > 0xFFFF'FFFF) return wolv::util::Unexpected(IPSError::PatchTooLarge); auto patches = generator.getPatches(); return Patches(std::move(patches)); } wolv::util::Expected Patches::fromIPSPatch(const std::vector &ipsPatch) { if (ipsPatch.size() < (5 + 3)) return wolv::util::Unexpected(IPSError::InvalidPatchHeader); const char *header = "PATCH"; if (std::memcmp(ipsPatch.data(), header, 5) != 0) return wolv::util::Unexpected(IPSError::InvalidPatchHeader); Patches result; bool foundEOF = false; u32 ipsOffset = 5; while (ipsOffset < ipsPatch.size() - (5 + 3)) { u32 offset = ipsPatch[ipsOffset + 2] | (ipsPatch[ipsOffset + 1] << 8) | (ipsPatch[ipsOffset + 0] << 16); u16 size = ipsPatch[ipsOffset + 4] | (ipsPatch[ipsOffset + 3] << 8); ipsOffset += 5; // Handle normal record if (size > 0x0000) { if (ipsOffset + size > ipsPatch.size() - 3) return wolv::util::Unexpected(IPSError::InvalidPatchFormat); for (u16 i = 0; i < size; i++) result.get()[offset + i] = ipsPatch[ipsOffset + i]; ipsOffset += size; } // Handle RLE record else { if (ipsOffset + 3 > ipsPatch.size() - 3) return wolv::util::Unexpected(IPSError::InvalidPatchFormat); u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8); ipsOffset += 2; for (u16 i = 0; i < rleSize; i++) result.get()[offset + i] = ipsPatch[ipsOffset + 0]; ipsOffset += 1; } const char *footer = "EOF"; if (std::memcmp(ipsPatch.data() + ipsOffset, footer, 3) == 0) foundEOF = true; } if (foundEOF) return result; else return wolv::util::Unexpected(IPSError::MissingEOF); } wolv::util::Expected Patches::fromIPS32Patch(const std::vector &ipsPatch) { if (ipsPatch.size() < (5 + 4)) return wolv::util::Unexpected(IPSError::InvalidPatchHeader); const char *header = "IPS32"; if (std::memcmp(ipsPatch.data(), header, 5) != 0) return wolv::util::Unexpected(IPSError::InvalidPatchHeader); Patches result; bool foundEEOF = false; u32 ipsOffset = 5; while (ipsOffset < ipsPatch.size() - (5 + 4)) { u32 offset = ipsPatch[ipsOffset + 3] | (ipsPatch[ipsOffset + 2] << 8) | (ipsPatch[ipsOffset + 1] << 16) | (ipsPatch[ipsOffset + 0] << 24); u16 size = ipsPatch[ipsOffset + 5] | (ipsPatch[ipsOffset + 4] << 8); ipsOffset += 6; // Handle normal record if (size > 0x0000) { if (ipsOffset + size > ipsPatch.size() - 3) return wolv::util::Unexpected(IPSError::InvalidPatchFormat); for (u16 i = 0; i < size; i++) result.get()[offset + i] = ipsPatch[ipsOffset + i]; ipsOffset += size; } // Handle RLE record else { if (ipsOffset + 3 > ipsPatch.size() - 3) return wolv::util::Unexpected(IPSError::InvalidPatchFormat); u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8); ipsOffset += 2; for (u16 i = 0; i < rleSize; i++) result.get()[offset + i] = ipsPatch[ipsOffset + 0]; ipsOffset += 1; } const char *footer = "EEOF"; if (std::memcmp(ipsPatch.data() + ipsOffset, footer, 4) == 0) foundEEOF = true; } if (foundEEOF) return result; else return wolv::util::Unexpected(IPSError::MissingEOF); } } ================================================ FILE: lib/libimhex/source/helpers/scaling.cpp ================================================ #include #include namespace hex { float operator""_scaled(long double value) { return value * ImHexApi::System::getGlobalScale(); } float operator""_scaled(unsigned long long value) { return value * ImHexApi::System::getGlobalScale(); } ImVec2 scaled(const ImVec2 &vector) { return vector * ImHexApi::System::getGlobalScale(); } ImVec2 scaled(float x, float y) { return ImVec2(x, y) * ImHexApi::System::getGlobalScale(); } } ================================================ FILE: lib/libimhex/source/helpers/semantic_version.cpp ================================================ #include #include #include namespace hex { SemanticVersion::SemanticVersion(u32 major, u32 minor, u32 patch) : SemanticVersion(fmt::format("{}.{}.{}", major, minor, patch)) { } SemanticVersion::SemanticVersion(const char *version) : SemanticVersion(std::string(version)) { } SemanticVersion::SemanticVersion(std::string_view version) : SemanticVersion(std::string(version.begin(), version.end())) { } SemanticVersion::SemanticVersion(std::string version) { if (version.empty()) return; if (version.starts_with("v")) version = version.substr(1); m_parts = wolv::util::splitString(version, "."); if (m_parts.size() != 3 && m_parts.size() != 4) { m_parts.clear(); return; } if (m_parts.back().contains("-")) { auto buildTypeParts = wolv::util::splitString(m_parts.back(), "-"); if (buildTypeParts.size() != 2) { m_parts.clear(); return; } m_parts.back() = buildTypeParts[0]; m_buildType = buildTypeParts[1]; } } u32 SemanticVersion::major() const { if (!isValid()) return 0; try { return std::stoul(m_parts[0]); } catch (...) { return 0; } } u32 SemanticVersion::minor() const { if (!isValid()) return 0; try { return std::stoul(m_parts[1]); } catch (...) { return 0; } } u32 SemanticVersion::patch() const { if (!isValid()) return 0; try { return std::stoul(m_parts[2]); } catch (...) { return 0; } } bool SemanticVersion::nightly() const { if (!isValid()) return false; return m_parts.size() == 4 && m_parts[3] == "WIP"; } const std::string& SemanticVersion::buildType() const { return m_buildType; } bool SemanticVersion::isValid() const { return !m_parts.empty(); } bool SemanticVersion::operator==(const SemanticVersion& other) const { return this->m_parts == other.m_parts; } std::strong_ordering SemanticVersion::operator<=>(const SemanticVersion &other) const { if (const auto result = major() <=> other.major(); result != std::strong_ordering::equal) return result; if (auto result = minor() <=> other.minor(); result != std::strong_ordering::equal) return result; if (auto result = patch() <=> other.patch(); result != std::strong_ordering::equal) return result; // nightly builds are considered "greater" than release builds if (nightly() != other.nightly()) return nightly() ? std::strong_ordering::greater : std::strong_ordering::less; return std::strong_ordering::equal; } std::string SemanticVersion::get(bool withBuildType) const { if (!isValid()) return ""; auto result = wolv::util::combineStrings(m_parts, "."); if (withBuildType && !m_buildType.empty()) result += fmt::format("-{}", m_buildType); return result; } } ================================================ FILE: lib/libimhex/source/helpers/tar.cpp ================================================ #include #include #include #include #include #include #include "wolv/utils/string.hpp" namespace hex { using namespace hex::literals; Tar::Tar(const std::fs::path &path, Mode mode) { int tarError = MTAR_ESUCCESS; // Explicitly create file so a short path gets generated if (mode == Mode::Create) { wolv::io::File file(path, wolv::io::File::Mode::Create); file.flush(); } m_ctx = std::make_unique(); auto shortPath = wolv::io::fs::toNormalizedPathString(wolv::io::fs::toShortPath(path)); if (mode == Tar::Mode::Read) tarError = mtar_open(m_ctx.get(), shortPath.c_str(), "r"); else if (mode == Tar::Mode::Write) tarError = mtar_open(m_ctx.get(), shortPath.c_str(), "a"); else if (mode == Tar::Mode::Create) tarError = mtar_open(m_ctx.get(), shortPath.c_str(), "w"); else tarError = MTAR_EFAILURE; m_path = path; m_valid = (tarError == MTAR_ESUCCESS); if (!m_valid) { m_tarOpenErrno = tarError; // Hopefully this errno corresponds to the file open call in mtar_open m_fileOpenErrno = errno; } } Tar::~Tar() { this->close(); } Tar::Tar(hex::Tar &&other) noexcept { m_ctx = std::move(other.m_ctx); m_path = other.m_path; m_valid = other.m_valid; m_tarOpenErrno = other.m_tarOpenErrno; m_fileOpenErrno = other.m_fileOpenErrno; other.m_ctx = { }; other.m_valid = false; } Tar &Tar::operator=(Tar &&other) noexcept { m_ctx = std::move(other.m_ctx); m_path = std::move(other.m_path); m_valid = other.m_valid; other.m_valid = false; m_tarOpenErrno = other.m_tarOpenErrno; m_fileOpenErrno = other.m_fileOpenErrno; return *this; } std::vector Tar::listEntries(const std::fs::path &basePath) const { std::vector result; const std::string PaxHeaderName = "@PaxHeader"; mtar_header_t header; while (mtar_read_header(m_ctx.get(), &header) != MTAR_ENULLRECORD) { std::fs::path path = header.name; if (header.name != PaxHeaderName && wolv::io::fs::isSubPath(basePath, path)) { result.emplace_back(header.name); } mtar_next(m_ctx.get()); } return result; } bool Tar::contains(const std::fs::path &path) const { mtar_header_t header; const auto fixedPath = wolv::io::fs::toNormalizedPathString(path); return mtar_find(m_ctx.get(), fixedPath.c_str(), &header) == MTAR_ESUCCESS; } std::string Tar::getOpenErrorString() const { return fmt::format("{}: {}", mtar_strerror(m_tarOpenErrno), std::strerror(m_fileOpenErrno)); } void Tar::close() { if (m_valid) { mtar_finalize(m_ctx.get()); mtar_close(m_ctx.get()); } m_ctx.reset(); m_valid = false; } std::vector Tar::readVector(const std::fs::path &path) const { mtar_header_t header; const auto fixedPath = wolv::io::fs::toNormalizedPathString(path); int ret = mtar_find(m_ctx.get(), fixedPath.c_str(), &header); if (ret != MTAR_ESUCCESS){ log::debug("Failed to read vector from path {} in tarred file {}: {}", path.string(), m_path.string(), mtar_strerror(ret)); return {}; } std::vector result(header.size, 0x00); mtar_read_data(m_ctx.get(), result.data(), result.size()); return result; } std::string Tar::readString(const std::fs::path &path) const { auto result = this->readVector(path); return { result.begin(), result.end() }; } void Tar::writeVector(const std::fs::path &path, const std::vector &data) const { if (path.has_parent_path()) { std::fs::path pathPart; for (const auto &part : path.parent_path()) { pathPart /= part; auto fixedPath = wolv::io::fs::toNormalizedPathString(pathPart); mtar_write_dir_header(m_ctx.get(), fixedPath.c_str()); } } const auto fixedPath = wolv::io::fs::toNormalizedPathString(path); mtar_write_file_header(m_ctx.get(), fixedPath.c_str(), data.size()); mtar_write_data(m_ctx.get(), data.data(), data.size()); } void Tar::writeString(const std::fs::path &path, const std::string &data) const { this->writeVector(path, { data.begin(), data.end() }); } static void writeFile(mtar_t *ctx, const mtar_header_t *header, const std::fs::path &path) { constexpr static u64 BufferSize = 1_MiB; wolv::io::File outputFile(path, wolv::io::File::Mode::Create); std::vector buffer; for (u64 offset = 0; offset < header->size; offset += BufferSize) { buffer.resize(std::min(BufferSize, header->size - offset)); mtar_read_data(ctx, buffer.data(), buffer.size()); outputFile.writeVector(buffer); } } void Tar::extract(const std::fs::path &path, const std::fs::path &outputPath) const { mtar_header_t header; mtar_find(m_ctx.get(), path.string().c_str(), &header); writeFile(m_ctx.get(), &header, outputPath); } void Tar::extractAll(const std::fs::path &outputPath) const { mtar_header_t header; while (mtar_read_header(m_ctx.get(), &header) != MTAR_ENULLRECORD) { const auto filePath = std::fs::absolute(outputPath / std::fs::path(header.name)); if (filePath.filename() != "@PaxHeader") { std::fs::create_directories(filePath.parent_path()); writeFile(m_ctx.get(), &header, filePath); } mtar_next(m_ctx.get()); } } } ================================================ FILE: lib/libimhex/source/helpers/udp_server.cpp ================================================ #include #if defined(OS_WINDOWS) #include #include using socklen_t = int; #else #include #include #include #include #endif namespace hex { UDPServer::UDPServer(u16 port, Callback callback) : m_port(port), m_callback(std::move(callback)), m_running(false) { } UDPServer::~UDPServer() { stop(); } void UDPServer::start() { m_running = true; m_thread = std::thread(&UDPServer::run, this); } void UDPServer::stop() { m_running = false; if (m_socketFd >= 0) { #if defined(OS_WINDOWS) ::closesocket(m_socketFd); #else ::close(m_socketFd); #endif } if (m_thread.joinable()) m_thread.join(); } void UDPServer::run() { m_socketFd = ::socket(AF_INET, SOCK_DGRAM, 0); if (m_socketFd < 0) { return; } sockaddr_in addr = { }; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(m_port); if (bind(m_socketFd, reinterpret_cast(&addr), sizeof(addr)) < 0) { return; } std::vector buffer(64 * 1024, 0x00); while (m_running) { sockaddr_in client{}; socklen_t len = sizeof(client); const auto bytes = ::recvfrom(m_socketFd, reinterpret_cast(buffer.data()), buffer.size(), 0, reinterpret_cast(&client), &len); if (bytes > 0) { buffer[bytes] = '\0'; m_callback({ buffer.data(), buffer.data() + bytes }); } } } } ================================================ FILE: lib/libimhex/source/helpers/utils.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(OS_WINDOWS) #include #include #include #include #elif defined(OS_LINUX) #include #include #include #include #elif defined(OS_MACOS) #include #include #include #include #include #elif defined(OS_WEB) #include "emscripten.h" #endif namespace hex { std::string to_string(u128 value) { char data[45] = { 0 }; u8 index = sizeof(data) - 2; while (value != 0 && index != 0) { data[index] = static_cast('0' + (value % 10)); value /= 10; index--; } return { data + index + 1 }; } std::string to_string(i128 value) { char data[45] = { 0 }; u128 unsignedValue = value < 0 ? -value : value; u8 index = sizeof(data) - 2; while (unsignedValue != 0 && index != 0) { data[index] = static_cast('0' + (unsignedValue % 10)); unsignedValue /= 10; index--; } if (value < 0) { data[index] = '-'; return { data + index }; } else { return { data + index + 1 }; } } std::string toLower(std::string string) { for (char &c : string) c = std::tolower(c); return string; } std::string toUpper(std::string string) { for (char &c : string) c = std::toupper(c); return string; } std::vector parseHexString(std::string string) { if (string.empty()) return { }; // Remove common hex prefixes and commas string = wolv::util::replaceStrings(string, "0x", ""); string = wolv::util::replaceStrings(string, "0X", ""); string = wolv::util::replaceStrings(string, ",", ""); // Check for non-hex characters bool isValidHexString = std::ranges::find_if(string, [](char c) { return !std::isxdigit(c) && !std::isspace(c); }) == string.end(); if (!isValidHexString) return { }; // Remove all whitespace std::erase_if(string, [](char c) { return std::isspace(c); }); // Only parse whole bytes if (string.length() % 2 != 0) return { }; // Convert hex string to bytes return crypt::decode16(string); } std::optional parseBinaryString(const std::string &string) { if (string.empty()) return std::nullopt; u8 byte = 0x00; for (char c : string) { byte <<= 1; if (c == '1') byte |= 0b01; else if (c == '0') byte |= 0b00; else return std::nullopt; } return byte; } std::string toByteString(u64 bytes) { double value = bytes; u8 unitIndex = 0; while (value > 1024) { value /= 1024; unitIndex++; if (unitIndex == 6) break; } std::string result; if (unitIndex == 0) result = fmt::format("{0:}", value); else result = fmt::format("{0:.2f}", value); switch (unitIndex) { case 0: result += ((value == 1) ? " Byte" : " Bytes"); break; case 1: result += " kiB"; break; case 2: result += " MiB"; break; case 3: result += " GiB"; break; case 4: result += " TiB"; break; case 5: result += " PiB"; break; case 6: result += " EiB"; break; default: result = "A lot!"; } return result; } std::string makeStringPrintable(const std::string &string) { std::string result; for (char c : string) { if (std::isprint(c)) result += c; else result += fmt::format("\\x{0:02X}", u8(c)); } return result; } std::string makePrintable(u8 c) { switch (c) { case 0: return "NUL"; case 1: return "SOH"; case 2: return "STX"; case 3: return "ETX"; case 4: return "EOT"; case 5: return "ENQ"; case 6: return "ACK"; case 7: return "BEL"; case 8: return "BS"; case 9: return "TAB"; case 10: return "LF"; case 11: return "VT"; case 12: return "FF"; case 13: return "CR"; case 14: return "SO"; case 15: return "SI"; case 16: return "DLE"; case 17: return "DC1"; case 18: return "DC2"; case 19: return "DC3"; case 20: return "DC4"; case 21: return "NAK"; case 22: return "SYN"; case 23: return "ETB"; case 24: return "CAN"; case 25: return "EM"; case 26: return "SUB"; case 27: return "ESC"; case 28: return "FS"; case 29: return "GS"; case 30: return "RS"; case 31: return "US"; case 32: return "Space"; case 127: return "DEL"; default: if (c >= 128) return " "; else return std::string() + static_cast(c); } } std::string toEngineeringString(double value) { constexpr static std::array Suffixes = { "a", "f", "p", "n", "u", "m", "", "k", "M", "G", "T", "P", "E" }; int8_t suffixIndex = 6; while (suffixIndex != 0 && suffixIndex != 12 && (value >= 1000 || value < 1) && value != 0) { if (value >= 1000) { value /= 1000; suffixIndex++; } else if (value < 1) { value *= 1000; suffixIndex--; } } return std::to_string(value).substr(0, 5) + Suffixes[suffixIndex]; } void startProgram(const std::vector &command) { #if defined(OS_WINDOWS) std::ignore = system(fmt::format("start \"\" {0:?}", fmt::join(command, " ")).c_str()); #elif defined(OS_MACOS) std::ignore = system(fmt::format("{0:?}", fmt::join(command, " ")).c_str()); #elif defined(OS_LINUX) std::vector xdgCommand = { "xdg-open" }; xdgCommand.insert(xdgCommand.end(), command.begin(), command.end()); executeCmd(xdgCommand); #elif defined(OS_WEB) std::ignore = command; #endif } int executeCommand(const std::string &command) { return ::system(command.c_str()); } std::optional executeCommandWithOutput(const std::string &command) { std::array buffer = {}; std::string result; #if defined(OS_WINDOWS) FILE* pipe = _popen(command.c_str(), "r"); #else FILE* pipe = popen(command.c_str(), "r"); #endif if (!pipe) { hex::log::error("Failed to open pipe for command: {}", command); return std::nullopt; } try { while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { result += buffer.data(); } } catch (const std::exception &e) { hex::log::error("Exception while reading command output: {}", e.what()); #if defined(OS_WINDOWS) _pclose(pipe); #else pclose(pipe); #endif return std::nullopt; } #if defined(OS_WINDOWS) int exitCode = _pclose(pipe); #else int exitCode = pclose(pipe); #endif if (exitCode != 0) { hex::log::debug("Command exited with code {}: {}", exitCode, command); } return result; } void executeCommandDetach(const std::string &command) { #if defined(OS_WINDOWS) STARTUPINFOA si = { }; PROCESS_INFORMATION pi = { }; si.cb = sizeof(si); DWORD flags = CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW; std::string cmdCopy = command; BOOL result = ::CreateProcessA( nullptr, cmdCopy.data(), nullptr, nullptr, false, flags, nullptr, nullptr, &si, &pi ); if (result) { ::CloseHandle(pi.hProcess); ::CloseHandle(pi.hThread); } #elif defined(OS_MACOS) || defined(OS_LINUX) pid_t pid; const char* argv[] = { "sh", "-c", command.c_str(), nullptr }; ::posix_spawnp(&pid, "sh", nullptr, nullptr, const_cast(argv), nullptr); #elif defined(OS_WEB) std::ignore = command; #endif } void openWebpage(std::string url) { if (!url.contains("://")) url = "https://" + url; #if defined(OS_WINDOWS) ShellExecuteA(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL); #elif defined(OS_MACOS) openWebpageMacos(url.c_str()); #elif defined(OS_LINUX) executeCmd({ "xdg-open", url }); #elif defined(OS_WEB) EM_ASM({ window.open(UTF8ToString($0), '_blank'); }, url.c_str()); #else #warning "Unknown OS, can't open webpages" #endif } std::optional hexCharToValue(char c) { if (std::isdigit(c)) return c - '0'; else if (std::isxdigit(c)) return std::toupper(c) - 'A' + 0x0A; else return { }; } std::string encodeByteString(const std::vector &bytes) { std::string result; for (u8 byte : bytes) { if (std::isprint(byte) && byte != '\\') { result += char(byte); } else { switch (byte) { case '\\': result += "\\"; break; case '\a': result += "\\a"; break; case '\b': result += "\\b"; break; case '\f': result += "\\f"; break; case '\n': result += "\\n"; break; case '\r': result += "\\r"; break; case '\t': result += "\\t"; break; case '\v': result += "\\v"; break; default: result += fmt::format("\\x{:02X}", byte); break; } } } return result; } std::vector decodeByteString(const std::string &string) { u32 offset = 0; std::vector result; while (offset < string.length()) { auto c = [&] { return string[offset]; }; if (c() == '\\') { if ((offset + 2) > string.length()) return {}; offset++; char escapeChar = c(); offset++; switch (escapeChar) { case 'a': result.push_back('\a'); break; case 'b': result.push_back('\b'); break; case 'f': result.push_back('\f'); break; case 'n': result.push_back('\n'); break; case 'r': result.push_back('\r'); break; case 't': result.push_back('\t'); break; case 'v': result.push_back('\v'); break; case '\\': result.push_back('\\'); break; case 'x': { u8 byte = 0x00; if ((offset + 1) >= string.length()) return {}; for (u8 i = 0; i < 2; i++) { byte <<= 4; if (auto hexValue = hexCharToValue(c()); hexValue.has_value()) byte |= hexValue.value(); else return {}; offset++; } result.push_back(byte); } break; default: return {}; } } else { result.push_back(c()); offset++; } } return result; } std::wstring utf8ToUtf16(const std::string& utf8) { std::vector unicodes; for (size_t byteIndex = 0; byteIndex < utf8.size();) { u32 unicode = 0; size_t unicodeSize = 0; u8 ch = utf8[byteIndex]; byteIndex += 1; if (ch <= 0x7F) { unicode = ch; unicodeSize = 0; } else if (ch <= 0xBF) { //NOLINT(bugprone-branch-clone) return { }; } else if (ch <= 0xDF) { unicode = ch&0x1F; unicodeSize = 1; } else if (ch <= 0xEF) { unicode = ch&0x0F; unicodeSize = 2; } else if (ch <= 0xF7) { unicode = ch&0x07; unicodeSize = 3; } else { return { }; } for (size_t unicodeByteIndex = 0; unicodeByteIndex < unicodeSize; unicodeByteIndex += 1) { if (byteIndex == utf8.size()) return { }; u8 byte = utf8[byteIndex]; if (byte < 0x80 || byte > 0xBF) return { }; unicode <<= 6; unicode += byte & 0x3F; byteIndex += 1; } if (unicode >= 0xD800 && unicode <= 0xDFFF) return { }; if (unicode > 0x10FFFF) return { }; unicodes.push_back(unicode); } std::wstring utf16; for (auto unicode : unicodes) { if (unicode <= 0xFFFF) { utf16 += static_cast(unicode); } else { unicode -= 0x10000; utf16 += static_cast(((unicode >> 10) + 0xD800)); utf16 += static_cast(((unicode & 0x3FF) + 0xDC00)); } } return utf16; } std::string utf16ToUtf8(const std::wstring& utf16) { std::vector unicodes; for (size_t index = 0; index < utf16.size();) { u32 unicode = 0; wchar_t wch = utf16[index]; index += 1; if (wch < 0xD800 || wch > 0xDFFF) { unicode = static_cast(wch); // NOLINT(cert-str34-c) } else if (wch >= 0xD800 && wch <= 0xDBFF) { if (index == utf16.size()) return ""; wchar_t nextWch = utf16[index]; index += 1; if (nextWch < 0xDC00 || nextWch > 0xDFFF) return ""; unicode = static_cast(((wch - 0xD800) << 10) + (nextWch - 0xDC00) + 0x10000); } else { return ""; } unicodes.push_back(unicode); } std::string utf8; for (auto unicode : unicodes) { if (unicode <= 0x7F) { utf8 += static_cast(unicode); } else if (unicode <= 0x7FF) { utf8 += static_cast(0xC0 | ((unicode >> 6) & 0x1F)); utf8 += static_cast(0x80 | (unicode & 0x3F)); } else if (unicode <= 0xFFFF) { utf8 += static_cast(0xE0 | ((unicode >> 12) & 0x0F)); utf8 += static_cast(0x80 | ((unicode >> 6) & 0x3F)); utf8 += static_cast(0x80 | (unicode & 0x3F)); } else if (unicode <= 0x10FFFF) { utf8 += static_cast(0xF0 | ((unicode >> 18) & 0x07)); utf8 += static_cast(0x80 | ((unicode >> 12) & 0x3F)); utf8 += static_cast(0x80 | ((unicode >> 6) & 0x3F)); utf8 += static_cast(0x80 | (unicode & 0x3F)); } else { return ""; } } return utf8; } bool isProcessElevated() { #if defined(OS_WINDOWS) bool elevated = false; HANDLE token = INVALID_HANDLE_VALUE; if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) { TOKEN_ELEVATION elevation; DWORD elevationSize = sizeof(TOKEN_ELEVATION); if (::GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &elevationSize)) elevated = elevation.TokenIsElevated; } if (token != INVALID_HANDLE_VALUE) ::CloseHandle(token); return elevated; #elif defined(OS_LINUX) || defined(OS_MACOS) return getuid() == 0 || getuid() != geteuid(); #else return false; #endif } std::optional getEnvironmentVariable(const std::string &env) { auto value = std::getenv(env.c_str()); if (value == nullptr) return std::nullopt; else return value; } [[nodiscard]] std::string limitStringLength(const std::string &string, size_t maxLength, bool fromBothEnds) { // If the string is shorter than the max length, return it as is if (string.size() < maxLength) return string; // If the string is longer than the max length, find the last space before the max length auto it = string.begin() + (fromBothEnds ? maxLength / 2 : maxLength); while (it != string.begin() && !std::isspace(*it)) --it; // If there's no space before the max length, just cut the string if (it == string.begin()) { it = string.begin() + maxLength / 2; // Try to find a UTF-8 character boundary while (it != string.begin() && (*it & 0xC0) == 0x80) --it; } // If we still didn't find a valid boundary, just return the string as is if (it == string.begin()) return string; auto result = std::string(string.begin(), it) + "…"; if (!fromBothEnds) return result; // If the string is longer than the max length, find the last space before the max length it = string.end() - 1 - maxLength / 2; while (it != string.end() && !std::isspace(*it)) ++it; // If there's no space before the max length, just cut the string if (it == string.end()) { it = string.end() - 1 - maxLength / 2; // Try to find a UTF-8 character boundary while (it != string.end() && (*it & 0xC0) == 0x80) ++it; } return result + std::string(it, string.end()); } static std::optional s_fileToOpen; extern "C" void openFile(const char *path) { log::info("Opening file: {0}", path); s_fileToOpen = path; } std::optional getInitialFilePath() { return s_fileToOpen; } static AutoReset> s_fonts; extern "C" void registerFont(const char *fontName, const char *fontPath) { s_fonts->emplace(fontPath, fontName); } const std::map& getFonts() { return s_fonts; } namespace { std::string generateHexViewImpl(u64 offset, auto begin, auto end) { constexpr static auto HeaderLine = "Hex View 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"; std::string result; const auto size = std::distance(begin, end); result.reserve(std::string(HeaderLine).size() * size / 0x10); result += HeaderLine; u64 address = offset & ~u64(0x0F); std::string asciiRow; for (auto it = begin; it != end; ++it) { u8 byte = *it; if ((address % 0x10) == 0) { result += fmt::format(" {}", asciiRow); result += fmt::format("\n{0:08X} ", address); asciiRow.clear(); if (address == (offset & ~u64(0x0F))) { for (u64 i = 0; i < (offset - address); i++) { result += " "; asciiRow += " "; } if (offset - address >= 8) result += " "; address = offset; } } result += fmt::format("{0:02X} ", byte); asciiRow += std::isprint(byte) ? char(byte) : '.'; if ((address % 0x10) == 0x07) result += " "; address++; } if ((address % 0x10) != 0x00) for (u32 i = 0; i < (0x10 - (address % 0x10)); i++) result += " "; result += fmt::format(" {}", asciiRow); return result; } } std::string generateHexView(u64 offset, u64 size, prv::Provider *provider) { auto reader = prv::ProviderReader(provider); reader.seek(offset); reader.setEndAddress((offset + size) - 1); return generateHexViewImpl(offset, reader.begin(), reader.end()); } std::string generateHexView(u64 offset, const std::vector &data) { return generateHexViewImpl(offset, data.begin(), data.end()); } std::string formatSystemError(i32 error) { #if defined(OS_WINDOWS) wchar_t *message = nullptr; auto wLength = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (wchar_t*)&message, 0, nullptr ); ON_SCOPE_EXIT { LocalFree(message); }; auto length = ::WideCharToMultiByte(CP_UTF8, 0, message, wLength, nullptr, 0, nullptr, nullptr); std::string result(length, '\x00'); ::WideCharToMultiByte(CP_UTF8, 0, message, wLength, result.data(), length, nullptr, nullptr); return result; #else return std::system_category().message(error); #endif } void* getContainingModule(void* symbol) { #if defined(OS_WINDOWS) MEMORY_BASIC_INFORMATION mbi; if (VirtualQuery(symbol, &mbi, sizeof(mbi))) return mbi.AllocationBase; return nullptr; #elif !defined(OS_WEB) Dl_info info = {}; if (dladdr(symbol, &info) == 0) return nullptr; return dlopen(info.dli_fname, RTLD_LAZY); #else std::ignore = symbol; return nullptr; #endif } std::optional blendColors(const std::optional &a, const std::optional &b) { if (!a.has_value() && !b.has_value()) return std::nullopt; else if (a.has_value() && !b.has_value()) return a; else if (!a.has_value() && b.has_value()) return b; else return ImAlphaBlendColors(a.value(), b.value()); } std::optional parseTime(std::string_view format, const std::string &timeString) { std::istringstream input(timeString); input.imbue(std::locale(std::setlocale(LC_ALL, nullptr))); tm time = {}; input >> std::get_time(&time, std::string(format).data()); if (input.fail()) { return std::nullopt; } return std::chrono::system_clock::from_time_t(std::mktime(&time)); } std::optional getOSLanguage() { const static auto osLanguage = [] -> std::optional { #if defined(OS_WINDOWS) const auto langId = ::GetUserDefaultUILanguage(); std::array localeName; if (::LCIDToLocaleName(MAKELCID(langId, SORT_DEFAULT), localeName.data(), localeName.size(), 0) > 0) { return utf16ToUtf8(localeName.data()); } return std::nullopt; #elif defined(OS_MACOS) const auto langs = CFLocaleCopyPreferredLanguages(); if (langs == nullptr || CFArrayGetCount(langs) == 0) return std::nullopt; ON_SCOPE_EXIT { CFRelease(langs); }; const auto lang = (CFStringRef)CFArrayGetValueAtIndex(langs, 0); std::array buffer; if (CFStringGetCString(lang, buffer.data(), buffer.size(), kCFStringEncodingUTF8)) { return std::string(buffer.data()); } return std::nullopt; #elif defined(OS_LINUX) auto lang = getEnvironmentVariable("LC_ALL"); if (!lang.has_value()) lang = getEnvironmentVariable("LC_MESSAGES"); if (!lang.has_value()) lang = getEnvironmentVariable("LANG"); if (lang.has_value() && !lang->empty() && *lang != "C" && *lang != "C.UTF-8") { auto parts = wolv::util::splitString(*lang, "."); if (!parts.empty()) return parts[0]; else return lang; } return std::nullopt; #elif defined(OS_WEB) char *resultRaw = (char*)EM_ASM_PTR({ return stringToNewUTF8(navigator.language.length > 0 ? navigator.language : navigator.languages[0]); }); std::string result(resultRaw); std::free(resultRaw); return result; #else return std::nullopt; #endif }(); return osLanguage; } extern "C" void macOSCloseButtonPressed() { EventCloseButtonPressed::post(); } extern "C" void macosEventDataReceived(const u8 *data, size_t length) { EventNativeMessageReceived::post(std::vector(data, data + length)); } void showErrorMessageBox(const std::string &message) { log::fatal("{}", message); #if defined(OS_WINDOWS) MessageBoxA(nullptr, message.c_str(), "Error", MB_ICONERROR | MB_OK); #elif defined (OS_MACOS) errorMessageMacos(message.c_str()); #elif defined(OS_LINUX) if (std::system(fmt::format(R"(zenity --error --text="{}")", message).c_str()) != 0) { std::ignore = std::system(fmt::format(R"(notify-send -i "net.werwolv.ImHex" "Error" "{}")", message).c_str()); } #elif defined(OS_WEB) EM_ASM({ alert(UTF8ToString($0)); }, message.c_str()); #endif } void showToastMessage(const std::string &title, const std::string &message) { #if defined(OS_WINDOWS) const auto wideTitle = wolv::util::utf8ToWstring(title).value_or(L"???"); const auto wideMessage = wolv::util::utf8ToWstring(message).value_or(L"???"); WNDCLASS wc = { }; wc.lpfnWndProc = DefWindowProc; wc.hInstance = GetModuleHandle(nullptr); wc.lpszClassName = L"ImHex Toast"; RegisterClass(&wc); HWND hwnd = CreateWindow( wc.lpszClassName, L"", 0, 0, 0, 0, 0, nullptr, nullptr, wc.hInstance, nullptr); NOTIFYICONDATA nid = { }; nid.cbSize = sizeof(nid); nid.hWnd = hwnd; nid.uID = 1; nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_INFO; nid.uCallbackMessage = WM_USER + 1; nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; nid.hIcon = LoadIcon(nullptr, IDI_INFORMATION); nid.uTimeout = 5000; wcsncpy(nid.szTip, L"ImHex", ARRAYSIZE(nid.szTip)); wcsncpy(nid.szInfoTitle, wideTitle.c_str(), ARRAYSIZE(nid.szInfoTitle)); wcsncpy(nid.szInfo, wideMessage.c_str(), ARRAYSIZE(nid.szInfo)); nid.dwInfoFlags = NIIF_INFO; Shell_NotifyIcon(NIM_ADD, &nid); Sleep(100); Shell_NotifyIcon(NIM_DELETE, &nid); CloseWindow(hwnd); DestroyWindow(hwnd); #elif defined(OS_MACOS) toastMessageMacos(title.c_str(), message.c_str()); #elif defined(OS_LINUX) if (std::system(fmt::format(R"(notify-send -i "net.werwolv.ImHex" "{}" "{}")", title, message).c_str()) != 0) { std::ignore = std::system(fmt::format(R"(zenity --info --title="{}" --text="{}")", title, message).c_str()); } #elif defined(OS_WEB) EM_ASM({ try { const t = UTF8ToString($0); const m = UTF8ToString($1); if (Notification.permission === "granted") { new Notification(t, { body: m }); } else if (Notification.permission !== "denied") { Notification.requestPermission().then(function(p) { if (p === "granted") { new Notification(t, { body: m }); } }); } } catch (e) { console.error(e); } }, title.c_str(), message.c_str()); #endif } } ================================================ FILE: lib/libimhex/source/helpers/utils_linux.cpp ================================================ #if defined(OS_LINUX) #include #include #include #include namespace hex { void executeCmd(const std::vector &argsVector) { std::vector cArgsVector; cArgsVector.reserve(argsVector.size()); for (const auto &str : argsVector) { cArgsVector.push_back(const_cast(str.c_str())); } cArgsVector.push_back(nullptr); if (fork() == 0) { execvp(cArgsVector[0], cArgsVector.data()); log::error("execvp() failed: {}", strerror(errno)); exit(EXIT_FAILURE); } } } #endif ================================================ FILE: lib/libimhex/source/helpers/utils_macos.m ================================================ #if defined(OS_MACOS) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #include #include #include #include #include #include #include #include #include #define GLFW_EXPOSE_NATIVE_COCOA #include #include #import #import #import #import #include void errorMessageMacos(const char *cMessage) { CFStringRef strMessage = CFStringCreateWithCString(NULL, cMessage, kCFStringEncodingUTF8); CFUserNotificationDisplayAlert(0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, strMessage, NULL, NULL, NULL, NULL, NULL); } void openFile(const char *path); void registerFont(const char *fontName, const char *fontPath); void openWebpageMacos(const char *url) { CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL); LSOpenCFURLRef(urlRef, NULL); CFRelease(urlRef); } bool isMacosSystemDarkModeEnabled(void) { NSString * appleInterfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; if (appleInterfaceStyle && [appleInterfaceStyle length] > 0) { return [[appleInterfaceStyle lowercaseString] containsString:@"dark"]; } else { return false; } } float getBackingScaleFactor(void) { return [[NSScreen mainScreen] backingScaleFactor]; } void macOSCloseButtonPressed(void); @interface CloseButtonHandler : NSObject @end @implementation CloseButtonHandler - (void)pressed:(id)sender { macOSCloseButtonPressed(); } @end void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode) { NSWindow* cocoaWindow = glfwGetCocoaWindow(window); cocoaWindow.titleVisibility = NSWindowTitleHidden; if (borderlessWindowMode) { cocoaWindow.titlebarAppearsTransparent = YES; cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView; // Setup liquid glass background effect { NSView* glfwContentView = [cocoaWindow contentView]; NSOpenGLContext* context = [NSOpenGLContext currentContext]; if (!context) { glfwMakeContextCurrent(window); context = [NSOpenGLContext currentContext]; } GLint opaque = 0; [context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; [context update]; NSView* containerView = [[NSView alloc] initWithFrame:[glfwContentView frame]]; containerView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; [containerView setWantsLayer:YES]; Class glassEffectClass = NSClassFromString(@"NSGlassEffectView"); NSView* effectView = nil; if (glassEffectClass) { // Use the new liquid glass effect effectView = [[glassEffectClass alloc] initWithFrame:[containerView bounds]]; } else { // Fall back to NSVisualEffectView for older systems NSVisualEffectView* visualEffectView = [[NSVisualEffectView alloc] initWithFrame:[containerView bounds]]; visualEffectView.material = NSVisualEffectMaterialHUDWindow; visualEffectView.blendingMode = NSVisualEffectBlendingModeBehindWindow; visualEffectView.state = NSVisualEffectStateActive; effectView = visualEffectView; } effectView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; [containerView addSubview:effectView]; [glfwContentView removeFromSuperview]; glfwContentView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; [containerView addSubview:glfwContentView]; [cocoaWindow setContentView:containerView]; } [cocoaWindow setOpaque:NO]; [cocoaWindow setHasShadow:YES]; [cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]]; } NSButton *closeButton = [cocoaWindow standardWindowButton:NSWindowCloseButton]; [closeButton setAction:@selector(pressed:)]; [closeButton setTarget:[CloseButtonHandler alloc]]; } bool isMacosFullScreenModeEnabled(GLFWwindow *window) { NSWindow* cocoaWindow = glfwGetCocoaWindow(window); return (cocoaWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; } void enumerateFontsMacos(void) { CFArrayRef fontDescriptors = CTFontManagerCopyAvailableFontFamilyNames(); CFIndex count = CFArrayGetCount(fontDescriptors); for (CFIndex i = 0; i < count; i++) { CFStringRef fontName = (CFStringRef)CFArrayGetValueAtIndex(fontDescriptors, i); // Get font path - skip fonts without valid URLs CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (__bridge NSString *)kCTFontFamilyNameAttribute : (__bridge NSString *)fontName }; CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attributes); CFURLRef fontURL = CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute); if (fontURL != NULL) { CFStringRef fontPath = CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle); if (fontPath != NULL) { registerFont([(__bridge NSString *)fontName UTF8String], [(__bridge NSString *)fontPath UTF8String]); CFRelease(fontPath); } CFRelease(fontURL); } CFRelease(descriptor); } CFRelease(fontDescriptors); } void macosHandleTitlebarDoubleClickGesture(GLFWwindow *window) { NSWindow* cocoaWindow = glfwGetCocoaWindow(window); // Consult user preferences: "System Settings -> Desktop & Dock -> Double-click a window's title bar to" NSString* action = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleActionOnDoubleClick"]; if (action == nil || [action isEqualToString:@"None"]) { // Nothing to do } else if ([action isEqualToString:@"Minimize"]) { if ([cocoaWindow isMiniaturizable]) { [cocoaWindow miniaturize:nil]; } } else if ([action isEqualToString:@"Maximize"]) { // `[NSWindow zoom:_ sender]` takes over pumping the main runloop for the duration of the resize, // and would interfere with our renderer's frame logic. Schedule it for the next frame CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ if ([cocoaWindow isZoomable]) { [cocoaWindow zoom:nil]; } }); } } void macosSetWindowMovable(GLFWwindow *window, bool movable) { NSWindow* cocoaWindow = glfwGetCocoaWindow(window); [cocoaWindow setMovable:movable]; } bool macosIsWindowBeingResizedByUser(GLFWwindow *window) { NSWindow* cocoaWindow = glfwGetCocoaWindow(window); return cocoaWindow.inLiveResize; } void macosMarkContentEdited(GLFWwindow *window, bool edited) { NSWindow* cocoaWindow = glfwGetCocoaWindow(window); [cocoaWindow setDocumentEdited:edited]; } static NSArray* getRunningInstances(NSString *bundleIdentifier) { return [NSRunningApplication runningApplicationsWithBundleIdentifier: bundleIdentifier]; } bool macosIsMainInstance(void) { NSArray *applications = getRunningInstances(@"net.WerWolv.ImHex"); return applications.count == 0; } extern void macosEventDataReceived(const unsigned char *data, size_t length); static OSErr handleAppleEvent(const AppleEvent *event, AppleEvent *reply, void *refcon) { (void)reply; (void)refcon; // Extract the raw binary data from the event's parameter AEDesc paramDesc; OSErr err = AEGetParamDesc(event, keyDirectObject, typeWildCard, ¶mDesc); if (err != noErr) { NSLog(@"Failed to get parameter: %d", err); return err; } // Convert the AEDesc to NSData NSAppleEventDescriptor *descriptor = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:¶mDesc]; NSData *binaryData = descriptor.data; // Process the binary data if (binaryData) { macosEventDataReceived(binaryData.bytes, binaryData.length); } return noErr; } void macosInstallEventListener(void) { AEInstallEventHandler('misc', 'imhx', NewAEEventHandlerUPP(handleAppleEvent), 0, false); } void macosSendMessageToMainInstance(const unsigned char *data, size_t size) { NSString *bundleIdentifier = @"net.WerWolv.ImHex"; NSData *binaryData = [NSData dataWithBytes:data length:size]; // Find the target application by its bundle identifier NSAppleEventDescriptor *targetApp = [NSAppleEventDescriptor descriptorWithBundleIdentifier:bundleIdentifier]; if (!targetApp) { NSLog(@"Application with bundle identifier %@ not found.", bundleIdentifier); return; } // Create the Apple event NSAppleEventDescriptor *event = [[NSAppleEventDescriptor alloc] initWithEventClass:'misc' eventID:'imhx' targetDescriptor:targetApp returnID:kAutoGenerateReturnID transactionID:kAnyTransactionID]; // Add a parameter with raw binary data NSAppleEventDescriptor *binaryDescriptor = [NSAppleEventDescriptor descriptorWithDescriptorType:typeData data:binaryData]; [event setParamDescriptor:binaryDescriptor forKeyword:keyDirectObject]; // Send the event OSStatus status = AESendMessage([event aeDesc], NULL, kAENoReply, kAEDefaultTimeout); if (status != noErr) { NSLog(@"Failed to send Apple event: %d", status); } } @interface HexDocument : NSDocument @end @implementation HexDocument - (BOOL) readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError { NSString* urlString = [url absoluteString]; const char* utf8String = [urlString UTF8String]; const char *prefix = "file://"; if (strncmp(utf8String, prefix, strlen(prefix)) == 0) utf8String += strlen(prefix); openFile(utf8String); return YES; } @end void macosGetKey(enum Keys key, int *output) { *output = 0x00; switch (key) { case Space: *output = ' '; break; case Apostrophe: *output = '\''; break; case Comma: *output = ','; break; case Minus: *output = '-'; break; case Period: *output = '.'; break; case Slash: *output = '/'; break; case Num0: *output = '0'; break; case Num1: *output = '1'; break; case Num2: *output = '2'; break; case Num3: *output = '3'; break; case Num4: *output = '4'; break; case Num5: *output = '5'; break; case Num6: *output = '6'; break; case Num7: *output = '7'; break; case Num8: *output = '8'; break; case Num9: *output = '9'; break; case Semicolon: *output = ';'; break; case Equals: *output = '='; break; case A: *output = 'a'; break; case B: *output = 'b'; break; case C: *output = 'c'; break; case D: *output = 'd'; break; case E: *output = 'e'; break; case F: *output = 'f'; break; case G: *output = 'g'; break; case H: *output = 'h'; break; case I: *output = 'i'; break; case J: *output = 'j'; break; case K: *output = 'k'; break; case L: *output = 'l'; break; case M: *output = 'm'; break; case N: *output = 'n'; break; case O: *output = 'o'; break; case P: *output = 'p'; break; case Q: *output = 'q'; break; case R: *output = 'r'; break; case S: *output = 's'; break; case T: *output = 't'; break; case U: *output = 'u'; break; case V: *output = 'v'; break; case W: *output = 'w'; break; case X: *output = 'x'; break; case Y: *output = 'y'; break; case Z: *output = 'z'; break; case LeftBracket: *output = '/'; break; case Backslash: *output = '\\'; break; case RightBracket: *output = ']'; break; case GraveAccent: *output = '`'; break; case World1: break; case World2: break; case Escape: break; case Enter: *output = NSEnterCharacter; break; case Tab: *output = NSTabCharacter; break; case Backspace: *output = NSBackspaceCharacter; break; case Insert: *output = NSInsertFunctionKey; break; case Delete: *output = NSDeleteCharacter; break; case Right: *output = NSRightArrowFunctionKey; break; case Left: *output = NSLeftArrowFunctionKey; break; case Down: *output = NSDownArrowFunctionKey; break; case Up: *output = NSUpArrowFunctionKey; break; case PageUp: *output = NSPageUpFunctionKey; break; case PageDown: *output = NSPageDownFunctionKey; break; case Home: *output = NSHomeFunctionKey; break; case End: *output = NSEndFunctionKey; break; case CapsLock: break; case ScrollLock: *output = NSScrollLockFunctionKey; break; case NumLock: break; case PrintScreen: *output = NSPrintScreenFunctionKey; break; case Pause: *output = NSPauseFunctionKey; break; case F1: *output = NSF1FunctionKey; break; case F2: *output = NSF2FunctionKey; break; case F3: *output = NSF3FunctionKey; break; case F4: *output = NSF4FunctionKey; break; case F5: *output = NSF5FunctionKey; break; case F6: *output = NSF6FunctionKey; break; case F7: *output = NSF7FunctionKey; break; case F8: *output = NSF8FunctionKey; break; case F9: *output = NSF9FunctionKey; break; case F10: *output = NSF10FunctionKey; break; case F11: *output = NSF11FunctionKey; break; case F12: *output = NSF12FunctionKey; break; case F13: *output = NSF13FunctionKey; break; case F14: *output = NSF14FunctionKey; break; case F15: *output = NSF15FunctionKey; break; case F16: *output = NSF16FunctionKey; break; case F17: *output = NSF17FunctionKey; break; case F18: *output = NSF18FunctionKey; break; case F19: *output = NSF19FunctionKey; break; case F20: *output = NSF20FunctionKey; break; case F21: *output = NSF21FunctionKey; break; case F22: *output = NSF22FunctionKey; break; case F23: *output = NSF23FunctionKey; break; case F24: *output = NSF24FunctionKey; break; case F25: *output = NSF25FunctionKey; break; case KeyPad0: *output = '0'; break; case KeyPad1: *output = '1'; break; case KeyPad2: *output = '2'; break; case KeyPad3: *output = '3'; break; case KeyPad4: *output = '4'; break; case KeyPad5: *output = '5'; break; case KeyPad6: *output = '6'; break; case KeyPad7: *output = '7'; break; case KeyPad8: *output = '8'; break; case KeyPad9: *output = '9'; break; case KeyPadDecimal: *output = '.'; break; case KeyPadDivide: *output = '/'; break; case KeyPadMultiply: *output = '*'; break; case KeyPadSubtract: *output = '-'; break; case KeyPadAdd: *output = '+'; break; case KeyPadEnter: *output = NSEnterCharacter; break; case KeyPadEqual: *output = '='; break; case Menu: *output = NSMenuFunctionKey; break; default: break; } } static bool isRunningInAppBundle(void) { NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; return [bundlePath.pathExtension.lowercaseString isEqualToString:@"app"]; } @interface NotificationDelegate : NSObject @end @implementation NotificationDelegate - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { if (@available(macOS 11.0, *)) { completionHandler(UNNotificationPresentationOptionBanner | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionList); } else { // For macOS 10.15 and earlier completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound); } } @end void toastMessageMacos(const char *title, const char *message) { @autoreleasepool { // Only show notification if we're inside a bundle if (!isRunningInAppBundle()) { return; } NSString *nsTitle = [NSString stringWithUTF8String:title]; NSString *nsMessage = [NSString stringWithUTF8String:message]; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; // Request permission if needed [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) { (void)error; if (!granted) return; UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.title = nsTitle; content.body = nsMessage; content.sound = [UNNotificationSound defaultSound]; // Show notification immediately UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1 repeats:NO]; NSString *identifier = [[NSUUID UUID] UUIDString]; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger]; [center addNotificationRequest:request withCompletionHandler:nil]; }]; } } #pragma clang diagnostic pop #endif ================================================ FILE: lib/libimhex/source/mcp/client.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include namespace hex::mcp { using namespace wolv::literals; int Client::run(std::istream &input, std::ostream &output) { wolv::net::SocketClient client(wolv::net::SocketClient::Type::TCP, true); client.connect("127.0.0.1", Server::McpInternalPort); while (true) { std::string request; std::getline(input, request); if (ImHexApi::System::isMainInstance()) { JsonRpc response(request); response.setError(JsonRpc::ErrorCode::InternalError, "No other instance of ImHex is running. Make sure that you have ImHex open already."); output << response.execute([](auto, auto){ return nlohmann::json::object(); }).value_or("") << '\n'; continue; } client.writeString(request); auto response = client.readBytesUntil(0x00); if (!response.empty() && response.front() != 0x00) output << std::string(response.begin(), response.end()) << std::endl; if (!client.isConnected()) break; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return EXIT_SUCCESS; } } ================================================ FILE: lib/libimhex/source/mcp/server.cpp ================================================ #include #include #include #include #include #include #include #include #include namespace hex::mcp { std::optional JsonRpc::execute(const Callback &callback) { try { auto requestJson = nlohmann::json::parse(m_request); if (requestJson.is_array()) { return handleBatchedMessages(requestJson, callback).transform([](const auto &response) { return response.dump(); }); } else { return handleMessage(requestJson, callback).transform([](const auto &response) { return response.dump(); }); } } catch (const nlohmann::json::exception &) { return createErrorMessage(ErrorCode::ParseError, "Parse error").dump(); } } void JsonRpc::setError(ErrorCode code, std::string message) { m_error = Error{ code, std::move(message) }; } std::optional JsonRpc::handleMessage(const nlohmann::json &request, const Callback &callback) { try { // Validate JSON-RPC request if (!request.contains("jsonrpc") || request["jsonrpc"] != "2.0" || !request.contains("method") || !request["method"].is_string()) { m_id = request.contains("id") ? std::optional(request["id"].get()) : std::nullopt; return createErrorMessage(ErrorCode::InvalidRequest, "Invalid Request"); } m_id = request.contains("id") ? std::optional(request["id"].get()) : std::nullopt; // Return a user-specified error if set if (m_error.has_value()) { return createErrorMessage(m_error->code, m_error->message); } // Execute the method auto result = callback(request["method"].get(), request.value("params", nlohmann::json::object())); if (!m_id.has_value()) return std::nullopt; return createResponseMessage(result.is_null() ? nlohmann::json::object() : result); } catch (const MethodNotFoundException &) { return createErrorMessage(ErrorCode::MethodNotFound, "Method not found"); } catch (const InvalidParametersException &) { return createErrorMessage(ErrorCode::InvalidParams, "Invalid params"); } catch (const std::exception &e) { return createErrorMessage(ErrorCode::InternalError, e.what()); } } std::optional JsonRpc::handleBatchedMessages(const nlohmann::json &request, const Callback &callback) { if (!request.is_array()) { return createErrorMessage(ErrorCode::InvalidRequest, "Invalid Request"); } nlohmann::json responses = nlohmann::json::array(); for (const auto &message : request) { auto response = handleMessage(message, callback); if (response.has_value()) responses.push_back(*response); } if (responses.empty()) return std::nullopt; return responses; } nlohmann::json JsonRpc::createDefaultMessage() { nlohmann::json message; message["jsonrpc"] = "2.0"; if (m_id.has_value()) message["id"] = m_id.value(); else message["id"] = nullptr; return message; } nlohmann::json JsonRpc::createErrorMessage(ErrorCode code, const std::string &message) { auto json = createDefaultMessage(); json["error"] = { { "code", int(code) }, { "message", message } }; return json; } nlohmann::json JsonRpc::createResponseMessage(const nlohmann::json &result) { auto json = createDefaultMessage(); json["result"] = result; return json; } Server::Server() : m_server(McpInternalPort, 1024, 1, true) { } Server::~Server() { this->shutdown(); } void Server::listen() { m_server.accept([this](auto, const std::vector &data) -> std::vector { std::string request(data.begin(), data.end()); TaskManager::setCurrentThreadName("MCP Server"); log::debug("MCP ----> {}", request); JsonRpc rpc(request); auto response = rpc.execute([this](const std::string &method, const nlohmann::json ¶ms) -> nlohmann::json { if (method == "initialize") { return handleInitialize(params); } else if (method.starts_with("notifications/")) { handleNotifications(method.substr(14), params); return {}; } else if (method.ends_with("/list")) { auto primitiveName = method.substr(0, method.size() - 5); if (m_primitives.contains(primitiveName)) { nlohmann::json capabilitiesList = nlohmann::json::array(); for (const auto &[name, primitive] : m_primitives[primitiveName]) { capabilitiesList.push_back(primitive.capabilities); } nlohmann::json result; result[primitiveName] = capabilitiesList; return result; } } else if (method.ends_with("/call")) { auto primitive = method.substr(0, method.size() - 5); if (auto primitiveIt = m_primitives.find(primitive); primitiveIt != m_primitives.end()) { auto name = params.value("name", ""); if (auto functionIt = primitiveIt->second.find(name); functionIt != primitiveIt->second.end()) { auto result = functionIt->second.function(params.value("arguments", nlohmann::json::object())); return result.is_null() ? nlohmann::json::object() : result; } } } throw JsonRpc::MethodNotFoundException(); }); log::debug("MCP <---- {}", response.value_or("")); if (response.has_value()) { response->push_back(0x00); return { response->begin(), response->end() }; } else return std::vector{ 0x00 }; }, [this](auto) { log::info("MCP client disconnected"); m_connected = false; m_clientInfo = {}; }, true); } void Server::shutdown() { m_server.shutdown(); } void Server::disconnect() { m_server.disconnectClients(); } void Server::addPrimitive(std::string type, std::string_view capabilities, std::function function) { auto json = nlohmann::json::parse(capabilities); auto name = json["name"].get(); m_primitives[type][name] = { .capabilities=json, .function=function }; } nlohmann::json Server::handleInitialize(const nlohmann::json ¶ms) { constexpr static auto ServerName = "ImHex"; constexpr static auto ProtocolVersion = "2025-06-18"; m_clientInfo = {}; if (params.contains("protocolVersion")) { auto clientProtocolVersion = params["protocolVersion"].get(); m_clientInfo.protocolVersion = clientProtocolVersion; } else { throw JsonRpc::InvalidParametersException(); } if (params.contains("clientInfo")) { const auto &clientInfo = params["clientInfo"]; m_clientInfo.name = clientInfo.value("name", "???"); m_clientInfo.version = clientInfo.value("version", "???"); log::info("MCP client connected: {} v{} (protocol {})", m_clientInfo.name, m_clientInfo.version, m_clientInfo.protocolVersion); } else { log::info("MCP client connected: Unknown client info"); } return { { "protocolVersion", ProtocolVersion }, { "capabilities", { { "tools", nlohmann::json::object() }, }, }, { "serverInfo", { { "name", ServerName }, { "version", ImHexApi::System::getImHexVersion().get() } } } }; } void Server::handleNotifications(const std::string &method, [[maybe_unused]] const nlohmann::json ¶ms) { if (method == "initialized") { m_connected = true; } } bool Server::isConnected() { return m_connected; } } ================================================ FILE: lib/libimhex/source/providers/cached_provider.cpp ================================================ #include "hex/providers/cached_provider.hpp" #include #include namespace hex::prv { CachedProvider::CachedProvider(size_t cacheBlockSize, size_t maxBlocks) : m_cacheBlockSize(cacheBlockSize), m_maxBlocks(maxBlocks), m_cache(maxBlocks) {} CachedProvider::~CachedProvider() { clearCache(); } Provider::OpenResult CachedProvider::open() { clearCache(); return {}; } void CachedProvider::close() { clearCache(); } void CachedProvider::readRaw(u64 offset, void* buffer, size_t size) { if (!isAvailable() || !isReadable()) return; auto out = static_cast(buffer); while (size > 0) { const auto blockIndex = calcBlockIndex(offset); const auto blockOffset = calcBlockOffset(offset); const auto toRead = std::min(m_cacheBlockSize - blockOffset, size); const auto cacheSlot = blockIndex % m_maxBlocks; { std::shared_lock lock(m_cacheMutex); const auto &slot = m_cache[cacheSlot]; if (slot && slot->index == blockIndex) { std::copy_n(slot->data.begin() + blockOffset, toRead, out); out += toRead; offset += toRead; size -= toRead; continue; } } std::vector blockData(m_cacheBlockSize); readFromSource(blockIndex * m_cacheBlockSize, blockData.data(), m_cacheBlockSize); { std::unique_lock lock(m_cacheMutex); m_cache[cacheSlot] = Block{.index=blockIndex, .data=std::move(blockData), .dirty=false}; std::copy_n(m_cache[cacheSlot]->data.begin() + blockOffset, toRead, out); } out += toRead; offset += toRead; size -= toRead; } } void CachedProvider::writeRaw(u64 offset, const void* buffer, size_t size) { if (!isAvailable() || !isWritable()) return; auto in = static_cast(buffer); while (size > 0) { const auto blockIndex = calcBlockIndex(offset); const auto blockOffset = calcBlockOffset(offset); const auto toWrite = std::min(m_cacheBlockSize - blockOffset, size); const auto cacheSlot = blockIndex % m_maxBlocks; { std::unique_lock lock(m_cacheMutex); auto& slot = m_cache[cacheSlot]; if (!slot || slot->index != blockIndex) { std::vector blockData(m_cacheBlockSize); readFromSource(blockIndex * m_cacheBlockSize, blockData.data(), m_cacheBlockSize); slot = Block { .index=blockIndex, .data=std::move(blockData), .dirty=false }; } std::copy_n(in, toWrite, slot->data.begin() + blockOffset); slot->dirty = true; } writeToSource(offset, in, toWrite); in += toWrite; offset += toWrite; size -= toWrite; } } void CachedProvider::resizeRaw(u64 newSize) { clearCache(); resizeSource(newSize); } u64 CachedProvider::getActualSize() const { if (!isAvailable()) return 0; if (m_cachedSize == 0) { std::unique_lock lock(m_cacheMutex); m_cachedSize = getSourceSize(); } return m_cachedSize; } void CachedProvider::clearCache() { std::unique_lock lock(m_cacheMutex); for (auto& slot : m_cache) slot.reset(); m_cachedSize = 0; } void CachedProvider::evictIfNeeded() { if (m_cache.size() < m_maxBlocks) return; m_cache.erase(m_cache.begin()); } } ================================================ FILE: lib/libimhex/source/providers/memory_provider.cpp ================================================ #include #include namespace hex::prv { Provider::OpenResult MemoryProvider::open() { if (m_data.empty()) { m_data.resize(1); } return {}; } void MemoryProvider::readRaw(u64 offset, void *buffer, size_t size) { auto actualSize = this->getActualSize(); if (actualSize == 0 || (offset + size) > actualSize || buffer == nullptr || size == 0) return; std::memcpy(buffer, &m_data.front() + offset, size); } void MemoryProvider::writeRaw(u64 offset, const void *buffer, size_t size) { if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0) return; std::memcpy(&m_data.front() + offset, buffer, size); } void MemoryProvider::resizeRaw(u64 newSize) { m_data.resize(newSize); } } ================================================ FILE: lib/libimhex/source/providers/provider.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace hex::prv { using namespace wolv::literals; namespace { u32 s_idCounter = 0; } IProviderDataBackupable::IProviderDataBackupable(Provider* provider) : m_provider(provider) { m_shouldCreateBackups = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.enable", true); m_maxSize = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.max_size", 1_MiB); m_backupExtension = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.extension", ".bak"); } void IProviderDataBackupable::createBackupIfNeeded(const std::fs::path &inputFilePath) { if (!m_shouldCreateBackups || m_backupCreated) return; if (m_provider->getActualSize() > m_maxSize) return; const std::fs::path backupFilePath = wolv::util::toUTF8String(inputFilePath) + m_backupExtension; if (wolv::io::fs::copyFile(inputFilePath, backupFilePath, std::fs::copy_options::overwrite_existing)) { if (wolv::io::fs::exists(backupFilePath)) { m_backupCreated = true; log::info("Created backup of provider data at '{}'", backupFilePath.string()); } } } Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) { } Provider::~Provider() { m_overlays.clear(); if (auto selection = ImHexApi::HexEditor::getSelection(); selection.has_value() && selection->provider == this) EventRegionSelected::post(ImHexApi::HexEditor::ProviderRegion { { .address=0x00, .size=0x00 }, nullptr }); } void Provider::read(u64 offset, void *buffer, size_t size, bool overlays) { this->readRaw(offset - this->getBaseAddress(), buffer, size); if (overlays) this->applyOverlays(offset, buffer, size); } void Provider::write(u64 offset, const void *buffer, size_t size) { if (!this->isWritable()) return; EventProviderDataModified::post(this, offset, size, static_cast(buffer)); this->markDirty(); } void Provider::save() { if (!this->isWritable()) return; EventProviderSaved::post(this); } void Provider::saveAs(const std::fs::path &path) { wolv::io::File file(path, wolv::io::File::Mode::Create); if (file.isValid()) { std::vector buffer(std::min(2_MiB, this->getActualSize()), 0x00); size_t bufferSize = 0; for (u64 offset = 0; offset < this->getActualSize(); offset += bufferSize) { bufferSize = std::min(buffer.size(), this->getActualSize() - offset); this->read(this->getBaseAddress() + offset, buffer.data(), bufferSize, true); file.writeBuffer(buffer.data(), bufferSize); } EventProviderSaved::post(this); } } bool Provider::resize(u64 newSize) { if (newSize >> 63) { log::error("new provider size is very large ({}). Is it a negative number ?", newSize); return false; } i64 difference = newSize - this->getActualSize(); if (difference > 0) EventProviderDataInserted::post(this, this->getActualSize(), difference); else if (difference < 0) EventProviderDataRemoved::post(this, this->getActualSize() + difference, -difference); this->markDirty(); return true; } void Provider::insert(u64 offset, u64 size) { EventProviderDataInserted::post(this, offset, size); this->markDirty(); } void Provider::remove(u64 offset, u64 size) { EventProviderDataRemoved::post(this, offset, size); this->markDirty(); } void Provider::insertRaw(u64 offset, u64 size) { auto oldSize = this->getActualSize(); auto newSize = oldSize + size; this->resizeRaw(newSize); std::vector buffer(0x1000, 0x00); auto position = oldSize; while (position > offset) { const auto readSize = std::min(position - offset, buffer.size()); position -= readSize; this->readRaw(position, buffer.data(), readSize); this->writeRaw(position + size, buffer.data(), readSize); } constexpr static std::array ZeroBuffer = {}; auto zeroPosition = offset; auto remainingZeros = size; while (remainingZeros > 0) { const auto writeSize = std::min(remainingZeros, ZeroBuffer.size()); this->writeRaw(zeroPosition, ZeroBuffer.data(), writeSize); zeroPosition += writeSize; remainingZeros -= writeSize; } } void Provider::removeRaw(u64 offset, u64 size) { if (offset > this->getActualSize() || size == 0) return; if ((offset + size) > this->getActualSize()) size = this->getActualSize() - offset; auto oldSize = this->getActualSize(); std::vector buffer(0x1000); const auto newSize = oldSize - size; auto position = offset; while (position < newSize) { const auto readSize = std::min(newSize - position, buffer.size()); this->readRaw(position + size, buffer.data(), readSize); this->writeRaw(position, buffer.data(), readSize); position += readSize; } this->resizeRaw(newSize); } void Provider::applyOverlays(u64 offset, void *buffer, size_t size) const { for (auto &overlay : m_overlays) { auto overlayOffset = overlay->getAddress(); auto overlaySize = overlay->getSize(); u64 overlapMin = std::max(offset, overlayOffset); u64 overlapMax = std::min(offset + size, overlayOffset + overlaySize); if (overlapMax > overlapMin) std::memcpy(static_cast(buffer) + std::max(0, overlapMin - offset), overlay->getData().data() + std::max(0, overlapMin - overlayOffset), overlapMax - overlapMin); } } Overlay *Provider::newOverlay() { return m_overlays.emplace_back(std::make_unique()).get(); } void Provider::deleteOverlay(Overlay *overlay) { m_overlays.remove_if([overlay](const auto &item) { return item.get() == overlay; }); } const std::list> &Provider::getOverlays() const { return m_overlays; } u64 Provider::getPageSize() const { return m_pageSize; } void Provider::setPageSize(u64 pageSize) { if (pageSize > MaxPageSize) pageSize = MaxPageSize; if (pageSize == 0) return; m_pageSize = pageSize; } u32 Provider::getPageCount() const { return (this->getActualSize() / this->getPageSize()) + (this->getActualSize() % this->getPageSize() != 0 ? 1 : 0); } u32 Provider::getCurrentPage() const { return m_currPage; } void Provider::setCurrentPage(u32 page) { if (page < getPageCount()) m_currPage = page; } void Provider::setBaseAddress(u64 address) { m_baseAddress = address; this->markDirty(); } u64 Provider::getBaseAddress() const { return m_baseAddress; } u64 Provider::getCurrentPageAddress() const { return this->getPageSize() * this->getCurrentPage(); } u64 Provider::getSize() const { return std::min(this->getActualSize() - this->getPageSize() * m_currPage, this->getPageSize()); } std::optional Provider::getPageOfAddress(u64 address) const { u32 page = std::floor((address - this->getBaseAddress()) / double(this->getPageSize())); if (page >= this->getPageCount()) return std::nullopt; return page; } void Provider::undo() { m_undoRedoStack.undo(); } void Provider::redo() { m_undoRedoStack.redo(); } bool Provider::canUndo() const { return m_undoRedoStack.canUndo(); } bool Provider::canRedo() const { return m_undoRedoStack.canRedo(); } nlohmann::json Provider::storeSettings(nlohmann::json settings) const { settings["displayName"] = this->getName(); settings["type"] = this->getTypeName(); settings["baseAddress"] = m_baseAddress; settings["currPage"] = m_currPage; return settings; } void Provider::loadSettings(const nlohmann::json &settings) { m_baseAddress = settings["baseAddress"]; m_currPage = settings["currPage"]; } std::pair Provider::getRegionValidity(u64 address) const { u64 absoluteAddress = address - this->getBaseAddress(); if (absoluteAddress < this->getActualSize()) return { Region { .address=this->getBaseAddress() + absoluteAddress, .size=this->getActualSize() - absoluteAddress }, true }; bool insideValidRegion = false; std::optional nextRegionAddress; for (const auto &overlay : m_overlays) { Region overlayRegion = { .address=overlay->getAddress(), .size=overlay->getSize() }; if (!nextRegionAddress.has_value() || overlay->getAddress() < nextRegionAddress) { nextRegionAddress = overlayRegion.getStartAddress(); } if (Region { .address=address, .size=1 }.overlaps(overlayRegion)) { insideValidRegion = true; } } if (!nextRegionAddress.has_value()) return { Region::Invalid(), false }; else return { Region { .address=address, .size=*nextRegionAddress - address }, insideValidRegion }; } u32 Provider::getID() const { return m_id; } void Provider::setID(u32 id) { m_id = id; if (id > s_idCounter) s_idCounter = id + 1; } [[nodiscard]] std::variant Provider::queryInformation(const std::string &category, const std::string &) { if (category == "mime") return magic::getMIMEType(this); else if (category == "description") return magic::getDescription(this); else if (category == "provider_type") return this->getTypeName().get(); else return 0; } [[nodiscard]] bool Provider::isDumpable() const { return true; } } ================================================ FILE: lib/libimhex/source/providers/undo/stack.cpp ================================================ #include #include #include #include #include #include namespace hex::prv::undo { namespace { std::recursive_mutex s_mutex; } Stack::Stack(Provider *provider) : m_provider(provider) { } std::recursive_mutex& Stack::getMutex() { return s_mutex; } void Stack::undo(u32 count) { std::lock_guard lock(s_mutex); // If there are no operations, we can't undo anything. if (m_undoStack.empty()) return; for (u32 i = 0; i < count; i += 1) { // If we reached the start of the list, we can't undo anymore. if (!this->canUndo()) { return; } // Move last element from the undo stack to the redo stack m_redoStack.emplace_back(std::move(m_undoStack.back())); m_redoStack.back()->undo(m_provider); m_undoStack.pop_back(); EventDataChanged::post(m_provider); } } void Stack::redo(u32 count) { std::lock_guard lock(s_mutex); // If there are no operations, we can't redo anything. if (m_redoStack.empty()) return; for (u32 i = 0; i < count; i += 1) { // If we reached the end of the list, we can't redo anymore. if (!this->canRedo()) { return; } // Move last element from the undo stack to the redo stack m_undoStack.emplace_back(std::move(m_redoStack.back())); m_undoStack.back()->redo(m_provider); m_redoStack.pop_back(); EventDataChanged::post(m_provider); } } void Stack::groupOperations(u32 count, const UnlocalizedString &unlocalizedName) { std::lock_guard lock(s_mutex); if (count <= 1) return; auto operation = std::make_unique(unlocalizedName); i64 startIndex = std::max(0, m_undoStack.size() - count); // Move operations from our stack to the group in the same order they were added for (u32 i = 0; i < count; i += 1) { i64 index = startIndex + i; if (index < 0 || u64(index) >= m_undoStack.size()) { break; } m_undoStack[index]->undo(m_provider); operation->addOperation(std::move(m_undoStack[index])); } // Remove the empty operations from the stack m_undoStack.resize(startIndex); this->add(std::move(operation)); } void Stack::apply(const Stack &otherStack) { std::lock_guard lock(s_mutex); for (const auto &operation : otherStack.m_undoStack) { this->add(operation->clone()); } } void Stack::reapply() { std::lock_guard lock(s_mutex); for (const auto &operation : m_undoStack) { operation->redo(m_provider); EventDataChanged::post(m_provider); } } bool Stack::add(std::unique_ptr &&operation) { std::lock_guard lock(s_mutex); // Clear the redo stack m_redoStack.clear(); // Insert the new operation at the end of the list m_undoStack.emplace_back(std::move(operation)); // Do the operation this->getLastOperation()->redo(m_provider); EventDataChanged::post(m_provider); return true; } bool Stack::canUndo() const { std::lock_guard lock(s_mutex); return !m_undoStack.empty(); } bool Stack::canRedo() const { std::lock_guard lock(s_mutex); return !m_redoStack.empty(); } } ================================================ FILE: lib/libimhex/source/subcommands/subcommands.cpp ================================================ #include #include #include #include "hex/subcommands/subcommands.hpp" #include #include #include #include #include namespace hex::subcommands { std::optional findSubCommand(const std::string &arg) { if (arg == "-" || arg == "--") return std::nullopt; for (auto &plugin : PluginManager::getPlugins()) { for (auto &subCommand : plugin.getSubCommands()) { if (fmt::format("--{}", subCommand.commandLong) == arg || fmt::format("-{}", subCommand.commandShort) == arg) { return subCommand; } } } return std::nullopt; } void processArguments(const std::vector &args) { // If no arguments, do not even try to process arguments // (important because this function will exit ImHex if an instance is already opened, // and we don't want that if no arguments were provided) if (args.empty()) return; std::vector>> subCommands; auto argsIter = args.begin(); std::optional currentSubCommand; std::vector currentSubCommandArgs; if (*argsIter == "--") { // Treat the rest of the args as files ++argsIter; std::vector remainingArgs(argsIter, args.end()); subCommands.emplace_back(*findSubCommand("--open"), remainingArgs); argsIter = args.end(); // Skip while loop } else if (!findSubCommand(*argsIter)) { // First argument not a subcommand, treat all args as files subCommands.emplace_back(*findSubCommand("--open"), args); argsIter = args.end(); // Skip while loop } while (argsIter != args.end()) { // Get subcommand associated with the argument // Guaranteed to find a match on the first argument, as the other case has been handled above const auto newSubCommand = findSubCommand(*argsIter); if (*argsIter == "--") { // Treat the rest of the args as files subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs); ++argsIter; std::vector remainingArgs(argsIter, args.end()); subCommands.emplace_back(*findSubCommand("--open"), remainingArgs); currentSubCommand = std::nullopt; currentSubCommandArgs.clear(); break; } // Will always take this `if` statement on the first time through the loop if (newSubCommand.has_value()) { if (currentSubCommand.has_value()) { subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs); } if (newSubCommand->type == SubCommand::Type::SubCommand) { ++argsIter; std::vector remainingArgs(argsIter, args.end()); subCommands.emplace_back(*newSubCommand, remainingArgs); currentSubCommand = std::nullopt; currentSubCommandArgs.clear(); break; } currentSubCommand = newSubCommand; currentSubCommandArgs.clear(); } else { currentSubCommandArgs.push_back(*argsIter); } ++argsIter; } // Save last command to run if (currentSubCommand.has_value()) { subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs); } // Run the subcommands for (const auto &[subcommand, subCommandArgs] : subCommands) { subcommand.callback(subCommandArgs); } // Exit the process if it's not the main instance (the commands have been forwarded to another instance) if (!ImHexApi::System::isMainInstance()) { std::exit(0); } } void forwardSubCommand(const std::string &cmdName, const std::vector &args) { log::debug("Forwarding subcommand {} (maybe to us)", cmdName); std::vector data; if (!args.empty()) { for (const auto &arg: args) { data.insert(data.end(), arg.begin(), arg.end()); data.push_back('\0'); } data.pop_back(); } SendMessageToMainInstance::post(fmt::format("command/{}", cmdName), data); } void registerSubCommand(const std::string &cmdName, const ForwardCommandHandler &handler) { log::debug("Registered new forward command handler: {}", cmdName); ImHexApi::Messaging::registerHandler(fmt::format("command/{}", cmdName), [handler](const std::vector &eventData){ std::string string(reinterpret_cast(eventData.data()), eventData.size()); std::vector args; for (const auto &argument : std::views::split(string, char(0x00))) { std::string arg(argument.data(), argument.size()); args.push_back(arg); } handler(args); }); } } ================================================ FILE: lib/libimhex/source/test/tests.cpp ================================================ #include namespace hex::test { static std::map s_tests; int Tests::addTest(const std::string &name, Function func, bool shouldFail) noexcept { s_tests.insert({ name, { .function=func, .shouldFail=shouldFail } }); return 0; } std::map &Tests::get() noexcept { return s_tests; } bool initPluginImpl(std::string name) { if (name != "Built-in") { if(!initPluginImpl("Built-in")) return false; } const auto *plugin = hex::PluginManager::getPlugin(name); if (plugin == nullptr) { hex::log::fatal("Plugin '{}' was not found !", name); return false; }else if (!plugin->initializePlugin()) { hex::log::fatal("Failed to initialize plugin '{}' !", name); return false; } hex::log::info("Initialized plugin '{}' successfully", name); return true; } } ================================================ FILE: lib/libimhex/source/ui/banner.cpp ================================================ #include #include namespace hex::impl { [[nodiscard]] std::list> &BannerBase::getOpenBanners() { static AutoReset>> openBanners; return openBanners; } std::mutex& BannerBase::getMutex() { static std::mutex mutex; return mutex; } } ================================================ FILE: lib/libimhex/source/ui/imgui_imhex_extensions.cpp ================================================ #include #include #include #include #include #include #include #undef IMGUI_DEFINE_MATH_OPERATORS #define STB_IMAGE_IMPLEMENTATION #include #include #include #include #include #include #include #include #include #include namespace ImGuiExt { using namespace ImGui; namespace { bool isOpenGLExtensionSupported(const char *name) { static std::set extensions; if (extensions.empty()) { GLint extensionCount = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount); for (GLint i = 0; i < extensionCount; i++) { std::string extension = reinterpret_cast(glGetStringi(GL_EXTENSIONS, i)); extensions.emplace(std::move(extension)); } } return extensions.contains(name); } bool isOpenGLVersionAtLeast(u8 major, u8 minor) { static GLint actualMajor = 0, actualMinor = 0; if (actualMajor == 0 || actualMinor == 0) { glGetIntegerv(GL_MAJOR_VERSION, &actualMajor); glGetIntegerv(GL_MINOR_VERSION, &actualMinor); } return actualMajor > major || (actualMajor == major && actualMinor >= minor); } constexpr auto getGLFilter(Texture::Filter filter) { switch (filter) { using enum Texture::Filter; case Nearest: return GL_NEAREST; case Linear: return GL_LINEAR; } return GL_NEAREST; } [[maybe_unused]] GLint getMaxSamples(GLenum target, GLenum format) { GLint maxSamples; glGetInternalformativ(target, format, GL_SAMPLES, 1, &maxSamples); return maxSamples; } GLuint createTextureFromRGBA8Array(const ImU8 *buffer, int width, int height, Texture::Filter filter) { GLuint texture; // Generate texture glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, getGLFilter(filter)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, getGLFilter(filter)); #if defined(GL_UNPACK_ROW_LENGTH) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); #endif // Allocate storage for the texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); return texture; } GLuint createMultisampleTextureFromRGBA8Array(const ImU8 *buffer, int width, int height, Texture::Filter filter) { // Create a regular texture from the RGBA8 array GLuint texture = createTextureFromRGBA8Array(buffer, width, height, filter); if (filter == Texture::Filter::Nearest) { return texture; } if (!isOpenGLVersionAtLeast(3,2)) { return texture; } if (!isOpenGLExtensionSupported("GL_ARB_texture_multisample")) { return texture; } #if defined(GL_TEXTURE_2D_MULTISAMPLE) static const auto sampleCount = std::min(static_cast(8), getMaxSamples(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8)); // Generate renderbuffer GLuint renderbuffer; glGenRenderbuffers(1, &renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); glRenderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH24_STENCIL8, width, height); // Generate framebuffer GLuint framebuffer; glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); // Unbind framebuffer on exit ON_SCOPE_EXIT { glBindFramebuffer(GL_FRAMEBUFFER, 0); }; // Attach texture to color attachment 0 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, texture, 0); // Attach renderbuffer to depth-stencil attachment glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer); // Check framebuffer status if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { hex::log::error("Driver claims to support texture multisampling but it's not working"); return texture; } #endif return texture; } } Texture Texture::fromImage(const ImU8 *buffer, int size, Filter filter) { if (size == 0) return {}; unsigned char *imageData = nullptr; Texture result; imageData = stbi_load_from_memory(buffer, size, &result.m_width, &result.m_height, nullptr, 4); if (imageData == nullptr) return {}; GLuint texture = createMultisampleTextureFromRGBA8Array(imageData, result.m_width, result.m_height, filter); STBI_FREE(imageData); result.m_textureId = texture; return result; } Texture Texture::fromImage(std::span buffer, Filter filter) { return Texture::fromImage(reinterpret_cast(buffer.data()), buffer.size(), filter); } Texture Texture::fromImage(const std::fs::path &path, Filter filter) { return Texture::fromImage(wolv::util::toUTF8String(path).c_str(), filter); } Texture Texture::fromImage(const char *path, Filter filter) { Texture result; unsigned char *imageData = stbi_load(path, &result.m_width, &result.m_height, nullptr, 4); if (imageData == nullptr) return {}; GLuint texture = createMultisampleTextureFromRGBA8Array(imageData, result.m_width, result.m_height, filter); STBI_FREE(imageData); result.m_textureId = texture; return result; } Texture Texture::fromGLTexture(unsigned int glTexture, int width, int height) { Texture texture; texture.m_textureId = glTexture; texture.m_width = width; texture.m_height = height; return texture; } Texture Texture::fromBitmap(std::span buffer, int width, int height, Filter filter) { return Texture::fromBitmap(reinterpret_cast(buffer.data()), buffer.size(), width, height, filter); } Texture Texture::fromBitmap(const ImU8 *buffer, int size, int width, int height, Filter filter) { if (width * height * 4 > size) return {}; GLuint texture = createMultisampleTextureFromRGBA8Array(buffer, width, height, filter); Texture result; result.m_width = width; result.m_height = height; result.m_textureId = texture; return result; } Texture Texture::fromSVG(const char *path, int width, int height, Filter filter) { const auto scaleFactor = hex::ImHexApi::System::getBackingScaleFactor(); auto document = lunasvg::Document::loadFromFile(path); auto bitmap = document->renderToBitmap(width * scaleFactor, height * scaleFactor); auto texture = createMultisampleTextureFromRGBA8Array(bitmap.data(), bitmap.width(), bitmap.height(), filter); Texture result; result.m_width = bitmap.width() / scaleFactor; result.m_height = bitmap.height() / scaleFactor; result.m_textureId = texture; return result; } Texture Texture::fromSVG(const std::fs::path &path, int width, int height, Filter filter) { return Texture::fromSVG(wolv::util::toUTF8String(path).c_str(), width, height, filter); } Texture Texture::fromSVG(std::span buffer, int width, int height, Filter filter) { const auto scaleFactor = hex::ImHexApi::System::getBackingScaleFactor(); auto document = lunasvg::Document::loadFromData(reinterpret_cast(buffer.data()), buffer.size()); auto bitmap = document->renderToBitmap(width * scaleFactor, height * scaleFactor); bitmap.convertToRGBA(); auto texture = createMultisampleTextureFromRGBA8Array(bitmap.data(), bitmap.width(), bitmap.height(), filter); Texture result; result.m_width = bitmap.width() / scaleFactor; result.m_height = bitmap.height() / scaleFactor; result.m_textureId = texture; return result; } Texture::Texture(Texture&& other) noexcept { m_textureId = other.m_textureId; m_width = other.m_width; m_height = other.m_height; other.m_textureId = 0; } Texture& Texture::operator=(Texture&& other) noexcept { if (this == &other) return *this; this->reset(); m_textureId = other.m_textureId; m_width = other.m_width; m_height = other.m_height; other.m_textureId = 0; return *this; } Texture::~Texture() { this->reset(); } void Texture::reset() { if (m_textureId != 0) { #if !defined(OS_WEB) if (glDeleteTextures != nullptr) glDeleteTextures(1, reinterpret_cast(&m_textureId)); #endif m_textureId = 0; } } std::vector Texture::toBytes() const noexcept { std::vector result(m_width * m_height * 4); #if defined(OS_WEB) GLuint fbo; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textureId, 0); glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, result.data()); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); #else glBindTexture(GL_TEXTURE_2D, m_textureId); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, result.data()); glBindTexture(GL_TEXTURE_2D, 0); #endif return result; } float GetTextWrapPos() { return GImGui->CurrentWindow->DC.TextWrapPos; } int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data) { if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) { auto &string = *static_cast(data->UserData); string.resize(data->BufTextLen); data->Buf = string.data(); } return 0; } bool IconHyperlink(const char *icon, const char *label, const ImVec2 &size_arg, ImGuiButtonFlags flags) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext &g = *GImGui; const ImGuiID id = window->GetID(label); ImVec2 label_size = CalcTextSize(icon, nullptr, false); label_size.x += CalcTextSize(" ", nullptr, false).x + CalcTextSize(label, nullptr, false).x; ImVec2 pos = window->DC.CursorPos; ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y); const ImRect bb(pos, pos + size); ItemAdd(bb, id); bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); // Render BeginGroup(); TextDisabled("%s", icon); SameLine(); const ImU32 col = hovered ? GetColorU32(ImGuiCol_ButtonHovered) : GetColorU32(ImGuiCol_ButtonActive); PushStyleColor(ImGuiCol_Text, ImU32(col)); TextUnformatted(label); if (hovered) GetWindowDrawList()->AddLine(ImVec2(pos.x, pos.y + size.y), pos + size, ImU32(col)); PopStyleColor(); EndGroup(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } bool Hyperlink(const char *label, const ImVec2 &size_arg, ImGuiButtonFlags flags) { ImGuiWindow *window = GetCurrentWindow(); ImGuiContext &g = *GImGui; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, nullptr, true); ImVec2 pos = window->DC.CursorPos; ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y); const ImRect bb(pos, pos + size); ItemAdd(bb, id); bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); // Render const ImU32 col = hovered ? GetColorU32(ImGuiCol_ButtonHovered) : GetColorU32(ImGuiCol_ButtonActive); PushStyleColor(ImGuiCol_Text, ImU32(col)); TextEx(label, nullptr, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting if (hovered) GetWindowDrawList()->AddLine(ImVec2(pos.x, pos.y + size.y), pos + size, ImU32(col)); PopStyleColor(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } bool BulletHyperlink(const char *label, const ImVec2 &size_arg, ImGuiButtonFlags flags) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, nullptr, true); ImVec2 pos = window->DC.CursorPos; ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y) + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0F); const ImRect bb(pos, pos + size); ItemSize(size, 0); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); // Render const ImU32 col = hovered ? GetColorU32(ImGuiCol_ButtonHovered) : GetColorU32(ImGuiCol_ButtonActive); PushStyleColor(ImGuiCol_Text, ImU32(col)); RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x, g.FontSize * 0.5F), col); RenderText(bb.Min + ImVec2(g.FontSize * 0.5 + style.FramePadding.x, 0.0F), label, nullptr, false); GetWindowDrawList()->AddLine(bb.Min + ImVec2(g.FontSize * 0.5 + style.FramePadding.x, size.y), pos + size - ImVec2(g.FontSize * 0.5 + style.FramePadding.x, 0), ImU32(col)); PopStyleColor(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } bool DescriptionButton(const char *label, const char *description, const char *icon, const ImVec2 &size_arg, ImGuiButtonFlags flags) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 text_size = label_size + CalcTextSize(description, nullptr, true); ImVec2 pos = window->DC.CursorPos; if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; ImVec2 size = CalcItemSize(size_arg, text_size.x + style.FramePadding.x * 4.0F, text_size.y + style.FramePadding.y * 4.0F); const ImRect bb(pos, pos + size); ItemSize(size, style.FramePadding.y); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0, 0.5)); PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); // Render const ImU32 col = GetCustomColorU32((held && hovered) ? ImGuiCustomCol_DescButtonActive : hovered ? ImGuiCustomCol_DescButtonHovered : ImGuiCustomCol_DescButton); float icon_padding = style.FramePadding.x * 2; float label_padding = icon_padding + style.FramePadding.x * 5; float description_padding = label_padding + style.FramePadding.x * 2; RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); PushFont(GImGui->Font, GImGui->FontSizeBase * 1.25F); const ImVec2 icon_size = CalcTextSize(icon, nullptr, true); RenderTextClipped(bb.Min + ImVec2(icon_padding, (size.y - icon_size.y) / 2), bb.Max - style.FramePadding, icon, nullptr, nullptr); PopFont(); PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive)); RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(label_padding, 0), bb.Max - style.FramePadding, label, nullptr, nullptr); PopStyleColor(); PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text)); auto clipBb = bb; clipBb.Max.x -= style.FramePadding.x; RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(description_padding, label_size.y), bb.Max - style.FramePadding, description, nullptr, &text_size, style.ButtonTextAlign, &clipBb); PopStyleColor(); PopStyleVar(2); // Automatically close popups // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } bool DescriptionButtonProgress(const char *label, const char *description, const char *icon, float fraction, const ImVec2 &size_arg, ImGuiButtonFlags flags) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 text_size = CalcTextSize(fmt::format("{}\n{}", label, description).c_str(), nullptr, true); const ImVec2 label_size = CalcTextSize(label, nullptr, true); ImVec2 pos = window->DC.CursorPos; if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; ImVec2 size = CalcItemSize(size_arg, text_size.x + style.FramePadding.x * 4.0F, text_size.y + style.FramePadding.y * 6.0F); const ImRect bb(pos, pos + size); ItemSize(size, style.FramePadding.y); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0, 0.5)); PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); // Render const ImU32 col = GetCustomColorU32((held && hovered) ? ImGuiCustomCol_DescButtonActive : hovered ? ImGuiCustomCol_DescButtonHovered : ImGuiCustomCol_DescButton); float icon_padding = style.FramePadding.x * 2; float label_padding = icon_padding + style.FramePadding.x * 5; float description_padding = label_padding + style.FramePadding.x * 2; RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); PushFont(GImGui->Font, GImGui->FontSizeBase * 1.25F); const ImVec2 icon_size = CalcTextSize(icon, nullptr, true); RenderTextClipped(bb.Min + ImVec2(icon_padding, (size.y - icon_size.y) / 2), bb.Max - style.FramePadding, icon, nullptr, nullptr); PopFont(); PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive)); RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(label_padding, 0), bb.Max - style.FramePadding, label, nullptr, nullptr); PopStyleColor(); PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text)); auto clipBb = bb; clipBb.Max.x -= style.FramePadding.x; RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(description_padding, label_size.y), bb.Max - style.FramePadding, description, nullptr, &text_size, style.ButtonTextAlign, &clipBb); PopStyleColor(); RenderFrame(ImVec2(bb.Min.x, bb.Max.y - 5 * hex::ImHexApi::System::getGlobalScale()), bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), false, style.FrameRounding); RenderFrame(ImVec2(bb.Min.x, bb.Max.y - 5 * hex::ImHexApi::System::getGlobalScale()), ImVec2(bb.Min.x + fraction * bb.GetSize().x, bb.Max.y), GetColorU32(ImGuiCol_Button), false, style.FrameRounding); RenderFrame(bb.Min, bb.Max, 0x00, true, style.FrameRounding); PopStyleVar(2); // Automatically close popups // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } void HelpHover(const char *text, const char *icon, ImU32 iconColor) { PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0)); PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0)); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F); PushStyleColor(ImGuiCol_Text, iconColor); ImGui::PushID(text); Button(icon, ImVec2(0, ImGui::GetTextLineHeightWithSpacing())); ImGui::PopID(); PopStyleColor(); if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { SetNextWindowSizeConstraints(ImVec2(GetTextLineHeight() * 25, 0), ImVec2(GetTextLineHeight() * 35, FLT_MAX)); BeginTooltip(); TextFormattedWrapped("{}", text); EndTooltip(); } PopStyleVar(2); PopStyleColor(3); } void UnderlinedText(const char *label, ImColor color, const ImVec2 &size_arg) { ImGuiWindow *window = GetCurrentWindow(); const ImVec2 label_size = CalcTextSize(label, nullptr, true); ImVec2 pos = window->DC.CursorPos; ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y); PushStyleColor(ImGuiCol_Text, ImU32(color)); TextEx(label, nullptr, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting GetWindowDrawList()->AddLine(ImVec2(pos.x, pos.y + size.y), pos + size, ImU32(color)); PopStyleColor(); } void UnderwavedText(const char *label, ImColor textColor, ImColor lineColor, const ImVec2 &size_arg) { ImGuiWindow *window = GetCurrentWindow(); std::string labelStr(label); for (char letter : labelStr) { std::string letterStr(1, letter); const ImVec2 label_size = CalcTextSize(letterStr.c_str(), nullptr, true); ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y); ImVec2 pos = window->DC.CursorPos; float lineWidth = size.x / 3.0f; float halfLineW = lineWidth / 2.0f; float lineY = pos.y + size.y; ImVec2 initial = ImVec2(pos.x, lineY); ImVec2 pos1 = ImVec2(pos.x + lineWidth, lineY - 2.0f); ImVec2 pos2 = ImVec2(pos.x + lineWidth + halfLineW, lineY); ImVec2 pos3 = ImVec2(pos.x + lineWidth * 2 + halfLineW, lineY - 2.0f); ImVec2 pos4 = ImVec2(pos.x + lineWidth * 3, lineY - 1.0f); PushStyleColor(ImGuiCol_Text, ImU32(textColor)); TextEx(letterStr.c_str(), nullptr, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting GetWindowDrawList()->AddLine(initial, pos1, ImU32(lineColor),0.4f); GetWindowDrawList()->AddLine(pos1, pos2, ImU32(lineColor),0.3f); GetWindowDrawList()->AddLine(pos2, pos3, ImU32(lineColor),0.4f); GetWindowDrawList()->AddLine(pos3, pos4, ImU32(lineColor),0.3f); PopStyleColor(); window->DC.CursorPos = ImVec2(pos.x + size.x, pos.y); } } void TextSpinner(const char *label) { Text("[%c] %s", "|/-\\"[ImU32(GetTime() * 20) % 4], label); } void Header(const char *label, bool firstEntry) { if (!firstEntry) NewLine(); SeparatorText(label); } void HeaderColored(const char *label, ImColor color, bool firstEntry) { if (!firstEntry) NewLine(); TextFormattedColored(color, "{}", label); Separator(); } bool InfoTooltip(const char *text, bool isSeparator) { static double lastMoveTime; static ImGuiID lastHoveredID; double currTime = GetTime(); ImGuiID hoveredID = GetHoveredID(); bool result = false; if (IsItemHovered(ImGuiHoveredFlags_DelayNormal) && (currTime - lastMoveTime) >= 0.5 && hoveredID == lastHoveredID) { if (!std::string_view(text).empty()) { const auto textWidth = CalcTextSize(text).x; const auto maxWidth = 300 * hex::ImHexApi::System::getGlobalScale(); const bool wrapping = textWidth > maxWidth; if (wrapping) ImGui::SetNextWindowSizeConstraints(ImVec2(maxWidth, 0), ImVec2(maxWidth, FLT_MAX)); else ImGui::SetNextWindowSize(ImVec2(textWidth + GetStyle().WindowPadding.x * 2, 0)); if (BeginTooltip()) { if (isSeparator) SeparatorText(text); else { if (wrapping) TextFormattedWrapped("{}", text); else TextFormatted("{}", text); } EndTooltip(); } } result = true; } if (hoveredID != lastHoveredID) { lastMoveTime = currTime; } lastHoveredID = hoveredID; return result; } ImU32 GetCustomColorU32(ImGuiCustomCol idx, float alpha_mul) { auto &customData = *static_cast(GImGui->IO.UserData); ImVec4 c = customData.Colors[idx]; c.w *= GImGui->Style.Alpha * alpha_mul; return ColorConvertFloat4ToU32(c); } ImVec4 GetCustomColorVec4(ImGuiCustomCol idx, float alpha_mul) { auto &customData = *static_cast(GImGui->IO.UserData); ImVec4 c = customData.Colors[idx]; c.w *= GImGui->Style.Alpha * alpha_mul; return c; } float GetCustomStyleFloat(ImGuiCustomStyle idx) { auto &customData = *static_cast(GImGui->IO.UserData); switch (idx) { case ImGuiCustomStyle_WindowBlur: return customData.styles.WindowBlur; default: return 0.0F; } } ImVec2 GetCustomStyleVec2(ImGuiCustomStyle idx) { std::ignore = idx; return {}; } void StyleCustomColorsDark() { auto &colors = static_cast(GImGui->IO.UserData)->Colors; colors[ImGuiCustomCol_DescButton] = ImColor(20, 20, 20); colors[ImGuiCustomCol_DescButtonHovered] = ImColor(40, 40, 40); colors[ImGuiCustomCol_DescButtonActive] = ImColor(60, 60, 60); colors[ImGuiCustomCol_ToolbarGray] = ImColor(230, 230, 230); colors[ImGuiCustomCol_ToolbarRed] = ImColor(231, 76, 60); colors[ImGuiCustomCol_ToolbarYellow] = ImColor(241, 196, 15); colors[ImGuiCustomCol_ToolbarGreen] = ImColor(56, 139, 66); colors[ImGuiCustomCol_ToolbarBlue] = ImColor(6, 83, 155); colors[ImGuiCustomCol_ToolbarPurple] = ImColor(103, 42, 120); colors[ImGuiCustomCol_ToolbarBrown] = ImColor(219, 179, 119); colors[ImGuiCustomCol_Highlight] = ImColor(77, 198, 155); colors[ImGuiCustomCol_IEEEToolSign] = ImColor(93, 93, 127); colors[ImGuiCustomCol_IEEEToolExp] = ImColor(93, 127, 93); colors[ImGuiCustomCol_IEEEToolMantissa] = ImColor(127, 93, 93); } void StyleCustomColorsLight() { auto &colors = static_cast(GImGui->IO.UserData)->Colors; colors[ImGuiCustomCol_DescButton] = ImColor(230, 230, 230); colors[ImGuiCustomCol_DescButtonHovered] = ImColor(210, 210, 210); colors[ImGuiCustomCol_DescButtonActive] = ImColor(190, 190, 190); colors[ImGuiCustomCol_ToolbarGray] = ImColor(25, 25, 25); colors[ImGuiCustomCol_ToolbarRed] = ImColor(231, 76, 60); colors[ImGuiCustomCol_ToolbarYellow] = ImColor(241, 196, 15); colors[ImGuiCustomCol_ToolbarGreen] = ImColor(56, 139, 66); colors[ImGuiCustomCol_ToolbarBlue] = ImColor(6, 83, 155); colors[ImGuiCustomCol_ToolbarPurple] = ImColor(103, 42, 120); colors[ImGuiCustomCol_ToolbarBrown] = ImColor(219, 179, 119); colors[ImGuiCustomCol_Highlight] = ImColor(41, 151, 112); colors[ImGuiCustomCol_IEEEToolSign] = ImColor(187, 187, 255); colors[ImGuiCustomCol_IEEEToolExp] = ImColor(187, 255, 187); colors[ImGuiCustomCol_IEEEToolMantissa] = ImColor(255, 187,187); } void StyleCustomColorsClassic() { auto &colors = static_cast(GImGui->IO.UserData)->Colors; colors[ImGuiCustomCol_DescButton] = ImColor(40, 40, 80); colors[ImGuiCustomCol_DescButtonHovered] = ImColor(60, 60, 100); colors[ImGuiCustomCol_DescButtonActive] = ImColor(80, 80, 120); colors[ImGuiCustomCol_ToolbarGray] = ImColor(230, 230, 230); colors[ImGuiCustomCol_ToolbarRed] = ImColor(231, 76, 60); colors[ImGuiCustomCol_ToolbarYellow] = ImColor(241, 196, 15); colors[ImGuiCustomCol_ToolbarGreen] = ImColor(56, 139, 66); colors[ImGuiCustomCol_ToolbarBlue] = ImColor(6, 83, 155); colors[ImGuiCustomCol_ToolbarPurple] = ImColor(103, 42, 120); colors[ImGuiCustomCol_ToolbarBrown] = ImColor(219, 179, 119); colors[ImGuiCustomCol_Highlight] = ImColor(77, 198, 155); colors[ImGuiCustomCol_IEEEToolSign] = ImColor(93, 93, 127); colors[ImGuiCustomCol_IEEEToolExp] = ImColor(93, 127, 93); colors[ImGuiCustomCol_IEEEToolMantissa] = ImColor(127, 93, 93); } void OpenPopupInWindow(const char *window_name, const char *popup_name) { if (Begin(window_name)) { OpenPopup(popup_name); } End(); } void DisableWindowResize(ImGuiDir dir) { const auto window = GetCurrentWindow(); const auto borderId = GetWindowResizeBorderID(window, dir); if (borderId == GetHoveredID()) { GImGui->ActiveIdMouseButton = 0; SetHoveredID(0); } if (borderId == GetActiveID()) SetActiveID(0, window); } bool TitleBarButton(const char *label, ImVec2 size_arg) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, nullptr, true); ImVec2 pos = window->DC.CursorPos; 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(size, style.FramePadding.y); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, col, false, style.FrameRounding); RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, nullptr, &label_size, style.ButtonTextAlign, &bb); // Automatically close popups // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } bool ToolBarButton(const char *symbol, ImVec4 color) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return false; color.w = 1.0F; ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; const ImGuiID id = window->GetID(symbol); const ImVec2 label_size = CalcTextSize(symbol, nullptr, true); ImVec2 pos = window->DC.CursorPos; ImVec2 size = CalcItemSize(ImVec2(1, 1) * GetCurrentWindow()->MenuBarHeight, label_size.x + style.FramePadding.x * 2.0F, label_size.y + style.FramePadding.y * 2.0F); ImVec2 padding = (size - label_size) / 2; const ImRect bb(pos, pos + size); ItemSize(size, style.FramePadding.y); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); PushStyleColor(ImGuiCol_Text, color); // Render ImU32 col = 0x00; if (held) col = GetColorU32(ImGuiCol_ScrollbarGrabActive); else if (hovered) col = GetColorU32(ImGuiCol_ScrollbarGrabHovered); RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, col, false, style.FrameRounding); RenderTextClipped(bb.Min + padding, bb.Max - padding, symbol, nullptr, &size, style.ButtonTextAlign, &bb); PopStyleColor(); // Automatically close popups // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); IMGUI_TEST_ENGINE_ITEM_INFO(id, symbol, g.LastItemData.StatusFlags); return pressed; } bool IconButton(const char *symbol, ImVec4 color, ImVec2 size_arg, ImVec2 iconOffset) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return false; color.w = 1.0F; ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; const ImGuiID id = window->GetID(symbol); const ImVec2 label_size = CalcTextSize(symbol, nullptr, true); ImVec2 pos = window->DC.CursorPos; 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(size, style.FramePadding.y); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); PushStyleColor(ImGuiCol_Text, color); // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); RenderTextClipped(bb.Min + style.FramePadding + iconOffset, bb.Max - style.FramePadding, symbol, nullptr, &label_size, style.ButtonTextAlign, &bb); PopStyleColor(); // Automatically close popups // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); IMGUI_TEST_ENGINE_ITEM_INFO(id, symbol, g.LastItemData.StatusFlags); return pressed; } bool InputPrefix(const char* label, const char *prefix, std::string &buffer, ImGuiInputTextFlags flags) { auto window = GetCurrentWindow(); const ImGuiStyle &style = GImGui->Style; const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), CalcTextSize(prefix).x, label_size.y + style.FramePadding.y * 2.0F); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(CalcItemWidth(), frame_size.y)); SetCursorPosX(GetCursorPosX() + frame_size.x); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); PushStyleVar(ImGuiStyleVar_Alpha, 0.6F); RenderText(ImVec2(frame_bb.Min.x + style.FramePadding.x, frame_bb.Min.y + style.FramePadding.y), prefix); PopStyleVar(); bool value_changed = false; PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0); PushStyleColor(ImGuiCol_FrameBg, 0x00000000); PushStyleColor(ImGuiCol_FrameBgHovered, 0x00000000); PushStyleColor(ImGuiCol_FrameBgActive, 0x00000000); value_changed = ImGui::InputText(label, buffer, flags); PopStyleColor(3); PopStyleVar(); if (value_changed) MarkItemEdited(GImGui->LastItemData.ID); return value_changed; } bool InputIntegerPrefix(const char *label, const char *prefix, void *value, ImGuiDataType type, const char *format, ImGuiInputTextFlags flags) { auto window = GetCurrentWindow(); const ImGuiStyle &style = GImGui->Style; const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), CalcTextSize(prefix).x, label_size.y + style.FramePadding.y * 2.0F); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(CalcItemWidth(), frame_size.y)); SetCursorPosX(GetCursorPosX() + frame_size.x); char buf[64]; DataTypeFormatString(buf, IM_ARRAYSIZE(buf), type, value, format); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); PushStyleVar(ImGuiStyleVar_Alpha, 0.6F); RenderText(ImVec2(frame_bb.Min.x + style.FramePadding.x, frame_bb.Min.y + style.FramePadding.y), prefix); PopStyleVar(); bool value_changed = false; PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0); PushStyleColor(ImGuiCol_FrameBg, 0x00000000); PushStyleColor(ImGuiCol_FrameBgHovered, 0x00000000); PushStyleColor(ImGuiCol_FrameBgActive, 0x00000000); if (InputTextEx(label, nullptr, buf, IM_ARRAYSIZE(buf), ImVec2(CalcItemWidth() - frame_size.x, label_size.y + style.FramePadding.y * 2.0F), flags)) value_changed = DataTypeApplyFromText(buf, type, value, format); PopStyleColor(3); PopStyleVar(); if (value_changed) MarkItemEdited(GImGui->LastItemData.ID); return value_changed; } bool InputHexadecimal(const char *label, u32 *value, ImGuiInputTextFlags flags) { return InputIntegerPrefix(label, "0x", value, ImGuiDataType_U32, "%X", flags | ImGuiInputTextFlags_CharsHexadecimal); } bool InputHexadecimal(const char *label, u64 *value, ImGuiInputTextFlags flags) { return InputIntegerPrefix(label, "0x", value, ImGuiDataType_U64, "%llX", flags | ImGuiInputTextFlags_CharsHexadecimal); } bool SliderBytes(const char *label, u64 *value, u64 min, u64 max, u64 stepSize, ImGuiSliderFlags flags) { std::string format; if (*value < 1024) { format = fmt::format("{} Bytes", *value); } else if (*value < 1024 * 1024) { format = fmt::format("{:.2f} KB", *value / 1024.0); } else if (*value < 1024 * 1024 * 1024) { format = fmt::format("{:.2f} MB", *value / (1024.0 * 1024.0)); } else { format = fmt::format("{:.2f} GB", *value / (1024.0 * 1024.0 * 1024.0)); } *value /= stepSize; min /= stepSize; max /= stepSize; auto result = ImGui::SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format.c_str(), flags | ImGuiSliderFlags_Logarithmic); *value *= stepSize; return result; } void ProgressBar(float fraction, ImVec2 size_value, float yOffset) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; ImVec2 pos = window->DC.CursorPos + ImVec2(0, yOffset); ImVec2 size = CalcItemSize(size_value, ImGui::GetContentRegionAvail().x, g.FontSize + style.FramePadding.y * 2.0F); ImRect bb(pos, pos + size); ItemSize(size, 0); if (!ItemAdd(bb, 0)) return; // Render bool no_progress = fraction < 0; fraction = ImSaturate(fraction); RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); if (no_progress) { auto time = (fmod(ImGui::GetTime() * 2, 1.8) - 0.4); RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), ImSaturate(time), ImSaturate(time + 0.2), style.FrameRounding); } else { RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0F, fraction, style.FrameRounding); } } void TextUnformattedCentered(const char *text) { auto availableSpace = ImGui::GetContentRegionAvail(); std::string drawString; auto textEnd = text + strlen(text); for (auto wrapPos = text; wrapPos != textEnd;) { wrapPos = ImGui::GetFont()->CalcWordWrapPosition(GetFontSize(), wrapPos, textEnd, availableSpace.x * 0.8F); if (text == wrapPos) break; drawString += std::string(text, wrapPos) + "\n"; text = wrapPos; } drawString.pop_back(); auto textSize = ImGui::CalcTextSize(drawString.c_str()); ImPlot::AddTextCentered(ImGui::GetWindowDrawList(), ImGui::GetCursorScreenPos() + availableSpace / 2 - ImVec2(0, textSize.y / 2), ImGui::GetColorU32(ImGuiCol_Text), drawString.c_str()); } bool InputTextIcon(const char *label, const char *icon, std::string &buffer, ImGuiInputTextFlags flags) { return InputTextIconHint(label, icon, nullptr, buffer, flags); } bool InputTextIconHint(const char* label, const char *icon, const char *hint, std::string &buffer, ImGuiInputTextFlags flags) { auto window = GetCurrentWindow(); const ImGuiID id = window->GetID(label); const ImGuiStyle &style = GImGui->Style; const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 icon_frame_size = CalcTextSize(icon) + style.FramePadding * 2.0F; const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), icon_frame_size.x, label_size.y + style.FramePadding.y * 2.0F); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); SetCursorPosX(GetCursorPosX() + frame_size.x); float width_adjustment = window->DC.ItemWidth < 0 ? 0 : icon_frame_size.x; bool value_changed = InputTextEx(label, hint, buffer.data(), buffer.size() + 1, ImVec2(CalcItemWidth() - width_adjustment, label_size.y + style.FramePadding.y * 2.0F), ImGuiInputTextFlags_CallbackResize | flags, UpdateStringSizeCallback, &buffer); if (value_changed) MarkItemEdited(GImGui->LastItemData.ID); RenderNavCursor(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); RenderFrame(frame_bb.Min, frame_bb.Min + icon_frame_size, GetColorU32(ImGuiCol_TableBorderStrong), true, style.FrameRounding); RenderText(ImVec2(frame_bb.Min.x + style.FramePadding.x, frame_bb.Min.y + style.FramePadding.y), icon); return value_changed; } bool InputScalarCallback(const char* label, ImGuiDataType data_type, void* p_data, const char* format, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; if (format == nullptr) format = DataTypeGetInfo(data_type)->PrintFmt; char buf[64]; DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); bool value_changed = false; if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) flags |= ImGuiInputTextFlags_CharsDecimal; flags |= ImGuiInputTextFlags_AutoSelectAll; if (ImGui::InputText(label, buf, IM_ARRAYSIZE(buf), flags, callback, user_data)) value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); if (value_changed) MarkItemEdited(g.LastItemData.ID); return value_changed; } void HideTooltip() { char window_name[16]; ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", GImGui->TooltipOverrideCount); if (ImGuiWindow* window = FindWindowByName(window_name); window != nullptr) { if (window->Active) window->Hidden = true; } } bool BitCheckbox(const char* label, bool* v) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 size = ImVec2(CalcTextSize("0").x + style.FramePadding.x * 2, GetFrameHeight()); const ImVec2 pos = window->DC.CursorPos; const ImRect total_bb(pos, pos + size); ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id)) { IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; } bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); if (pressed) { *v = !(*v); MarkItemEdited(id); } const ImRect check_bb(pos, pos + size); RenderNavCursor(total_bb, id); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); RenderText(check_bb.Min + style.FramePadding, *v ? "1" : "0"); ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); if (label_size.x > 0.0F) RenderText(label_pos, label); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } bool DimmedButton(const char* label, ImVec2 size, ImGuiButtonFlags flags){ PushStyleColor(ImGuiCol_ButtonHovered, GetCustomColorU32(ImGuiCustomCol_DescButtonHovered)); PushStyleColor(ImGuiCol_Button, GetCustomColorU32(ImGuiCustomCol_DescButton)); PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive)); PushStyleColor(ImGuiCol_ButtonActive, GetCustomColorU32(ImGuiCustomCol_DescButtonActive)); PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); bool res = ButtonEx(label, size, flags); PopStyleColor(4); PopStyleVar(1); return res; } bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size, ImVec2 iconOffset) { PushStyleColor(ImGuiCol_ButtonHovered, GetCustomColorU32(ImGuiCustomCol_DescButtonHovered)); PushStyleColor(ImGuiCol_Button, GetCustomColorU32(ImGuiCustomCol_DescButton)); PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive)); PushStyleColor(ImGuiCol_ButtonActive, GetCustomColorU32(ImGuiCustomCol_DescButtonActive)); PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5 * hex::ImHexApi::System::getGlobalScale()); bool res = IconButton(symbol, color, size, iconOffset); PopStyleColor(4); PopStyleVar(1); return res; } bool DimmedArrowButton(const char *id, ImGuiDir dir, ImVec2 size) { PushStyleColor(ImGuiCol_ButtonHovered, GetCustomColorU32(ImGuiCustomCol_DescButtonHovered)); PushStyleColor(ImGuiCol_Button, GetCustomColorU32(ImGuiCustomCol_DescButton)); PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive)); PushStyleColor(ImGuiCol_ButtonActive, GetCustomColorU32(ImGuiCustomCol_DescButtonActive)); PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5 * hex::ImHexApi::System::getGlobalScale()); bool res = ArrowButtonEx(id, dir, size); PopStyleColor(4); PopStyleVar(1); return res; } bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size, ImVec2 iconOffset) { bool pushed = false; bool toggled = false; if (*v) { PushStyleColor(ImGuiCol_Border, GetStyleColorVec4(ImGuiCol_ButtonActive)); pushed = true; } if (DimmedIconButton(icon, GetStyleColorVec4(ImGuiCol_Text), size, iconOffset)) { *v = !*v; toggled = true; } if (pushed) PopStyleColor(); return toggled; } bool DimmedIconToggle(const char *icon, bool *v) { bool pushed = false; bool toggled = false; if (*v) { PushStyleColor(ImGuiCol_Border, GetStyleColorVec4(ImGuiCol_ButtonActive)); pushed = true; } if (DimmedIconButton(icon, GetStyleColorVec4(ImGuiCol_Text))) { *v = !*v; toggled = true; } if (pushed) PopStyleColor(); return toggled; } bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v) { bool pushed = false; bool toggled = false; if (*v) { PushStyleColor(ImGuiCol_Border, GetStyleColorVec4(ImGuiCol_ButtonActive)); pushed = true; } if (DimmedIconButton(*v ? iconOn : iconOff, GetStyleColorVec4(ImGuiCol_Text))) { *v = !*v; toggled = true; } if (pushed) PopStyleColor(); return toggled; } void TextOverlay(const char *text, ImVec2 pos, float maxWidth) { const auto textSize = CalcTextSize(text, nullptr, false, maxWidth); const auto textPos = pos - textSize / 2; const auto margin = GetStyle().FramePadding * 2; const auto textRect = ImRect(textPos - margin, textPos + textSize + margin); auto drawList = GetWindowDrawList(); drawList->AddDrawCmd(); drawList->AddRectFilled(textRect.Min, textRect.Max, GetColorU32(ImGuiCol_WindowBg) | 0xFF000000); drawList->AddRect(textRect.Min, textRect.Max, GetColorU32(ImGuiCol_Border)); drawList->AddDrawCmd(); drawList->AddText(nullptr, 0.0F, textPos, GetColorU32(ImGuiCol_Text), text, nullptr, maxWidth); } bool BeginBox() { PushStyleVar(ImGuiStyleVar_CellPadding, hex::scaled(5, 5)); if (BeginTable("##box", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_SizingStretchSame)) { TableNextRow(); TableNextColumn(); return true; } return false; } void EndBox() { EndTable(); PopStyleVar(); } bool BeginSubWindow(const char *label, bool *collapsed, ImVec2 size, ImGuiChildFlags flags) { const bool hasMenuBar = !std::string_view(label).empty(); bool result = false; ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0F); ImGui::PushID("SubWindow"); if (ImGui::BeginChild(label, size, ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY | flags, hasMenuBar ? ImGuiWindowFlags_MenuBar : ImGuiWindowFlags_None)) { result = true; if (hasMenuBar && ImGui::BeginMenuBar()) { if (collapsed == nullptr) ImGui::TextUnformatted(label); else { const auto &style = ImGui::GetStyle(); const auto framePadding = style.FramePadding.x; ImGui::PushStyleVarX(ImGuiStyleVar_FramePadding, 0); ImGui::SetCursorPosX(ImGui::GetCursorPosX() - style.WindowPadding.x + framePadding); ImGui::TreeNodeSetOpen(ImGui::GetID("##CollapseHeader"), !*collapsed); *collapsed = !ImGui::TreeNodeEx("##CollapseHeader", ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanLabelWidth); ImGui::SameLine(0, framePadding); ImGui::TextUnformatted(label); if (!*collapsed) ImGui::TreePop(); ImGui::PopStyleVar(); } ImGui::EndMenuBar(); } if (collapsed != nullptr && *collapsed) { result = false; } } ImGui::PopStyleVar(); return result; } void EndSubWindow() { ImGui::EndChild(); ImGui::PopID(); } bool VSliderAngle(const char* label, const ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) { if (format == nullptr) format = "%.0f deg"; float v_deg = (*v_rad) * 360.0F / (2 * std::numbers::pi_v); bool value_changed = ImGui::VSliderFloat(label, size, &v_deg, v_degrees_min, v_degrees_max, format, flags); *v_rad = v_deg * (2 * std::numbers::pi_v) / 360.0F; return value_changed; } bool InputFilePicker(const char *label, std::fs::path &path, const std::vector &validExtensions) { bool picked = false; ImGui::PushID(label); const auto framePadding = ImGui::GetStyle().FramePadding.x; const auto buttonSize = ImVec2(ImGui::CalcTextSize("...").x + framePadding * 2, ImGui::GetFrameHeight()); ImGui::PushItemWidth(ImGui::CalcItemWidth() - buttonSize.x - framePadding); std::string string = wolv::util::toUTF8String(path); if (ImGui::InputText("##pathInput", string, ImGuiInputTextFlags_AutoSelectAll)) { path = std::u8string(string.begin(), string.end()); picked = true; } ImGui::PopItemWidth(); ImGui::SameLine(0, framePadding); if (ImGui::Button("...", buttonSize)) { hex::fs::openFileBrowser(hex::fs::DialogMode::Open, validExtensions, [&](const std::fs::path &pickedPath) { path = pickedPath; picked = true; }); } ImGui::SameLine(); ImGui::TextUnformatted(label); ImGui::PopID(); return picked; } bool ToggleSwitch(const char *label, bool *v) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 size = ImVec2(GetFrameHeight() * 2.0F, GetFrameHeight()); const ImVec2 pos = window->DC.CursorPos; const ImRect total_bb(pos, pos + ImVec2(size.x + (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)) { IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; } bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); if (pressed) { *v = !(*v); MarkItemEdited(id); } const ImRect knob_bb(pos, pos + size); window->DrawList->AddRectFilled(knob_bb.Min, knob_bb.Max, GetColorU32(held ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : *v ? ImGuiCol_ButtonActive : ImGuiCol_Button), size.y / 2); if (*v) window->DrawList->AddCircleFilled(knob_bb.Max - ImVec2(size.y / 2, size.y / 2), (size.y - style.ItemInnerSpacing.y) / 2, GetColorU32(ImGuiCol_ScrollbarGrabActive), 16); else window->DrawList->AddCircleFilled(knob_bb.Min + ImVec2(size.y / 2, size.y / 2), (size.y - style.ItemInnerSpacing.y) / 2, GetColorU32(ImGuiCol_ScrollbarGrabActive), 16); ImVec2 label_pos = ImVec2(knob_bb.Max.x + style.ItemInnerSpacing.x, knob_bb.Min.y + style.FramePadding.y); if (g.LogEnabled) LogRenderedText(&label_pos, *v ? "((*) )" : "( (*))"); if (label_size.x > 0.0F) RenderText(label_pos, label); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } bool ToggleSwitch(const char *label, bool v) { return ToggleSwitch(label, &v); } bool PopupTitleBarButton(const char* label, bool p_enabled) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImGuiID id = window->GetID(label); const ImRect title_rect = window->TitleBarRect(); const ImVec2 size(g.FontSize, g.FontSize); // Button size matches font size for aesthetic consistency. const ImVec2 pos = window->DC.CursorPos; const ImVec2 max_pos = pos + size; const ImRect bb(pos.x, title_rect.Min.y, max_pos.x, title_rect.Max.y); ImGui::PushClipRect(title_rect.Min, title_rect.Max, false); // Check for item addition (similar to how clipping is handled in the original button functions). bool is_clipped = !ItemAdd(bb, id); bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); if (is_clipped) { ImGui::PopClipRect(); return pressed; } // const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); // window->DrawList->AddCircleFilled(bb.GetCenter(), ImMax(2.0f, g.FontSize * 0.5f + 1.0f), bg_col); // Draw the label in the center ImU32 text_col = GetColorU32(p_enabled || hovered ? ImGuiCol_Text : ImGuiCol_TextDisabled); window->DrawList->AddText(bb.GetCenter() - ImVec2(g.FontSize * 0.45F, g.FontSize * 0.5F), text_col, label); // Return the button press state ImGui::PopClipRect(); return pressed; } void PopupTitleBarText(const char* text) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImRect title_rect = window->TitleBarRect(); const ImVec2 size(g.FontSize, g.FontSize); // Button size matches font size for aesthetic consistency. const ImVec2 pos = window->DC.CursorPos; const ImVec2 max_pos = pos + size; const ImRect bb(pos.x, title_rect.Min.y, max_pos.x, title_rect.Max.y); ImGui::PushClipRect(title_rect.Min, title_rect.Max, false); // Draw the label in the center ImU32 text_col = GetColorU32(ImGuiCol_Text); window->DrawList->AddText(bb.GetCenter() - ImVec2(g.FontSize * 0.45F, g.FontSize * 0.5F), text_col, text); // Return the button press state ImGui::PopClipRect(); } bool IsDarkBackground(const ImColor& bgColor) { // Extract RGB components in 0–255 range int r = static_cast(bgColor.Value.x * 255.0F); int g = static_cast(bgColor.Value.y * 255.0F); int b = static_cast(bgColor.Value.z * 255.0F); // Compute brightness using perceived luminance int brightness = (r * 299 + g * 587 + b * 114) / 1000; // If brightness is below threshold, use white text return brightness < 128; } static bool s_imguiTestEngineEnabled = false; void ImGuiTestEngine::setEnabled(bool enabled) { s_imguiTestEngineEnabled = enabled; } bool ImGuiTestEngine::isEnabled() { return s_imguiTestEngineEnabled; } } namespace ImGui { bool InputText(const char *label, std::u8string &buffer, ImGuiInputTextFlags flags) { return ImGui::InputText(label, reinterpret_cast(buffer.data()), buffer.size() + 1, ImGuiInputTextFlags_CallbackResize | flags, ImGuiExt::UpdateStringSizeCallback, &buffer); } bool InputText(const char *label, std::string &buffer, ImGuiInputTextFlags flags) { return ImGui::InputText(label, buffer.data(), buffer.size() + 1, ImGuiInputTextFlags_CallbackResize | flags, ImGuiExt::UpdateStringSizeCallback, &buffer); } bool InputTextMultiline(const char *label, std::string &buffer, const ImVec2 &size, ImGuiInputTextFlags flags) { return ImGui::InputTextMultiline(label, buffer.data(), buffer.size() + 1, size, ImGuiInputTextFlags_CallbackResize | flags, ImGuiExt::UpdateStringSizeCallback, &buffer); } bool InputTextWithHint(const char *label, const char *hint, std::string &buffer, ImGuiInputTextFlags flags) { return ImGui::InputTextWithHint(label, hint, buffer.data(), buffer.size() + 1, ImGuiInputTextFlags_CallbackResize | flags, ImGuiExt::UpdateStringSizeCallback, &buffer); } } ================================================ FILE: lib/libimhex/source/ui/popup.cpp ================================================ #include #include namespace hex::impl { [[nodiscard]] std::vector> &PopupBase::getOpenPopups() { static AutoReset>> openPopups; return openPopups; } std::mutex& PopupBase::getMutex() { static std::mutex mutex; return mutex; } } ================================================ FILE: lib/libimhex/source/ui/toast.cpp ================================================ #include #include namespace hex::impl { [[nodiscard]] std::list> &ToastBase::getQueuedToasts() { static AutoReset>> queuedToasts; return queuedToasts; } std::mutex& ToastBase::getMutex() { static std::mutex mutex; return mutex; } } ================================================ FILE: lib/libimhex/source/ui/view.cpp ================================================ #include #include #include #include #include #include #include #include #include namespace hex { static AutoReset s_lastFocusedView = nullptr; View::View(UnlocalizedString unlocalizedName, const char *icon) : m_unlocalizedViewName(std::move(unlocalizedName)), m_icon(icon) { } bool View::shouldDraw() const { return ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isAvailable(); } bool View::shouldProcess() const { return this->shouldDraw() && this->getWindowOpenState(); } bool View::hasViewMenuItemEntry() const { return true; } ImVec2 View::getMinSize() const { return scaled({ 300, 400 }); } ImVec2 View::getMaxSize() const { return { FLT_MAX, FLT_MAX }; } ImGuiWindowFlags View::getWindowFlags() const { return ImGuiWindowFlags_None; } bool &View::getWindowOpenState() { return m_windowOpen; } const bool &View::getWindowOpenState() const { return m_windowOpen; } const UnlocalizedString &View::getUnlocalizedName() const { return m_unlocalizedViewName; } std::string View::getName() const { return View::toWindowName(m_unlocalizedViewName); } bool View::didWindowJustOpen() { return std::exchange(m_windowJustOpened, false); } void View::setWindowJustOpened(const bool state) { m_windowJustOpened = state; } bool View::didWindowJustClose() { return std::exchange(m_windowJustClosed, false); } void View::setWindowJustClosed(const bool state) { m_windowJustClosed = state; } void View::trackViewState() { if (m_windowOpen && !m_prevWindowOpen) { this->setWindowJustOpened(true); this->onOpen(); } else if (!m_windowOpen && m_prevWindowOpen) { this->setWindowJustClosed(true); this->onClose(); } m_prevWindowOpen = m_windowOpen; } void View::discardNavigationRequests() { if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard; } void View::bringToFront() { getWindowOpenState() = true; TaskManager::doLater([this]{ ImGui::SetWindowFocus(toWindowName(getUnlocalizedName()).c_str()); }); } std::string View::toWindowName(const UnlocalizedString &unlocalizedName) { return fmt::format("{}###{}", Lang(unlocalizedName), unlocalizedName.get()); } void View::setFocused(bool focused) { m_focused = focused; if (focused) s_lastFocusedView = this; } const View* View::getLastFocusedView() { if (!ImHexApi::Provider::isValid()) return nullptr; return s_lastFocusedView; } void View::Window::handleFocusRestoration() { const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName())); const ImGuiContext& g = *ImGui::GetCurrentContext(); bool foundTopFocused = false; ImGuiWindow *imguiFocusedWindow = nullptr; ImGuiWindow *focusedSubWindow = nullptr; if (g.NavWindow != nullptr) { imguiFocusedWindow = g.NavWindow; foundTopFocused = true; } for (auto focusedWindow: g.WindowsFocusOrder | std::views::reverse) { if (focusedWindow == nullptr || !focusedWindow->WasActive) continue; std::string focusedWindowName = focusedWindow->Name; if (!foundTopFocused) { imguiFocusedWindow = focusedWindow; foundTopFocused = true; } if (imguiFocusedWindow == nullptr || !focusedWindowName.contains("###hex.builtin.view.")) continue; if (auto focusedChild = focusedWindow->NavLastChildNavWindow; focusedChild != nullptr) focusedSubWindow = focusedChild; else if (focusedWindow == focusedWindow->RootWindow) focusedSubWindow = focusedWindow; break; } std::string imguiFocusedWindowName = "NULL"; if (imguiFocusedWindow != nullptr) imguiFocusedWindowName.assign(imguiFocusedWindow->Name); std::string focusedSubWindowName; if (focusedSubWindow != nullptr || m_focusedSubWindow != nullptr) { if (glfwGetWindowAttrib(ImHexApi::System::getMainWindowHandle(), GLFW_FOCUSED)) { focusedSubWindowName = focusedSubWindow != nullptr ? focusedSubWindow->Name : m_focusedSubWindow->Name; if (focusedSubWindow != nullptr && m_focusedSubWindow != nullptr) { std::string_view windowName = m_focusedSubWindow->Name; auto stringsVector = wolv::util::splitString(focusedSubWindowName, "/"); if (stringsVector.back().contains("resize") || (focusedSubWindow == focusedSubWindow->RootWindow && windowName.starts_with(focusedSubWindowName))) focusedSubWindowName = windowName; else m_focusedSubWindow = focusedSubWindow; } else if (focusedSubWindow != nullptr) m_focusedSubWindow = focusedSubWindow; bool windowAlreadyFocused = focusedSubWindowName == imguiFocusedWindowName; bool titleFocused = focusedSubWindowName.starts_with(title); if (titleFocused && !windowAlreadyFocused) { bool windowMayNeedFocus = focusedSubWindowName.starts_with(imguiFocusedWindowName); std::string activeName = g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"; if ((activeName == "NULL" || windowMayNeedFocus) && (imguiFocusedWindowName == "##MainMenuBar" || imguiFocusedWindowName.starts_with("ImHexDockSpace") || imguiFocusedWindowName.contains("###hex.builtin.view."))) { if (m_focusedSubWindow == m_focusedSubWindow->RootWindow) ImGui::FocusWindow(m_focusedSubWindow, ImGuiFocusRequestFlags_RestoreFocusedChild); else ImGui::FocusWindow(m_focusedSubWindow, ImGuiFocusRequestFlags_None); } } } } } void View::Window::draw(ImGuiWindowFlags extraFlags) { if (this->shouldDraw()) { const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName())); handleFocusRestoration(); if (!allowScroll()) extraFlags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize()); if (ImGui::Begin(title.c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | extraFlags | this->getWindowFlags())) { TutorialManager::setLastItemInteractiveHelpPopup([this]{ this->drawHelpText(); }); this->drawContent(); } ImGui::End(); } } void View::Special::draw(ImGuiWindowFlags extraFlags) { std::ignore = extraFlags; if (this->shouldDraw()) { ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize()); this->drawContent(); } } void View::Floating::draw(ImGuiWindowFlags extraFlags) { Window::draw(extraFlags | ImGuiWindowFlags_NoDocking); } void View::Scrolling::draw(ImGuiWindowFlags extraFlags) { Window::draw(extraFlags); } void View::Modal::draw(ImGuiWindowFlags extraFlags) { if (this->shouldDraw()) { if (this->getWindowOpenState()) ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str()); ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F)); ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize()); const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName())); if (ImGui::BeginPopupModal(title.c_str(), this->hasCloseButton() ? &this->getWindowOpenState() : nullptr, ImGuiWindowFlags_NoCollapse | extraFlags | this->getWindowFlags())) { this->drawContent(); ImGui::EndPopup(); } if (ImGui::IsKeyPressed(ImGuiKey_Escape)) this->getWindowOpenState() = false; } } void View::FullScreen::draw(ImGuiWindowFlags extraFlags) { std::ignore = extraFlags; this->drawContent(); this->drawAlwaysVisibleContent(); } } ================================================ FILE: lib/third_party/.clang-tidy ================================================ # Disable all checks Checks: '-*' ================================================ FILE: lib/third_party/boost/CMakeLists.txt ================================================ project(boost) add_subdirectory(regex) ================================================ FILE: lib/third_party/boost/regex/CMakeLists.txt ================================================ project(boost-regex) add_library(boost-regex INTERFACE) target_include_directories(boost-regex INTERFACE include ) target_compile_definitions(boost-regex INTERFACE BOOST_REGEX_STANDALONE) add_library(boost::regex ALIAS boost-regex) ================================================ FILE: lib/third_party/boost/regex/include/boost/cregex.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org/libs/regex for most recent version. * FILE cregex.cpp * VERSION see * DESCRIPTION: Declares POSIX API functions * + boost::RegEx high level wrapper. */ #ifndef BOOST_RE_CREGEX_HPP #define BOOST_RE_CREGEX_HPP #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #ifdef BOOST_REGEX_CXX03 #include #else #include #endif #endif /* include guard */ ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/concepts.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE concepts.hpp * VERSION see * DESCRIPTION: Declares regular expression concepts. */ #ifndef BOOST_REGEX_CONCEPTS_HPP_INCLUDED #define BOOST_REGEX_CONCEPTS_HPP_INCLUDED #include #include #include #include #include #ifndef BOOST_TEST_TR1_REGEX #include #endif #include #include #include #ifdef BOOST_REGEX_CXX03 #define RW_NS boost #else #define RW_NS std #endif namespace boost{ // // bitmask_archetype: // this can be either an integer type, an enum, or a std::bitset, // we use the latter as the architype as it offers the "strictest" // of the possible interfaces: // typedef std::bitset<512> bitmask_archetype; // // char_architype: // A strict model for the character type interface. // struct char_architype { // default constructable: char_architype(); // copy constructable / assignable: char_architype(const char_architype&); char_architype& operator=(const char_architype&); // constructable from an integral value: char_architype(unsigned long val); // comparable: bool operator==(const char_architype&)const; bool operator!=(const char_architype&)const; bool operator<(const char_architype&)const; bool operator<=(const char_architype&)const; bool operator>=(const char_architype&)const; bool operator>(const char_architype&)const; // conversion to integral type: operator long()const; }; inline long hash_value(char_architype val) { return val; } // // char_architype can not be used with basic_string: // } // namespace boost namespace std{ template<> struct char_traits { // The intent is that this template is not instantiated, // but this typedef gives us a chance of compilation in // case it is: typedef boost::char_architype char_type; }; } // // Allocator architype: // template class allocator_architype { public: typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; typedef unsigned size_type; typedef int difference_type; template struct rebind { typedef allocator_architype other; }; pointer address(reference r){ return &r; } const_pointer address(const_reference r) { return &r; } pointer allocate(size_type n) { return static_cast(std::malloc(n)); } pointer allocate(size_type n, pointer) { return static_cast(std::malloc(n)); } void deallocate(pointer p, size_type) { std::free(p); } size_type max_size()const { return UINT_MAX; } allocator_architype(){} allocator_architype(const allocator_architype&){} template allocator_architype(const allocator_architype&){} void construct(pointer p, const_reference r) { new (p)T(r); } void destroy(pointer p) { p->~T(); } }; template bool operator == (const allocator_architype&, const allocator_architype&) {return true; } template bool operator != (const allocator_architype&, const allocator_architype&) { return false; } namespace boost{ // // regex_traits_architype: // A strict interpretation of the regular expression traits class requirements. // template struct regex_traits_architype { public: regex_traits_architype(){} typedef charT char_type; // typedef std::size_t size_type; typedef std::vector string_type; typedef copy_constructible_archetype > locale_type; typedef bitmask_archetype char_class_type; static std::size_t length(const char_type* ) { return 0; } charT translate(charT ) const { return charT(); } charT translate_nocase(charT ) const { return static_object::get(); } template string_type transform(ForwardIterator , ForwardIterator ) const { return static_object::get(); } template string_type transform_primary(ForwardIterator , ForwardIterator ) const { return static_object::get(); } template char_class_type lookup_classname(ForwardIterator , ForwardIterator ) const { return static_object::get(); } template string_type lookup_collatename(ForwardIterator , ForwardIterator ) const { return static_object::get(); } bool isctype(charT, char_class_type) const { return false; } int value(charT, int) const { return 0; } locale_type imbue(locale_type l) { return l; } locale_type getloc()const { return static_object::get(); } private: // this type is not copyable: regex_traits_architype(const regex_traits_architype&){} regex_traits_architype& operator=(const regex_traits_architype&){ return *this; } }; // // alter this to std::tr1, to test a std implementation: // #ifndef BOOST_TEST_TR1_REGEX namespace global_regex_namespace = ::boost; #else namespace global_regex_namespace = ::std::tr1; #endif template struct BitmaskConcept { void constraints() { function_requires >(); function_requires >(); m_mask1 = m_mask2 | m_mask3; m_mask1 = m_mask2 & m_mask3; m_mask1 = m_mask2 ^ m_mask3; m_mask1 = ~m_mask2; m_mask1 |= m_mask2; m_mask1 &= m_mask2; m_mask1 ^= m_mask2; } Bitmask m_mask1, m_mask2, m_mask3; }; template struct RegexTraitsConcept { RegexTraitsConcept(); // required typedefs: typedef typename traits::char_type char_type; // typedef typename traits::size_type size_type; typedef typename traits::string_type string_type; typedef typename traits::locale_type locale_type; typedef typename traits::char_class_type char_class_type; void constraints() { //function_requires >(); function_requires >(); function_requires >(); function_requires >(); function_requires >(); function_requires >(); std::size_t n = traits::length(m_pointer); ignore_unused_variable_warning(n); char_type c = m_ctraits.translate(m_char); ignore_unused_variable_warning(c); c = m_ctraits.translate_nocase(m_char); //string_type::foobar bar; string_type s1 = m_ctraits.transform(m_pointer, m_pointer); ignore_unused_variable_warning(s1); string_type s2 = m_ctraits.transform_primary(m_pointer, m_pointer); ignore_unused_variable_warning(s2); char_class_type cc = m_ctraits.lookup_classname(m_pointer, m_pointer); ignore_unused_variable_warning(cc); string_type s3 = m_ctraits.lookup_collatename(m_pointer, m_pointer); ignore_unused_variable_warning(s3); bool b = m_ctraits.isctype(m_char, cc); ignore_unused_variable_warning(b); int v = m_ctraits.value(m_char, 16); ignore_unused_variable_warning(v); locale_type l(m_ctraits.getloc()); m_traits.imbue(l); ignore_unused_variable_warning(l); } traits m_traits; const traits m_ctraits; const char_type* m_pointer; char_type m_char; private: RegexTraitsConcept& operator=(RegexTraitsConcept&); }; // // helper class to compute what traits class a regular expression type is using: // template struct regex_traits_computer; template struct regex_traits_computer< global_regex_namespace::basic_regex > { typedef traits type; }; // // BaseRegexConcept does not test anything dependent on basic_string, // in case our charT does not have an associated char_traits: // template struct BaseRegexConcept { typedef typename Regex::value_type value_type; //typedef typename Regex::size_type size_type; typedef typename Regex::flag_type flag_type; typedef typename Regex::locale_type locale_type; typedef input_iterator_archetype input_iterator_type; // derived test types: typedef const value_type* pointer_type; typedef bidirectional_iterator_archetype BidiIterator; typedef global_regex_namespace::sub_match sub_match_type; typedef global_regex_namespace::match_results > match_results_type; typedef global_regex_namespace::match_results match_results_default_type; typedef output_iterator_archetype OutIterator; typedef typename regex_traits_computer::type traits_type; typedef global_regex_namespace::regex_iterator regex_iterator_type; typedef global_regex_namespace::regex_token_iterator regex_token_iterator_type; void global_constraints() { // // test non-template components: // function_requires >(); global_regex_namespace::regex_constants::syntax_option_type opts = global_regex_namespace::regex_constants::icase | global_regex_namespace::regex_constants::nosubs | global_regex_namespace::regex_constants::optimize | global_regex_namespace::regex_constants::collate | global_regex_namespace::regex_constants::ECMAScript | global_regex_namespace::regex_constants::basic | global_regex_namespace::regex_constants::extended | global_regex_namespace::regex_constants::awk | global_regex_namespace::regex_constants::grep | global_regex_namespace::regex_constants::egrep; ignore_unused_variable_warning(opts); function_requires >(); global_regex_namespace::regex_constants::match_flag_type mopts = global_regex_namespace::regex_constants::match_default | global_regex_namespace::regex_constants::match_not_bol | global_regex_namespace::regex_constants::match_not_eol | global_regex_namespace::regex_constants::match_not_bow | global_regex_namespace::regex_constants::match_not_eow | global_regex_namespace::regex_constants::match_any | global_regex_namespace::regex_constants::match_not_null | global_regex_namespace::regex_constants::match_continuous | global_regex_namespace::regex_constants::match_prev_avail | global_regex_namespace::regex_constants::format_default | global_regex_namespace::regex_constants::format_sed | global_regex_namespace::regex_constants::format_no_copy | global_regex_namespace::regex_constants::format_first_only; ignore_unused_variable_warning(mopts); BOOST_STATIC_ASSERT((::boost::is_enum::value)); global_regex_namespace::regex_constants::error_type e1 = global_regex_namespace::regex_constants::error_collate; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_ctype; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_escape; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_backref; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_brack; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_paren; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_brace; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_badbrace; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_range; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_space; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_badrepeat; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_complexity; ignore_unused_variable_warning(e1); e1 = global_regex_namespace::regex_constants::error_stack; ignore_unused_variable_warning(e1); BOOST_STATIC_ASSERT((::boost::is_base_and_derived::value )); const global_regex_namespace::regex_error except(e1); e1 = except.code(); typedef typename Regex::value_type regex_value_type; function_requires< RegexTraitsConcept > >(); function_requires< BaseRegexConcept > >(); } void constraints() { global_constraints(); BOOST_STATIC_ASSERT((::boost::is_same< flag_type, global_regex_namespace::regex_constants::syntax_option_type>::value)); flag_type opts = Regex::icase | Regex::nosubs | Regex::optimize | Regex::collate | Regex::ECMAScript | Regex::basic | Regex::extended | Regex::awk | Regex::grep | Regex::egrep; ignore_unused_variable_warning(opts); function_requires >(); function_requires >(); // Regex constructors: Regex e1(m_pointer); ignore_unused_variable_warning(e1); Regex e2(m_pointer, m_flags); ignore_unused_variable_warning(e2); Regex e3(m_pointer, m_size, m_flags); ignore_unused_variable_warning(e3); Regex e4(in1, in2); ignore_unused_variable_warning(e4); Regex e5(in1, in2, m_flags); ignore_unused_variable_warning(e5); // assign etc: Regex e; e = m_pointer; e = e1; e.assign(e1); e.assign(m_pointer); e.assign(m_pointer, m_flags); e.assign(m_pointer, m_size, m_flags); e.assign(in1, in2); e.assign(in1, in2, m_flags); // access: const Regex ce; typename Regex::size_type i = ce.mark_count(); ignore_unused_variable_warning(i); m_flags = ce.flags(); e.imbue(ce.getloc()); e.swap(e1); global_regex_namespace::swap(e, e1); // sub_match: BOOST_STATIC_ASSERT((::boost::is_base_and_derived, sub_match_type>::value)); typedef typename sub_match_type::value_type sub_value_type; typedef typename sub_match_type::difference_type sub_diff_type; typedef typename sub_match_type::iterator sub_iter_type; BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); bool b = m_sub.matched; ignore_unused_variable_warning(b); BidiIterator bi = m_sub.first; ignore_unused_variable_warning(bi); bi = m_sub.second; ignore_unused_variable_warning(bi); sub_diff_type diff = m_sub.length(); ignore_unused_variable_warning(diff); // match_results tests - some typedefs are not used, however these // guarante that they exist (some compilers may warn on non-usage) typedef typename match_results_type::value_type mr_value_type; typedef typename match_results_type::const_reference mr_const_reference; typedef typename match_results_type::reference mr_reference; typedef typename match_results_type::const_iterator mr_const_iterator; typedef typename match_results_type::iterator mr_iterator; typedef typename match_results_type::difference_type mr_difference_type; typedef typename match_results_type::size_type mr_size_type; typedef typename match_results_type::allocator_type mr_allocator_type; typedef typename match_results_type::char_type mr_char_type; typedef typename match_results_type::string_type mr_string_type; match_results_type m1; mr_allocator_type at; match_results_type m2(at); match_results_type m3(m1); m1 = m2; int ival = 0; mr_size_type mrs = m_cresults.size(); ignore_unused_variable_warning(mrs); mrs = m_cresults.max_size(); ignore_unused_variable_warning(mrs); b = m_cresults.empty(); ignore_unused_variable_warning(b); mr_difference_type mrd = m_cresults.length(); ignore_unused_variable_warning(mrd); mrd = m_cresults.length(ival); ignore_unused_variable_warning(mrd); mrd = m_cresults.position(); ignore_unused_variable_warning(mrd); mrd = m_cresults.position(mrs); ignore_unused_variable_warning(mrd); mr_const_reference mrcr = m_cresults[ival]; ignore_unused_variable_warning(mrcr); mr_const_reference mrcr2 = m_cresults.prefix(); ignore_unused_variable_warning(mrcr2); mr_const_reference mrcr3 = m_cresults.suffix(); ignore_unused_variable_warning(mrcr3); mr_const_iterator mrci = m_cresults.begin(); ignore_unused_variable_warning(mrci); mrci = m_cresults.end(); ignore_unused_variable_warning(mrci); (void) m_cresults.get_allocator(); m_results.swap(m_results); global_regex_namespace::swap(m_results, m_results); // regex_match: b = global_regex_namespace::regex_match(m_in, m_in, m_results, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_in, m_in, m_results, e, m_mft); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_in, m_in, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_in, m_in, e, m_mft); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_pointer, m_pmatch, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_pointer, m_pmatch, e, m_mft); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_pointer, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_pointer, e, m_mft); ignore_unused_variable_warning(b); // regex_search: b = global_regex_namespace::regex_search(m_in, m_in, m_results, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_in, m_in, m_results, e, m_mft); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_in, m_in, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_in, m_in, e, m_mft); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_pointer, m_pmatch, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_pointer, m_pmatch, e, m_mft); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_pointer, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_pointer, e, m_mft); ignore_unused_variable_warning(b); // regex_iterator: typedef typename regex_iterator_type::regex_type rit_regex_type; typedef typename regex_iterator_type::value_type rit_value_type; typedef typename regex_iterator_type::difference_type rit_difference_type; typedef typename regex_iterator_type::pointer rit_pointer; typedef typename regex_iterator_type::reference rit_reference; typedef typename regex_iterator_type::iterator_category rit_iterator_category; BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_convertible::value)); // this takes care of most of the checks needed: function_requires >(); regex_iterator_type iter1(m_in, m_in, e); ignore_unused_variable_warning(iter1); regex_iterator_type iter2(m_in, m_in, e, m_mft); ignore_unused_variable_warning(iter2); // regex_token_iterator: typedef typename regex_token_iterator_type::regex_type rtit_regex_type; typedef typename regex_token_iterator_type::value_type rtit_value_type; typedef typename regex_token_iterator_type::difference_type rtit_difference_type; typedef typename regex_token_iterator_type::pointer rtit_pointer; typedef typename regex_token_iterator_type::reference rtit_reference; typedef typename regex_token_iterator_type::iterator_category rtit_iterator_category; BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_same::value)); BOOST_STATIC_ASSERT((::boost::is_convertible::value)); // this takes care of most of the checks needed: function_requires >(); regex_token_iterator_type ti1(m_in, m_in, e); ignore_unused_variable_warning(ti1); regex_token_iterator_type ti2(m_in, m_in, e, 0); ignore_unused_variable_warning(ti2); regex_token_iterator_type ti3(m_in, m_in, e, 0, m_mft); ignore_unused_variable_warning(ti3); std::vector subs; regex_token_iterator_type ti4(m_in, m_in, e, subs); ignore_unused_variable_warning(ti4); regex_token_iterator_type ti5(m_in, m_in, e, subs, m_mft); ignore_unused_variable_warning(ti5); static const int i_array[3] = { 1, 2, 3, }; regex_token_iterator_type ti6(m_in, m_in, e, i_array); ignore_unused_variable_warning(ti6); regex_token_iterator_type ti7(m_in, m_in, e, i_array, m_mft); ignore_unused_variable_warning(ti7); } pointer_type m_pointer; flag_type m_flags; std::size_t m_size; input_iterator_type in1, in2; const sub_match_type m_sub; const value_type m_char; match_results_type m_results; const match_results_type m_cresults; OutIterator m_out; BidiIterator m_in; global_regex_namespace::regex_constants::match_flag_type m_mft; global_regex_namespace::match_results< pointer_type, allocator_architype > > m_pmatch; BaseRegexConcept(); BaseRegexConcept(const BaseRegexConcept&); BaseRegexConcept& operator=(const BaseRegexConcept&); }; // // RegexConcept: // Test every interface in the std: // template struct RegexConcept { typedef typename Regex::value_type value_type; //typedef typename Regex::size_type size_type; typedef typename Regex::flag_type flag_type; typedef typename Regex::locale_type locale_type; // derived test types: typedef const value_type* pointer_type; typedef std::basic_string string_type; typedef boost::bidirectional_iterator_archetype BidiIterator; typedef global_regex_namespace::sub_match sub_match_type; typedef global_regex_namespace::match_results > match_results_type; typedef output_iterator_archetype OutIterator; void constraints() { function_requires >(); // string based construct: Regex e1(m_string); ignore_unused_variable_warning(e1); Regex e2(m_string, m_flags); ignore_unused_variable_warning(e2); // assign etc: Regex e; e = m_string; e.assign(m_string); e.assign(m_string, m_flags); // sub_match: string_type s(m_sub); ignore_unused_variable_warning(s); s = m_sub.str(); ignore_unused_variable_warning(s); int i = m_sub.compare(m_string); ignore_unused_variable_warning(i); int i2 = m_sub.compare(m_sub); ignore_unused_variable_warning(i2); i2 = m_sub.compare(m_pointer); ignore_unused_variable_warning(i2); bool b = m_sub == m_sub; ignore_unused_variable_warning(b); b = m_sub != m_sub; ignore_unused_variable_warning(b); b = m_sub <= m_sub; ignore_unused_variable_warning(b); b = m_sub <= m_sub; ignore_unused_variable_warning(b); b = m_sub > m_sub; ignore_unused_variable_warning(b); b = m_sub >= m_sub; ignore_unused_variable_warning(b); b = m_sub == m_pointer; ignore_unused_variable_warning(b); b = m_sub != m_pointer; ignore_unused_variable_warning(b); b = m_sub <= m_pointer; ignore_unused_variable_warning(b); b = m_sub <= m_pointer; ignore_unused_variable_warning(b); b = m_sub > m_pointer; ignore_unused_variable_warning(b); b = m_sub >= m_pointer; ignore_unused_variable_warning(b); b = m_pointer == m_sub; ignore_unused_variable_warning(b); b = m_pointer != m_sub; ignore_unused_variable_warning(b); b = m_pointer <= m_sub; ignore_unused_variable_warning(b); b = m_pointer <= m_sub; ignore_unused_variable_warning(b); b = m_pointer > m_sub; ignore_unused_variable_warning(b); b = m_pointer >= m_sub; ignore_unused_variable_warning(b); b = m_sub == m_char; ignore_unused_variable_warning(b); b = m_sub != m_char; ignore_unused_variable_warning(b); b = m_sub <= m_char; ignore_unused_variable_warning(b); b = m_sub <= m_char; ignore_unused_variable_warning(b); b = m_sub > m_char; ignore_unused_variable_warning(b); b = m_sub >= m_char; ignore_unused_variable_warning(b); b = m_char == m_sub; ignore_unused_variable_warning(b); b = m_char != m_sub; ignore_unused_variable_warning(b); b = m_char <= m_sub; ignore_unused_variable_warning(b); b = m_char <= m_sub; ignore_unused_variable_warning(b); b = m_char > m_sub; ignore_unused_variable_warning(b); b = m_char >= m_sub; ignore_unused_variable_warning(b); b = m_sub == m_string; ignore_unused_variable_warning(b); b = m_sub != m_string; ignore_unused_variable_warning(b); b = m_sub <= m_string; ignore_unused_variable_warning(b); b = m_sub <= m_string; ignore_unused_variable_warning(b); b = m_sub > m_string; ignore_unused_variable_warning(b); b = m_sub >= m_string; ignore_unused_variable_warning(b); b = m_string == m_sub; ignore_unused_variable_warning(b); b = m_string != m_sub; ignore_unused_variable_warning(b); b = m_string <= m_sub; ignore_unused_variable_warning(b); b = m_string <= m_sub; ignore_unused_variable_warning(b); b = m_string > m_sub; ignore_unused_variable_warning(b); b = m_string >= m_sub; ignore_unused_variable_warning(b); // match results: m_string = m_results.str(); ignore_unused_variable_warning(m_string); m_string = m_results.str(0); ignore_unused_variable_warning(m_string); m_out = m_cresults.format(m_out, m_string); m_out = m_cresults.format(m_out, m_string, m_mft); m_string = m_cresults.format(m_string); ignore_unused_variable_warning(m_string); m_string = m_cresults.format(m_string, m_mft); ignore_unused_variable_warning(m_string); // regex_match: b = global_regex_namespace::regex_match(m_string, m_smatch, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_string, m_smatch, e, m_mft); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_string, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_match(m_string, e, m_mft); ignore_unused_variable_warning(b); // regex_search: b = global_regex_namespace::regex_search(m_string, m_smatch, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_string, m_smatch, e, m_mft); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_string, e); ignore_unused_variable_warning(b); b = global_regex_namespace::regex_search(m_string, e, m_mft); ignore_unused_variable_warning(b); // regex_replace: m_out = global_regex_namespace::regex_replace(m_out, m_in, m_in, e, m_string, m_mft); m_out = global_regex_namespace::regex_replace(m_out, m_in, m_in, e, m_string); m_string = global_regex_namespace::regex_replace(m_string, e, m_string, m_mft); ignore_unused_variable_warning(m_string); m_string = global_regex_namespace::regex_replace(m_string, e, m_string); ignore_unused_variable_warning(m_string); } flag_type m_flags; string_type m_string; const sub_match_type m_sub; match_results_type m_results; pointer_type m_pointer; value_type m_char; const match_results_type m_cresults; OutIterator m_out; BidiIterator m_in; global_regex_namespace::regex_constants::match_flag_type m_mft; global_regex_namespace::match_results > > m_smatch; RegexConcept(); RegexConcept(const RegexConcept&); RegexConcept& operator=(const RegexConcept&); }; #ifndef BOOST_REGEX_TEST_STD template struct functor1 { typedef typename M::char_type char_type; const char_type* operator()(const M&)const { static const char_type c = static_cast(0); return &c; } }; template struct functor1b { typedef typename M::char_type char_type; std::vector operator()(const M&)const { static const std::vector c; return c; } }; template struct functor2 { template O operator()(const M& /*m*/, O i)const { return i; } }; template struct functor3 { template O operator()(const M& /*m*/, O i, regex_constants::match_flag_type)const { return i; } }; // // BoostRegexConcept: // Test every interface in the Boost implementation: // template struct BoostRegexConcept { typedef typename Regex::value_type value_type; typedef typename Regex::size_type size_type; typedef typename Regex::flag_type flag_type; typedef typename Regex::locale_type locale_type; // derived test types: typedef const value_type* pointer_type; typedef std::basic_string string_type; typedef typename Regex::const_iterator const_iterator; typedef bidirectional_iterator_archetype BidiIterator; typedef output_iterator_archetype OutputIterator; typedef global_regex_namespace::sub_match sub_match_type; typedef global_regex_namespace::match_results > match_results_type; typedef global_regex_namespace::match_results match_results_default_type; void constraints() { global_regex_namespace::regex_constants::match_flag_type mopts = global_regex_namespace::regex_constants::match_default | global_regex_namespace::regex_constants::match_not_bol | global_regex_namespace::regex_constants::match_not_eol | global_regex_namespace::regex_constants::match_not_bow | global_regex_namespace::regex_constants::match_not_eow | global_regex_namespace::regex_constants::match_any | global_regex_namespace::regex_constants::match_not_null | global_regex_namespace::regex_constants::match_continuous | global_regex_namespace::regex_constants::match_partial | global_regex_namespace::regex_constants::match_prev_avail | global_regex_namespace::regex_constants::format_default | global_regex_namespace::regex_constants::format_sed | global_regex_namespace::regex_constants::format_perl | global_regex_namespace::regex_constants::format_no_copy | global_regex_namespace::regex_constants::format_first_only; (void)mopts; function_requires >(); const global_regex_namespace::regex_error except(global_regex_namespace::regex_constants::error_collate); std::ptrdiff_t pt = except.position(); ignore_unused_variable_warning(pt); const Regex ce, ce2; #ifndef BOOST_NO_STD_LOCALE m_stream << ce; #endif unsigned i = ce.error_code(); ignore_unused_variable_warning(i); pointer_type p = ce.expression(); ignore_unused_variable_warning(p); int i2 = ce.compare(ce2); ignore_unused_variable_warning(i2); bool b = ce == ce2; ignore_unused_variable_warning(b); b = ce.empty(); ignore_unused_variable_warning(b); b = ce != ce2; ignore_unused_variable_warning(b); b = ce < ce2; ignore_unused_variable_warning(b); b = ce > ce2; ignore_unused_variable_warning(b); b = ce <= ce2; ignore_unused_variable_warning(b); b = ce >= ce2; ignore_unused_variable_warning(b); i = ce.status(); ignore_unused_variable_warning(i); size_type s = ce.max_size(); ignore_unused_variable_warning(s); s = ce.size(); ignore_unused_variable_warning(s); const_iterator pi = ce.begin(); ignore_unused_variable_warning(pi); pi = ce.end(); ignore_unused_variable_warning(pi); string_type s2 = ce.str(); ignore_unused_variable_warning(s2); m_string = m_sub + m_sub; ignore_unused_variable_warning(m_string); m_string = m_sub + m_pointer; ignore_unused_variable_warning(m_string); m_string = m_pointer + m_sub; ignore_unused_variable_warning(m_string); m_string = m_sub + m_string; ignore_unused_variable_warning(m_string); m_string = m_string + m_sub; ignore_unused_variable_warning(m_string); m_string = m_sub + m_char; ignore_unused_variable_warning(m_string); m_string = m_char + m_sub; ignore_unused_variable_warning(m_string); // Named sub-expressions: m_sub = m_cresults[&m_char]; ignore_unused_variable_warning(m_sub); m_sub = m_cresults[m_string]; ignore_unused_variable_warning(m_sub); m_sub = m_cresults[""]; ignore_unused_variable_warning(m_sub); m_sub = m_cresults[std::string("")]; ignore_unused_variable_warning(m_sub); m_string = m_cresults.str(&m_char); ignore_unused_variable_warning(m_string); m_string = m_cresults.str(m_string); ignore_unused_variable_warning(m_string); m_string = m_cresults.str(""); ignore_unused_variable_warning(m_string); m_string = m_cresults.str(std::string("")); ignore_unused_variable_warning(m_string); typename match_results_type::difference_type diff; diff = m_cresults.length(&m_char); ignore_unused_variable_warning(diff); diff = m_cresults.length(m_string); ignore_unused_variable_warning(diff); diff = m_cresults.length(""); ignore_unused_variable_warning(diff); diff = m_cresults.length(std::string("")); ignore_unused_variable_warning(diff); diff = m_cresults.position(&m_char); ignore_unused_variable_warning(diff); diff = m_cresults.position(m_string); ignore_unused_variable_warning(diff); diff = m_cresults.position(""); ignore_unused_variable_warning(diff); diff = m_cresults.position(std::string("")); ignore_unused_variable_warning(diff); #ifndef BOOST_NO_STD_LOCALE m_stream << m_sub; m_stream << m_cresults; #endif // // Extended formatting with a functor: // regex_constants::match_flag_type f = regex_constants::match_default; OutputIterator out = static_object::get(); functor3 func3; functor2 func2; functor1 func1; functor3 func3b; functor2 func2b; functor1 func1b; out = regex_format(out, m_cresults, func3b, f); out = regex_format(out, m_cresults, func3b); out = regex_format(out, m_cresults, func2b, f); out = regex_format(out, m_cresults, func2b); out = regex_format(out, m_cresults, func1b, f); out = regex_format(out, m_cresults, func1b); out = regex_format(out, m_cresults, RW_NS::ref(func3b), f); out = regex_format(out, m_cresults, RW_NS::ref(func3b)); out = regex_format(out, m_cresults, RW_NS::ref(func2b), f); out = regex_format(out, m_cresults, RW_NS::ref(func2b)); out = regex_format(out, m_cresults, RW_NS::ref(func1b), f); out = regex_format(out, m_cresults, RW_NS::ref(func1b)); out = regex_format(out, m_cresults, RW_NS::cref(func3b), f); out = regex_format(out, m_cresults, RW_NS::cref(func3b)); out = regex_format(out, m_cresults, RW_NS::cref(func2b), f); out = regex_format(out, m_cresults, RW_NS::cref(func2b)); out = regex_format(out, m_cresults, RW_NS::cref(func1b), f); out = regex_format(out, m_cresults, RW_NS::cref(func1b)); m_string += regex_format(m_cresults, func3b, f); m_string += regex_format(m_cresults, func3b); m_string += regex_format(m_cresults, func2b, f); m_string += regex_format(m_cresults, func2b); m_string += regex_format(m_cresults, func1b, f); m_string += regex_format(m_cresults, func1b); m_string += regex_format(m_cresults, RW_NS::ref(func3b), f); m_string += regex_format(m_cresults, RW_NS::ref(func3b)); m_string += regex_format(m_cresults, RW_NS::ref(func2b), f); m_string += regex_format(m_cresults, RW_NS::ref(func2b)); m_string += regex_format(m_cresults, RW_NS::ref(func1b), f); m_string += regex_format(m_cresults, RW_NS::ref(func1b)); m_string += regex_format(m_cresults, RW_NS::cref(func3b), f); m_string += regex_format(m_cresults, RW_NS::cref(func3b)); m_string += regex_format(m_cresults, RW_NS::cref(func2b), f); m_string += regex_format(m_cresults, RW_NS::cref(func2b)); m_string += regex_format(m_cresults, RW_NS::cref(func1b), f); m_string += regex_format(m_cresults, RW_NS::cref(func1b)); out = m_cresults.format(out, func3b, f); out = m_cresults.format(out, func3b); out = m_cresults.format(out, func2b, f); out = m_cresults.format(out, func2b); out = m_cresults.format(out, func1b, f); out = m_cresults.format(out, func1b); out = m_cresults.format(out, RW_NS::ref(func3b), f); out = m_cresults.format(out, RW_NS::ref(func3b)); out = m_cresults.format(out, RW_NS::ref(func2b), f); out = m_cresults.format(out, RW_NS::ref(func2b)); out = m_cresults.format(out, RW_NS::ref(func1b), f); out = m_cresults.format(out, RW_NS::ref(func1b)); out = m_cresults.format(out, RW_NS::cref(func3b), f); out = m_cresults.format(out, RW_NS::cref(func3b)); out = m_cresults.format(out, RW_NS::cref(func2b), f); out = m_cresults.format(out, RW_NS::cref(func2b)); out = m_cresults.format(out, RW_NS::cref(func1b), f); out = m_cresults.format(out, RW_NS::cref(func1b)); m_string += m_cresults.format(func3b, f); m_string += m_cresults.format(func3b); m_string += m_cresults.format(func2b, f); m_string += m_cresults.format(func2b); m_string += m_cresults.format(func1b, f); m_string += m_cresults.format(func1b); m_string += m_cresults.format(RW_NS::ref(func3b), f); m_string += m_cresults.format(RW_NS::ref(func3b)); m_string += m_cresults.format(RW_NS::ref(func2b), f); m_string += m_cresults.format(RW_NS::ref(func2b)); m_string += m_cresults.format(RW_NS::ref(func1b), f); m_string += m_cresults.format(RW_NS::ref(func1b)); m_string += m_cresults.format(RW_NS::cref(func3b), f); m_string += m_cresults.format(RW_NS::cref(func3b)); m_string += m_cresults.format(RW_NS::cref(func2b), f); m_string += m_cresults.format(RW_NS::cref(func2b)); m_string += m_cresults.format(RW_NS::cref(func1b), f); m_string += m_cresults.format(RW_NS::cref(func1b)); out = regex_replace(out, m_in, m_in, ce, func3, f); out = regex_replace(out, m_in, m_in, ce, func3); out = regex_replace(out, m_in, m_in, ce, func2, f); out = regex_replace(out, m_in, m_in, ce, func2); out = regex_replace(out, m_in, m_in, ce, func1, f); out = regex_replace(out, m_in, m_in, ce, func1); out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func3), f); out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func3)); out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func2), f); out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func2)); out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func1), f); out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func1)); out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func3), f); out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func3)); out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func2), f); out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func2)); out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func1), f); out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func1)); functor3 > func3s; functor2 > func2s; functor1 > func1s; m_string += regex_replace(m_string, ce, func3s, f); m_string += regex_replace(m_string, ce, func3s); m_string += regex_replace(m_string, ce, func2s, f); m_string += regex_replace(m_string, ce, func2s); m_string += regex_replace(m_string, ce, func1s, f); m_string += regex_replace(m_string, ce, func1s); m_string += regex_replace(m_string, ce, RW_NS::ref(func3s), f); m_string += regex_replace(m_string, ce, RW_NS::ref(func3s)); m_string += regex_replace(m_string, ce, RW_NS::ref(func2s), f); m_string += regex_replace(m_string, ce, RW_NS::ref(func2s)); m_string += regex_replace(m_string, ce, RW_NS::ref(func1s), f); m_string += regex_replace(m_string, ce, RW_NS::ref(func1s)); m_string += regex_replace(m_string, ce, RW_NS::cref(func3s), f); m_string += regex_replace(m_string, ce, RW_NS::cref(func3s)); m_string += regex_replace(m_string, ce, RW_NS::cref(func2s), f); m_string += regex_replace(m_string, ce, RW_NS::cref(func2s)); m_string += regex_replace(m_string, ce, RW_NS::cref(func1s), f); m_string += regex_replace(m_string, ce, RW_NS::cref(func1s)); } std::basic_ostream m_stream; sub_match_type m_sub; pointer_type m_pointer; string_type m_string; const value_type m_char; match_results_type m_results; const match_results_type m_cresults; BidiIterator m_in; BoostRegexConcept(); BoostRegexConcept(const BoostRegexConcept&); BoostRegexConcept& operator=(const BoostRegexConcept&); }; #endif // BOOST_REGEX_TEST_STD } #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/config/borland.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE boost/regex/config/borland.hpp * VERSION see * DESCRIPTION: regex borland-specific config setup. */ #if defined(__BORLANDC__) && !defined(__clang__) # if (__BORLANDC__ == 0x550) || (__BORLANDC__ == 0x551) // problems with std::basic_string and dll RTL: # if defined(_RTLDLL) && defined(_RWSTD_COMPILE_INSTANTIATE) # ifdef BOOST_REGEX_BUILD_DLL # error _RWSTD_COMPILE_INSTANTIATE must not be defined when building regex++ as a DLL # else # pragma message("Defining _RWSTD_COMPILE_INSTANTIATE when linking to the DLL version of the RTL may produce memory corruption problems in std::basic_string, as a result of separate versions of basic_string's static data in the RTL and you're exe/dll: be warned!!") # endif # endif # ifndef _RTLDLL // this is harmless for a staic link: # define _RWSTD_COMPILE_INSTANTIATE # endif // external templates cause problems for some reason: # define BOOST_REGEX_NO_EXTERNAL_TEMPLATES # endif # if (__BORLANDC__ <= 0x540) && !defined(BOOST_REGEX_NO_LIB) && !defined(_NO_VCL) // C++ Builder 4 and earlier, we can't tell whether we should be using // the VCL runtime or not, do a static link instead: # define BOOST_REGEX_STATIC_LINK # endif // // VCL support: // if we're building a console app then there can't be any VCL (can there?) # if !defined(__CONSOLE__) && !defined(_NO_VCL) # define BOOST_REGEX_USE_VCL # endif // // if this isn't Win32 then don't automatically select link // libraries: // # ifndef _Windows # ifndef BOOST_REGEX_NO_LIB # define BOOST_REGEX_NO_LIB # endif # ifndef BOOST_REGEX_STATIC_LINK # define BOOST_REGEX_STATIC_LINK # endif # endif #if __BORLANDC__ < 0x600 // // string workarounds: // #include #undef strcmp #undef strcpy #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/config/cwchar.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE boost/regex/config/cwchar.hpp * VERSION see * DESCRIPTION: regex wide character string fixes. */ #ifndef BOOST_REGEX_CONFIG_CWCHAR_HPP #define BOOST_REGEX_CONFIG_CWCHAR_HPP #include #include #include #if defined(__STD_RWCOMPILER_H__) || defined(_RWSTD_VER) // apparently this is required for the RW STL on Linux: #undef iswalnum #undef iswalpha #undef iswblank #undef iswcntrl #undef iswdigit #undef iswgraph #undef iswlower #undef iswprint #undef iswprint #undef iswpunct #undef iswspace #undef iswupper #undef iswxdigit #undef iswctype #undef towlower #undef towupper #undef towctrans #undef wctrans #undef wctype #endif namespace std{ #ifndef BOOST_NO_STDC_NAMESPACE extern "C"{ #endif #ifdef iswalnum inline int (iswalnum)(wint_t i) { return iswalnum(i); } #undef iswalnum #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswalnum; #endif #ifdef iswalpha inline int (iswalpha)(wint_t i) { return iswalpha(i); } #undef iswalpha #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswalpha; #endif #ifdef iswcntrl inline int (iswcntrl)(wint_t i) { return iswcntrl(i); } #undef iswcntrl #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswcntrl; #endif #ifdef iswdigit inline int (iswdigit)(wint_t i) { return iswdigit(i); } #undef iswdigit #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswdigit; #endif #ifdef iswgraph inline int (iswgraph)(wint_t i) { return iswgraph(i); } #undef iswgraph #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswgraph; #endif #ifdef iswlower inline int (iswlower)(wint_t i) { return iswlower(i); } #undef iswlower #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswlower; #endif #ifdef iswprint inline int (iswprint)(wint_t i) { return iswprint(i); } #undef iswprint #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswprint; #endif #ifdef iswpunct inline int (iswpunct)(wint_t i) { return iswpunct(i); } #undef iswpunct #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswpunct; #endif #ifdef iswspace inline int (iswspace)(wint_t i) { return iswspace(i); } #undef iswspace #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswspace; #endif #ifdef iswupper inline int (iswupper)(wint_t i) { return iswupper(i); } #undef iswupper #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswupper; #endif #ifdef iswxdigit inline int (iswxdigit)(wint_t i) { return iswxdigit(i); } #undef iswxdigit #elif defined(BOOST_NO_STDC_NAMESPACE) using ::iswxdigit; #endif #ifdef towlower inline wint_t (towlower)(wint_t i) { return towlower(i); } #undef towlower #elif defined(BOOST_NO_STDC_NAMESPACE) using ::towlower; #endif #ifdef towupper inline wint_t (towupper)(wint_t i) { return towupper(i); } #undef towupper #elif defined(BOOST_NO_STDC_NAMESPACE) using :: towupper; #endif #ifdef wcscmp inline int (wcscmp)(const wchar_t *p1, const wchar_t *p2) { return wcscmp(p1,p2); } #undef wcscmp #elif defined(BOOST_NO_STDC_NAMESPACE) using ::wcscmp; #endif #ifdef wcscoll inline int (wcscoll)(const wchar_t *p1, const wchar_t *p2) { return wcscoll(p1,p2); } #undef wcscoll #elif defined(BOOST_NO_STDC_NAMESPACE) && !defined(UNDER_CE) using ::wcscoll; #endif #ifdef wcscpy inline wchar_t *(wcscpy)(wchar_t *p1, const wchar_t *p2) { return wcscpy(p1,p2); } #undef wcscpy #elif defined(BOOST_NO_STDC_NAMESPACE) using ::wcscpy; #endif #ifdef wcslen inline size_t (wcslen)(const wchar_t *p) { return wcslen(p); } #undef wcslen #elif defined(BOOST_NO_STDC_NAMESPACE) using ::wcslen; #endif #ifdef wcsxfrm size_t wcsxfrm(wchar_t *p1, const wchar_t *p2, size_t s) { return wcsxfrm(p1,p2,s); } #undef wcsxfrm #elif defined(BOOST_NO_STDC_NAMESPACE) using ::wcsxfrm; #endif #ifndef BOOST_NO_STDC_NAMESPACE } // extern "C" #endif } // namespace std #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/config.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE config.hpp * VERSION see * DESCRIPTION: regex extended config setup. */ #ifndef BOOST_REGEX_CONFIG_HPP #define BOOST_REGEX_CONFIG_HPP #if !((__cplusplus >= 201103L) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(BOOST_REGEX_CXX03)) # define BOOST_REGEX_CXX03 #endif #if defined(BOOST_REGEX_RECURSIVE) && !defined(BOOST_REGEX_CXX03) # define BOOST_REGEX_CXX03 #endif #if defined(__has_include) #if !defined(BOOST_REGEX_STANDALONE) && !__has_include() #define BOOST_REGEX_STANDALONE #endif #endif /* * Borland C++ Fix/error check * this has to go *before* we include any std lib headers: */ #if defined(__BORLANDC__) && !defined(__clang__) # include #endif #ifndef BOOST_REGEX_STANDALONE #include #endif /************************************************************************* * * Asserts: * *************************************************************************/ #ifdef BOOST_REGEX_STANDALONE #include # define BOOST_REGEX_ASSERT(x) assert(x) #else #include # define BOOST_REGEX_ASSERT(x) BOOST_ASSERT(x) #endif /***************************************************************************** * * Include all the headers we need here: * ****************************************************************************/ #ifdef __cplusplus # ifndef BOOST_REGEX_USER_CONFIG # define BOOST_REGEX_USER_CONFIG # endif # include BOOST_REGEX_USER_CONFIG #ifndef BOOST_REGEX_STANDALONE # include # include #endif #else /* * C build, * don't include because that may * do C++ specific things in future... */ # include # include # ifdef _MSC_VER # define BOOST_MSVC _MSC_VER # endif #endif /**************************************************************************** * * Legacy support: * *******************************************************************************/ #if defined(BOOST_NO_STD_LOCALE) || defined(BOOST_NO_CXX11_HDR_MUTEX) || defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) \ || defined(BOOST_NO_CXX11_HDR_ATOMIC) || defined(BOOST_NO_CXX11_ALLOCATOR) || defined(BOOST_NO_CXX11_SMART_PTR) \ || defined(BOOST_NO_CXX11_STATIC_ASSERT) || defined(BOOST_NO_NOEXCEPT) #ifndef BOOST_REGEX_CXX03 # define BOOST_REGEX_CXX03 #endif #endif /***************************************************************************** * * Boilerplate regex config options: * ****************************************************************************/ /* Obsolete macro, use BOOST_VERSION instead: */ #define BOOST_RE_VERSION 500 /* fix: */ #if defined(_UNICODE) && !defined(UNICODE) #define UNICODE #endif #define BOOST_REGEX_JOIN(X, Y) BOOST_REGEX_DO_JOIN(X, Y) #define BOOST_REGEX_DO_JOIN(X, Y) BOOST_REGEX_DO_JOIN2(X,Y) #define BOOST_REGEX_DO_JOIN2(X, Y) X##Y #ifdef BOOST_FALLTHROUGH # define BOOST_REGEX_FALLTHROUGH BOOST_FALLTHROUGH #else #if defined(__clang__) && (__cplusplus >= 201103L) && defined(__has_warning) # if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") # define BOOST_REGEX_FALLTHROUGH [[clang::fallthrough]] # endif #endif #if !defined(BOOST_REGEX_FALLTHROUGH) && defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1800) && (__cplusplus >= 201703) # define BOOST_REGEX_FALLTHROUGH [[fallthrough]] #endif #if !defined(BOOST_REGEX_FALLTHROUGH) && defined(__GNUC__) && (__GNUC__ >= 7) # define BOOST_REGEX_FALLTHROUGH __attribute__((fallthrough)) #endif #if !defined(BOOST_REGEX_FALLTHROUGH) # define BOOST_REGEX_FALLTHROUGH #endif #endif #ifdef BOOST_NORETURN # define BOOST_REGEX_NORETURN BOOST_NORETURN #else # define BOOST_REGEX_NORETURN #endif /* * Define a macro for the namespace that details are placed in, this includes the Boost * version number to avoid mismatched header and library versions: */ #define BOOST_REGEX_DETAIL_NS BOOST_REGEX_JOIN(re_detail_, BOOST_RE_VERSION) /* * Fix for gcc prior to 3.4: std::ctype doesn't allow * masks to be combined, for example: * std::use_facet >.is(std::ctype_base::lower|std::ctype_base::upper, L'a'); * returns *false*. */ #if defined(__GLIBCPP__) && defined(BOOST_REGEX_CXX03) # define BOOST_REGEX_BUGGY_CTYPE_FACET #endif /* * If there isn't good enough wide character support then there will * be no wide character regular expressions: */ #if (defined(BOOST_NO_CWCHAR) || defined(BOOST_NO_CWCTYPE) || defined(BOOST_NO_STD_WSTRING)) # if !defined(BOOST_NO_WREGEX) # define BOOST_NO_WREGEX # endif #else # if defined(__sgi) && (defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)) /* STLPort on IRIX is misconfigured: does not compile * as a temporary fix include instead and prevent inclusion * of STLPort version of */ # include # define __STLPORT_CWCTYPE # define _STLP_CWCTYPE # endif #if defined(__cplusplus) && defined(BOOST_REGEX_CXX03) # include #endif #endif /* * If Win32 support has been disabled for boost in general, then * it is for regex in particular: */ #if defined(BOOST_DISABLE_WIN32) && !defined(BOOST_REGEX_NO_W32) # define BOOST_REGEX_NO_W32 #endif /* disable our own file-iterators and mapfiles if we can't * support them: */ #if defined(_WIN32) # if defined(BOOST_REGEX_NO_W32) || BOOST_PLAT_WINDOWS_RUNTIME # define BOOST_REGEX_NO_FILEITER # endif #else /* defined(_WIN32) */ # if !defined(BOOST_HAS_DIRENT_H) # define BOOST_REGEX_NO_FILEITER # endif #endif /* backwards compatibitity: */ #if defined(BOOST_RE_NO_LIB) # define BOOST_REGEX_NO_LIB #endif #if defined(__GNUC__) && !defined(_MSC_VER) && (defined(_WIN32) || defined(__CYGWIN__)) /* gcc on win32 has problems if you include (sporadically generates bad code). */ # define BOOST_REGEX_NO_W32 #endif #if defined(__COMO__) && !defined(BOOST_REGEX_NO_W32) && !defined(_MSC_EXTENSIONS) # define BOOST_REGEX_NO_W32 #endif #ifdef BOOST_REGEX_STANDALONE # if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) # define BOOST_REGEX_MSVC _MSC_VER #endif #elif defined(BOOST_MSVC) # define BOOST_REGEX_MSVC BOOST_MSVC #endif /***************************************************************************** * * Set up dll import/export options: * ****************************************************************************/ #if (defined(BOOST_REGEX_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) && !defined(BOOST_REGEX_STATIC_LINK) && defined(BOOST_SYMBOL_IMPORT) # if defined(BOOST_REGEX_SOURCE) # define BOOST_REGEX_BUILD_DLL # define BOOST_REGEX_DECL BOOST_SYMBOL_EXPORT # else # define BOOST_REGEX_DECL BOOST_SYMBOL_IMPORT # endif #else # define BOOST_REGEX_DECL #endif #ifdef BOOST_REGEX_CXX03 #if !defined(BOOST_REGEX_NO_LIB) && !defined(BOOST_REGEX_SOURCE) && !defined(BOOST_ALL_NO_LIB) && defined(__cplusplus) # define BOOST_LIB_NAME boost_regex # if defined(BOOST_REGEX_DYN_LINK) || defined(BOOST_ALL_DYN_LINK) # define BOOST_DYN_LINK # endif # ifdef BOOST_REGEX_DIAG # define BOOST_LIB_DIAGNOSTIC # endif # include #endif #endif /***************************************************************************** * * Set up function call type: * ****************************************************************************/ #if defined(_MSC_VER) && defined(_MSC_EXTENSIONS) #if defined(_DEBUG) || defined(__MSVC_RUNTIME_CHECKS) || defined(_MANAGED) || defined(BOOST_REGEX_NO_FASTCALL) # define BOOST_REGEX_CALL __cdecl #else # define BOOST_REGEX_CALL __fastcall #endif # define BOOST_REGEX_CCALL __cdecl #endif #if defined(__BORLANDC__) && !defined(BOOST_DISABLE_WIN32) #if defined(__clang__) # define BOOST_REGEX_CALL __cdecl # define BOOST_REGEX_CCALL __cdecl #else # define BOOST_REGEX_CALL __fastcall # define BOOST_REGEX_CCALL __stdcall #endif #endif #ifndef BOOST_REGEX_CALL # define BOOST_REGEX_CALL #endif #ifndef BOOST_REGEX_CCALL #define BOOST_REGEX_CCALL #endif /***************************************************************************** * * Set up localisation model: * ****************************************************************************/ /* backwards compatibility: */ #ifdef BOOST_RE_LOCALE_C # define BOOST_REGEX_USE_C_LOCALE #endif #ifdef BOOST_RE_LOCALE_CPP # define BOOST_REGEX_USE_CPP_LOCALE #endif #if defined(__CYGWIN__) # define BOOST_REGEX_USE_C_LOCALE #endif /* use C++ locale when targeting windows store */ #if BOOST_PLAT_WINDOWS_RUNTIME # define BOOST_REGEX_USE_CPP_LOCALE # define BOOST_REGEX_NO_WIN32_LOCALE #endif /* Win32 defaults to native Win32 locale: */ #if defined(_WIN32) && \ !defined(BOOST_REGEX_USE_WIN32_LOCALE) && \ !defined(BOOST_REGEX_USE_C_LOCALE) && \ !defined(BOOST_REGEX_USE_CPP_LOCALE) && \ !defined(BOOST_REGEX_NO_W32) && \ !defined(BOOST_REGEX_NO_WIN32_LOCALE) # define BOOST_REGEX_USE_WIN32_LOCALE #endif /* otherwise use C++ locale if supported: */ #if !defined(BOOST_REGEX_USE_WIN32_LOCALE) && !defined(BOOST_REGEX_USE_C_LOCALE) && !defined(BOOST_REGEX_USE_CPP_LOCALE) && !defined(BOOST_NO_STD_LOCALE) # define BOOST_REGEX_USE_CPP_LOCALE #endif /* otherwise use C locale: */ #if !defined(BOOST_REGEX_USE_WIN32_LOCALE) && !defined(BOOST_REGEX_USE_C_LOCALE) && !defined(BOOST_REGEX_USE_CPP_LOCALE) # define BOOST_REGEX_USE_C_LOCALE #endif #ifndef BOOST_REGEX_MAX_STATE_COUNT # define BOOST_REGEX_MAX_STATE_COUNT 100000000 #endif /***************************************************************************** * * Error Handling for exception free compilers: * ****************************************************************************/ #ifdef BOOST_NO_EXCEPTIONS /* * If there are no exceptions then we must report critical-errors * the only way we know how; by terminating. */ #include #include #include # define BOOST_REGEX_NOEH_ASSERT(x)\ if(0 == (x))\ {\ std::string s("Error: critical regex++ failure in: ");\ s.append(#x);\ std::runtime_error e(s);\ boost::throw_exception(e);\ } #else /* * With exceptions then error handling is taken care of and * there is no need for these checks: */ # define BOOST_REGEX_NOEH_ASSERT(x) #endif /***************************************************************************** * * Stack protection under MS Windows: * ****************************************************************************/ #if !defined(BOOST_REGEX_NO_W32) && !defined(BOOST_REGEX_V3) # if(defined(_WIN32) || defined(_WIN64) || defined(_WINCE)) \ && !(defined(__GNUC__) || defined(__BORLANDC__) && defined(__clang__)) \ && !(defined(__BORLANDC__) && (__BORLANDC__ >= 0x600)) \ && !(defined(__MWERKS__) && (__MWERKS__ <= 0x3003)) # define BOOST_REGEX_HAS_MS_STACK_GUARD # endif #elif defined(BOOST_REGEX_HAS_MS_STACK_GUARD) # undef BOOST_REGEX_HAS_MS_STACK_GUARD #endif #if defined(__cplusplus) && defined(BOOST_REGEX_HAS_MS_STACK_GUARD) namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ BOOST_REGEX_DECL void BOOST_REGEX_CALL reset_stack_guard_page(); } } #endif /***************************************************************************** * * Algorithm selection and configuration. * These options are now obsolete for C++11 and later (regex v5). * ****************************************************************************/ #if !defined(BOOST_REGEX_RECURSIVE) && !defined(BOOST_REGEX_NON_RECURSIVE) # if defined(BOOST_REGEX_HAS_MS_STACK_GUARD) && !defined(_STLP_DEBUG) && !defined(__STL_DEBUG) && !(defined(_MSC_VER) && (_MSC_VER >= 1400)) && defined(BOOST_REGEX_CXX03) # define BOOST_REGEX_RECURSIVE # else # define BOOST_REGEX_NON_RECURSIVE # endif #endif #ifdef BOOST_REGEX_NON_RECURSIVE # ifdef BOOST_REGEX_RECURSIVE # error "Can't set both BOOST_REGEX_RECURSIVE and BOOST_REGEX_NON_RECURSIVE" # endif # ifndef BOOST_REGEX_BLOCKSIZE # define BOOST_REGEX_BLOCKSIZE 4096 # endif # if BOOST_REGEX_BLOCKSIZE < 512 # error "BOOST_REGEX_BLOCKSIZE must be at least 512" # endif # ifndef BOOST_REGEX_MAX_BLOCKS # define BOOST_REGEX_MAX_BLOCKS 1024 # endif # ifdef BOOST_REGEX_HAS_MS_STACK_GUARD # undef BOOST_REGEX_HAS_MS_STACK_GUARD # endif # ifndef BOOST_REGEX_MAX_CACHE_BLOCKS # define BOOST_REGEX_MAX_CACHE_BLOCKS 16 # endif #endif /***************************************************************************** * * Diagnostics: * ****************************************************************************/ #ifdef BOOST_REGEX_CONFIG_INFO BOOST_REGEX_DECL void BOOST_REGEX_CALL print_regex_library_info(); #endif #if defined(BOOST_REGEX_DIAG) # pragma message ("BOOST_REGEX_DECL" BOOST_STRINGIZE(=BOOST_REGEX_DECL)) # pragma message ("BOOST_REGEX_CALL" BOOST_STRINGIZE(=BOOST_REGEX_CALL)) # pragma message ("BOOST_REGEX_CCALL" BOOST_STRINGIZE(=BOOST_REGEX_CCALL)) #ifdef BOOST_REGEX_USE_C_LOCALE # pragma message ("Using C locale in regex traits class") #elif BOOST_REGEX_USE_CPP_LOCALE # pragma message ("Using C++ locale in regex traits class") #else # pragma message ("Using Win32 locale in regex traits class") #endif #if defined(BOOST_REGEX_DYN_LINK) || defined(BOOST_ALL_DYN_LINK) # pragma message ("Dynamic linking enabled") #endif #if defined(BOOST_REGEX_NO_LIB) || defined(BOOST_ALL_NO_LIB) # pragma message ("Auto-linking disabled") #endif #ifdef BOOST_REGEX_NO_EXTERNAL_TEMPLATES # pragma message ("Extern templates disabled") #endif #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/icu.hpp ================================================ /* * * Copyright (c) 2020 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE icu.hpp * VERSION see * DESCRIPTION: Unicode regular expressions on top of the ICU Library. */ #ifndef BOOST_REGEX_ICU_HPP #define BOOST_REGEX_ICU_HPP #include #ifdef BOOST_REGEX_CXX03 #include #else #include #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/mfc.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE mfc.hpp * VERSION see * DESCRIPTION: Overloads and helpers for using MFC/ATL string types with Boost.Regex. */ #ifndef BOOST_REGEX_MFC_HPP #define BOOST_REGEX_MFC_HPP #include #include namespace boost{ // // define the types used for TCHAR's: typedef basic_regex tregex; typedef match_results tmatch; typedef regex_iterator tregex_iterator; typedef regex_token_iterator tregex_token_iterator; // Obsolete. Remove #define SIMPLE_STRING_PARAM class B, bool b #define SIMPLE_STRING_ARG_LIST B, b // // define regex creation functions: // template inline basic_regex make_regex(const ATL::CSimpleStringT& s, ::boost::regex_constants::syntax_option_type f = boost::regex_constants::normal) { basic_regex result(s.GetString(), s.GetString() + s.GetLength(), f); return result; } // // regex_match overloads: // template inline bool regex_match(const ATL::CSimpleStringT& s, match_results& what, const basic_regex& e, boost::regex_constants::match_flag_type f = boost::regex_constants::match_default) { return ::boost::regex_match(s.GetString(), s.GetString() + s.GetLength(), what, e, f); } template inline bool regex_match(const ATL::CSimpleStringT& s, const basic_regex& e, boost::regex_constants::match_flag_type f = boost::regex_constants::match_default) { return ::boost::regex_match(s.GetString(), s.GetString() + s.GetLength(), e, f); } // // regex_search overloads: // template inline bool regex_search(const ATL::CSimpleStringT& s, match_results& what, const basic_regex& e, boost::regex_constants::match_flag_type f = boost::regex_constants::match_default) { return ::boost::regex_search(s.GetString(), s.GetString() + s.GetLength(), what, e, f); } template inline bool regex_search(const ATL::CSimpleStringT& s, const basic_regex& e, boost::regex_constants::match_flag_type f = boost::regex_constants::match_default) { return ::boost::regex_search(s.GetString(), s.GetString() + s.GetLength(), e, f); } // // regex_iterator creation: // template inline regex_iterator make_regex_iterator(const ATL::CSimpleStringT& s, const basic_regex& e, ::boost::regex_constants::match_flag_type f = boost::regex_constants::match_default) { regex_iterator result(s.GetString(), s.GetString() + s.GetLength(), e, f); return result; } template inline regex_token_iterator make_regex_token_iterator(const ATL::CSimpleStringT& s, const basic_regex& e, int sub = 0, ::boost::regex_constants::match_flag_type f = boost::regex_constants::match_default) { regex_token_iterator result(s.GetString(), s.GetString() + s.GetLength(), e, sub, f); return result; } template inline regex_token_iterator make_regex_token_iterator(const ATL::CSimpleStringT& s, const basic_regex& e, const std::vector& subs, ::boost::regex_constants::match_flag_type f = boost::regex_constants::match_default) { regex_token_iterator result(s.GetString(), s.GetString() + s.GetLength(), e, subs, f); return result; } template inline regex_token_iterator make_regex_token_iterator(const ATL::CSimpleStringT& s, const basic_regex& e, const int (& subs)[N], ::boost::regex_constants::match_flag_type f = boost::regex_constants::match_default) { regex_token_iterator result(s.GetString(), s.GetString() + s.GetLength(), e, subs, f); return result; } template OutputIterator regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex& e, const ATL::CSimpleStringT& fmt, match_flag_type flags = match_default) { return ::boost::regex_replace(out, first, last, e, fmt.GetString(), flags); } namespace BOOST_REGEX_DETAIL_NS{ template class mfc_string_out_iterator { ATL::CSimpleStringT* out; public: mfc_string_out_iterator(ATL::CSimpleStringT& s) : out(&s) {} mfc_string_out_iterator& operator++() { return *this; } mfc_string_out_iterator& operator++(int) { return *this; } mfc_string_out_iterator& operator*() { return *this; } mfc_string_out_iterator& operator=(B v) { out->AppendChar(v); return *this; } typedef std::ptrdiff_t difference_type; typedef B value_type; typedef value_type* pointer; typedef value_type& reference; typedef std::output_iterator_tag iterator_category; }; } template ATL::CSimpleStringT regex_replace(const ATL::CSimpleStringT& s, const basic_regex& e, const ATL::CSimpleStringT& fmt, match_flag_type flags = match_default) { ATL::CSimpleStringT result(s.GetManager()); BOOST_REGEX_DETAIL_NS::mfc_string_out_iterator i(result); regex_replace(i, s.GetString(), s.GetString() + s.GetLength(), e, fmt.GetString(), flags); return result; } } // namespace boost. #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/pattern_except.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE pattern_except.hpp * VERSION see * DESCRIPTION: Declares pattern-matching exception classes. */ #ifndef BOOST_RE_PAT_EXCEPT_HPP #define BOOST_RE_PAT_EXCEPT_HPP #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #ifdef BOOST_REGEX_CXX03 #include #else #include #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/pending/object_cache.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE object_cache.hpp * VERSION see * DESCRIPTION: Implements a generic object cache. */ #ifndef BOOST_REGEX_OBJECT_CACHE_HPP #define BOOST_REGEX_OBJECT_CACHE_HPP #include #ifdef BOOST_REGEX_CXX03 #include #else #include #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/pending/static_mutex.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE static_mutex.hpp * VERSION see * DESCRIPTION: Declares static_mutex lock type, there are three different * implementations: POSIX pthreads, WIN32 threads, and portable, * these are described in more detail below. */ #ifndef BOOST_REGEX_STATIC_MUTEX_HPP #define BOOST_REGEX_STATIC_MUTEX_HPP #include #include // dll import/export options. #ifdef BOOST_HAS_PTHREADS #include #endif #if defined(BOOST_HAS_PTHREADS) && defined(PTHREAD_MUTEX_INITIALIZER) // // pthreads version: // simple wrap around a pthread_mutex_t initialized with // PTHREAD_MUTEX_INITIALIZER. // namespace boost{ class static_mutex; #define BOOST_STATIC_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER, } class BOOST_REGEX_DECL scoped_static_mutex_lock { public: scoped_static_mutex_lock(static_mutex& mut, bool lk = true); ~scoped_static_mutex_lock(); inline bool locked()const { return m_have_lock; } inline operator void const*()const { return locked() ? this : 0; } void lock(); void unlock(); private: static_mutex& m_mutex; bool m_have_lock; }; class static_mutex { public: typedef scoped_static_mutex_lock scoped_lock; pthread_mutex_t m_mutex; }; } // namespace boost #elif defined(BOOST_HAS_WINTHREADS) // // Win32 version: // Use a 32-bit int as a lock, along with a test-and-set // implementation using InterlockedCompareExchange. // #include namespace boost{ class BOOST_REGEX_DECL scoped_static_mutex_lock; class static_mutex { public: typedef scoped_static_mutex_lock scoped_lock; boost::int32_t m_mutex; }; #define BOOST_STATIC_MUTEX_INIT { 0, } class BOOST_REGEX_DECL scoped_static_mutex_lock { public: scoped_static_mutex_lock(static_mutex& mut, bool lk = true); ~scoped_static_mutex_lock(); operator void const*()const { return locked() ? this : 0; } bool locked()const { return m_have_lock; } void lock(); void unlock(); private: static_mutex& m_mutex; bool m_have_lock; scoped_static_mutex_lock(const scoped_static_mutex_lock&); scoped_static_mutex_lock& operator=(const scoped_static_mutex_lock&); }; } // namespace #else // // Portable version of a static mutex based on Boost.Thread library: // This has to use a single mutex shared by all instances of static_mutex // because boost::call_once doesn't alow us to pass instance information // down to the initialisation proceedure. In fact the initialisation routine // may need to be called more than once - but only once per instance. // // Since this preprocessor path is almost never taken, we hide these header // dependencies so that build tools don't find them. // #define BOOST_REGEX_H1 #define BOOST_REGEX_H2 #define BOOST_REGEX_H3 #include BOOST_REGEX_H1 #include BOOST_REGEX_H2 #include BOOST_REGEX_H3 #undef BOOST_REGEX_H1 #undef BOOST_REGEX_H2 #undef BOOST_REGEX_H3 namespace boost{ class BOOST_REGEX_DECL scoped_static_mutex_lock; extern "C" BOOST_REGEX_DECL void boost_regex_free_static_mutex(); class BOOST_REGEX_DECL static_mutex { public: typedef scoped_static_mutex_lock scoped_lock; static void init(); static boost::recursive_mutex* m_pmutex; static boost::once_flag m_once; }; #define BOOST_STATIC_MUTEX_INIT { } class BOOST_REGEX_DECL scoped_static_mutex_lock { public: scoped_static_mutex_lock(static_mutex& mut, bool lk = true); ~scoped_static_mutex_lock(); operator void const*()const; bool locked()const; void lock(); void unlock(); private: boost::unique_lock* m_plock; bool m_have_lock; }; inline scoped_static_mutex_lock::operator void const*()const { return locked() ? this : 0; } inline bool scoped_static_mutex_lock::locked()const { return m_have_lock; } } // namespace #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/pending/unicode_iterator.hpp ================================================ /* * * Copyright (c) 2020 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE unicode_iterator.hpp * VERSION see * DESCRIPTION: Iterator adapters for converting between different Unicode encodings. */ #ifndef BOOST_REGEX_PENDING_UNICODE_ITERATOR_HPP #define BOOST_REGEX_PENDING_UNICODE_ITERATOR_HPP #include #if defined(BOOST_REGEX_CXX03) #include #else #include #endif #endif // BOOST_REGEX_PENDING_UNICODE_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/regex_traits.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits classes. */ #ifndef BOOST_REGEX_TRAITS_HPP #define BOOST_REGEX_TRAITS_HPP #ifndef BOOST_REGEX_CONFIG_HPP # include #endif # ifndef BOOST_REGEX_TRAITS_HPP_INCLUDED #ifdef BOOST_REGEX_CXX03 # include #else # include #endif # endif #endif // include ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/user.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE user.hpp * VERSION see * DESCRIPTION: User settable options. */ // define if you want the regex library to use the C locale // even on Win32: // #define BOOST_REGEX_USE_C_LOCALE // define this is you want the regex library to use the C++ // locale: // #define BOOST_REGEX_USE_CPP_LOCALE // define this if the runtime library is a dll, and you // want BOOST_REGEX_DYN_LINK to set up dll exports/imports // with __declspec(dllexport)/__declspec(dllimport.) // #define BOOST_REGEX_HAS_DLL_RUNTIME // define this if you want to dynamically link to regex, // if the runtime library is also a dll (Probably Win32 specific, // and has no effect unless BOOST_REGEX_HAS_DLL_RUNTIME is set): // #define BOOST_REGEX_DYN_LINK // define this if you don't want the lib to automatically // select its link libraries: // #define BOOST_REGEX_NO_LIB // define this if templates with switch statements cause problems: // #define BOOST_REGEX_NO_TEMPLATE_SWITCH_MERGE // define this to disable Win32 support when available: // #define BOOST_REGEX_NO_W32 // define this if bool is not a real type: // #define BOOST_REGEX_NO_BOOL // define this if no template instances are to be placed in // the library rather than users object files: // #define BOOST_REGEX_NO_EXTERNAL_TEMPLATES // define this if the forward declarations in regex_fwd.hpp // cause more problems than they are worth: // #define BOOST_REGEX_NO_FWD // define this if your compiler supports MS Windows structured // exception handling. // #define BOOST_REGEX_HAS_MS_STACK_GUARD // define this if you want to use the recursive algorithm // even if BOOST_REGEX_HAS_MS_STACK_GUARD is not defined. // NOTE: OBSOLETE!! // #define BOOST_REGEX_RECURSIVE // define this if you want to use the non-recursive // algorithm, even if the recursive version would be the default. // NOTE: OBSOLETE!! // #define BOOST_REGEX_NON_RECURSIVE // define this if you want to set the size of the memory blocks // used by the non-recursive algorithm. // #define BOOST_REGEX_BLOCKSIZE 4096 // define this if you want to set the maximum number of memory blocks // used by the non-recursive algorithm. // #define BOOST_REGEX_MAX_BLOCKS 1024 // define this if you want to set the maximum number of memory blocks // cached by the non-recursive algorithm: Normally this is 16, but can be // higher if you have multiple threads all using boost.regex, or lower // if you don't want boost.regex to cache memory. // #define BOOST_REGEX_MAX_CACHE_BLOCKS 16 // define this if you want to be able to access extended capture // information in your sub_match's (caution this will slow things // down quite a bit). // #define BOOST_REGEX_MATCH_EXTRA // define this if you want to enable support for Unicode via ICU. // #define BOOST_HAS_ICU // define this if you want regex to use __cdecl calling convensions, even when __fastcall is available: // #define BOOST_REGEX_NO_FASTCALL ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/basic_regex.hpp ================================================ /* * * Copyright (c) 1998-2004 John Maddock * Copyright 2011 Garmin Ltd. or its subsidiaries * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org/ for most recent version. * FILE basic_regex.cpp * VERSION see * DESCRIPTION: Declares template class basic_regex. */ #ifndef BOOST_REGEX_V4_BASIC_REGEX_HPP #define BOOST_REGEX_V4_BASIC_REGEX_HPP #include #include #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable : 4251) #if BOOST_MSVC < 1700 # pragma warning(disable : 4231) #endif #if BOOST_MSVC < 1600 #pragma warning(disable : 4660) #endif #if BOOST_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace BOOST_REGEX_DETAIL_NS{ // // forward declaration, we will need this one later: // template class basic_regex_parser; template void bubble_down_one(I first, I last) { if(first != last) { I next = last - 1; while((next != first) && (*next < *(next-1))) { (next-1)->swap(*next); --next; } } } static const int hash_value_mask = 1 << (std::numeric_limits::digits - 1); template inline int hash_value_from_capture_name(Iterator i, Iterator j) { std::size_t r = boost::hash_range(i, j); r %= ((std::numeric_limits::max)()); return static_cast(r) | hash_value_mask; } class named_subexpressions { public: struct name { template name(const charT* i, const charT* j, int idx) : index(idx) { hash = hash_value_from_capture_name(i, j); } name(int h, int idx) : index(idx), hash(h) { } int index; int hash; bool operator < (const name& other)const { return hash < other.hash; } bool operator == (const name& other)const { return hash == other.hash; } void swap(name& other) { std::swap(index, other.index); std::swap(hash, other.hash); } }; typedef std::vector::const_iterator const_iterator; typedef std::pair range_type; named_subexpressions(){} template void set_name(const charT* i, const charT* j, int index) { m_sub_names.push_back(name(i, j, index)); bubble_down_one(m_sub_names.begin(), m_sub_names.end()); } template int get_id(const charT* i, const charT* j)const { name t(i, j, 0); typename std::vector::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t); if((pos != m_sub_names.end()) && (*pos == t)) { return pos->index; } return -1; } template range_type equal_range(const charT* i, const charT* j)const { name t(i, j, 0); return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t); } int get_id(int h)const { name t(h, 0); std::vector::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t); if((pos != m_sub_names.end()) && (*pos == t)) { return pos->index; } return -1; } range_type equal_range(int h)const { name t(h, 0); return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t); } private: std::vector m_sub_names; }; // // class regex_data: // represents the data we wish to expose to the matching algorithms. // template struct regex_data : public named_subexpressions { typedef regex_constants::syntax_option_type flag_type; typedef std::size_t size_type; regex_data(const ::boost::shared_ptr< ::boost::regex_traits_wrapper >& t) : m_ptraits(t), m_flags(0), m_status(0), m_expression(0), m_expression_len(0), m_mark_count(0), m_first_state(0), m_restart_type(0), #if !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) && !(defined(BOOST_MSVC) && (BOOST_MSVC < 1900)) m_startmap{ 0 }, #endif m_can_be_null(0), m_word_mask(0), m_has_recursions(false), m_disable_match_any(false) {} regex_data() : m_ptraits(new ::boost::regex_traits_wrapper()), m_flags(0), m_status(0), m_expression(0), m_expression_len(0), m_mark_count(0), m_first_state(0), m_restart_type(0), #if !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) && !(defined(BOOST_MSVC) && (BOOST_MSVC < 1900)) m_startmap{ 0 }, #endif m_can_be_null(0), m_word_mask(0), m_has_recursions(false), m_disable_match_any(false) {} ::boost::shared_ptr< ::boost::regex_traits_wrapper > m_ptraits; // traits class instance flag_type m_flags; // flags with which we were compiled int m_status; // error code (0 implies OK). const charT* m_expression; // the original expression std::ptrdiff_t m_expression_len; // the length of the original expression size_type m_mark_count; // the number of marked sub-expressions BOOST_REGEX_DETAIL_NS::re_syntax_base* m_first_state; // the first state of the machine unsigned m_restart_type; // search optimisation type unsigned char m_startmap[1 << CHAR_BIT]; // which characters can start a match unsigned int m_can_be_null; // whether we can match a null string BOOST_REGEX_DETAIL_NS::raw_storage m_data; // the buffer in which our states are constructed typename traits::char_class_type m_word_mask; // mask used to determine if a character is a word character std::vector< std::pair< std::size_t, std::size_t> > m_subs; // Position of sub-expressions within the *string*. bool m_has_recursions; // whether we have recursive expressions; bool m_disable_match_any; // when set we need to disable the match_any flag as it causes different/buggy behaviour. }; // // class basic_regex_implementation // pimpl implementation class for basic_regex. // template class basic_regex_implementation : public regex_data { public: typedef regex_constants::syntax_option_type flag_type; typedef std::ptrdiff_t difference_type; typedef std::size_t size_type; typedef typename traits::locale_type locale_type; typedef const charT* const_iterator; basic_regex_implementation(){} basic_regex_implementation(const ::boost::shared_ptr< ::boost::regex_traits_wrapper >& t) : regex_data(t) {} void assign(const charT* arg_first, const charT* arg_last, flag_type f) { regex_data* pdat = this; basic_regex_parser parser(pdat); parser.parse(arg_first, arg_last, f); } locale_type BOOST_REGEX_CALL imbue(locale_type l) { return this->m_ptraits->imbue(l); } locale_type BOOST_REGEX_CALL getloc()const { return this->m_ptraits->getloc(); } std::basic_string BOOST_REGEX_CALL str()const { std::basic_string result; if(this->m_status == 0) result = std::basic_string(this->m_expression, this->m_expression_len); return result; } const_iterator BOOST_REGEX_CALL expression()const { return this->m_expression; } std::pair BOOST_REGEX_CALL subexpression(std::size_t n)const { const std::pair& pi = this->m_subs.at(n); std::pair p(expression() + pi.first, expression() + pi.second); return p; } // // begin, end: const_iterator BOOST_REGEX_CALL begin()const { return (this->m_status ? 0 : this->m_expression); } const_iterator BOOST_REGEX_CALL end()const { return (this->m_status ? 0 : this->m_expression + this->m_expression_len); } flag_type BOOST_REGEX_CALL flags()const { return this->m_flags; } size_type BOOST_REGEX_CALL size()const { return this->m_expression_len; } int BOOST_REGEX_CALL status()const { return this->m_status; } size_type BOOST_REGEX_CALL mark_count()const { return this->m_mark_count - 1; } const BOOST_REGEX_DETAIL_NS::re_syntax_base* get_first_state()const { return this->m_first_state; } unsigned get_restart_type()const { return this->m_restart_type; } const unsigned char* get_map()const { return this->m_startmap; } const ::boost::regex_traits_wrapper& get_traits()const { return *(this->m_ptraits); } bool can_be_null()const { return this->m_can_be_null; } const regex_data& get_data()const { basic_regex_implementation const* p = this; return *static_cast*>(p); } }; } // namespace BOOST_REGEX_DETAIL_NS // // class basic_regex: // represents the compiled // regular expression: // #ifdef BOOST_REGEX_NO_FWD template > #else template #endif class basic_regex : public regbase { public: // typedefs: typedef std::size_t traits_size_type; typedef typename traits::string_type traits_string_type; typedef charT char_type; typedef traits traits_type; typedef charT value_type; typedef charT& reference; typedef const charT& const_reference; typedef const charT* const_iterator; typedef const_iterator iterator; typedef std::ptrdiff_t difference_type; typedef std::size_t size_type; typedef regex_constants::syntax_option_type flag_type; // locale_type // placeholder for actual locale type used by the // traits class to localise *this. typedef typename traits::locale_type locale_type; public: explicit basic_regex(){} explicit basic_regex(const charT* p, flag_type f = regex_constants::normal) { assign(p, f); } basic_regex(const charT* p1, const charT* p2, flag_type f = regex_constants::normal) { assign(p1, p2, f); } basic_regex(const charT* p, size_type len, flag_type f) { assign(p, len, f); } basic_regex(const basic_regex& that) : m_pimpl(that.m_pimpl) {} ~basic_regex(){} basic_regex& BOOST_REGEX_CALL operator=(const basic_regex& that) { return assign(that); } basic_regex& BOOST_REGEX_CALL operator=(const charT* ptr) { return assign(ptr); } // // assign: basic_regex& assign(const basic_regex& that) { m_pimpl = that.m_pimpl; return *this; } basic_regex& assign(const charT* p, flag_type f = regex_constants::normal) { return assign(p, p + traits::length(p), f); } basic_regex& assign(const charT* p, size_type len, flag_type f) { return assign(p, p + len, f); } private: basic_regex& do_assign(const charT* p1, const charT* p2, flag_type f); public: basic_regex& assign(const charT* p1, const charT* p2, flag_type f = regex_constants::normal) { return do_assign(p1, p2, f); } #if !defined(BOOST_NO_MEMBER_TEMPLATES) template unsigned int BOOST_REGEX_CALL set_expression(const std::basic_string& p, flag_type f = regex_constants::normal) { return set_expression(p.data(), p.data() + p.size(), f); } template explicit basic_regex(const std::basic_string& p, flag_type f = regex_constants::normal) { assign(p, f); } template basic_regex(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal) { typedef typename traits::string_type seq_type; seq_type a(arg_first, arg_last); if(!a.empty()) assign(static_cast(&*a.begin()), static_cast(&*a.begin() + a.size()), f); else assign(static_cast(0), static_cast(0), f); } template basic_regex& BOOST_REGEX_CALL operator=(const std::basic_string& p) { return assign(p.data(), p.data() + p.size(), regex_constants::normal); } template basic_regex& BOOST_REGEX_CALL assign( const std::basic_string& s, flag_type f = regex_constants::normal) { return assign(s.data(), s.data() + s.size(), f); } template basic_regex& BOOST_REGEX_CALL assign(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal) { typedef typename traits::string_type seq_type; seq_type a(arg_first, arg_last); if(a.size()) { const charT* p1 = &*a.begin(); const charT* p2 = &*a.begin() + a.size(); return assign(p1, p2, f); } return assign(static_cast(0), static_cast(0), f); } #else unsigned int BOOST_REGEX_CALL set_expression(const std::basic_string& p, flag_type f = regex_constants::normal) { return set_expression(p.data(), p.data() + p.size(), f); } basic_regex(const std::basic_string& p, flag_type f = regex_constants::normal) { assign(p, f); } basic_regex& BOOST_REGEX_CALL operator=(const std::basic_string& p) { return assign(p.data(), p.data() + p.size(), regex_constants::normal); } basic_regex& BOOST_REGEX_CALL assign( const std::basic_string& s, flag_type f = regex_constants::normal) { return assign(s.data(), s.data() + s.size(), f); } #endif // // locale: locale_type BOOST_REGEX_CALL imbue(locale_type l); locale_type BOOST_REGEX_CALL getloc()const { return m_pimpl.get() ? m_pimpl->getloc() : locale_type(); } // // getflags: // retained for backwards compatibility only, "flags" // is now the preferred name: flag_type BOOST_REGEX_CALL getflags()const { return flags(); } flag_type BOOST_REGEX_CALL flags()const { return m_pimpl.get() ? m_pimpl->flags() : 0; } // // str: std::basic_string BOOST_REGEX_CALL str()const { return m_pimpl.get() ? m_pimpl->str() : std::basic_string(); } // // begin, end, subexpression: std::pair BOOST_REGEX_CALL subexpression(std::size_t n)const { if(!m_pimpl.get()) boost::throw_exception(std::logic_error("Can't access subexpressions in an invalid regex.")); return m_pimpl->subexpression(n); } const_iterator BOOST_REGEX_CALL begin()const { return (m_pimpl.get() ? m_pimpl->begin() : 0); } const_iterator BOOST_REGEX_CALL end()const { return (m_pimpl.get() ? m_pimpl->end() : 0); } // // swap: void BOOST_REGEX_CALL swap(basic_regex& that)throw() { m_pimpl.swap(that.m_pimpl); } // // size: size_type BOOST_REGEX_CALL size()const { return (m_pimpl.get() ? m_pimpl->size() : 0); } // // max_size: size_type BOOST_REGEX_CALL max_size()const { return UINT_MAX; } // // empty: bool BOOST_REGEX_CALL empty()const { return (m_pimpl.get() ? 0 != m_pimpl->status() : true); } size_type BOOST_REGEX_CALL mark_count()const { return (m_pimpl.get() ? m_pimpl->mark_count() : 0); } int status()const { return (m_pimpl.get() ? m_pimpl->status() : regex_constants::error_empty); } int BOOST_REGEX_CALL compare(const basic_regex& that) const { if(m_pimpl.get() == that.m_pimpl.get()) return 0; if(!m_pimpl.get()) return -1; if(!that.m_pimpl.get()) return 1; if(status() != that.status()) return status() - that.status(); if(flags() != that.flags()) return flags() - that.flags(); return str().compare(that.str()); } bool BOOST_REGEX_CALL operator==(const basic_regex& e)const { return compare(e) == 0; } bool BOOST_REGEX_CALL operator != (const basic_regex& e)const { return compare(e) != 0; } bool BOOST_REGEX_CALL operator<(const basic_regex& e)const { return compare(e) < 0; } bool BOOST_REGEX_CALL operator>(const basic_regex& e)const { return compare(e) > 0; } bool BOOST_REGEX_CALL operator<=(const basic_regex& e)const { return compare(e) <= 0; } bool BOOST_REGEX_CALL operator>=(const basic_regex& e)const { return compare(e) >= 0; } // // The following are deprecated as public interfaces // but are available for compatibility with earlier versions. const charT* BOOST_REGEX_CALL expression()const { return (m_pimpl.get() && !m_pimpl->status() ? m_pimpl->expression() : 0); } unsigned int BOOST_REGEX_CALL set_expression(const charT* p1, const charT* p2, flag_type f = regex_constants::normal) { assign(p1, p2, f | regex_constants::no_except); return status(); } unsigned int BOOST_REGEX_CALL set_expression(const charT* p, flag_type f = regex_constants::normal) { assign(p, f | regex_constants::no_except); return status(); } unsigned int BOOST_REGEX_CALL error_code()const { return status(); } // // private access methods: // const BOOST_REGEX_DETAIL_NS::re_syntax_base* get_first_state()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_first_state(); } unsigned get_restart_type()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_restart_type(); } const unsigned char* get_map()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_map(); } const ::boost::regex_traits_wrapper& get_traits()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_traits(); } bool can_be_null()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->can_be_null(); } const BOOST_REGEX_DETAIL_NS::regex_data& get_data()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_data(); } boost::shared_ptr get_named_subs()const { return m_pimpl; } private: shared_ptr > m_pimpl; }; // // out of line members; // these are the only members that mutate the basic_regex object, // and are designed to provide the strong exception guarantee // (in the event of a throw, the state of the object remains unchanged). // template basic_regex& basic_regex::do_assign(const charT* p1, const charT* p2, flag_type f) { shared_ptr > temp; if(!m_pimpl.get()) { temp = shared_ptr >(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation()); } else { temp = shared_ptr >(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation(m_pimpl->m_ptraits)); } temp->assign(p1, p2, f); temp.swap(m_pimpl); return *this; } template typename basic_regex::locale_type BOOST_REGEX_CALL basic_regex::imbue(locale_type l) { shared_ptr > temp(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation()); locale_type result = temp->imbue(l); temp.swap(m_pimpl); return result; } // // non-members: // template void swap(basic_regex& e1, basic_regex& e2) { e1.swap(e2); } #ifndef BOOST_NO_STD_LOCALE template std::basic_ostream& operator << (std::basic_ostream& os, const basic_regex& e) { return (os << e.str()); } #else template std::ostream& operator << (std::ostream& os, const basic_regex& e) { return (os << e.str()); } #endif // // class reg_expression: // this is provided for backwards compatibility only, // it is deprecated, no not use! // #ifdef BOOST_REGEX_NO_FWD template > #else template #endif class reg_expression : public basic_regex { public: typedef typename basic_regex::flag_type flag_type; typedef typename basic_regex::size_type size_type; explicit reg_expression(){} explicit reg_expression(const charT* p, flag_type f = regex_constants::normal) : basic_regex(p, f){} reg_expression(const charT* p1, const charT* p2, flag_type f = regex_constants::normal) : basic_regex(p1, p2, f){} reg_expression(const charT* p, size_type len, flag_type f) : basic_regex(p, len, f){} reg_expression(const reg_expression& that) : basic_regex(that) {} ~reg_expression(){} reg_expression& BOOST_REGEX_CALL operator=(const reg_expression& that) { return this->assign(that); } #if !defined(BOOST_NO_MEMBER_TEMPLATES) template explicit reg_expression(const std::basic_string& p, flag_type f = regex_constants::normal) : basic_regex(p, f) { } template reg_expression(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal) : basic_regex(arg_first, arg_last, f) { } template reg_expression& BOOST_REGEX_CALL operator=(const std::basic_string& p) { this->assign(p); return *this; } #else explicit reg_expression(const std::basic_string& p, flag_type f = regex_constants::normal) : basic_regex(p, f) { } reg_expression& BOOST_REGEX_CALL operator=(const std::basic_string& p) { this->assign(p); return *this; } #endif }; #ifdef BOOST_MSVC #pragma warning (pop) #endif } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/basic_regex_creator.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE basic_regex_creator.cpp * VERSION see * DESCRIPTION: Declares template class basic_regex_creator which fills in * the data members of a regex_data object. */ #ifndef BOOST_REGEX_V4_BASIC_REGEX_CREATOR_HPP #define BOOST_REGEX_V4_BASIC_REGEX_CREATOR_HPP #include #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC # pragma warning(push) #if BOOST_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ template struct digraph : public std::pair { digraph() : std::pair(charT(0), charT(0)){} digraph(charT c1) : std::pair(c1, charT(0)){} digraph(charT c1, charT c2) : std::pair(c1, c2) {} digraph(const digraph& d) : std::pair(d.first, d.second){} #ifndef BOOST_NO_CXX11_DEFAULTED_FUNCTIONS digraph& operator=(const digraph&) = default; #endif template digraph(const Seq& s) : std::pair() { BOOST_REGEX_ASSERT(s.size() <= 2); BOOST_REGEX_ASSERT(s.size()); this->first = s[0]; this->second = (s.size() > 1) ? s[1] : 0; } }; template class basic_char_set { public: typedef digraph digraph_type; typedef typename traits::string_type string_type; typedef typename traits::char_class_type m_type; basic_char_set() { m_negate = false; m_has_digraphs = false; m_classes = 0; m_negated_classes = 0; m_empty = true; } void add_single(const digraph_type& s) { m_singles.insert(s); if(s.second) m_has_digraphs = true; m_empty = false; } void add_range(const digraph_type& first, const digraph_type& end) { m_ranges.push_back(first); m_ranges.push_back(end); if(first.second) { m_has_digraphs = true; add_single(first); } if(end.second) { m_has_digraphs = true; add_single(end); } m_empty = false; } void add_class(m_type m) { m_classes |= m; m_empty = false; } void add_negated_class(m_type m) { m_negated_classes |= m; m_empty = false; } void add_equivalent(const digraph_type& s) { m_equivalents.insert(s); if(s.second) { m_has_digraphs = true; add_single(s); } m_empty = false; } void negate() { m_negate = true; //m_empty = false; } // // accessor functions: // bool has_digraphs()const { return m_has_digraphs; } bool is_negated()const { return m_negate; } typedef typename std::vector::const_iterator list_iterator; typedef typename std::set::const_iterator set_iterator; set_iterator singles_begin()const { return m_singles.begin(); } set_iterator singles_end()const { return m_singles.end(); } list_iterator ranges_begin()const { return m_ranges.begin(); } list_iterator ranges_end()const { return m_ranges.end(); } set_iterator equivalents_begin()const { return m_equivalents.begin(); } set_iterator equivalents_end()const { return m_equivalents.end(); } m_type classes()const { return m_classes; } m_type negated_classes()const { return m_negated_classes; } bool empty()const { return m_empty; } private: std::set m_singles; // a list of single characters to match std::vector m_ranges; // a list of end points of our ranges bool m_negate; // true if the set is to be negated bool m_has_digraphs; // true if we have digraphs present m_type m_classes; // character classes to match m_type m_negated_classes; // negated character classes to match bool m_empty; // whether we've added anything yet std::set m_equivalents; // a list of equivalence classes }; template class basic_regex_creator { public: basic_regex_creator(regex_data* data); std::ptrdiff_t getoffset(void* addr) { return getoffset(addr, m_pdata->m_data.data()); } std::ptrdiff_t getoffset(const void* addr, const void* base) { return static_cast(addr) - static_cast(base); } re_syntax_base* getaddress(std::ptrdiff_t off) { return getaddress(off, m_pdata->m_data.data()); } re_syntax_base* getaddress(std::ptrdiff_t off, void* base) { return static_cast(static_cast(static_cast(base) + off)); } void init(unsigned l_flags) { m_pdata->m_flags = l_flags; m_icase = l_flags & regex_constants::icase; } regbase::flag_type flags() { return m_pdata->m_flags; } void flags(regbase::flag_type f) { m_pdata->m_flags = f; if(m_icase != static_cast(f & regbase::icase)) { m_icase = static_cast(f & regbase::icase); } } re_syntax_base* append_state(syntax_element_type t, std::size_t s = sizeof(re_syntax_base)); re_syntax_base* insert_state(std::ptrdiff_t pos, syntax_element_type t, std::size_t s = sizeof(re_syntax_base)); re_literal* append_literal(charT c); re_syntax_base* append_set(const basic_char_set& char_set); re_syntax_base* append_set(const basic_char_set& char_set, mpl::false_*); re_syntax_base* append_set(const basic_char_set& char_set, mpl::true_*); void finalize(const charT* p1, const charT* p2); protected: regex_data* m_pdata; // pointer to the basic_regex_data struct we are filling in const ::boost::regex_traits_wrapper& m_traits; // convenience reference to traits class re_syntax_base* m_last_state; // the last state we added bool m_icase; // true for case insensitive matches unsigned m_repeater_id; // the state_id of the next repeater bool m_has_backrefs; // true if there are actually any backrefs indexed_bit_flag m_backrefs; // bitmask of permitted backrefs boost::uintmax_t m_bad_repeats; // bitmask of repeats we can't deduce a startmap for; bool m_has_recursions; // set when we have recursive expressions to fixup std::vector m_recursion_checks; // notes which recursions we've followed while analysing this expression typename traits::char_class_type m_word_mask; // mask used to determine if a character is a word character typename traits::char_class_type m_mask_space; // mask used to determine if a character is a word character typename traits::char_class_type m_lower_mask; // mask used to determine if a character is a lowercase character typename traits::char_class_type m_upper_mask; // mask used to determine if a character is an uppercase character typename traits::char_class_type m_alpha_mask; // mask used to determine if a character is an alphabetic character private: basic_regex_creator& operator=(const basic_regex_creator&); basic_regex_creator(const basic_regex_creator&); void fixup_pointers(re_syntax_base* state); void fixup_recursions(re_syntax_base* state); void create_startmaps(re_syntax_base* state); int calculate_backstep(re_syntax_base* state); void create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask); unsigned get_restart_type(re_syntax_base* state); void set_all_masks(unsigned char* bits, unsigned char); bool is_bad_repeat(re_syntax_base* pt); void set_bad_repeat(re_syntax_base* pt); syntax_element_type get_repeat_type(re_syntax_base* state); void probe_leading_repeat(re_syntax_base* state); }; template basic_regex_creator::basic_regex_creator(regex_data* data) : m_pdata(data), m_traits(*(data->m_ptraits)), m_last_state(0), m_icase(false), m_repeater_id(0), m_has_backrefs(false), m_bad_repeats(0), m_has_recursions(false), m_word_mask(0), m_mask_space(0), m_lower_mask(0), m_upper_mask(0), m_alpha_mask(0) { m_pdata->m_data.clear(); m_pdata->m_status = ::boost::regex_constants::error_ok; static const charT w = 'w'; static const charT s = 's'; static const charT l[5] = { 'l', 'o', 'w', 'e', 'r', }; static const charT u[5] = { 'u', 'p', 'p', 'e', 'r', }; static const charT a[5] = { 'a', 'l', 'p', 'h', 'a', }; m_word_mask = m_traits.lookup_classname(&w, &w +1); m_mask_space = m_traits.lookup_classname(&s, &s +1); m_lower_mask = m_traits.lookup_classname(l, l + 5); m_upper_mask = m_traits.lookup_classname(u, u + 5); m_alpha_mask = m_traits.lookup_classname(a, a + 5); m_pdata->m_word_mask = m_word_mask; BOOST_REGEX_ASSERT(m_word_mask != 0); BOOST_REGEX_ASSERT(m_mask_space != 0); BOOST_REGEX_ASSERT(m_lower_mask != 0); BOOST_REGEX_ASSERT(m_upper_mask != 0); BOOST_REGEX_ASSERT(m_alpha_mask != 0); } template re_syntax_base* basic_regex_creator::append_state(syntax_element_type t, std::size_t s) { // if the state is a backref then make a note of it: if(t == syntax_element_backref) this->m_has_backrefs = true; // append a new state, start by aligning our last one: m_pdata->m_data.align(); // set the offset to the next state in our last one: if(m_last_state) m_last_state->next.i = m_pdata->m_data.size() - getoffset(m_last_state); // now actually extend our data: m_last_state = static_cast(m_pdata->m_data.extend(s)); // fill in boilerplate options in the new state: m_last_state->next.i = 0; m_last_state->type = t; return m_last_state; } template re_syntax_base* basic_regex_creator::insert_state(std::ptrdiff_t pos, syntax_element_type t, std::size_t s) { // append a new state, start by aligning our last one: m_pdata->m_data.align(); // set the offset to the next state in our last one: if(m_last_state) m_last_state->next.i = m_pdata->m_data.size() - getoffset(m_last_state); // remember the last state position: std::ptrdiff_t off = getoffset(m_last_state) + s; // now actually insert our data: re_syntax_base* new_state = static_cast(m_pdata->m_data.insert(pos, s)); // fill in boilerplate options in the new state: new_state->next.i = s; new_state->type = t; m_last_state = getaddress(off); return new_state; } template re_literal* basic_regex_creator::append_literal(charT c) { re_literal* result; // start by seeing if we have an existing re_literal we can extend: if((0 == m_last_state) || (m_last_state->type != syntax_element_literal)) { // no existing re_literal, create a new one: result = static_cast(append_state(syntax_element_literal, sizeof(re_literal) + sizeof(charT))); result->length = 1; *static_cast(static_cast(result+1)) = m_traits.translate(c, m_icase); } else { // we have an existing re_literal, extend it: std::ptrdiff_t off = getoffset(m_last_state); m_pdata->m_data.extend(sizeof(charT)); m_last_state = result = static_cast(getaddress(off)); charT* characters = static_cast(static_cast(result+1)); characters[result->length] = m_traits.translate(c, m_icase); result->length += 1; } return result; } template inline re_syntax_base* basic_regex_creator::append_set( const basic_char_set& char_set) { typedef mpl::bool_< (sizeof(charT) == 1) > truth_type; return char_set.has_digraphs() ? append_set(char_set, static_cast(0)) : append_set(char_set, static_cast(0)); } template re_syntax_base* basic_regex_creator::append_set( const basic_char_set& char_set, mpl::false_*) { typedef typename traits::string_type string_type; typedef typename basic_char_set::list_iterator item_iterator; typedef typename basic_char_set::set_iterator set_iterator; typedef typename traits::char_class_type m_type; re_set_long* result = static_cast*>(append_state(syntax_element_long_set, sizeof(re_set_long))); // // fill in the basics: // result->csingles = static_cast(::boost::BOOST_REGEX_DETAIL_NS::distance(char_set.singles_begin(), char_set.singles_end())); result->cranges = static_cast(::boost::BOOST_REGEX_DETAIL_NS::distance(char_set.ranges_begin(), char_set.ranges_end())) / 2; result->cequivalents = static_cast(::boost::BOOST_REGEX_DETAIL_NS::distance(char_set.equivalents_begin(), char_set.equivalents_end())); result->cclasses = char_set.classes(); result->cnclasses = char_set.negated_classes(); if(flags() & regbase::icase) { // adjust classes as needed: if(((result->cclasses & m_lower_mask) == m_lower_mask) || ((result->cclasses & m_upper_mask) == m_upper_mask)) result->cclasses |= m_alpha_mask; if(((result->cnclasses & m_lower_mask) == m_lower_mask) || ((result->cnclasses & m_upper_mask) == m_upper_mask)) result->cnclasses |= m_alpha_mask; } result->isnot = char_set.is_negated(); result->singleton = !char_set.has_digraphs(); // // remember where the state is for later: // std::ptrdiff_t offset = getoffset(result); // // now extend with all the singles: // item_iterator first, last; set_iterator sfirst, slast; sfirst = char_set.singles_begin(); slast = char_set.singles_end(); while(sfirst != slast) { charT* p = static_cast(this->m_pdata->m_data.extend(sizeof(charT) * (sfirst->first == static_cast(0) ? 1 : sfirst->second ? 3 : 2))); p[0] = m_traits.translate(sfirst->first, m_icase); if(sfirst->first == static_cast(0)) { p[0] = 0; } else if(sfirst->second) { p[1] = m_traits.translate(sfirst->second, m_icase); p[2] = 0; } else p[1] = 0; ++sfirst; } // // now extend with all the ranges: // first = char_set.ranges_begin(); last = char_set.ranges_end(); while(first != last) { // first grab the endpoints of the range: digraph c1 = *first; c1.first = this->m_traits.translate(c1.first, this->m_icase); c1.second = this->m_traits.translate(c1.second, this->m_icase); ++first; digraph c2 = *first; c2.first = this->m_traits.translate(c2.first, this->m_icase); c2.second = this->m_traits.translate(c2.second, this->m_icase); ++first; string_type s1, s2; // different actions now depending upon whether collation is turned on: if(flags() & regex_constants::collate) { // we need to transform our range into sort keys: charT a1[3] = { c1.first, c1.second, charT(0), }; charT a2[3] = { c2.first, c2.second, charT(0), }; s1 = this->m_traits.transform(a1, (a1[1] ? a1+2 : a1+1)); s2 = this->m_traits.transform(a2, (a2[1] ? a2+2 : a2+1)); if(s1.empty()) s1 = string_type(1, charT(0)); if(s2.empty()) s2 = string_type(1, charT(0)); } else { if(c1.second) { s1.insert(s1.end(), c1.first); s1.insert(s1.end(), c1.second); } else s1 = string_type(1, c1.first); if(c2.second) { s2.insert(s2.end(), c2.first); s2.insert(s2.end(), c2.second); } else s2.insert(s2.end(), c2.first); } if(s1 > s2) { // Oops error: return 0; } charT* p = static_cast(this->m_pdata->m_data.extend(sizeof(charT) * (s1.size() + s2.size() + 2) ) ); BOOST_REGEX_DETAIL_NS::copy(s1.begin(), s1.end(), p); p[s1.size()] = charT(0); p += s1.size() + 1; BOOST_REGEX_DETAIL_NS::copy(s2.begin(), s2.end(), p); p[s2.size()] = charT(0); } // // now process the equivalence classes: // sfirst = char_set.equivalents_begin(); slast = char_set.equivalents_end(); while(sfirst != slast) { string_type s; if(sfirst->second) { charT cs[3] = { sfirst->first, sfirst->second, charT(0), }; s = m_traits.transform_primary(cs, cs+2); } else s = m_traits.transform_primary(&sfirst->first, &sfirst->first+1); if(s.empty()) return 0; // invalid or unsupported equivalence class charT* p = static_cast(this->m_pdata->m_data.extend(sizeof(charT) * (s.size()+1) ) ); BOOST_REGEX_DETAIL_NS::copy(s.begin(), s.end(), p); p[s.size()] = charT(0); ++sfirst; } // // finally reset the address of our last state: // m_last_state = result = static_cast*>(getaddress(offset)); return result; } template inline bool char_less(T t1, T t2) { return t1 < t2; } inline bool char_less(char t1, char t2) { return static_cast(t1) < static_cast(t2); } inline bool char_less(signed char t1, signed char t2) { return static_cast(t1) < static_cast(t2); } template re_syntax_base* basic_regex_creator::append_set( const basic_char_set& char_set, mpl::true_*) { typedef typename traits::string_type string_type; typedef typename basic_char_set::list_iterator item_iterator; typedef typename basic_char_set::set_iterator set_iterator; re_set* result = static_cast(append_state(syntax_element_set, sizeof(re_set))); bool negate = char_set.is_negated(); std::memset(result->_map, 0, sizeof(result->_map)); // // handle singles first: // item_iterator first, last; set_iterator sfirst, slast; sfirst = char_set.singles_begin(); slast = char_set.singles_end(); while(sfirst != slast) { for(unsigned int i = 0; i < (1 << CHAR_BIT); ++i) { if(this->m_traits.translate(static_cast(i), this->m_icase) == this->m_traits.translate(sfirst->first, this->m_icase)) result->_map[i] = true; } ++sfirst; } // // OK now handle ranges: // first = char_set.ranges_begin(); last = char_set.ranges_end(); while(first != last) { // first grab the endpoints of the range: charT c1 = this->m_traits.translate(first->first, this->m_icase); ++first; charT c2 = this->m_traits.translate(first->first, this->m_icase); ++first; // different actions now depending upon whether collation is turned on: if(flags() & regex_constants::collate) { // we need to transform our range into sort keys: charT c3[2] = { c1, charT(0), }; string_type s1 = this->m_traits.transform(c3, c3+1); c3[0] = c2; string_type s2 = this->m_traits.transform(c3, c3+1); if(s1 > s2) { // Oops error: return 0; } BOOST_REGEX_ASSERT(c3[1] == charT(0)); for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { c3[0] = static_cast(i); string_type s3 = this->m_traits.transform(c3, c3 +1); if((s1 <= s3) && (s3 <= s2)) result->_map[i] = true; } } else { if(char_less(c2, c1)) { // Oops error: return 0; } // everything in range matches: std::memset(result->_map + static_cast(c1), true, static_cast(1u) + static_cast(static_cast(c2) - static_cast(c1))); } } // // and now the classes: // typedef typename traits::char_class_type m_type; m_type m = char_set.classes(); if(flags() & regbase::icase) { // adjust m as needed: if(((m & m_lower_mask) == m_lower_mask) || ((m & m_upper_mask) == m_upper_mask)) m |= m_alpha_mask; } if(m != 0) { for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { if(this->m_traits.isctype(static_cast(i), m)) result->_map[i] = true; } } // // and now the negated classes: // m = char_set.negated_classes(); if(flags() & regbase::icase) { // adjust m as needed: if(((m & m_lower_mask) == m_lower_mask) || ((m & m_upper_mask) == m_upper_mask)) m |= m_alpha_mask; } if(m != 0) { for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { if(0 == this->m_traits.isctype(static_cast(i), m)) result->_map[i] = true; } } // // now process the equivalence classes: // sfirst = char_set.equivalents_begin(); slast = char_set.equivalents_end(); while(sfirst != slast) { string_type s; BOOST_REGEX_ASSERT(static_cast(0) == sfirst->second); s = m_traits.transform_primary(&sfirst->first, &sfirst->first+1); if(s.empty()) return 0; // invalid or unsupported equivalence class for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { charT c[2] = { (static_cast(i)), charT(0), }; string_type s2 = this->m_traits.transform_primary(c, c+1); if(s == s2) result->_map[i] = true; } ++sfirst; } if(negate) { for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { result->_map[i] = !(result->_map[i]); } } return result; } template void basic_regex_creator::finalize(const charT* p1, const charT* p2) { if(this->m_pdata->m_status) return; // we've added all the states we need, now finish things off. // start by adding a terminating state: append_state(syntax_element_match); // extend storage to store original expression: std::ptrdiff_t len = p2 - p1; m_pdata->m_expression_len = len; charT* ps = static_cast(m_pdata->m_data.extend(sizeof(charT) * (1 + (p2 - p1)))); m_pdata->m_expression = ps; BOOST_REGEX_DETAIL_NS::copy(p1, p2, ps); ps[p2 - p1] = 0; // fill in our other data... // successful parsing implies a zero status: m_pdata->m_status = 0; // get the first state of the machine: m_pdata->m_first_state = static_cast(m_pdata->m_data.data()); // fixup pointers in the machine: fixup_pointers(m_pdata->m_first_state); if(m_has_recursions) { m_pdata->m_has_recursions = true; fixup_recursions(m_pdata->m_first_state); if(this->m_pdata->m_status) return; } else m_pdata->m_has_recursions = false; // create nested startmaps: create_startmaps(m_pdata->m_first_state); // create main startmap: std::memset(m_pdata->m_startmap, 0, sizeof(m_pdata->m_startmap)); m_pdata->m_can_be_null = 0; m_bad_repeats = 0; if(m_has_recursions) m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u); create_startmap(m_pdata->m_first_state, m_pdata->m_startmap, &(m_pdata->m_can_be_null), mask_all); // get the restart type: m_pdata->m_restart_type = get_restart_type(m_pdata->m_first_state); // optimise a leading repeat if there is one: probe_leading_repeat(m_pdata->m_first_state); } template void basic_regex_creator::fixup_pointers(re_syntax_base* state) { while(state) { switch(state->type) { case syntax_element_recurse: m_has_recursions = true; if(state->next.i) state->next.p = getaddress(state->next.i, state); else state->next.p = 0; break; case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: // set the state_id of this repeat: static_cast(state)->state_id = m_repeater_id++; BOOST_FALLTHROUGH; case syntax_element_alt: std::memset(static_cast(state)->_map, 0, sizeof(static_cast(state)->_map)); static_cast(state)->can_be_null = 0; BOOST_FALLTHROUGH; case syntax_element_jump: static_cast(state)->alt.p = getaddress(static_cast(state)->alt.i, state); BOOST_FALLTHROUGH; default: if(state->next.i) state->next.p = getaddress(state->next.i, state); else state->next.p = 0; } state = state->next.p; } } template void basic_regex_creator::fixup_recursions(re_syntax_base* state) { re_syntax_base* base = state; while(state) { switch(state->type) { case syntax_element_assert_backref: { // just check that the index is valid: int idx = static_cast(state)->index; if(idx < 0) { idx = -idx-1; if(idx >= hash_value_mask) { idx = m_pdata->get_id(idx); if(idx <= 0) { // check of sub-expression that doesn't exist: if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = boost::regex_constants::error_bad_pattern; // // clear the expression, we should be empty: // this->m_pdata->m_expression = 0; this->m_pdata->m_expression_len = 0; // // and throw if required: // if(0 == (this->flags() & regex_constants::no_except)) { std::string message = "Encountered a forward reference to a marked sub-expression that does not exist."; boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0); e.raise(); } } } } } break; case syntax_element_recurse: { bool ok = false; re_syntax_base* p = base; std::ptrdiff_t idx = static_cast(state)->alt.i; if(idx >= hash_value_mask) { // // There may be more than one capture group with this hash, just do what Perl // does and recurse to the leftmost: // idx = m_pdata->get_id(static_cast(idx)); } if(idx < 0) { ok = false; } else { while(p) { if((p->type == syntax_element_startmark) && (static_cast(p)->index == idx)) { // // We've found the target of the recursion, set the jump target: // static_cast(state)->alt.p = p; ok = true; // // Now scan the target for nested repeats: // p = p->next.p; int next_rep_id = 0; while(p) { switch(p->type) { case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: next_rep_id = static_cast(p)->state_id; break; case syntax_element_endmark: if(static_cast(p)->index == idx) next_rep_id = -1; break; default: break; } if(next_rep_id) break; p = p->next.p; } if(next_rep_id > 0) { static_cast(state)->state_id = next_rep_id - 1; } break; } p = p->next.p; } } if(!ok) { // recursion to sub-expression that doesn't exist: if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = boost::regex_constants::error_bad_pattern; // // clear the expression, we should be empty: // this->m_pdata->m_expression = 0; this->m_pdata->m_expression_len = 0; // // and throw if required: // if(0 == (this->flags() & regex_constants::no_except)) { std::string message = "Encountered a forward reference to a recursive sub-expression that does not exist."; boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0); e.raise(); } } } break; default: break; } state = state->next.p; } } template void basic_regex_creator::create_startmaps(re_syntax_base* state) { // non-recursive implementation: // create the last map in the machine first, so that earlier maps // can make use of the result... // // This was originally a recursive implementation, but that caused stack // overflows with complex expressions on small stacks (think COM+). // start by saving the case setting: bool l_icase = m_icase; std::vector > v; while(state) { switch(state->type) { case syntax_element_toggle_case: // we need to track case changes here: m_icase = static_cast(state)->icase; state = state->next.p; continue; case syntax_element_alt: case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: // just push the state onto our stack for now: v.push_back(std::pair(m_icase, state)); state = state->next.p; break; case syntax_element_backstep: // we need to calculate how big the backstep is: static_cast(state)->index = this->calculate_backstep(state->next.p); if(static_cast(state)->index < 0) { // Oops error: if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = boost::regex_constants::error_bad_pattern; // // clear the expression, we should be empty: // this->m_pdata->m_expression = 0; this->m_pdata->m_expression_len = 0; // // and throw if required: // if(0 == (this->flags() & regex_constants::no_except)) { std::string message = "Invalid lookbehind assertion encountered in the regular expression."; boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0); e.raise(); } } BOOST_FALLTHROUGH; default: state = state->next.p; } } // now work through our list, building all the maps as we go: while(!v.empty()) { // Initialize m_recursion_checks if we need it: if(m_has_recursions) m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u); const std::pair& p = v.back(); m_icase = p.first; state = p.second; v.pop_back(); // Build maps: m_bad_repeats = 0; create_startmap(state->next.p, static_cast(state)->_map, &static_cast(state)->can_be_null, mask_take); m_bad_repeats = 0; if(m_has_recursions) m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u); create_startmap(static_cast(state)->alt.p, static_cast(state)->_map, &static_cast(state)->can_be_null, mask_skip); // adjust the type of the state to allow for faster matching: state->type = this->get_repeat_type(state); } // restore case sensitivity: m_icase = l_icase; } template int basic_regex_creator::calculate_backstep(re_syntax_base* state) { typedef typename traits::char_class_type m_type; int result = 0; while(state) { switch(state->type) { case syntax_element_startmark: if((static_cast(state)->index == -1) || (static_cast(state)->index == -2)) { state = static_cast(state->next.p)->alt.p->next.p; continue; } else if(static_cast(state)->index == -3) { state = state->next.p->next.p; continue; } break; case syntax_element_endmark: if((static_cast(state)->index == -1) || (static_cast(state)->index == -2)) return result; break; case syntax_element_literal: result += static_cast(state)->length; break; case syntax_element_wild: case syntax_element_set: result += 1; break; case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_backref: case syntax_element_rep: case syntax_element_combining: case syntax_element_long_set_rep: case syntax_element_backstep: { re_repeat* rep = static_cast(state); // adjust the type of the state to allow for faster matching: state->type = this->get_repeat_type(state); if((state->type == syntax_element_dot_rep) || (state->type == syntax_element_char_rep) || (state->type == syntax_element_short_set_rep)) { if(rep->max != rep->min) return -1; result += static_cast(rep->min); state = rep->alt.p; continue; } else if(state->type == syntax_element_long_set_rep) { BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_long_set); if(static_cast*>(rep->next.p)->singleton == 0) return -1; if(rep->max != rep->min) return -1; result += static_cast(rep->min); state = rep->alt.p; continue; } } return -1; case syntax_element_long_set: if(static_cast*>(state)->singleton == 0) return -1; result += 1; break; case syntax_element_jump: state = static_cast(state)->alt.p; continue; case syntax_element_alt: { int r1 = calculate_backstep(state->next.p); int r2 = calculate_backstep(static_cast(state)->alt.p); if((r1 < 0) || (r1 != r2)) return -1; return result + r1; } default: break; } state = state->next.p; } return -1; } struct recursion_saver { std::vector saved_state; std::vector* state; recursion_saver(std::vector* p) : saved_state(*p), state(p) {} ~recursion_saver() { state->swap(saved_state); } }; template void basic_regex_creator::create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask) { recursion_saver saved_recursions(&m_recursion_checks); int not_last_jump = 1; re_syntax_base* recursion_start = 0; int recursion_sub = 0; re_syntax_base* recursion_restart = 0; // track case sensitivity: bool l_icase = m_icase; while(state) { switch(state->type) { case syntax_element_toggle_case: l_icase = static_cast(state)->icase; state = state->next.p; break; case syntax_element_literal: { // don't set anything in *pnull, set each element in l_map // that could match the first character in the literal: if(l_map) { l_map[0] |= mask_init; charT first_char = *static_cast(static_cast(static_cast(state) + 1)); for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { if(m_traits.translate(static_cast(i), l_icase) == first_char) l_map[i] |= mask; } } return; } case syntax_element_end_line: { // next character must be a line separator (if there is one): if(l_map) { l_map[0] |= mask_init; l_map[static_cast('\n')] |= mask; l_map[static_cast('\r')] |= mask; l_map[static_cast('\f')] |= mask; l_map[0x85] |= mask; } // now figure out if we can match a NULL string at this point: if(pnull) create_startmap(state->next.p, 0, pnull, mask); return; } case syntax_element_recurse: { BOOST_REGEX_ASSERT(static_cast(state)->alt.p->type == syntax_element_startmark); recursion_sub = static_cast(static_cast(state)->alt.p)->index; if(m_recursion_checks[recursion_sub] & 1u) { // Infinite recursion!! if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = boost::regex_constants::error_bad_pattern; // // clear the expression, we should be empty: // this->m_pdata->m_expression = 0; this->m_pdata->m_expression_len = 0; // // and throw if required: // if(0 == (this->flags() & regex_constants::no_except)) { std::string message = "Encountered an infinite recursion."; boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0); e.raise(); } } else if(recursion_start == 0) { recursion_start = state; recursion_restart = state->next.p; state = static_cast(state)->alt.p; m_recursion_checks[recursion_sub] |= 1u; break; } m_recursion_checks[recursion_sub] |= 1u; // can't handle nested recursion here... BOOST_FALLTHROUGH; } case syntax_element_backref: // can be null, and any character can match: if(pnull) *pnull |= mask; BOOST_FALLTHROUGH; case syntax_element_wild: { // can't be null, any character can match: set_all_masks(l_map, mask); return; } case syntax_element_accept: case syntax_element_match: { // must be null, any character can match: set_all_masks(l_map, mask); if(pnull) *pnull |= mask; return; } case syntax_element_word_start: { // recurse, then AND with all the word characters: create_startmap(state->next.p, l_map, pnull, mask); if(l_map) { l_map[0] |= mask_init; for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { if(!m_traits.isctype(static_cast(i), m_word_mask)) l_map[i] &= static_cast(~mask); } } return; } case syntax_element_word_end: { // recurse, then AND with all the word characters: create_startmap(state->next.p, l_map, pnull, mask); if(l_map) { l_map[0] |= mask_init; for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { if(m_traits.isctype(static_cast(i), m_word_mask)) l_map[i] &= static_cast(~mask); } } return; } case syntax_element_buffer_end: { // we *must be null* : if(pnull) *pnull |= mask; return; } case syntax_element_long_set: if(l_map) { typedef typename traits::char_class_type m_type; if(static_cast*>(state)->singleton) { l_map[0] |= mask_init; for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { charT c = static_cast(i); if(&c != re_is_set_member(&c, &c + 1, static_cast*>(state), *m_pdata, l_icase)) l_map[i] |= mask; } } else set_all_masks(l_map, mask); } return; case syntax_element_set: if(l_map) { l_map[0] |= mask_init; for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { if(static_cast(state)->_map[ static_cast(m_traits.translate(static_cast(i), l_icase))]) l_map[i] |= mask; } } return; case syntax_element_jump: // take the jump: state = static_cast(state)->alt.p; not_last_jump = -1; break; case syntax_element_alt: case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: { re_alt* rep = static_cast(state); if(rep->_map[0] & mask_init) { if(l_map) { // copy previous results: l_map[0] |= mask_init; for(unsigned int i = 0; i <= UCHAR_MAX; ++i) { if(rep->_map[i] & mask_any) l_map[i] |= mask; } } if(pnull) { if(rep->can_be_null & mask_any) *pnull |= mask; } } else { // we haven't created a startmap for this alternative yet // so take the union of the two options: if(is_bad_repeat(state)) { set_all_masks(l_map, mask); if(pnull) *pnull |= mask; return; } set_bad_repeat(state); create_startmap(state->next.p, l_map, pnull, mask); if((state->type == syntax_element_alt) || (static_cast(state)->min == 0) || (not_last_jump == 0)) create_startmap(rep->alt.p, l_map, pnull, mask); } } return; case syntax_element_soft_buffer_end: // match newline or null: if(l_map) { l_map[0] |= mask_init; l_map[static_cast('\n')] |= mask; l_map[static_cast('\r')] |= mask; } if(pnull) *pnull |= mask; return; case syntax_element_endmark: // need to handle independent subs as a special case: if(static_cast(state)->index < 0) { // can be null, any character can match: set_all_masks(l_map, mask); if(pnull) *pnull |= mask; return; } else if(recursion_start && (recursion_sub != 0) && (recursion_sub == static_cast(state)->index)) { // recursion termination: recursion_start = 0; state = recursion_restart; break; } // // Normally we just go to the next state... but if this sub-expression is // the target of a recursion, then we might be ending a recursion, in which // case we should check whatever follows that recursion, as well as whatever // follows this state: // if(m_pdata->m_has_recursions && static_cast(state)->index) { bool ok = false; re_syntax_base* p = m_pdata->m_first_state; while(p) { if(p->type == syntax_element_recurse) { re_brace* p2 = static_cast(static_cast(p)->alt.p); if((p2->type == syntax_element_startmark) && (p2->index == static_cast(state)->index)) { ok = true; break; } } p = p->next.p; } if(ok && ((m_recursion_checks[static_cast(state)->index] & 2u) == 0)) { m_recursion_checks[static_cast(state)->index] |= 2u; create_startmap(p->next.p, l_map, pnull, mask); } } state = state->next.p; break; case syntax_element_commit: set_all_masks(l_map, mask); // Continue scanning so we can figure out whether we can be null: state = state->next.p; break; case syntax_element_startmark: // need to handle independent subs as a special case: if(static_cast(state)->index == -3) { state = state->next.p->next.p; break; } BOOST_FALLTHROUGH; default: state = state->next.p; } ++not_last_jump; } } template unsigned basic_regex_creator::get_restart_type(re_syntax_base* state) { // // find out how the machine starts, so we can optimise the search: // while(state) { switch(state->type) { case syntax_element_startmark: case syntax_element_endmark: state = state->next.p; continue; case syntax_element_start_line: return regbase::restart_line; case syntax_element_word_start: return regbase::restart_word; case syntax_element_buffer_start: return regbase::restart_buf; case syntax_element_restart_continue: return regbase::restart_continue; default: state = 0; continue; } } return regbase::restart_any; } template void basic_regex_creator::set_all_masks(unsigned char* bits, unsigned char mask) { // // set mask in all of bits elements, // if bits[0] has mask_init not set then we can // optimise this to a call to memset: // if(bits) { if(bits[0] == 0) (std::memset)(bits, mask, 1u << CHAR_BIT); else { for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) bits[i] |= mask; } bits[0] |= mask_init; } } template bool basic_regex_creator::is_bad_repeat(re_syntax_base* pt) { switch(pt->type) { case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: { unsigned state_id = static_cast(pt)->state_id; if(state_id >= sizeof(m_bad_repeats) * CHAR_BIT) return true; // run out of bits, assume we can't traverse this one. static const boost::uintmax_t one = 1uL; return m_bad_repeats & (one << state_id); } default: return false; } } template void basic_regex_creator::set_bad_repeat(re_syntax_base* pt) { switch(pt->type) { case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: { unsigned state_id = static_cast(pt)->state_id; static const boost::uintmax_t one = 1uL; if(state_id <= sizeof(m_bad_repeats) * CHAR_BIT) m_bad_repeats |= (one << state_id); } break; default: break; } } template syntax_element_type basic_regex_creator::get_repeat_type(re_syntax_base* state) { typedef typename traits::char_class_type m_type; if(state->type == syntax_element_rep) { // check to see if we are repeating a single state: if(state->next.p->next.p->next.p == static_cast(state)->alt.p) { switch(state->next.p->type) { case BOOST_REGEX_DETAIL_NS::syntax_element_wild: return BOOST_REGEX_DETAIL_NS::syntax_element_dot_rep; case BOOST_REGEX_DETAIL_NS::syntax_element_literal: return BOOST_REGEX_DETAIL_NS::syntax_element_char_rep; case BOOST_REGEX_DETAIL_NS::syntax_element_set: return BOOST_REGEX_DETAIL_NS::syntax_element_short_set_rep; case BOOST_REGEX_DETAIL_NS::syntax_element_long_set: if(static_cast*>(state->next.p)->singleton) return BOOST_REGEX_DETAIL_NS::syntax_element_long_set_rep; break; default: break; } } } return state->type; } template void basic_regex_creator::probe_leading_repeat(re_syntax_base* state) { // enumerate our states, and see if we have a leading repeat // for which failed search restarts can be optimized; do { switch(state->type) { case syntax_element_startmark: if(static_cast(state)->index >= 0) { state = state->next.p; continue; } #ifdef BOOST_MSVC # pragma warning(push) #pragma warning(disable:6011) #endif if((static_cast(state)->index == -1) || (static_cast(state)->index == -2)) { // skip past the zero width assertion: state = static_cast(state->next.p)->alt.p->next.p; continue; } #ifdef BOOST_MSVC # pragma warning(pop) #endif if(static_cast(state)->index == -3) { // Have to skip the leading jump state: state = state->next.p->next.p; continue; } return; case syntax_element_endmark: case syntax_element_start_line: case syntax_element_end_line: case syntax_element_word_boundary: case syntax_element_within_word: case syntax_element_word_start: case syntax_element_word_end: case syntax_element_buffer_start: case syntax_element_buffer_end: case syntax_element_restart_continue: state = state->next.p; break; case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: if(this->m_has_backrefs == 0) static_cast(state)->leading = true; BOOST_FALLTHROUGH; default: return; } }while(state); } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_MSVC # pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/basic_regex_parser.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE basic_regex_parser.cpp * VERSION see * DESCRIPTION: Declares template class basic_regex_parser. */ #ifndef BOOST_REGEX_V4_BASIC_REGEX_PARSER_HPP #define BOOST_REGEX_V4_BASIC_REGEX_PARSER_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #if BOOST_MSVC >= 1800 #pragma warning(disable: 26812) #endif #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4244) #if BOOST_MSVC < 1910 #pragma warning(disable:4800) #endif #endif inline boost::intmax_t umax(mpl::false_ const&) { // Get out clause here, just in case numeric_limits is unspecialized: return std::numeric_limits::is_specialized ? (std::numeric_limits::max)() : INT_MAX; } inline boost::intmax_t umax(mpl::true_ const&) { return (std::numeric_limits::max)(); } inline boost::intmax_t umax() { return umax(mpl::bool_::digits >= std::numeric_limits::digits>()); } template class basic_regex_parser : public basic_regex_creator { public: basic_regex_parser(regex_data* data); void parse(const charT* p1, const charT* p2, unsigned flags); void fail(regex_constants::error_type error_code, std::ptrdiff_t position); void fail(regex_constants::error_type error_code, std::ptrdiff_t position, std::string message, std::ptrdiff_t start_pos); void fail(regex_constants::error_type error_code, std::ptrdiff_t position, const std::string& message) { fail(error_code, position, message, position); } bool parse_all(); bool parse_basic(); bool parse_extended(); bool parse_literal(); bool parse_open_paren(); bool parse_basic_escape(); bool parse_extended_escape(); bool parse_match_any(); bool parse_repeat(std::size_t low = 0, std::size_t high = (std::numeric_limits::max)()); bool parse_repeat_range(bool isbasic); bool parse_alt(); bool parse_set(); bool parse_backref(); void parse_set_literal(basic_char_set& char_set); bool parse_inner_set(basic_char_set& char_set); bool parse_QE(); bool parse_perl_extension(); bool parse_perl_verb(); bool match_verb(const char*); bool add_emacs_code(bool negate); bool unwind_alts(std::ptrdiff_t last_paren_start); digraph get_next_set_literal(basic_char_set& char_set); charT unescape_character(); regex_constants::syntax_option_type parse_options(); private: typedef bool (basic_regex_parser::*parser_proc_type)(); typedef typename traits::string_type string_type; typedef typename traits::char_class_type char_class_type; parser_proc_type m_parser_proc; // the main parser to use const charT* m_base; // the start of the string being parsed const charT* m_end; // the end of the string being parsed const charT* m_position; // our current parser position unsigned m_mark_count; // how many sub-expressions we have int m_mark_reset; // used to indicate that we're inside a (?|...) block. unsigned m_max_mark; // largest mark count seen inside a (?|...) block. std::ptrdiff_t m_paren_start; // where the last seen ')' began (where repeats are inserted). std::ptrdiff_t m_alt_insert_point; // where to insert the next alternative bool m_has_case_change; // true if somewhere in the current block the case has changed unsigned m_recursion_count; // How many times we've called parse_all. #if defined(BOOST_MSVC) && defined(_M_IX86) // This is an ugly warning suppression workaround (for warnings *inside* std::vector // that can not otherwise be suppressed)... BOOST_STATIC_ASSERT(sizeof(long) >= sizeof(void*)); std::vector m_alt_jumps; // list of alternative in the current scope. #else std::vector m_alt_jumps; // list of alternative in the current scope. #endif basic_regex_parser& operator=(const basic_regex_parser&); basic_regex_parser(const basic_regex_parser&); }; template basic_regex_parser::basic_regex_parser(regex_data* data) : basic_regex_creator(data), m_parser_proc(), m_base(0), m_end(0), m_position(0), m_mark_count(0), m_mark_reset(-1), m_max_mark(0), m_paren_start(0), m_alt_insert_point(0), m_has_case_change(false), m_recursion_count(0) { } template void basic_regex_parser::parse(const charT* p1, const charT* p2, unsigned l_flags) { // pass l_flags on to base class: this->init(l_flags); // set up pointers: m_position = m_base = p1; m_end = p2; // empty strings are errors: if((p1 == p2) && ( ((l_flags & regbase::main_option_type) != regbase::perl_syntax_group) || (l_flags & regbase::no_empty_expressions) ) ) { fail(regex_constants::error_empty, 0); return; } // select which parser to use: switch(l_flags & regbase::main_option_type) { case regbase::perl_syntax_group: { m_parser_proc = &basic_regex_parser::parse_extended; // // Add a leading paren with index zero to give recursions a target: // re_brace* br = static_cast(this->append_state(syntax_element_startmark, sizeof(re_brace))); br->index = 0; br->icase = this->flags() & regbase::icase; break; } case regbase::basic_syntax_group: m_parser_proc = &basic_regex_parser::parse_basic; break; case regbase::literal: m_parser_proc = &basic_regex_parser::parse_literal; break; default: // Oops, someone has managed to set more than one of the main option flags, // so this must be an error: fail(regex_constants::error_unknown, 0, "An invalid combination of regular expression syntax flags was used."); return; } // parse all our characters: bool result = parse_all(); // // Unwind our alternatives: // unwind_alts(-1); // reset l_flags as a global scope (?imsx) may have altered them: this->flags(l_flags); // if we haven't gobbled up all the characters then we must // have had an unexpected ')' : if(!result) { fail(regex_constants::error_paren, ::boost::BOOST_REGEX_DETAIL_NS::distance(m_base, m_position), "Found a closing ) with no corresponding opening parenthesis."); return; } // if an error has been set then give up now: if(this->m_pdata->m_status) return; // fill in our sub-expression count: this->m_pdata->m_mark_count = 1u + (std::size_t)m_mark_count; this->finalize(p1, p2); } template void basic_regex_parser::fail(regex_constants::error_type error_code, std::ptrdiff_t position) { // get the error message: std::string message = this->m_pdata->m_ptraits->error_string(error_code); fail(error_code, position, message); } template void basic_regex_parser::fail(regex_constants::error_type error_code, std::ptrdiff_t position, std::string message, std::ptrdiff_t start_pos) { if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = error_code; m_position = m_end; // don't bother parsing anything else #ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS // // Augment error message with the regular expression text: // if(start_pos == position) start_pos = (std::max)(static_cast(0), position - static_cast(10)); std::ptrdiff_t end_pos = (std::min)(position + static_cast(10), static_cast(m_end - m_base)); if(error_code != regex_constants::error_empty) { if((start_pos != 0) || (end_pos != (m_end - m_base))) message += " The error occurred while parsing the regular expression fragment: '"; else message += " The error occurred while parsing the regular expression: '"; if(start_pos != end_pos) { message += std::string(m_base + start_pos, m_base + position); message += ">>>HERE>>>"; message += std::string(m_base + position, m_base + end_pos); } message += "'."; } #endif #ifndef BOOST_NO_EXCEPTIONS if(0 == (this->flags() & regex_constants::no_except)) { boost::regex_error e(message, error_code, position); e.raise(); } #else (void)position; // suppress warnings. #endif } template bool basic_regex_parser::parse_all() { if (++m_recursion_count > 400) { // exceeded internal limits fail(boost::regex_constants::error_complexity, m_position - m_base, "Exceeded nested brace limit."); } bool result = true; while(result && (m_position != m_end)) { result = (this->*m_parser_proc)(); } --m_recursion_count; return result; } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4702) #endif template bool basic_regex_parser::parse_basic() { switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_escape: return parse_basic_escape(); case regex_constants::syntax_dot: return parse_match_any(); case regex_constants::syntax_caret: ++m_position; this->append_state(syntax_element_start_line); break; case regex_constants::syntax_dollar: ++m_position; this->append_state(syntax_element_end_line); break; case regex_constants::syntax_star: if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line)) return parse_literal(); else { ++m_position; return parse_repeat(); } case regex_constants::syntax_plus: if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line) || !(this->flags() & regbase::emacs_ex)) return parse_literal(); else { ++m_position; return parse_repeat(1); } case regex_constants::syntax_question: if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line) || !(this->flags() & regbase::emacs_ex)) return parse_literal(); else { ++m_position; return parse_repeat(0, 1); } case regex_constants::syntax_open_set: return parse_set(); case regex_constants::syntax_newline: if(this->flags() & regbase::newline_alt) return parse_alt(); else return parse_literal(); default: return parse_literal(); } return true; } #ifdef BOOST_MSVC # pragma warning(push) #if BOOST_MSVC >= 1800 #pragma warning(disable:26812) #endif #endif template bool basic_regex_parser::parse_extended() { bool result = true; switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_open_mark: return parse_open_paren(); case regex_constants::syntax_close_mark: return false; case regex_constants::syntax_escape: return parse_extended_escape(); case regex_constants::syntax_dot: return parse_match_any(); case regex_constants::syntax_caret: ++m_position; this->append_state( (this->flags() & regex_constants::no_mod_m ? syntax_element_buffer_start : syntax_element_start_line)); break; case regex_constants::syntax_dollar: ++m_position; this->append_state( (this->flags() & regex_constants::no_mod_m ? syntax_element_buffer_end : syntax_element_end_line)); break; case regex_constants::syntax_star: if(m_position == this->m_base) { fail(regex_constants::error_badrepeat, 0, "The repeat operator \"*\" cannot start a regular expression."); return false; } ++m_position; return parse_repeat(); case regex_constants::syntax_question: if(m_position == this->m_base) { fail(regex_constants::error_badrepeat, 0, "The repeat operator \"?\" cannot start a regular expression."); return false; } ++m_position; return parse_repeat(0,1); case regex_constants::syntax_plus: if(m_position == this->m_base) { fail(regex_constants::error_badrepeat, 0, "The repeat operator \"+\" cannot start a regular expression."); return false; } ++m_position; return parse_repeat(1); case regex_constants::syntax_open_brace: ++m_position; return parse_repeat_range(false); case regex_constants::syntax_close_brace: if((this->flags() & regbase::no_perl_ex) == regbase::no_perl_ex) { fail(regex_constants::error_brace, this->m_position - this->m_base, "Found a closing repetition operator } with no corresponding {."); return false; } result = parse_literal(); break; case regex_constants::syntax_or: return parse_alt(); case regex_constants::syntax_open_set: return parse_set(); case regex_constants::syntax_newline: if(this->flags() & regbase::newline_alt) return parse_alt(); else return parse_literal(); case regex_constants::syntax_hash: // // If we have a mod_x flag set, then skip until // we get to a newline character: // if((this->flags() & (regbase::no_perl_ex|regbase::mod_x)) == regbase::mod_x) { while((m_position != m_end) && !is_separator(*m_position++)){} return true; } BOOST_FALLTHROUGH; default: result = parse_literal(); break; } return result; } #ifdef BOOST_MSVC # pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif template bool basic_regex_parser::parse_literal() { // append this as a literal provided it's not a space character // or the perl option regbase::mod_x is not set: if( ((this->flags() & (regbase::main_option_type|regbase::mod_x|regbase::no_perl_ex)) != regbase::mod_x) || !this->m_traits.isctype(*m_position, this->m_mask_space)) this->append_literal(*m_position); ++m_position; return true; } template bool basic_regex_parser::parse_open_paren() { // // skip the '(' and error check: // if(++m_position == m_end) { fail(regex_constants::error_paren, m_position - m_base); return false; } // // begin by checking for a perl-style (?...) extension: // if( ((this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) == 0) || ((this->flags() & (regbase::main_option_type | regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex)) ) { if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_question) return parse_perl_extension(); if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_star) return parse_perl_verb(); } // // update our mark count, and append the required state: // unsigned markid = 0; if(0 == (this->flags() & regbase::nosubs)) { markid = ++m_mark_count; #ifndef BOOST_NO_STD_DISTANCE if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.push_back(std::pair(std::distance(m_base, m_position) - 1, 0)); #else if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.push_back(std::pair((m_position - m_base) - 1, 0)); #endif } re_brace* pb = static_cast(this->append_state(syntax_element_startmark, sizeof(re_brace))); pb->index = markid; pb->icase = this->flags() & regbase::icase; std::ptrdiff_t last_paren_start = this->getoffset(pb); // back up insertion point for alternations, and set new point: std::ptrdiff_t last_alt_point = m_alt_insert_point; this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); // // back up the current flags in case we have a nested (?imsx) group: // regex_constants::syntax_option_type opts = this->flags(); bool old_case_change = m_has_case_change; m_has_case_change = false; // no changes to this scope as yet... // // Back up branch reset data in case we have a nested (?|...) // int mark_reset = m_mark_reset; m_mark_reset = -1; // // now recursively add more states, this will terminate when we get to a // matching ')' : // parse_all(); // // Unwind pushed alternatives: // if(0 == unwind_alts(last_paren_start)) return false; // // restore flags: // if(m_has_case_change) { // the case has changed in one or more of the alternatives // within the scoped (...) block: we have to add a state // to reset the case sensitivity: static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = opts & regbase::icase; } this->flags(opts); m_has_case_change = old_case_change; // // restore branch reset: // m_mark_reset = mark_reset; // // we either have a ')' or we have run out of characters prematurely: // if(m_position == m_end) { this->fail(regex_constants::error_paren, ::boost::BOOST_REGEX_DETAIL_NS::distance(m_base, m_end)); return false; } if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) return false; #ifndef BOOST_NO_STD_DISTANCE if(markid && (this->flags() & regbase::save_subexpression_location)) this->m_pdata->m_subs.at(markid - 1).second = std::distance(m_base, m_position); #else if(markid && (this->flags() & regbase::save_subexpression_location)) this->m_pdata->m_subs.at(markid - 1).second = (m_position - m_base); #endif ++m_position; // // append closing parenthesis state: // pb = static_cast(this->append_state(syntax_element_endmark, sizeof(re_brace))); pb->index = markid; pb->icase = this->flags() & regbase::icase; this->m_paren_start = last_paren_start; // // restore the alternate insertion point: // this->m_alt_insert_point = last_alt_point; // // allow backrefs to this mark: // if(markid > 0) this->m_backrefs.set(markid); return true; } template bool basic_regex_parser::parse_basic_escape() { if(++m_position == m_end) { fail(regex_constants::error_paren, m_position - m_base); return false; } bool result = true; switch(this->m_traits.escape_syntax_type(*m_position)) { case regex_constants::syntax_open_mark: return parse_open_paren(); case regex_constants::syntax_close_mark: return false; case regex_constants::syntax_plus: if(this->flags() & regex_constants::bk_plus_qm) { ++m_position; return parse_repeat(1); } else return parse_literal(); case regex_constants::syntax_question: if(this->flags() & regex_constants::bk_plus_qm) { ++m_position; return parse_repeat(0, 1); } else return parse_literal(); case regex_constants::syntax_open_brace: if(this->flags() & regbase::no_intervals) return parse_literal(); ++m_position; return parse_repeat_range(true); case regex_constants::syntax_close_brace: if(this->flags() & regbase::no_intervals) return parse_literal(); fail(regex_constants::error_brace, this->m_position - this->m_base, "Found a closing repetition operator } with no corresponding {."); return false; case regex_constants::syntax_or: if(this->flags() & regbase::bk_vbar) return parse_alt(); else result = parse_literal(); break; case regex_constants::syntax_digit: return parse_backref(); case regex_constants::escape_type_start_buffer: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_buffer_start); } else result = parse_literal(); break; case regex_constants::escape_type_end_buffer: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_buffer_end); } else result = parse_literal(); break; case regex_constants::escape_type_word_assert: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_word_boundary); } else result = parse_literal(); break; case regex_constants::escape_type_not_word_assert: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_within_word); } else result = parse_literal(); break; case regex_constants::escape_type_left_word: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_word_start); } else result = parse_literal(); break; case regex_constants::escape_type_right_word: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_word_end); } else result = parse_literal(); break; default: if(this->flags() & regbase::emacs_ex) { bool negate = true; switch(*m_position) { case 'w': negate = false; BOOST_FALLTHROUGH; case 'W': { basic_char_set char_set; if(negate) char_set.negate(); char_set.add_class(this->m_word_mask); if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } ++m_position; return true; } case 's': negate = false; BOOST_FALLTHROUGH; case 'S': return add_emacs_code(negate); case 'c': case 'C': // not supported yet: fail(regex_constants::error_escape, m_position - m_base, "The \\c and \\C escape sequences are not supported by POSIX basic regular expressions: try the Perl syntax instead."); return false; default: break; } } result = parse_literal(); break; } return result; } template bool basic_regex_parser::parse_extended_escape() { ++m_position; if(m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, "Incomplete escape sequence found."); return false; } bool negate = false; // in case this is a character class escape: \w \d etc switch(this->m_traits.escape_syntax_type(*m_position)) { case regex_constants::escape_type_not_class: negate = true; BOOST_FALLTHROUGH; case regex_constants::escape_type_class: { escape_type_class_jump: typedef typename traits::char_class_type m_type; m_type m = this->m_traits.lookup_classname(m_position, m_position+1); if(m != 0) { basic_char_set char_set; if(negate) char_set.negate(); char_set.add_class(m); if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } ++m_position; return true; } // // not a class, just a regular unknown escape: // this->append_literal(unescape_character()); break; } case regex_constants::syntax_digit: return parse_backref(); case regex_constants::escape_type_left_word: ++m_position; this->append_state(syntax_element_word_start); break; case regex_constants::escape_type_right_word: ++m_position; this->append_state(syntax_element_word_end); break; case regex_constants::escape_type_start_buffer: ++m_position; this->append_state(syntax_element_buffer_start); break; case regex_constants::escape_type_end_buffer: ++m_position; this->append_state(syntax_element_buffer_end); break; case regex_constants::escape_type_word_assert: ++m_position; this->append_state(syntax_element_word_boundary); break; case regex_constants::escape_type_not_word_assert: ++m_position; this->append_state(syntax_element_within_word); break; case regex_constants::escape_type_Z: ++m_position; this->append_state(syntax_element_soft_buffer_end); break; case regex_constants::escape_type_Q: return parse_QE(); case regex_constants::escape_type_C: return parse_match_any(); case regex_constants::escape_type_X: ++m_position; this->append_state(syntax_element_combining); break; case regex_constants::escape_type_G: ++m_position; this->append_state(syntax_element_restart_continue); break; case regex_constants::escape_type_not_property: negate = true; BOOST_FALLTHROUGH; case regex_constants::escape_type_property: { ++m_position; char_class_type m; if(m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, "Incomplete property escape found."); return false; } // maybe have \p{ddd} if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace) { const charT* base = m_position; // skip forward until we find enclosing brace: while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace)) ++m_position; if(m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, "Closing } missing from property escape sequence."); return false; } m = this->m_traits.lookup_classname(++base, m_position++); } else { m = this->m_traits.lookup_classname(m_position, m_position+1); ++m_position; } if(m != 0) { basic_char_set char_set; if(negate) char_set.negate(); char_set.add_class(m); if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } return true; } fail(regex_constants::error_ctype, m_position - m_base, "Escape sequence was neither a valid property nor a valid character class name."); return false; } case regex_constants::escape_type_reset_start_mark: if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) { re_brace* pb = static_cast(this->append_state(syntax_element_startmark, sizeof(re_brace))); pb->index = -5; pb->icase = this->flags() & regbase::icase; this->m_pdata->m_data.align(); ++m_position; return true; } goto escape_type_class_jump; case regex_constants::escape_type_line_ending: if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) { const charT* e = get_escape_R_string(); const charT* old_position = m_position; const charT* old_end = m_end; const charT* old_base = m_base; m_position = e; m_base = e; m_end = e + traits::length(e); bool r = parse_all(); m_position = ++old_position; m_end = old_end; m_base = old_base; return r; } goto escape_type_class_jump; case regex_constants::escape_type_extended_backref: if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) { bool have_brace = false; bool negative = false; static const char incomplete_message[] = "Incomplete \\g escape found."; if(++m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, incomplete_message); return false; } // maybe have \g{ddd} regex_constants::syntax_type syn = this->m_traits.syntax_type(*m_position); regex_constants::syntax_type syn_end = 0; if((syn == regex_constants::syntax_open_brace) || (syn == regex_constants::escape_type_left_word) || (syn == regex_constants::escape_type_end_buffer)) { if(++m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, incomplete_message); return false; } have_brace = true; switch(syn) { case regex_constants::syntax_open_brace: syn_end = regex_constants::syntax_close_brace; break; case regex_constants::escape_type_left_word: syn_end = regex_constants::escape_type_right_word; break; default: syn_end = regex_constants::escape_type_end_buffer; break; } } negative = (*m_position == static_cast('-')); if((negative) && (++m_position == m_end)) { fail(regex_constants::error_escape, m_position - m_base, incomplete_message); return false; } const charT* pc = m_position; boost::intmax_t i = this->m_traits.toi(pc, m_end, 10); if((i < 0) && syn_end) { // Check for a named capture, get the leftmost one if there is more than one: const charT* base = m_position; while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != syn_end)) { ++m_position; } i = hash_value_from_capture_name(base, m_position); pc = m_position; } if(negative) i = 1 + (static_cast(m_mark_count) - i); if(((i < hash_value_mask) && (i > 0) && (this->m_backrefs.test(i))) || ((i >= hash_value_mask) && (this->m_pdata->get_id(i) > 0) && (this->m_backrefs.test(this->m_pdata->get_id(i))))) { m_position = pc; re_brace* pb = static_cast(this->append_state(syntax_element_backref, sizeof(re_brace))); pb->index = i; pb->icase = this->flags() & regbase::icase; } else { fail(regex_constants::error_backref, m_position - m_base); return false; } m_position = pc; if(have_brace) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != syn_end)) { fail(regex_constants::error_escape, m_position - m_base, incomplete_message); return false; } ++m_position; } return true; } goto escape_type_class_jump; case regex_constants::escape_type_control_v: if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) goto escape_type_class_jump; BOOST_FALLTHROUGH; default: this->append_literal(unescape_character()); break; } return true; } template bool basic_regex_parser::parse_match_any() { // // we have a '.' that can match any character: // ++m_position; static_cast( this->append_state(syntax_element_wild, sizeof(re_dot)) )->mask = static_cast(this->flags() & regbase::no_mod_s ? BOOST_REGEX_DETAIL_NS::force_not_newline : this->flags() & regbase::mod_s ? BOOST_REGEX_DETAIL_NS::force_newline : BOOST_REGEX_DETAIL_NS::dont_care); return true; } template bool basic_regex_parser::parse_repeat(std::size_t low, std::size_t high) { bool greedy = true; bool possessive = false; std::size_t insert_point; // // when we get to here we may have a non-greedy ? mark still to come: // if((m_position != m_end) && ( (0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) || ((regbase::basic_syntax_group|regbase::emacs_ex) == (this->flags() & (regbase::main_option_type | regbase::emacs_ex))) ) ) { // OK we have a perl or emacs regex, check for a '?': if ((this->flags() & (regbase::main_option_type | regbase::mod_x | regbase::no_perl_ex)) == regbase::mod_x) { // whitespace skip: while ((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; } if((m_position != m_end) && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_question)) { greedy = false; ++m_position; } // for perl regexes only check for possessive ++ repeats. if((m_position != m_end) && (0 == (this->flags() & regbase::main_option_type)) && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_plus)) { possessive = true; ++m_position; } } if(0 == this->m_last_state) { fail(regex_constants::error_badrepeat, ::boost::BOOST_REGEX_DETAIL_NS::distance(m_base, m_position), "Nothing to repeat."); return false; } if(this->m_last_state->type == syntax_element_endmark) { // insert a repeat before the '(' matching the last ')': insert_point = this->m_paren_start; } else if((this->m_last_state->type == syntax_element_literal) && (static_cast(this->m_last_state)->length > 1)) { // the last state was a literal with more than one character, split it in two: re_literal* lit = static_cast(this->m_last_state); charT c = (static_cast(static_cast(lit+1)))[lit->length - 1]; lit->length -= 1; // now append new state: lit = static_cast(this->append_state(syntax_element_literal, sizeof(re_literal) + sizeof(charT))); lit->length = 1; (static_cast(static_cast(lit+1)))[0] = c; insert_point = this->getoffset(this->m_last_state); } else { // repeat the last state whatever it was, need to add some error checking here: switch(this->m_last_state->type) { case syntax_element_start_line: case syntax_element_end_line: case syntax_element_word_boundary: case syntax_element_within_word: case syntax_element_word_start: case syntax_element_word_end: case syntax_element_buffer_start: case syntax_element_buffer_end: case syntax_element_alt: case syntax_element_soft_buffer_end: case syntax_element_restart_continue: case syntax_element_jump: case syntax_element_startmark: case syntax_element_backstep: case syntax_element_toggle_case: // can't legally repeat any of the above: fail(regex_constants::error_badrepeat, m_position - m_base); return false; default: // do nothing... break; } insert_point = this->getoffset(this->m_last_state); } // // OK we now know what to repeat, so insert the repeat around it: // re_repeat* rep = static_cast(this->insert_state(insert_point, syntax_element_rep, re_repeater_size)); rep->min = low; rep->max = high; rep->greedy = greedy; rep->leading = false; // store our repeater position for later: std::ptrdiff_t rep_off = this->getoffset(rep); // and append a back jump to the repeat: re_jump* jmp = static_cast(this->append_state(syntax_element_jump, sizeof(re_jump))); jmp->alt.i = rep_off - this->getoffset(jmp); this->m_pdata->m_data.align(); // now fill in the alt jump for the repeat: rep = static_cast(this->getaddress(rep_off)); rep->alt.i = this->m_pdata->m_data.size() - rep_off; // // If the repeat is possessive then bracket the repeat with a (?>...) // independent sub-expression construct: // if(possessive) { if(m_position != m_end) { // // Check for illegal following quantifier, we have to do this here, because // the extra states we insert below circumvents our usual error checking :-( // bool contin = false; do { if ((this->flags() & (regbase::main_option_type | regbase::mod_x | regbase::no_perl_ex)) == regbase::mod_x) { // whitespace skip: while ((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; } if (m_position != m_end) { switch (this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_star: case regex_constants::syntax_plus: case regex_constants::syntax_question: case regex_constants::syntax_open_brace: fail(regex_constants::error_badrepeat, m_position - m_base); return false; case regex_constants::syntax_open_mark: // Do we have a comment? If so we need to skip it here... if ((m_position + 2 < m_end) && this->m_traits.syntax_type(*(m_position + 1)) == regex_constants::syntax_question && this->m_traits.syntax_type(*(m_position + 2)) == regex_constants::syntax_hash) { while ((m_position != m_end) && (this->m_traits.syntax_type(*m_position++) != regex_constants::syntax_close_mark)) { } contin = true; } else contin = false; break; default: contin = false; } } else contin = false; } while (contin); } re_brace* pb = static_cast(this->insert_state(insert_point, syntax_element_startmark, sizeof(re_brace))); pb->index = -3; pb->icase = this->flags() & regbase::icase; jmp = static_cast(this->insert_state(insert_point + sizeof(re_brace), syntax_element_jump, sizeof(re_jump))); this->m_pdata->m_data.align(); jmp->alt.i = this->m_pdata->m_data.size() - this->getoffset(jmp); pb = static_cast(this->append_state(syntax_element_endmark, sizeof(re_brace))); pb->index = -3; pb->icase = this->flags() & regbase::icase; } return true; } template bool basic_regex_parser::parse_repeat_range(bool isbasic) { static const char incomplete_message[] = "Missing } in quantified repetition."; // // parse a repeat-range: // std::size_t min, max; boost::intmax_t v; // skip whitespace: while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; if(this->m_position == this->m_end) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } // get min: v = this->m_traits.toi(m_position, m_end, 10); // skip whitespace: if((v < 0) || (v > umax())) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; if(this->m_position == this->m_end) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } min = static_cast(v); // see if we have a comma: if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_comma) { // move on and error check: ++m_position; // skip whitespace: while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; if(this->m_position == this->m_end) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } // get the value if any: v = this->m_traits.toi(m_position, m_end, 10); max = ((v >= 0) && (v < umax())) ? (std::size_t)v : (std::numeric_limits::max)(); } else { // no comma, max = min: max = min; } // skip whitespace: while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; // OK now check trailing }: if(this->m_position == this->m_end) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } if(isbasic) { if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_escape) { ++m_position; if(this->m_position == this->m_end) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } } else { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } } if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_brace) ++m_position; else { // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } // // finally go and add the repeat, unless error: // if(min > max) { // Backtrack to error location: m_position -= 2; while(this->m_traits.isctype(*m_position, this->m_word_mask)) --m_position; ++m_position; fail(regex_constants::error_badbrace, m_position - m_base); return false; } return parse_repeat(min, max); } template bool basic_regex_parser::parse_alt() { // // error check: if there have been no previous states, // or if the last state was a '(' then error: // if( ((this->m_last_state == 0) || (this->m_last_state->type == syntax_element_startmark)) && !( ((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group) && ((this->flags() & regbase::no_empty_expressions) == 0) ) ) { fail(regex_constants::error_empty, this->m_position - this->m_base, "A regular expression cannot start with the alternation operator |."); return false; } // // Reset mark count if required: // if(m_max_mark < m_mark_count) m_max_mark = m_mark_count; if(m_mark_reset >= 0) m_mark_count = m_mark_reset; ++m_position; // // we need to append a trailing jump: // re_syntax_base* pj = this->append_state(BOOST_REGEX_DETAIL_NS::syntax_element_jump, sizeof(re_jump)); std::ptrdiff_t jump_offset = this->getoffset(pj); // // now insert the alternative: // re_alt* palt = static_cast(this->insert_state(this->m_alt_insert_point, syntax_element_alt, re_alt_size)); jump_offset += re_alt_size; this->m_pdata->m_data.align(); palt->alt.i = this->m_pdata->m_data.size() - this->getoffset(palt); // // update m_alt_insert_point so that the next alternate gets // inserted at the start of the second of the two we've just created: // this->m_alt_insert_point = this->m_pdata->m_data.size(); // // the start of this alternative must have a case changes state // if the current block has messed around with case changes: // if(m_has_case_change) { static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = this->m_icase; } // // push the alternative onto our stack, a recursive // implementation here is easier to understand (and faster // as it happens), but causes all kinds of stack overflow problems // on programs with small stacks (COM+). // m_alt_jumps.push_back(jump_offset); return true; } template bool basic_regex_parser::parse_set() { static const char incomplete_message[] = "Character set declaration starting with [ terminated prematurely - either no ] was found or the set had no content."; ++m_position; if(m_position == m_end) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } basic_char_set char_set; const charT* base = m_position; // where the '[' was const charT* item_base = m_position; // where the '[' or '^' was while(m_position != m_end) { switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_caret: if(m_position == base) { char_set.negate(); ++m_position; item_base = m_position; } else parse_set_literal(char_set); break; case regex_constants::syntax_close_set: if(m_position == item_base) { parse_set_literal(char_set); break; } else { ++m_position; if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } } return true; case regex_constants::syntax_open_set: if(parse_inner_set(char_set)) break; return true; case regex_constants::syntax_escape: { // // look ahead and see if this is a character class shortcut // \d \w \s etc... // ++m_position; if(this->m_traits.escape_syntax_type(*m_position) == regex_constants::escape_type_class) { char_class_type m = this->m_traits.lookup_classname(m_position, m_position+1); if(m != 0) { char_set.add_class(m); ++m_position; break; } } else if(this->m_traits.escape_syntax_type(*m_position) == regex_constants::escape_type_not_class) { // negated character class: char_class_type m = this->m_traits.lookup_classname(m_position, m_position+1); if(m != 0) { char_set.add_negated_class(m); ++m_position; break; } } // not a character class, just a regular escape: --m_position; parse_set_literal(char_set); break; } default: parse_set_literal(char_set); break; } } return m_position != m_end; } template bool basic_regex_parser::parse_inner_set(basic_char_set& char_set) { static const char incomplete_message[] = "Character class declaration starting with [ terminated prematurely - either no ] was found or the set had no content."; // // we have either a character class [:name:] // a collating element [.name.] // or an equivalence class [=name=] // if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_dot: // // a collating element is treated as a literal: // --m_position; parse_set_literal(char_set); return true; case regex_constants::syntax_colon: { // check that character classes are actually enabled: if((this->flags() & (regbase::main_option_type | regbase::no_char_classes)) == (regbase::basic_syntax_group | regbase::no_char_classes)) { --m_position; parse_set_literal(char_set); return true; } // skip the ':' if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } const charT* name_first = m_position; // skip at least one character, then find the matching ':]' if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_colon)) ++m_position; const charT* name_last = m_position; if(m_end == m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } if((m_end == ++m_position) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } // // check for negated class: // bool negated = false; if(this->m_traits.syntax_type(*name_first) == regex_constants::syntax_caret) { ++name_first; negated = true; } typedef typename traits::char_class_type m_type; m_type m = this->m_traits.lookup_classname(name_first, name_last); if(m == 0) { if(char_set.empty() && (name_last - name_first == 1)) { // maybe a special case: ++m_position; if( (m_position != m_end) && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_set)) { if(this->m_traits.escape_syntax_type(*name_first) == regex_constants::escape_type_left_word) { ++m_position; this->append_state(syntax_element_word_start); return false; } if(this->m_traits.escape_syntax_type(*name_first) == regex_constants::escape_type_right_word) { ++m_position; this->append_state(syntax_element_word_end); return false; } } } fail(regex_constants::error_ctype, name_first - m_base); return false; } if(!negated) char_set.add_class(m); else char_set.add_negated_class(m); ++m_position; break; } case regex_constants::syntax_equal: { // skip the '=' if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } const charT* name_first = m_position; // skip at least one character, then find the matching '=]' if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal)) ++m_position; const charT* name_last = m_position; if(m_end == m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } if((m_end == ++m_position) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } string_type m = this->m_traits.lookup_collatename(name_first, name_last); if(m.empty() || (m.size() > 2)) { fail(regex_constants::error_collate, name_first - m_base); return false; } digraph d; d.first = m[0]; if(m.size() > 1) d.second = m[1]; else d.second = 0; char_set.add_equivalent(d); ++m_position; break; } default: --m_position; parse_set_literal(char_set); break; } return true; } template void basic_regex_parser::parse_set_literal(basic_char_set& char_set) { digraph start_range(get_next_set_literal(char_set)); if(m_end == m_position) { fail(regex_constants::error_brack, m_position - m_base); return; } if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_dash) { // we have a range: if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base); return; } if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set) { digraph end_range = get_next_set_literal(char_set); char_set.add_range(start_range, end_range); if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_dash) { if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base); return; } if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_set) { // trailing - : --m_position; return; } fail(regex_constants::error_range, m_position - m_base); return; } return; } --m_position; } char_set.add_single(start_range); } template digraph basic_regex_parser::get_next_set_literal(basic_char_set& char_set) { digraph result; switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_dash: if(!char_set.empty()) { // see if we are at the end of the set: if((++m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)) { fail(regex_constants::error_range, m_position - m_base); return result; } --m_position; } result.first = *m_position++; return result; case regex_constants::syntax_escape: // check to see if escapes are supported first: if(this->flags() & regex_constants::no_escape_in_lists) { result = *m_position++; break; } ++m_position; result = unescape_character(); break; case regex_constants::syntax_open_set: { if(m_end == ++m_position) { fail(regex_constants::error_collate, m_position - m_base); return result; } if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_dot) { --m_position; result.first = *m_position; ++m_position; return result; } if(m_end == ++m_position) { fail(regex_constants::error_collate, m_position - m_base); return result; } const charT* name_first = m_position; // skip at least one character, then find the matching ':]' if(m_end == ++m_position) { fail(regex_constants::error_collate, name_first - m_base); return result; } while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_dot)) ++m_position; const charT* name_last = m_position; if(m_end == m_position) { fail(regex_constants::error_collate, name_first - m_base); return result; } if((m_end == ++m_position) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)) { fail(regex_constants::error_collate, name_first - m_base); return result; } ++m_position; string_type s = this->m_traits.lookup_collatename(name_first, name_last); if(s.empty() || (s.size() > 2)) { fail(regex_constants::error_collate, name_first - m_base); return result; } result.first = s[0]; if(s.size() > 1) result.second = s[1]; else result.second = 0; return result; } default: result = *m_position++; } return result; } // // does a value fit in the specified charT type? // template bool valid_value(charT, boost::intmax_t v, const mpl::true_&) { return (v >> (sizeof(charT) * CHAR_BIT)) == 0; } template bool valid_value(charT, boost::intmax_t, const mpl::false_&) { return true; // v will alsways fit in a charT } template bool valid_value(charT c, boost::intmax_t v) { return valid_value(c, v, mpl::bool_<(sizeof(charT) < sizeof(boost::intmax_t))>()); } template charT basic_regex_parser::unescape_character() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif charT result(0); if(m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, "Escape sequence terminated prematurely."); return false; } switch(this->m_traits.escape_syntax_type(*m_position)) { case regex_constants::escape_type_control_a: result = charT('\a'); break; case regex_constants::escape_type_e: result = charT(27); break; case regex_constants::escape_type_control_f: result = charT('\f'); break; case regex_constants::escape_type_control_n: result = charT('\n'); break; case regex_constants::escape_type_control_r: result = charT('\r'); break; case regex_constants::escape_type_control_t: result = charT('\t'); break; case regex_constants::escape_type_control_v: result = charT('\v'); break; case regex_constants::escape_type_word_assert: result = charT('\b'); break; case regex_constants::escape_type_ascii_control: ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "ASCII escape sequence terminated prematurely."); return result; } result = static_cast(*m_position % 32); break; case regex_constants::escape_type_hex: ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "Hexadecimal escape sequence terminated prematurely."); return result; } // maybe have \x{ddd} if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace) { ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "Missing } in hexadecimal escape sequence."); return result; } boost::intmax_t i = this->m_traits.toi(m_position, m_end, 16); if((m_position == m_end) || (i < 0) || ((std::numeric_limits::is_specialized) && (i > (boost::intmax_t)(std::numeric_limits::max)())) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace)) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_badbrace, m_position - m_base, "Hexadecimal escape sequence was invalid."); return result; } ++m_position; result = charT(i); } else { std::ptrdiff_t len = (std::min)(static_cast(2), static_cast(m_end - m_position)); boost::intmax_t i = this->m_traits.toi(m_position, m_position + len, 16); if((i < 0) || !valid_value(charT(0), i)) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "Escape sequence did not encode a valid character."); return result; } result = charT(i); } return result; case regex_constants::syntax_digit: { // an octal escape sequence, the first character must be a zero // followed by up to 3 octal digits: std::ptrdiff_t len = (std::min)(::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end), static_cast(4)); const charT* bp = m_position; boost::intmax_t val = this->m_traits.toi(bp, bp + 1, 8); if(val != 0) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; // Oops not an octal escape after all: fail(regex_constants::error_escape, m_position - m_base, "Invalid octal escape sequence."); return result; } val = this->m_traits.toi(m_position, m_position + len, 8); if((val < 0) || (val > (boost::intmax_t)(std::numeric_limits::max)())) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "Octal escape sequence is invalid."); return result; } return static_cast(val); } case regex_constants::escape_type_named_char: { ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base); return false; } // maybe have \N{name} if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace) { const charT* base = m_position; // skip forward until we find enclosing brace: while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace)) ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base); return false; } string_type s = this->m_traits.lookup_collatename(++base, m_position++); if(s.empty()) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_collate, m_position - m_base); return false; } if(s.size() == 1) { return s[0]; } } // fall through is a failure: // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base); return false; } default: result = *m_position; break; } ++m_position; return result; #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool basic_regex_parser::parse_backref() { BOOST_REGEX_ASSERT(m_position != m_end); const charT* pc = m_position; boost::intmax_t i = this->m_traits.toi(pc, pc + 1, 10); if((i == 0) || (((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group) && (this->flags() & regbase::no_bk_refs))) { // not a backref at all but an octal escape sequence: charT c = unescape_character(); this->append_literal(c); } else if((i > 0) && (this->m_backrefs.test(i))) { m_position = pc; re_brace* pb = static_cast(this->append_state(syntax_element_backref, sizeof(re_brace))); pb->index = i; pb->icase = this->flags() & regbase::icase; } else { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_backref, m_position - m_base); return false; } return true; } template bool basic_regex_parser::parse_QE() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif // // parse a \Q...\E sequence: // ++m_position; // skip the Q const charT* start = m_position; const charT* end; do { while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape)) ++m_position; if(m_position == m_end) { // a \Q...\E sequence may terminate with the end of the expression: end = m_position; break; } if(++m_position == m_end) // skip the escape { fail(regex_constants::error_escape, m_position - m_base, "Unterminated \\Q...\\E sequence."); return false; } // check to see if it's a \E: if(this->m_traits.escape_syntax_type(*m_position) == regex_constants::escape_type_E) { ++m_position; end = m_position - 2; break; } // otherwise go round again: }while(true); // // now add all the character between the two escapes as literals: // while(start != end) { this->append_literal(*start); ++start; } return true; #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool basic_regex_parser::parse_perl_extension() { if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } // // treat comments as a special case, as these // are the only ones that don't start with a leading // startmark state: // if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_hash) { while((m_position != m_end) && (this->m_traits.syntax_type(*m_position++) != regex_constants::syntax_close_mark)) {} return true; } // // backup some state, and prepare the way: // int markid = 0; std::ptrdiff_t jump_offset = 0; re_brace* pb = static_cast(this->append_state(syntax_element_startmark, sizeof(re_brace))); pb->icase = this->flags() & regbase::icase; std::ptrdiff_t last_paren_start = this->getoffset(pb); // back up insertion point for alternations, and set new point: std::ptrdiff_t last_alt_point = m_alt_insert_point; this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); std::ptrdiff_t expected_alt_point = m_alt_insert_point; bool restore_flags = true; regex_constants::syntax_option_type old_flags = this->flags(); bool old_case_change = m_has_case_change; m_has_case_change = false; charT name_delim; int mark_reset = m_mark_reset; int max_mark = m_max_mark; m_mark_reset = -1; m_max_mark = m_mark_count; boost::intmax_t v; // // select the actual extension used: // switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_or: m_mark_reset = m_mark_count; BOOST_FALLTHROUGH; case regex_constants::syntax_colon: // // a non-capturing mark: // pb->index = markid = 0; ++m_position; break; case regex_constants::syntax_digit: { // // a recursive subexpression: // v = this->m_traits.toi(m_position, m_end, 10); if((v < 0) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "The recursive sub-expression refers to an invalid marking group, or is unterminated."); return false; } insert_recursion: pb->index = markid = 0; re_recurse* pr = static_cast(this->append_state(syntax_element_recurse, sizeof(re_recurse))); pr->alt.i = v; pr->state_id = 0; static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = this->flags() & regbase::icase; break; } case regex_constants::syntax_plus: // // A forward-relative recursive subexpression: // ++m_position; v = this->m_traits.toi(m_position, m_end, 10); if((v <= 0) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "An invalid or unterminated recursive sub-expression."); return false; } if ((std::numeric_limits::max)() - m_mark_count < v) { fail(regex_constants::error_perl_extension, m_position - m_base, "An invalid or unterminated recursive sub-expression."); return false; } v += m_mark_count; goto insert_recursion; case regex_constants::syntax_dash: // // Possibly a backward-relative recursive subexpression: // ++m_position; v = this->m_traits.toi(m_position, m_end, 10); if(v <= 0) { --m_position; // Oops not a relative recursion at all, but a (?-imsx) group: goto option_group_jump; } v = static_cast(m_mark_count) + 1 - v; if(v <= 0) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "An invalid or unterminated recursive sub-expression."); return false; } goto insert_recursion; case regex_constants::syntax_equal: pb->index = markid = -1; ++m_position; jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump))); this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); break; case regex_constants::syntax_not: pb->index = markid = -2; ++m_position; jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump))); this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); break; case regex_constants::escape_type_left_word: { // a lookbehind assertion: if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } regex_constants::syntax_type t = this->m_traits.syntax_type(*m_position); if(t == regex_constants::syntax_not) pb->index = markid = -2; else if(t == regex_constants::syntax_equal) pb->index = markid = -1; else { // Probably a named capture which also starts (?< : name_delim = '>'; --m_position; goto named_capture_jump; } ++m_position; jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump))); this->append_state(syntax_element_backstep, sizeof(re_brace)); this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); break; } case regex_constants::escape_type_right_word: // // an independent sub-expression: // pb->index = markid = -3; ++m_position; jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump))); this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); break; case regex_constants::syntax_open_mark: { // a conditional expression: pb->index = markid = -4; if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = this->m_traits.toi(m_position, m_end, 10); if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(*m_position == charT('R')) { if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(*m_position == charT('&')) { const charT* base = ++m_position; while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = -static_cast(hash_value_from_capture_name(base, m_position)); } else { v = -this->m_traits.toi(m_position, m_end, 10); } re_brace* br = static_cast(this->append_state(syntax_element_assert_backref, sizeof(re_brace))); br->index = v < 0 ? (v - 1) : 0; if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } } else if((*m_position == charT('\'')) || (*m_position == charT('<'))) { const charT* base = ++m_position; while((m_position != m_end) && (*m_position != charT('>')) && (*m_position != charT('\''))) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = static_cast(hash_value_from_capture_name(base, m_position)); re_brace* br = static_cast(this->append_state(syntax_element_assert_backref, sizeof(re_brace))); br->index = v; if(((*m_position != charT('>')) && (*m_position != charT('\''))) || (++m_position == m_end)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "Unterminated named capture."); return false; } if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } } else if(*m_position == charT('D')) { const char* def = "DEFINE"; while(*def && (m_position != m_end) && (*m_position == charT(*def))) ++m_position, ++def; if((m_position == m_end) || *def) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } re_brace* br = static_cast(this->append_state(syntax_element_assert_backref, sizeof(re_brace))); br->index = 9999; // special magic value! if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } } else if(v > 0) { re_brace* br = static_cast(this->append_state(syntax_element_assert_backref, sizeof(re_brace))); br->index = v; if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } } else { // verify that we have a lookahead or lookbehind assert: if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_question) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(this->m_traits.syntax_type(*m_position) == regex_constants::escape_type_left_word) { if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if((this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_not)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } m_position -= 3; } else { if((this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_not)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } m_position -= 2; } } break; } case regex_constants::syntax_close_mark: // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; case regex_constants::escape_type_end_buffer: { name_delim = *m_position; named_capture_jump: markid = 0; if(0 == (this->flags() & regbase::nosubs)) { markid = ++m_mark_count; #ifndef BOOST_NO_STD_DISTANCE if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.push_back(std::pair(std::distance(m_base, m_position) - 2, 0)); #else if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.push_back(std::pair((m_position - m_base) - 2, 0)); #endif } pb->index = markid; const charT* base = ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } while((m_position != m_end) && (*m_position != name_delim)) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } this->m_pdata->set_name(base, m_position, markid); ++m_position; break; } default: if(*m_position == charT('R')) { ++m_position; v = 0; if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } goto insert_recursion; } if(*m_position == charT('&')) { ++m_position; const charT* base = m_position; while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = static_cast(hash_value_from_capture_name(base, m_position)); goto insert_recursion; } if(*m_position == charT('P')) { ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(*m_position == charT('>')) { ++m_position; const charT* base = m_position; while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = static_cast(hash_value_from_capture_name(base, m_position)); goto insert_recursion; } } // // lets assume that we have a (?imsx) group and try and parse it: // option_group_jump: regex_constants::syntax_option_type opts = parse_options(); if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } // make a note of whether we have a case change: m_has_case_change = ((opts & regbase::icase) != (this->flags() & regbase::icase)); pb->index = markid = 0; if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark) { // update flags and carry on as normal: this->flags(opts); restore_flags = false; old_case_change |= m_has_case_change; // defer end of scope by one ')' } else if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_colon) { // update flags and carry on until the matching ')' is found: this->flags(opts); ++m_position; } else { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } // finally append a case change state if we need it: if(m_has_case_change) { static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = opts & regbase::icase; } } // // now recursively add more states, this will terminate when we get to a // matching ')' : // parse_all(); // // Unwind alternatives: // if(0 == unwind_alts(last_paren_start)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "Invalid alternation operators within (?...) block."); return false; } // // we either have a ')' or we have run out of characters prematurely: // if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; this->fail(regex_constants::error_paren, ::boost::BOOST_REGEX_DETAIL_NS::distance(m_base, m_end)); return false; } BOOST_REGEX_ASSERT(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark); ++m_position; // // restore the flags: // if(restore_flags) { // append a case change state if we need it: if(m_has_case_change) { static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = old_flags & regbase::icase; } this->flags(old_flags); } // // set up the jump pointer if we have one: // if(jump_offset) { this->m_pdata->m_data.align(); re_jump* jmp = static_cast(this->getaddress(jump_offset)); jmp->alt.i = this->m_pdata->m_data.size() - this->getoffset(jmp); if((this->m_last_state == jmp) && (markid != -2)) { // Oops... we didn't have anything inside the assertion. // Note we don't get here for negated forward lookahead as (?!) // does have some uses. // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "Invalid or empty zero width assertion."); return false; } } // // verify that if this is conditional expression, that we do have // an alternative, if not add one: // if(markid == -4) { re_syntax_base* b = this->getaddress(expected_alt_point); // Make sure we have exactly one alternative following this state: if(b->type != syntax_element_alt) { re_alt* alt = static_cast(this->insert_state(expected_alt_point, syntax_element_alt, sizeof(re_alt))); alt->alt.i = this->m_pdata->m_data.size() - this->getoffset(alt); } else if(((std::ptrdiff_t)this->m_pdata->m_data.size() > (static_cast(b)->alt.i + this->getoffset(b))) && (static_cast(b)->alt.i > 0) && this->getaddress(static_cast(b)->alt.i, b)->type == syntax_element_alt) { // Can't have seen more than one alternative: // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_bad_pattern, m_position - m_base, "More than one alternation operator | was encountered inside a conditional expression."); return false; } else { // We must *not* have seen an alternative inside a (DEFINE) block: b = this->getaddress(b->next.i, b); if((b->type == syntax_element_assert_backref) && (static_cast(b)->index == 9999)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_bad_pattern, m_position - m_base, "Alternation operators are not allowed inside a DEFINE block."); return false; } } // check for invalid repetition of next state: b = this->getaddress(expected_alt_point); b = this->getaddress(static_cast(b)->next.i, b); if((b->type != syntax_element_assert_backref) && (b->type != syntax_element_startmark)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_badrepeat, m_position - m_base, "A repetition operator cannot be applied to a zero-width assertion."); return false; } } // // append closing parenthesis state: // pb = static_cast(this->append_state(syntax_element_endmark, sizeof(re_brace))); pb->index = markid; pb->icase = this->flags() & regbase::icase; this->m_paren_start = last_paren_start; // // restore the alternate insertion point: // this->m_alt_insert_point = last_alt_point; // // and the case change data: // m_has_case_change = old_case_change; // // And the mark_reset data: // if(m_max_mark > m_mark_count) { m_mark_count = m_max_mark; } m_mark_reset = mark_reset; m_max_mark = max_mark; if(markid > 0) { #ifndef BOOST_NO_STD_DISTANCE if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.at((std::size_t)markid - 1).second = std::distance(m_base, m_position) - 1; #else if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.at(markid - 1).second = (m_position - m_base) - 1; #endif // // allow backrefs to this mark: // this->m_backrefs.set(markid); } return true; } template bool basic_regex_parser::match_verb(const char* verb) { while(*verb) { if(static_cast(*verb) != *m_position) { while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++verb; } return true; } #ifdef BOOST_MSVC # pragma warning(push) #if BOOST_MSVC >= 1800 #pragma warning(disable:26812) #endif #endif template bool basic_regex_parser::parse_perl_verb() { if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } switch(*m_position) { case 'F': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if((this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark) || match_verb("AIL")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; this->append_state(syntax_element_fail); return true; } break; case 'A': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("CCEPT")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; this->append_state(syntax_element_accept); return true; } break; case 'C': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("OMMIT")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; static_cast(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_commit; this->m_pdata->m_disable_match_any = true; return true; } break; case 'P': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("RUNE")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; static_cast(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_prune; this->m_pdata->m_disable_match_any = true; return true; } break; case 'S': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("KIP")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; static_cast(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_skip; this->m_pdata->m_disable_match_any = true; return true; } break; case 'T': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("HEN")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; this->append_state(syntax_element_then); this->m_pdata->m_disable_match_any = true; return true; } break; } // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } #ifdef BOOST_MSVC # pragma warning(pop) #endif template bool basic_regex_parser::add_emacs_code(bool negate) { // // parses an emacs style \sx or \Sx construct. // if(++m_position == m_end) { // Rewind to start of sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base); return false; } basic_char_set char_set; if(negate) char_set.negate(); static const charT s_punct[5] = { 'p', 'u', 'n', 'c', 't', }; switch(*m_position) { case 's': case ' ': char_set.add_class(this->m_mask_space); break; case 'w': char_set.add_class(this->m_word_mask); break; case '_': char_set.add_single(digraph(charT('$'))); char_set.add_single(digraph(charT('&'))); char_set.add_single(digraph(charT('*'))); char_set.add_single(digraph(charT('+'))); char_set.add_single(digraph(charT('-'))); char_set.add_single(digraph(charT('_'))); char_set.add_single(digraph(charT('<'))); char_set.add_single(digraph(charT('>'))); break; case '.': char_set.add_class(this->m_traits.lookup_classname(s_punct, s_punct+5)); break; case '(': char_set.add_single(digraph(charT('('))); char_set.add_single(digraph(charT('['))); char_set.add_single(digraph(charT('{'))); break; case ')': char_set.add_single(digraph(charT(')'))); char_set.add_single(digraph(charT(']'))); char_set.add_single(digraph(charT('}'))); break; case '"': char_set.add_single(digraph(charT('"'))); char_set.add_single(digraph(charT('\''))); char_set.add_single(digraph(charT('`'))); break; case '\'': char_set.add_single(digraph(charT('\''))); char_set.add_single(digraph(charT(','))); char_set.add_single(digraph(charT('#'))); break; case '<': char_set.add_single(digraph(charT(';'))); break; case '>': char_set.add_single(digraph(charT('\n'))); char_set.add_single(digraph(charT('\f'))); break; default: fail(regex_constants::error_ctype, m_position - m_base); return false; } if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } ++m_position; return true; } template regex_constants::syntax_option_type basic_regex_parser::parse_options() { // we have a (?imsx-imsx) group, convert it into a set of flags: regex_constants::syntax_option_type f = this->flags(); bool breakout = false; do { switch(*m_position) { case 's': f |= regex_constants::mod_s; f &= ~regex_constants::no_mod_s; break; case 'm': f &= ~regex_constants::no_mod_m; break; case 'i': f |= regex_constants::icase; break; case 'x': f |= regex_constants::mod_x; break; default: breakout = true; continue; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_paren, m_position - m_base); return false; } } while(!breakout); breakout = false; if(*m_position == static_cast('-')) { if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_paren, m_position - m_base); return false; } do { switch(*m_position) { case 's': f &= ~regex_constants::mod_s; f |= regex_constants::no_mod_s; break; case 'm': f |= regex_constants::no_mod_m; break; case 'i': f &= ~regex_constants::icase; break; case 'x': f &= ~regex_constants::mod_x; break; default: breakout = true; continue; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_paren, m_position - m_base); return false; } } while(!breakout); } return f; } template bool basic_regex_parser::unwind_alts(std::ptrdiff_t last_paren_start) { // // If we didn't actually add any states after the last // alternative then that's an error: // if((this->m_alt_insert_point == static_cast(this->m_pdata->m_data.size())) && (!m_alt_jumps.empty()) && (m_alt_jumps.back() > last_paren_start) && !( ((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group) && ((this->flags() & regbase::no_empty_expressions) == 0) ) ) { fail(regex_constants::error_empty, this->m_position - this->m_base, "Can't terminate a sub-expression with an alternation operator |."); return false; } // // Fix up our alternatives: // while((!m_alt_jumps.empty()) && (m_alt_jumps.back() > last_paren_start)) { // // fix up the jump to point to the end of the states // that we've just added: // std::ptrdiff_t jump_offset = m_alt_jumps.back(); m_alt_jumps.pop_back(); this->m_pdata->m_data.align(); re_jump* jmp = static_cast(this->getaddress(jump_offset)); if (jmp->type != syntax_element_jump) { // Something really bad happened, this used to be an assert, // but we'll make it an error just in case we should ever get here. fail(regex_constants::error_unknown, this->m_position - this->m_base, "Internal logic failed while compiling the expression, probably you added a repeat to something non-repeatable!"); return false; } jmp->alt.i = this->m_pdata->m_data.size() - jump_offset; } return true; } #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/c_regex_traits.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE c_regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits class that wraps the global C locale. */ #ifndef BOOST_C_REGEX_TRAITS_HPP_INCLUDED #define BOOST_C_REGEX_TRAITS_HPP_INCLUDED #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #ifndef BOOST_REGEX_WORKAROUND_HPP #include #endif #include #ifdef BOOST_NO_STDC_NAMESPACE namespace std{ using ::strlen; using ::tolower; } #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103 4244) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS { enum { char_class_space = 1 << 0, char_class_print = 1 << 1, char_class_cntrl = 1 << 2, char_class_upper = 1 << 3, char_class_lower = 1 << 4, char_class_alpha = 1 << 5, char_class_digit = 1 << 6, char_class_punct = 1 << 7, char_class_xdigit = 1 << 8, char_class_alnum = char_class_alpha | char_class_digit, char_class_graph = char_class_alnum | char_class_punct, char_class_blank = 1 << 9, char_class_word = 1 << 10, char_class_unicode = 1 << 11, char_class_horizontal = 1 << 12, char_class_vertical = 1 << 13 }; } template struct c_regex_traits; template<> struct c_regex_traits { c_regex_traits(){} typedef char char_type; typedef std::size_t size_type; typedef std::string string_type; struct locale_type{}; typedef boost::uint32_t char_class_type; static size_type length(const char_type* p) { return (std::strlen)(p); } char translate(char c) const { return c; } char translate_nocase(char c) const { return static_cast((std::tolower)(static_cast(c))); } static string_type BOOST_REGEX_CALL transform(const char* p1, const char* p2); static string_type BOOST_REGEX_CALL transform_primary(const char* p1, const char* p2); static char_class_type BOOST_REGEX_CALL lookup_classname(const char* p1, const char* p2); static string_type BOOST_REGEX_CALL lookup_collatename(const char* p1, const char* p2); static bool BOOST_REGEX_CALL isctype(char, char_class_type); static int BOOST_REGEX_CALL value(char, int); locale_type imbue(locale_type l) { return l; } locale_type getloc()const { return locale_type(); } private: // this type is not copyable: c_regex_traits(const c_regex_traits&); c_regex_traits& operator=(const c_regex_traits&); }; #ifndef BOOST_NO_WREGEX template<> struct c_regex_traits { c_regex_traits(){} typedef wchar_t char_type; typedef std::size_t size_type; typedef std::wstring string_type; struct locale_type{}; typedef boost::uint32_t char_class_type; static size_type length(const char_type* p) { return (std::wcslen)(p); } wchar_t translate(wchar_t c) const { return c; } wchar_t translate_nocase(wchar_t c) const { return (std::towlower)(c); } static string_type BOOST_REGEX_CALL transform(const wchar_t* p1, const wchar_t* p2); static string_type BOOST_REGEX_CALL transform_primary(const wchar_t* p1, const wchar_t* p2); static char_class_type BOOST_REGEX_CALL lookup_classname(const wchar_t* p1, const wchar_t* p2); static string_type BOOST_REGEX_CALL lookup_collatename(const wchar_t* p1, const wchar_t* p2); static bool BOOST_REGEX_CALL isctype(wchar_t, char_class_type); static int BOOST_REGEX_CALL value(wchar_t, int); locale_type imbue(locale_type l) { return l; } locale_type getloc()const { return locale_type(); } private: // this type is not copyable: c_regex_traits(const c_regex_traits&); c_regex_traits& operator=(const c_regex_traits&); }; #endif // BOOST_NO_WREGEX inline c_regex_traits::string_type BOOST_REGEX_CALL c_regex_traits::transform(const char* p1, const char* p2) { std::string result(10, ' '); std::size_t s = result.size(); std::size_t r; std::string src(p1, p2); while (s < (r = std::strxfrm(&*result.begin(), src.c_str(), s))) { #if defined(_CPPLIB_VER) // // A bug in VC11 and 12 causes the program to hang if we pass a null-string // to std::strxfrm, but only for certain locales :-( // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware). // if (r == INT_MAX) { result.erase(); result.insert(result.begin(), static_cast(0)); return result; } #endif result.append(r - s + 3, ' '); s = result.size(); } result.erase(r); return result; } inline c_regex_traits::string_type BOOST_REGEX_CALL c_regex_traits::transform_primary(const char* p1, const char* p2) { static char s_delim; static const int s_collate_type = ::boost::BOOST_REGEX_DETAIL_NS::find_sort_syntax(static_cast*>(0), &s_delim); std::string result; // // What we do here depends upon the format of the sort key returned by // sort key returned by this->transform: // switch (s_collate_type) { case ::boost::BOOST_REGEX_DETAIL_NS::sort_C: case ::boost::BOOST_REGEX_DETAIL_NS::sort_unknown: // the best we can do is translate to lower case, then get a regular sort key: { result.assign(p1, p2); for (std::string::size_type i = 0; i < result.size(); ++i) result[i] = static_cast((std::tolower)(static_cast(result[i]))); result = transform(&*result.begin(), &*result.begin() + result.size()); break; } case ::boost::BOOST_REGEX_DETAIL_NS::sort_fixed: { // get a regular sort key, and then truncate it: result = transform(p1, p2); result.erase(s_delim); break; } case ::boost::BOOST_REGEX_DETAIL_NS::sort_delim: // get a regular sort key, and then truncate everything after the delim: result = transform(p1, p2); if ((!result.empty()) && (result[0] == s_delim)) break; std::size_t i; for (i = 0; i < result.size(); ++i) { if (result[i] == s_delim) break; } result.erase(i); break; } if (result.empty()) result = std::string(1, char(0)); return result; } inline c_regex_traits::char_class_type BOOST_REGEX_CALL c_regex_traits::lookup_classname(const char* p1, const char* p2) { using namespace BOOST_REGEX_DETAIL_NS; static const char_class_type masks[] = { 0, char_class_alnum, char_class_alpha, char_class_blank, char_class_cntrl, char_class_digit, char_class_digit, char_class_graph, char_class_horizontal, char_class_lower, char_class_lower, char_class_print, char_class_punct, char_class_space, char_class_space, char_class_upper, char_class_unicode, char_class_upper, char_class_vertical, char_class_alnum | char_class_word, char_class_alnum | char_class_word, char_class_xdigit, }; int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); if (idx < 0) { std::string s(p1, p2); for (std::string::size_type i = 0; i < s.size(); ++i) s[i] = static_cast((std::tolower)(static_cast(s[i]))); idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size()); } BOOST_REGEX_ASSERT(std::size_t(idx) + 1u < sizeof(masks) / sizeof(masks[0])); return masks[idx + 1]; } inline bool BOOST_REGEX_CALL c_regex_traits::isctype(char c, char_class_type mask) { using namespace BOOST_REGEX_DETAIL_NS; return ((mask & char_class_space) && (std::isspace)(static_cast(c))) || ((mask & char_class_print) && (std::isprint)(static_cast(c))) || ((mask & char_class_cntrl) && (std::iscntrl)(static_cast(c))) || ((mask & char_class_upper) && (std::isupper)(static_cast(c))) || ((mask & char_class_lower) && (std::islower)(static_cast(c))) || ((mask & char_class_alpha) && (std::isalpha)(static_cast(c))) || ((mask & char_class_digit) && (std::isdigit)(static_cast(c))) || ((mask & char_class_punct) && (std::ispunct)(static_cast(c))) || ((mask & char_class_xdigit) && (std::isxdigit)(static_cast(c))) || ((mask & char_class_blank) && (std::isspace)(static_cast(c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c)) || ((mask & char_class_word) && (c == '_')) || ((mask & char_class_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\v'))) || ((mask & char_class_horizontal) && (std::isspace)(static_cast(c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && (c != '\v')); } inline c_regex_traits::string_type BOOST_REGEX_CALL c_regex_traits::lookup_collatename(const char* p1, const char* p2) { std::string s(p1, p2); s = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(s); if (s.empty() && (p2 - p1 == 1)) s.append(1, *p1); return s; } inline int BOOST_REGEX_CALL c_regex_traits::value(char c, int radix) { char b[2] = { c, '\0', }; char* ep; int result = std::strtol(b, &ep, radix); if (ep == b) return -1; return result; } #ifndef BOOST_NO_WREGEX inline c_regex_traits::string_type BOOST_REGEX_CALL c_regex_traits::transform(const wchar_t* p1, const wchar_t* p2) { std::size_t r; std::size_t s = 10; std::wstring src(p1, p2); std::wstring result(s, L' '); while (s < (r = std::wcsxfrm(&*result.begin(), src.c_str(), s))) { #if defined(_CPPLIB_VER) // // A bug in VC11 and 12 causes the program to hang if we pass a null-string // to std::strxfrm, but only for certain locales :-( // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware). // if (r == INT_MAX) { result.erase(); result.insert(result.begin(), static_cast(0)); return result; } #endif result.append(r - s + 3, L' '); s = result.size(); } result.erase(r); return result; } inline c_regex_traits::string_type BOOST_REGEX_CALL c_regex_traits::transform_primary(const wchar_t* p1, const wchar_t* p2) { static wchar_t s_delim; static const int s_collate_type = ::boost::BOOST_REGEX_DETAIL_NS::find_sort_syntax(static_cast*>(0), &s_delim); std::wstring result; // // What we do here depends upon the format of the sort key returned by // sort key returned by this->transform: // switch (s_collate_type) { case ::boost::BOOST_REGEX_DETAIL_NS::sort_C: case ::boost::BOOST_REGEX_DETAIL_NS::sort_unknown: // the best we can do is translate to lower case, then get a regular sort key: { result.assign(p1, p2); for (std::wstring::size_type i = 0; i < result.size(); ++i) result[i] = (std::towlower)(result[i]); result = c_regex_traits::transform(&*result.begin(), &*result.begin() + result.size()); break; } case ::boost::BOOST_REGEX_DETAIL_NS::sort_fixed: { // get a regular sort key, and then truncate it: result = c_regex_traits::transform(&*result.begin(), &*result.begin() + result.size()); result.erase(s_delim); break; } case ::boost::BOOST_REGEX_DETAIL_NS::sort_delim: // get a regular sort key, and then truncate everything after the delim: result = c_regex_traits::transform(&*result.begin(), &*result.begin() + result.size()); if ((!result.empty()) && (result[0] == s_delim)) break; std::size_t i; for (i = 0; i < result.size(); ++i) { if (result[i] == s_delim) break; } result.erase(i); break; } if (result.empty()) result = std::wstring(1, char(0)); return result; } inline c_regex_traits::char_class_type BOOST_REGEX_CALL c_regex_traits::lookup_classname(const wchar_t* p1, const wchar_t* p2) { using namespace BOOST_REGEX_DETAIL_NS; static const char_class_type masks[] = { 0, char_class_alnum, char_class_alpha, char_class_blank, char_class_cntrl, char_class_digit, char_class_digit, char_class_graph, char_class_horizontal, char_class_lower, char_class_lower, char_class_print, char_class_punct, char_class_space, char_class_space, char_class_upper, char_class_unicode, char_class_upper, char_class_vertical, char_class_alnum | char_class_word, char_class_alnum | char_class_word, char_class_xdigit, }; int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); if (idx < 0) { std::wstring s(p1, p2); for (std::wstring::size_type i = 0; i < s.size(); ++i) s[i] = (std::towlower)(s[i]); idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size()); } BOOST_REGEX_ASSERT(idx + 1 < static_cast(sizeof(masks) / sizeof(masks[0]))); return masks[idx + 1]; } inline bool BOOST_REGEX_CALL c_regex_traits::isctype(wchar_t c, char_class_type mask) { using namespace BOOST_REGEX_DETAIL_NS; return ((mask & char_class_space) && (std::iswspace)(c)) || ((mask & char_class_print) && (std::iswprint)(c)) || ((mask & char_class_cntrl) && (std::iswcntrl)(c)) || ((mask & char_class_upper) && (std::iswupper)(c)) || ((mask & char_class_lower) && (std::iswlower)(c)) || ((mask & char_class_alpha) && (std::iswalpha)(c)) || ((mask & char_class_digit) && (std::iswdigit)(c)) || ((mask & char_class_punct) && (std::iswpunct)(c)) || ((mask & char_class_xdigit) && (std::iswxdigit)(c)) || ((mask & char_class_blank) && (std::iswspace)(c) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c)) || ((mask & char_class_word) && (c == '_')) || ((mask & char_class_unicode) && (c & ~static_cast(0xff))) || ((mask & char_class_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == L'\v'))) || ((mask & char_class_horizontal) && (std::iswspace)(c) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && (c != L'\v')); } inline c_regex_traits::string_type BOOST_REGEX_CALL c_regex_traits::lookup_collatename(const wchar_t* p1, const wchar_t* p2) { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4244) #endif std::string name(p1, p2); #ifdef BOOST_MSVC #pragma warning(pop) #endif name = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(name); if (!name.empty()) return string_type(name.begin(), name.end()); if (p2 - p1 == 1) return string_type(1, *p1); return string_type(); } inline int BOOST_REGEX_CALL c_regex_traits::value(wchar_t c, int radix) { #ifdef BOOST_BORLANDC // workaround for broken wcstol: if ((std::iswxdigit)(c) == 0) return -1; #endif wchar_t b[2] = { c, '\0', }; wchar_t* ep; int result = std::wcstol(b, &ep, radix); if (ep == b) return -1; return result; } #endif } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/char_regex_traits.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE char_regex_traits.cpp * VERSION see * DESCRIPTION: Declares deprecated traits classes char_regex_traits<>. */ #ifndef BOOST_REGEX_V4_CHAR_REGEX_TRAITS_HPP #define BOOST_REGEX_V4_CHAR_REGEX_TRAITS_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ namespace deprecated{ // // class char_regex_traits_i // provides case insensitive traits classes (deprecated): template class char_regex_traits_i : public regex_traits {}; template<> class char_regex_traits_i : public regex_traits { public: typedef char char_type; typedef unsigned char uchar_type; typedef unsigned int size_type; typedef regex_traits base_type; }; #ifndef BOOST_NO_WREGEX template<> class char_regex_traits_i : public regex_traits { public: typedef wchar_t char_type; typedef unsigned short uchar_type; typedef unsigned int size_type; typedef regex_traits base_type; }; #endif } // namespace deprecated } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif // include ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/cpp_regex_traits.hpp ================================================ /* * * Copyright (c) 2004 John Maddock * Copyright 2011 Garmin Ltd. or its subsidiaries * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE cpp_regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits class cpp_regex_traits. */ #ifndef BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED #define BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED #include #include #include #ifndef BOOST_NO_STD_LOCALE #ifndef BOOST_RE_PAT_EXCEPT_HPP #include #endif #ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED #include #endif #ifdef BOOST_HAS_THREADS #include #endif #ifndef BOOST_REGEX_PRIMARY_TRANSFORM #include #endif #ifndef BOOST_REGEX_OBJECT_CACHE_HPP #include #endif #include #include #include #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4786 4251) #endif namespace boost{ // // forward declaration is needed by some compilers: // template class cpp_regex_traits; namespace BOOST_REGEX_DETAIL_NS{ // // class parser_buf: // acts as a stream buffer which wraps around a pair of pointers: // template > class parser_buf : public ::std::basic_streambuf { typedef ::std::basic_streambuf base_type; typedef typename base_type::int_type int_type; typedef typename base_type::char_type char_type; typedef typename base_type::pos_type pos_type; typedef ::std::streamsize streamsize; typedef typename base_type::off_type off_type; public: parser_buf() : base_type() { setbuf(0, 0); } const charT* getnext() { return this->gptr(); } protected: std::basic_streambuf* setbuf(char_type* s, streamsize n) BOOST_OVERRIDE; typename parser_buf::pos_type seekpos(pos_type sp, ::std::ios_base::openmode which) BOOST_OVERRIDE; typename parser_buf::pos_type seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which) BOOST_OVERRIDE; private: parser_buf& operator=(const parser_buf&); parser_buf(const parser_buf&); }; template std::basic_streambuf* parser_buf::setbuf(char_type* s, streamsize n) { this->setg(s, s, s + n); return this; } template typename parser_buf::pos_type parser_buf::seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which) { typedef typename boost::int_t::least cast_type; if(which & ::std::ios_base::out) return pos_type(off_type(-1)); std::ptrdiff_t size = this->egptr() - this->eback(); std::ptrdiff_t pos = this->gptr() - this->eback(); charT* g = this->eback(); switch(static_cast(way)) { case ::std::ios_base::beg: if((off < 0) || (off > size)) return pos_type(off_type(-1)); else this->setg(g, g + off, g + size); break; case ::std::ios_base::end: if((off < 0) || (off > size)) return pos_type(off_type(-1)); else this->setg(g, g + size - off, g + size); break; case ::std::ios_base::cur: { std::ptrdiff_t newpos = static_cast(pos + off); if((newpos < 0) || (newpos > size)) return pos_type(off_type(-1)); else this->setg(g, g + newpos, g + size); break; } default: ; } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4244) #endif return static_cast(this->gptr() - this->eback()); #ifdef BOOST_MSVC #pragma warning(pop) #endif } template typename parser_buf::pos_type parser_buf::seekpos(pos_type sp, ::std::ios_base::openmode which) { if(which & ::std::ios_base::out) return pos_type(off_type(-1)); off_type size = static_cast(this->egptr() - this->eback()); charT* g = this->eback(); if(off_type(sp) <= size) { this->setg(g, g + off_type(sp), g + size); } return pos_type(off_type(-1)); } // // class cpp_regex_traits_base: // acts as a container for locale and the facets we are using. // template struct cpp_regex_traits_base { cpp_regex_traits_base(const std::locale& l) { (void)imbue(l); } std::locale imbue(const std::locale& l); std::locale m_locale; std::ctype const* m_pctype; #ifndef BOOST_NO_STD_MESSAGES std::messages const* m_pmessages; #endif std::collate const* m_pcollate; bool operator<(const cpp_regex_traits_base& b)const { if(m_pctype == b.m_pctype) { #ifndef BOOST_NO_STD_MESSAGES if(m_pmessages == b.m_pmessages) { return m_pcollate < b.m_pcollate; } return m_pmessages < b.m_pmessages; #else return m_pcollate < b.m_pcollate; #endif } return m_pctype < b.m_pctype; } bool operator==(const cpp_regex_traits_base& b)const { return (m_pctype == b.m_pctype) #ifndef BOOST_NO_STD_MESSAGES && (m_pmessages == b.m_pmessages) #endif && (m_pcollate == b.m_pcollate); } }; template std::locale cpp_regex_traits_base::imbue(const std::locale& l) { std::locale result(m_locale); m_locale = l; m_pctype = &BOOST_USE_FACET(std::ctype, l); #ifndef BOOST_NO_STD_MESSAGES m_pmessages = BOOST_HAS_FACET(std::messages, l) ? &BOOST_USE_FACET(std::messages, l) : 0; #endif m_pcollate = &BOOST_USE_FACET(std::collate, l); return result; } // // class cpp_regex_traits_char_layer: // implements methods that require specialization for narrow characters: // template class cpp_regex_traits_char_layer : public cpp_regex_traits_base { typedef std::basic_string string_type; typedef std::map map_type; typedef typename map_type::const_iterator map_iterator_type; public: cpp_regex_traits_char_layer(const std::locale& l) : cpp_regex_traits_base(l) { init(); } cpp_regex_traits_char_layer(const cpp_regex_traits_base& b) : cpp_regex_traits_base(b) { init(); } void init(); regex_constants::syntax_type syntax_type(charT c)const { map_iterator_type i = m_char_map.find(c); return ((i == m_char_map.end()) ? 0 : i->second); } regex_constants::escape_syntax_type escape_syntax_type(charT c) const { map_iterator_type i = m_char_map.find(c); if(i == m_char_map.end()) { if(this->m_pctype->is(std::ctype_base::lower, c)) return regex_constants::escape_type_class; if(this->m_pctype->is(std::ctype_base::upper, c)) return regex_constants::escape_type_not_class; return 0; } return i->second; } private: string_type get_default_message(regex_constants::syntax_type); // TODO: use a hash table when available! map_type m_char_map; }; template void cpp_regex_traits_char_layer::init() { // we need to start by initialising our syntax map so we know which // character is used for which purpose: #ifndef BOOST_NO_STD_MESSAGES #ifndef __IBMCPP__ typename std::messages::catalog cat = static_cast::catalog>(-1); #else typename std::messages::catalog cat = reinterpret_cast::catalog>(-1); #endif std::string cat_name(cpp_regex_traits::get_catalog_name()); if((!cat_name.empty()) && (this->m_pmessages != 0)) { cat = this->m_pmessages->open( cat_name, this->m_locale); if((int)cat < 0) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if((int)cat >= 0) { #ifndef BOOST_NO_EXCEPTIONS try{ #endif for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { string_type mss = this->m_pmessages->get(cat, 0, i, get_default_message(i)); for(typename string_type::size_type j = 0; j < mss.size(); ++j) { m_char_map[mss[j]] = i; } } this->m_pmessages->close(cat); #ifndef BOOST_NO_EXCEPTIONS } catch(...) { if(this->m_pmessages) this->m_pmessages->close(cat); throw; } #endif } else { #endif for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { const char* ptr = get_default_syntax(i); while(ptr && *ptr) { m_char_map[this->m_pctype->widen(*ptr)] = i; ++ptr; } } #ifndef BOOST_NO_STD_MESSAGES } #endif } template typename cpp_regex_traits_char_layer::string_type cpp_regex_traits_char_layer::get_default_message(regex_constants::syntax_type i) { const char* ptr = get_default_syntax(i); string_type result; while(ptr && *ptr) { result.append(1, this->m_pctype->widen(*ptr)); ++ptr; } return result; } // // specialized version for narrow characters: // template <> class cpp_regex_traits_char_layer : public cpp_regex_traits_base { typedef std::string string_type; public: cpp_regex_traits_char_layer(const std::locale& l) : cpp_regex_traits_base(l) { init(); } cpp_regex_traits_char_layer(const cpp_regex_traits_base& l) : cpp_regex_traits_base(l) { init(); } regex_constants::syntax_type syntax_type(char c)const { return m_char_map[static_cast(c)]; } regex_constants::escape_syntax_type escape_syntax_type(char c) const { return m_char_map[static_cast(c)]; } private: regex_constants::syntax_type m_char_map[1u << CHAR_BIT]; void init(); }; #ifdef BOOST_REGEX_BUGGY_CTYPE_FACET enum { char_class_space=1<<0, char_class_print=1<<1, char_class_cntrl=1<<2, char_class_upper=1<<3, char_class_lower=1<<4, char_class_alpha=1<<5, char_class_digit=1<<6, char_class_punct=1<<7, char_class_xdigit=1<<8, char_class_alnum=char_class_alpha|char_class_digit, char_class_graph=char_class_alnum|char_class_punct, char_class_blank=1<<9, char_class_word=1<<10, char_class_unicode=1<<11, char_class_horizontal_space=1<<12, char_class_vertical_space=1<<13 }; #endif // // class cpp_regex_traits_implementation: // provides pimpl implementation for cpp_regex_traits. // template class cpp_regex_traits_implementation : public cpp_regex_traits_char_layer { public: typedef typename cpp_regex_traits::char_class_type char_class_type; typedef typename std::ctype::mask native_mask_type; typedef typename boost::make_unsigned::type unsigned_native_mask_type; #ifndef BOOST_REGEX_BUGGY_CTYPE_FACET BOOST_STATIC_CONSTANT(char_class_type, mask_blank = 1u << 24); BOOST_STATIC_CONSTANT(char_class_type, mask_word = 1u << 25); BOOST_STATIC_CONSTANT(char_class_type, mask_unicode = 1u << 26); BOOST_STATIC_CONSTANT(char_class_type, mask_horizontal = 1u << 27); BOOST_STATIC_CONSTANT(char_class_type, mask_vertical = 1u << 28); #endif typedef std::basic_string string_type; typedef charT char_type; //cpp_regex_traits_implementation(); cpp_regex_traits_implementation(const std::locale& l) : cpp_regex_traits_char_layer(l) { init(); } cpp_regex_traits_implementation(const cpp_regex_traits_base& l) : cpp_regex_traits_char_layer(l) { init(); } std::string error_string(regex_constants::error_type n) const { if(!m_error_strings.empty()) { std::map::const_iterator p = m_error_strings.find(n); return (p == m_error_strings.end()) ? std::string(get_default_error_string(n)) : p->second; } return get_default_error_string(n); } char_class_type lookup_classname(const charT* p1, const charT* p2) const { char_class_type result = lookup_classname_imp(p1, p2); if(result == 0) { string_type temp(p1, p2); this->m_pctype->tolower(&*temp.begin(), &*temp.begin() + temp.size()); result = lookup_classname_imp(&*temp.begin(), &*temp.begin() + temp.size()); } return result; } string_type lookup_collatename(const charT* p1, const charT* p2) const; string_type transform_primary(const charT* p1, const charT* p2) const; string_type transform(const charT* p1, const charT* p2) const; private: std::map m_error_strings; // error messages indexed by numberic ID std::map m_custom_class_names; // character class names std::map m_custom_collate_names; // collating element names unsigned m_collate_type; // the form of the collation string charT m_collate_delim; // the collation group delimiter // // helpers: // char_class_type lookup_classname_imp(const charT* p1, const charT* p2) const; void init(); #ifdef BOOST_REGEX_BUGGY_CTYPE_FACET public: bool isctype(charT c, char_class_type m)const; #endif }; #ifndef BOOST_REGEX_BUGGY_CTYPE_FACET #if !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_blank; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_word; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_unicode; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_vertical; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_horizontal; #endif #endif template typename cpp_regex_traits_implementation::string_type cpp_regex_traits_implementation::transform_primary(const charT* p1, const charT* p2) const { // // PRECONDITIONS: // // A bug in gcc 3.2 (and maybe other versions as well) treats // p1 as a null terminated string, for efficiency reasons // we work around this elsewhere, but just assert here that // we adhere to gcc's (buggy) preconditions... // BOOST_REGEX_ASSERT(*p2 == 0); string_type result; #if defined(_CPPLIB_VER) // // A bug in VC11 and 12 causes the program to hang if we pass a null-string // to std::collate::transform, but only for certain locales :-( // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware). // if(*p1 == 0) { return string_type(1, charT(0)); } #endif // // swallowing all exceptions here is a bad idea // however at least one std lib will always throw // std::bad_alloc for certain arguments... // #ifndef BOOST_NO_EXCEPTIONS try{ #endif // // What we do here depends upon the format of the sort key returned by // sort key returned by this->transform: // switch(m_collate_type) { case sort_C: case sort_unknown: // the best we can do is translate to lower case, then get a regular sort key: { result.assign(p1, p2); this->m_pctype->tolower(&*result.begin(), &*result.begin() + result.size()); result = this->m_pcollate->transform(&*result.begin(), &*result.begin() + result.size()); break; } case sort_fixed: { // get a regular sort key, and then truncate it: result.assign(this->m_pcollate->transform(p1, p2)); result.erase(this->m_collate_delim); break; } case sort_delim: // get a regular sort key, and then truncate everything after the delim: result.assign(this->m_pcollate->transform(p1, p2)); std::size_t i; for(i = 0; i < result.size(); ++i) { if(result[i] == m_collate_delim) break; } result.erase(i); break; } #ifndef BOOST_NO_EXCEPTIONS }catch(...){} #endif while((!result.empty()) && (charT(0) == *result.rbegin())) result.erase(result.size() - 1); if(result.empty()) { // character is ignorable at the primary level: result = string_type(1, charT(0)); } return result; } template typename cpp_regex_traits_implementation::string_type cpp_regex_traits_implementation::transform(const charT* p1, const charT* p2) const { // // PRECONDITIONS: // // A bug in gcc 3.2 (and maybe other versions as well) treats // p1 as a null terminated string, for efficiency reasons // we work around this elsewhere, but just assert here that // we adhere to gcc's (buggy) preconditions... // BOOST_REGEX_ASSERT(*p2 == 0); // // swallowing all exceptions here is a bad idea // however at least one std lib will always throw // std::bad_alloc for certain arguments... // string_type result, result2; #if defined(_CPPLIB_VER) // // A bug in VC11 and 12 causes the program to hang if we pass a null-string // to std::collate::transform, but only for certain locales :-( // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware). // if(*p1 == 0) { return result; } #endif #ifndef BOOST_NO_EXCEPTIONS try{ #endif result = this->m_pcollate->transform(p1, p2); // // Borland's STLPort version returns a NULL-terminated // string that has garbage at the end - each call to // std::collate::transform returns a different string! // So as a workaround, we'll truncate the string at the first NULL // which _seems_ to work.... #if BOOST_WORKAROUND(BOOST_BORLANDC, < 0x580) result.erase(result.find(charT(0))); #else // // some implementations (Dinkumware) append unnecessary trailing \0's: while((!result.empty()) && (charT(0) == *result.rbegin())) result.erase(result.size() - 1); #endif // // We may have NULL's used as separators between sections of the collate string, // an example would be Boost.Locale. We have no way to detect this case via // #defines since this can be used with any compiler/platform combination. // Unfortunately our state machine (which was devised when all implementations // used underlying C language API's) can't cope with that case. One workaround // is to replace each character with 2, fortunately this code isn't used that // much as this is now slower than before :-( // typedef typename make_unsigned::type uchar_type; result2.reserve(result.size() * 2 + 2); for(unsigned i = 0; i < result.size(); ++i) { if(static_cast(result[i]) == (std::numeric_limits::max)()) { result2.append(1, charT((std::numeric_limits::max)())).append(1, charT('b')); } else { result2.append(1, static_cast(1 + static_cast(result[i]))).append(1, charT('b') - 1); } } BOOST_REGEX_ASSERT(std::find(result2.begin(), result2.end(), charT(0)) == result2.end()); #ifndef BOOST_NO_EXCEPTIONS } catch(...) { } #endif return result2; } template typename cpp_regex_traits_implementation::string_type cpp_regex_traits_implementation::lookup_collatename(const charT* p1, const charT* p2) const { typedef typename std::map::const_iterator iter_type; if(!m_custom_collate_names.empty()) { iter_type pos = m_custom_collate_names.find(string_type(p1, p2)); if(pos != m_custom_collate_names.end()) return pos->second; } #if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\ && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551) std::string name(p1, p2); #else std::string name; const charT* p0 = p1; while(p0 != p2) name.append(1, char(*p0++)); #endif name = lookup_default_collate_name(name); #if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\ && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551) if(!name.empty()) return string_type(name.begin(), name.end()); #else if(!name.empty()) { string_type result; typedef std::string::const_iterator iter; iter b = name.begin(); iter e = name.end(); while(b != e) result.append(1, charT(*b++)); return result; } #endif if(p2 - p1 == 1) return string_type(1, *p1); return string_type(); } template void cpp_regex_traits_implementation::init() { #ifndef BOOST_NO_STD_MESSAGES #ifndef __IBMCPP__ typename std::messages::catalog cat = static_cast::catalog>(-1); #else typename std::messages::catalog cat = reinterpret_cast::catalog>(-1); #endif std::string cat_name(cpp_regex_traits::get_catalog_name()); if((!cat_name.empty()) && (this->m_pmessages != 0)) { cat = this->m_pmessages->open( cat_name, this->m_locale); if((int)cat < 0) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if((int)cat >= 0) { // // Error messages: // for(boost::regex_constants::error_type i = static_cast(0); i <= boost::regex_constants::error_unknown; i = static_cast(i + 1)) { const char* p = get_default_error_string(i); string_type default_message; while(*p) { default_message.append(1, this->m_pctype->widen(*p)); ++p; } string_type s = this->m_pmessages->get(cat, 0, i+200, default_message); std::string result; for(std::string::size_type j = 0; j < s.size(); ++j) { result.append(1, this->m_pctype->narrow(s[j], 0)); } m_error_strings[i] = result; } // // Custom class names: // #ifndef BOOST_REGEX_BUGGY_CTYPE_FACET static const char_class_type masks[16] = { static_cast(std::ctype::alnum), static_cast(std::ctype::alpha), static_cast(std::ctype::cntrl), static_cast(std::ctype::digit), static_cast(std::ctype::graph), cpp_regex_traits_implementation::mask_horizontal, static_cast(std::ctype::lower), static_cast(std::ctype::print), static_cast(std::ctype::punct), static_cast(std::ctype::space), static_cast(std::ctype::upper), cpp_regex_traits_implementation::mask_vertical, static_cast(std::ctype::xdigit), cpp_regex_traits_implementation::mask_blank, cpp_regex_traits_implementation::mask_word, cpp_regex_traits_implementation::mask_unicode, }; #else static const char_class_type masks[16] = { ::boost::BOOST_REGEX_DETAIL_NS::char_class_alnum, ::boost::BOOST_REGEX_DETAIL_NS::char_class_alpha, ::boost::BOOST_REGEX_DETAIL_NS::char_class_cntrl, ::boost::BOOST_REGEX_DETAIL_NS::char_class_digit, ::boost::BOOST_REGEX_DETAIL_NS::char_class_graph, ::boost::BOOST_REGEX_DETAIL_NS::char_class_horizontal_space, ::boost::BOOST_REGEX_DETAIL_NS::char_class_lower, ::boost::BOOST_REGEX_DETAIL_NS::char_class_print, ::boost::BOOST_REGEX_DETAIL_NS::char_class_punct, ::boost::BOOST_REGEX_DETAIL_NS::char_class_space, ::boost::BOOST_REGEX_DETAIL_NS::char_class_upper, ::boost::BOOST_REGEX_DETAIL_NS::char_class_vertical_space, ::boost::BOOST_REGEX_DETAIL_NS::char_class_xdigit, ::boost::BOOST_REGEX_DETAIL_NS::char_class_blank, ::boost::BOOST_REGEX_DETAIL_NS::char_class_word, ::boost::BOOST_REGEX_DETAIL_NS::char_class_unicode, }; #endif static const string_type null_string; for(unsigned int j = 0; j <= 13; ++j) { string_type s(this->m_pmessages->get(cat, 0, j+300, null_string)); if(!s.empty()) this->m_custom_class_names[s] = masks[j]; } } #endif // // get the collation format used by m_pcollate: // m_collate_type = BOOST_REGEX_DETAIL_NS::find_sort_syntax(this, &m_collate_delim); } template typename cpp_regex_traits_implementation::char_class_type cpp_regex_traits_implementation::lookup_classname_imp(const charT* p1, const charT* p2) const { #ifndef BOOST_REGEX_BUGGY_CTYPE_FACET static const char_class_type masks[22] = { 0, static_cast(std::ctype::alnum), static_cast(std::ctype::alpha), cpp_regex_traits_implementation::mask_blank, static_cast(std::ctype::cntrl), static_cast(std::ctype::digit), static_cast(std::ctype::digit), static_cast(std::ctype::graph), cpp_regex_traits_implementation::mask_horizontal, static_cast(std::ctype::lower), static_cast(std::ctype::lower), static_cast(std::ctype::print), static_cast(std::ctype::punct), static_cast(std::ctype::space), static_cast(std::ctype::space), static_cast(std::ctype::upper), cpp_regex_traits_implementation::mask_unicode, static_cast(std::ctype::upper), cpp_regex_traits_implementation::mask_vertical, static_cast(std::ctype::alnum) | cpp_regex_traits_implementation::mask_word, static_cast(std::ctype::alnum) | cpp_regex_traits_implementation::mask_word, static_cast(std::ctype::xdigit), }; #else static const char_class_type masks[22] = { 0, ::boost::BOOST_REGEX_DETAIL_NS::char_class_alnum, ::boost::BOOST_REGEX_DETAIL_NS::char_class_alpha, ::boost::BOOST_REGEX_DETAIL_NS::char_class_blank, ::boost::BOOST_REGEX_DETAIL_NS::char_class_cntrl, ::boost::BOOST_REGEX_DETAIL_NS::char_class_digit, ::boost::BOOST_REGEX_DETAIL_NS::char_class_digit, ::boost::BOOST_REGEX_DETAIL_NS::char_class_graph, ::boost::BOOST_REGEX_DETAIL_NS::char_class_horizontal_space, ::boost::BOOST_REGEX_DETAIL_NS::char_class_lower, ::boost::BOOST_REGEX_DETAIL_NS::char_class_lower, ::boost::BOOST_REGEX_DETAIL_NS::char_class_print, ::boost::BOOST_REGEX_DETAIL_NS::char_class_punct, ::boost::BOOST_REGEX_DETAIL_NS::char_class_space, ::boost::BOOST_REGEX_DETAIL_NS::char_class_space, ::boost::BOOST_REGEX_DETAIL_NS::char_class_upper, ::boost::BOOST_REGEX_DETAIL_NS::char_class_unicode, ::boost::BOOST_REGEX_DETAIL_NS::char_class_upper, ::boost::BOOST_REGEX_DETAIL_NS::char_class_vertical_space, ::boost::BOOST_REGEX_DETAIL_NS::char_class_alnum | ::boost::BOOST_REGEX_DETAIL_NS::char_class_word, ::boost::BOOST_REGEX_DETAIL_NS::char_class_alnum | ::boost::BOOST_REGEX_DETAIL_NS::char_class_word, ::boost::BOOST_REGEX_DETAIL_NS::char_class_xdigit, }; #endif if(!m_custom_class_names.empty()) { typedef typename std::map, char_class_type>::const_iterator map_iter; map_iter pos = m_custom_class_names.find(string_type(p1, p2)); if(pos != m_custom_class_names.end()) return pos->second; } std::size_t state_id = 1 + BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); BOOST_REGEX_ASSERT(state_id < sizeof(masks) / sizeof(masks[0])); return masks[state_id]; } #ifdef BOOST_REGEX_BUGGY_CTYPE_FACET template bool cpp_regex_traits_implementation::isctype(const charT c, char_class_type mask) const { return ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_space) && (this->m_pctype->is(std::ctype::space, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_print) && (this->m_pctype->is(std::ctype::print, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_cntrl) && (this->m_pctype->is(std::ctype::cntrl, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_upper) && (this->m_pctype->is(std::ctype::upper, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_lower) && (this->m_pctype->is(std::ctype::lower, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_alpha) && (this->m_pctype->is(std::ctype::alpha, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_digit) && (this->m_pctype->is(std::ctype::digit, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_punct) && (this->m_pctype->is(std::ctype::punct, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_xdigit) && (this->m_pctype->is(std::ctype::xdigit, c))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_blank) && (this->m_pctype->is(std::ctype::space, c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c)) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_word) && (c == '_')) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_unicode) && ::boost::BOOST_REGEX_DETAIL_NS::is_extended(c)) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_vertical_space) && (is_separator(c) || (c == '\v'))) || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_horizontal_space) && this->m_pctype->is(std::ctype::space, c) && !(is_separator(c) || (c == '\v'))); } #endif template inline boost::shared_ptr > create_cpp_regex_traits(const std::locale& l) { cpp_regex_traits_base key(l); return ::boost::object_cache, cpp_regex_traits_implementation >::get(key, 5); } } // BOOST_REGEX_DETAIL_NS template class cpp_regex_traits { private: typedef std::ctype ctype_type; public: typedef charT char_type; typedef std::size_t size_type; typedef std::basic_string string_type; typedef std::locale locale_type; typedef boost::uint_least32_t char_class_type; struct boost_extensions_tag{}; cpp_regex_traits() : m_pimpl(BOOST_REGEX_DETAIL_NS::create_cpp_regex_traits(std::locale())) { } static size_type length(const char_type* p) { return std::char_traits::length(p); } regex_constants::syntax_type syntax_type(charT c)const { return m_pimpl->syntax_type(c); } regex_constants::escape_syntax_type escape_syntax_type(charT c) const { return m_pimpl->escape_syntax_type(c); } charT translate(charT c) const { return c; } charT translate_nocase(charT c) const { return m_pimpl->m_pctype->tolower(c); } charT translate(charT c, bool icase) const { return icase ? m_pimpl->m_pctype->tolower(c) : c; } charT tolower(charT c) const { return m_pimpl->m_pctype->tolower(c); } charT toupper(charT c) const { return m_pimpl->m_pctype->toupper(c); } string_type transform(const charT* p1, const charT* p2) const { return m_pimpl->transform(p1, p2); } string_type transform_primary(const charT* p1, const charT* p2) const { return m_pimpl->transform_primary(p1, p2); } char_class_type lookup_classname(const charT* p1, const charT* p2) const { return m_pimpl->lookup_classname(p1, p2); } string_type lookup_collatename(const charT* p1, const charT* p2) const { return m_pimpl->lookup_collatename(p1, p2); } bool isctype(charT c, char_class_type f) const { #ifndef BOOST_REGEX_BUGGY_CTYPE_FACET typedef typename std::ctype::mask ctype_mask; static const ctype_mask mask_base = static_cast( std::ctype::alnum | std::ctype::alpha | std::ctype::cntrl | std::ctype::digit | std::ctype::graph | std::ctype::lower | std::ctype::print | std::ctype::punct | std::ctype::space | std::ctype::upper | std::ctype::xdigit); if((f & mask_base) && (m_pimpl->m_pctype->is( static_cast(f & mask_base), c))) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_unicode) && BOOST_REGEX_DETAIL_NS::is_extended(c)) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_word) && (c == '_')) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_blank) && m_pimpl->m_pctype->is(std::ctype::space, c) && !BOOST_REGEX_DETAIL_NS::is_separator(c)) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\v'))) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_horizontal) && this->isctype(c, std::ctype::space) && !this->isctype(c, BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_vertical)) return true; #ifdef __CYGWIN__ // // Cygwin has a buggy ctype facet, see https://www.cygwin.com/ml/cygwin/2012-08/msg00178.html: // else if((f & std::ctype::xdigit) == std::ctype::xdigit) { if((c >= 'a') && (c <= 'f')) return true; if((c >= 'A') && (c <= 'F')) return true; } #endif return false; #else return m_pimpl->isctype(c, f); #endif } boost::intmax_t toi(const charT*& p1, const charT* p2, int radix)const; int value(charT c, int radix)const { const charT* pc = &c; return (int)toi(pc, pc + 1, radix); } locale_type imbue(locale_type l) { std::locale result(getloc()); m_pimpl = BOOST_REGEX_DETAIL_NS::create_cpp_regex_traits(l); return result; } locale_type getloc()const { return m_pimpl->m_locale; } std::string error_string(regex_constants::error_type n) const { return m_pimpl->error_string(n); } // // extension: // set the name of the message catalog in use (defaults to "boost_regex"). // static std::string catalog_name(const std::string& name); static std::string get_catalog_name(); private: boost::shared_ptr > m_pimpl; // // catalog name handler: // static std::string& get_catalog_name_inst(); #ifdef BOOST_HAS_THREADS static static_mutex& get_mutex_inst(); #endif }; template boost::intmax_t cpp_regex_traits::toi(const charT*& first, const charT* last, int radix)const { BOOST_REGEX_DETAIL_NS::parser_buf sbuf; // buffer for parsing numbers. std::basic_istream is(&sbuf); // stream for parsing numbers. // we do NOT want to parse any thousands separators inside the stream: last = std::find(first, last, BOOST_USE_FACET(std::numpunct, is.getloc()).thousands_sep()); sbuf.pubsetbuf(const_cast(static_cast(first)), static_cast(last-first)); is.clear(); if(std::abs(radix) == 16) is >> std::hex; else if(std::abs(radix) == 8) is >> std::oct; else is >> std::dec; boost::intmax_t val; if(is >> val) { first = first + ((last - first) - sbuf.in_avail()); return val; } else return -1; } template std::string cpp_regex_traits::catalog_name(const std::string& name) { #ifdef BOOST_HAS_THREADS static_mutex::scoped_lock lk(get_mutex_inst()); #endif std::string result(get_catalog_name_inst()); get_catalog_name_inst() = name; return result; } template std::string& cpp_regex_traits::get_catalog_name_inst() { static std::string s_name; return s_name; } template std::string cpp_regex_traits::get_catalog_name() { #ifdef BOOST_HAS_THREADS static_mutex::scoped_lock lk(get_mutex_inst()); #endif std::string result(get_catalog_name_inst()); return result; } #ifdef BOOST_HAS_THREADS template static_mutex& cpp_regex_traits::get_mutex_inst() { static static_mutex s_mutex = BOOST_STATIC_MUTEX_INIT; return s_mutex; } #endif namespace BOOST_REGEX_DETAIL_NS { inline void cpp_regex_traits_char_layer::init() { // we need to start by initialising our syntax map so we know which // character is used for which purpose: std::memset(m_char_map, 0, sizeof(m_char_map)); #ifndef BOOST_NO_STD_MESSAGES #ifndef __IBMCPP__ std::messages::catalog cat = static_cast::catalog>(-1); #else std::messages::catalog cat = reinterpret_cast::catalog>(-1); #endif std::string cat_name(cpp_regex_traits::get_catalog_name()); if ((!cat_name.empty()) && (m_pmessages != 0)) { cat = this->m_pmessages->open( cat_name, this->m_locale); if ((int)cat < 0) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if ((int)cat >= 0) { #ifndef BOOST_NO_EXCEPTIONS try { #endif for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { string_type mss = this->m_pmessages->get(cat, 0, i, get_default_syntax(i)); for (string_type::size_type j = 0; j < mss.size(); ++j) { m_char_map[static_cast(mss[j])] = i; } } this->m_pmessages->close(cat); #ifndef BOOST_NO_EXCEPTIONS } catch (...) { this->m_pmessages->close(cat); throw; } #endif } else { #endif for (regex_constants::syntax_type j = 1; j < regex_constants::syntax_max; ++j) { const char* ptr = get_default_syntax(j); while (ptr && *ptr) { m_char_map[static_cast(*ptr)] = j; ++ptr; } } #ifndef BOOST_NO_STD_MESSAGES } #endif // // finish off by calculating our escape types: // unsigned char i = 'A'; do { if (m_char_map[i] == 0) { if (this->m_pctype->is(std::ctype_base::lower, i)) m_char_map[i] = regex_constants::escape_type_class; else if (this->m_pctype->is(std::ctype_base::upper, i)) m_char_map[i] = regex_constants::escape_type_not_class; } } while (0xFF != i++); } } // namespace detail } // boost #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/cregex.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE cregex.cpp * VERSION see * DESCRIPTION: Declares POSIX API functions * + boost::RegEx high level wrapper. */ #ifndef BOOST_RE_CREGEX_HPP_INCLUDED #define BOOST_RE_CREGEX_HPP_INCLUDED #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #include #include #ifdef __cplusplus #include #else #include #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif /* include these defs only for POSIX compatablity */ #ifdef __cplusplus namespace boost{ extern "C" { #endif #if defined(__cplusplus) && !defined(BOOST_NO_STDC_NAMESPACE) typedef std::ptrdiff_t regoff_t; typedef std::size_t regsize_t; #else typedef ptrdiff_t regoff_t; typedef size_t regsize_t; #endif typedef struct { unsigned int re_magic; #ifdef __cplusplus std::size_t re_nsub; /* number of parenthesized subexpressions */ #else size_t re_nsub; #endif const char* re_endp; /* end pointer for REG_PEND */ void* guts; /* none of your business :-) */ match_flag_type eflags; /* none of your business :-) */ } regex_tA; #ifndef BOOST_NO_WREGEX typedef struct { unsigned int re_magic; #ifdef __cplusplus std::size_t re_nsub; /* number of parenthesized subexpressions */ #else size_t re_nsub; #endif const wchar_t* re_endp; /* end pointer for REG_PEND */ void* guts; /* none of your business :-) */ match_flag_type eflags; /* none of your business :-) */ } regex_tW; #endif typedef struct { regoff_t rm_so; /* start of match */ regoff_t rm_eo; /* end of match */ } regmatch_t; /* regcomp() flags */ typedef enum{ REG_BASIC = 0000, REG_EXTENDED = 0001, REG_ICASE = 0002, REG_NOSUB = 0004, REG_NEWLINE = 0010, REG_NOSPEC = 0020, REG_PEND = 0040, REG_DUMP = 0200, REG_NOCOLLATE = 0400, REG_ESCAPE_IN_LISTS = 01000, REG_NEWLINE_ALT = 02000, REG_PERLEX = 04000, REG_PERL = REG_EXTENDED | REG_NOCOLLATE | REG_ESCAPE_IN_LISTS | REG_PERLEX, REG_AWK = REG_EXTENDED | REG_ESCAPE_IN_LISTS, REG_GREP = REG_BASIC | REG_NEWLINE_ALT, REG_EGREP = REG_EXTENDED | REG_NEWLINE_ALT, REG_ASSERT = 15, REG_INVARG = 16, REG_ATOI = 255, /* convert name to number (!) */ REG_ITOA = 0400 /* convert number to name (!) */ } reg_comp_flags; /* regexec() flags */ typedef enum{ REG_NOTBOL = 00001, REG_NOTEOL = 00002, REG_STARTEND = 00004 } reg_exec_flags; /* * POSIX error codes: */ typedef unsigned reg_error_t; typedef reg_error_t reg_errcode_t; /* backwards compatibility */ static const reg_error_t REG_NOERROR = 0; /* Success. */ static const reg_error_t REG_NOMATCH = 1; /* Didn't find a match (for regexec). */ /* POSIX regcomp return error codes. (In the order listed in the standard.) */ static const reg_error_t REG_BADPAT = 2; /* Invalid pattern. */ static const reg_error_t REG_ECOLLATE = 3; /* Undefined collating element. */ static const reg_error_t REG_ECTYPE = 4; /* Invalid character class name. */ static const reg_error_t REG_EESCAPE = 5; /* Trailing backslash. */ static const reg_error_t REG_ESUBREG = 6; /* Invalid back reference. */ static const reg_error_t REG_EBRACK = 7; /* Unmatched left bracket. */ static const reg_error_t REG_EPAREN = 8; /* Parenthesis imbalance. */ static const reg_error_t REG_EBRACE = 9; /* Unmatched \{. */ static const reg_error_t REG_BADBR = 10; /* Invalid contents of \{\}. */ static const reg_error_t REG_ERANGE = 11; /* Invalid range end. */ static const reg_error_t REG_ESPACE = 12; /* Ran out of memory. */ static const reg_error_t REG_BADRPT = 13; /* No preceding re for repetition op. */ static const reg_error_t REG_EEND = 14; /* unexpected end of expression */ static const reg_error_t REG_ESIZE = 15; /* expression too big */ static const reg_error_t REG_ERPAREN = 8; /* = REG_EPAREN : unmatched right parenthesis */ static const reg_error_t REG_EMPTY = 17; /* empty expression */ static const reg_error_t REG_E_MEMORY = 15; /* = REG_ESIZE : out of memory */ static const reg_error_t REG_ECOMPLEXITY = 18; /* complexity too high */ static const reg_error_t REG_ESTACK = 19; /* out of stack space */ static const reg_error_t REG_E_PERL = 20; /* Perl (?...) error */ static const reg_error_t REG_E_UNKNOWN = 21; /* unknown error */ static const reg_error_t REG_ENOSYS = 21; /* = REG_E_UNKNOWN : Reserved. */ BOOST_REGEX_DECL int BOOST_REGEX_CCALL regcompA(regex_tA*, const char*, int); BOOST_REGEX_DECL regsize_t BOOST_REGEX_CCALL regerrorA(int, const regex_tA*, char*, regsize_t); BOOST_REGEX_DECL int BOOST_REGEX_CCALL regexecA(const regex_tA*, const char*, regsize_t, regmatch_t*, int); BOOST_REGEX_DECL void BOOST_REGEX_CCALL regfreeA(regex_tA*); #ifndef BOOST_NO_WREGEX BOOST_REGEX_DECL int BOOST_REGEX_CCALL regcompW(regex_tW*, const wchar_t*, int); BOOST_REGEX_DECL regsize_t BOOST_REGEX_CCALL regerrorW(int, const regex_tW*, wchar_t*, regsize_t); BOOST_REGEX_DECL int BOOST_REGEX_CCALL regexecW(const regex_tW*, const wchar_t*, regsize_t, regmatch_t*, int); BOOST_REGEX_DECL void BOOST_REGEX_CCALL regfreeW(regex_tW*); #endif #ifdef UNICODE #define regcomp regcompW #define regerror regerrorW #define regexec regexecW #define regfree regfreeW #define regex_t regex_tW #else #define regcomp regcompA #define regerror regerrorA #define regexec regexecA #define regfree regfreeA #define regex_t regex_tA #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef __cplusplus } /* extern "C" */ } /* namespace */ #endif #endif /* include guard */ ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/error_type.hpp ================================================ /* * * Copyright (c) 2003-2005 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE error_type.hpp * VERSION see * DESCRIPTION: Declares regular expression error type enumerator. */ #ifndef BOOST_REGEX_ERROR_TYPE_HPP #define BOOST_REGEX_ERROR_TYPE_HPP #ifdef __cplusplus namespace boost{ #endif #ifdef __cplusplus namespace regex_constants{ enum error_type{ error_ok = 0, /* not used */ error_no_match = 1, /* not used */ error_bad_pattern = 2, error_collate = 3, error_ctype = 4, error_escape = 5, error_backref = 6, error_brack = 7, error_paren = 8, error_brace = 9, error_badbrace = 10, error_range = 11, error_space = 12, error_badrepeat = 13, error_end = 14, /* not used */ error_size = 15, error_right_paren = 16, /* not used */ error_empty = 17, error_complexity = 18, error_stack = 19, error_perl_extension = 20, error_unknown = 21 }; } } #endif /* __cplusplus */ #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/icu.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE icu.hpp * VERSION see * DESCRIPTION: Unicode regular expressions on top of the ICU Library. */ #ifndef BOOST_REGEX_ICU_V4_HPP #define BOOST_REGEX_ICU_V4_HPP #include #include #include #include #include #include #include #include #include #ifdef BOOST_MSVC #pragma warning (push) #pragma warning (disable: 4251) #endif namespace boost { namespace BOOST_REGEX_DETAIL_NS { // // Implementation details: // class icu_regex_traits_implementation { typedef UChar32 char_type; typedef std::size_t size_type; typedef std::vector string_type; typedef U_NAMESPACE_QUALIFIER Locale locale_type; typedef boost::uint_least32_t char_class_type; public: icu_regex_traits_implementation(const U_NAMESPACE_QUALIFIER Locale& l) : m_locale(l) { UErrorCode success = U_ZERO_ERROR; m_collator.reset(U_NAMESPACE_QUALIFIER Collator::createInstance(l, success)); if (U_SUCCESS(success) == 0) init_error(); m_collator->setStrength(U_NAMESPACE_QUALIFIER Collator::IDENTICAL); success = U_ZERO_ERROR; m_primary_collator.reset(U_NAMESPACE_QUALIFIER Collator::createInstance(l, success)); if (U_SUCCESS(success) == 0) init_error(); m_primary_collator->setStrength(U_NAMESPACE_QUALIFIER Collator::PRIMARY); } U_NAMESPACE_QUALIFIER Locale getloc()const { return m_locale; } string_type do_transform(const char_type* p1, const char_type* p2, const U_NAMESPACE_QUALIFIER Collator* pcoll) const { // TODO make thread safe!!!! : typedef u32_to_u16_iterator itt; itt i(p1), j(p2); #ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS std::vector< ::UChar> t(i, j); #else std::vector< ::UChar> t; while (i != j) t.push_back(*i++); #endif ::uint8_t result[100]; ::int32_t len; if (!t.empty()) len = pcoll->getSortKey(&*t.begin(), static_cast< ::int32_t>(t.size()), result, sizeof(result)); else len = pcoll->getSortKey(static_cast(0), static_cast< ::int32_t>(0), result, sizeof(result)); if (std::size_t(len) > sizeof(result)) { scoped_array< ::uint8_t> presult(new ::uint8_t[len + 1]); if (!t.empty()) len = pcoll->getSortKey(&*t.begin(), static_cast< ::int32_t>(t.size()), presult.get(), len + 1); else len = pcoll->getSortKey(static_cast(0), static_cast< ::int32_t>(0), presult.get(), len + 1); if ((0 == presult[len - 1]) && (len > 1)) --len; #ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS return string_type(presult.get(), presult.get() + len); #else string_type sresult; ::uint8_t const* ia = presult.get(); ::uint8_t const* ib = presult.get() + len; while (ia != ib) sresult.push_back(*ia++); return sresult; #endif } if ((0 == result[len - 1]) && (len > 1)) --len; #ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS return string_type(result, result + len); #else string_type sresult; ::uint8_t const* ia = result; ::uint8_t const* ib = result + len; while (ia != ib) sresult.push_back(*ia++); return sresult; #endif } string_type transform(const char_type* p1, const char_type* p2) const { return do_transform(p1, p2, m_collator.get()); } string_type transform_primary(const char_type* p1, const char_type* p2) const { return do_transform(p1, p2, m_primary_collator.get()); } private: void init_error() { std::runtime_error e("Could not initialize ICU resources"); boost::throw_exception(e); } U_NAMESPACE_QUALIFIER Locale m_locale; // The ICU locale that we're using boost::scoped_ptr< U_NAMESPACE_QUALIFIER Collator> m_collator; // The full collation object boost::scoped_ptr< U_NAMESPACE_QUALIFIER Collator> m_primary_collator; // The primary collation object }; inline boost::shared_ptr get_icu_regex_traits_implementation(const U_NAMESPACE_QUALIFIER Locale& loc) { return boost::shared_ptr(new icu_regex_traits_implementation(loc)); } } class icu_regex_traits { public: typedef UChar32 char_type; typedef std::size_t size_type; typedef std::vector string_type; typedef U_NAMESPACE_QUALIFIER Locale locale_type; #ifdef BOOST_NO_INT64_T typedef std::bitset<64> char_class_type; #else typedef boost::uint64_t char_class_type; #endif struct boost_extensions_tag {}; icu_regex_traits() : m_pimpl(BOOST_REGEX_DETAIL_NS::get_icu_regex_traits_implementation(U_NAMESPACE_QUALIFIER Locale())) { } static size_type length(const char_type* p) { size_type result = 0; while (*p) { ++p; ++result; } return result; } ::boost::regex_constants::syntax_type syntax_type(char_type c)const { return ((c < 0x7f) && (c > 0)) ? BOOST_REGEX_DETAIL_NS::get_default_syntax_type(static_cast(c)) : regex_constants::syntax_char; } ::boost::regex_constants::escape_syntax_type escape_syntax_type(char_type c) const { return ((c < 0x7f) && (c > 0)) ? BOOST_REGEX_DETAIL_NS::get_default_escape_syntax_type(static_cast(c)) : regex_constants::syntax_char; } char_type translate(char_type c) const { return c; } char_type translate_nocase(char_type c) const { return ::u_foldCase(c, U_FOLD_CASE_DEFAULT); } char_type translate(char_type c, bool icase) const { return icase ? translate_nocase(c) : translate(c); } char_type tolower(char_type c) const { return ::u_tolower(c); } char_type toupper(char_type c) const { return ::u_toupper(c); } string_type transform(const char_type* p1, const char_type* p2) const { return m_pimpl->transform(p1, p2); } string_type transform_primary(const char_type* p1, const char_type* p2) const { return m_pimpl->transform_primary(p1, p2); } char_class_type lookup_classname(const char_type* p1, const char_type* p2) const { static const char_class_type mask_blank = char_class_type(1) << offset_blank; static const char_class_type mask_space = char_class_type(1) << offset_space; static const char_class_type mask_xdigit = char_class_type(1) << offset_xdigit; static const char_class_type mask_underscore = char_class_type(1) << offset_underscore; static const char_class_type mask_unicode = char_class_type(1) << offset_unicode; static const char_class_type mask_any = char_class_type(1) << offset_any; static const char_class_type mask_ascii = char_class_type(1) << offset_ascii; static const char_class_type mask_horizontal = char_class_type(1) << offset_horizontal; static const char_class_type mask_vertical = char_class_type(1) << offset_vertical; static const char_class_type masks[] = { 0, U_GC_L_MASK | U_GC_ND_MASK, U_GC_L_MASK, mask_blank, U_GC_CC_MASK | U_GC_CF_MASK | U_GC_ZL_MASK | U_GC_ZP_MASK, U_GC_ND_MASK, U_GC_ND_MASK, (0x3FFFFFFFu) & ~(U_GC_CC_MASK | U_GC_CF_MASK | U_GC_CS_MASK | U_GC_CN_MASK | U_GC_Z_MASK), mask_horizontal, U_GC_LL_MASK, U_GC_LL_MASK, ~(U_GC_C_MASK), U_GC_P_MASK, char_class_type(U_GC_Z_MASK) | mask_space, char_class_type(U_GC_Z_MASK) | mask_space, U_GC_LU_MASK, mask_unicode, U_GC_LU_MASK, mask_vertical, char_class_type(U_GC_L_MASK | U_GC_ND_MASK | U_GC_MN_MASK) | mask_underscore, char_class_type(U_GC_L_MASK | U_GC_ND_MASK | U_GC_MN_MASK) | mask_underscore, char_class_type(U_GC_ND_MASK) | mask_xdigit, }; int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); if (idx >= 0) return masks[idx + 1]; char_class_type result = lookup_icu_mask(p1, p2); if (result != 0) return result; if (idx < 0) { string_type s(p1, p2); string_type::size_type i = 0; while (i < s.size()) { s[i] = static_cast((::u_tolower)(s[i])); if (::u_isspace(s[i]) || (s[i] == '-') || (s[i] == '_')) s.erase(s.begin() + i, s.begin() + i + 1); else { s[i] = static_cast((::u_tolower)(s[i])); ++i; } } if (!s.empty()) idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size()); if (idx >= 0) return masks[idx + 1]; if (!s.empty()) result = lookup_icu_mask(&*s.begin(), &*s.begin() + s.size()); if (result != 0) return result; } BOOST_ASSERT(std::size_t(idx + 1) < sizeof(masks) / sizeof(masks[0])); return masks[idx + 1]; } string_type lookup_collatename(const char_type* p1, const char_type* p2) const { string_type result; #ifdef BOOST_NO_CXX98_BINDERS if (std::find_if(p1, p2, std::bind(std::greater< ::UChar32>(), std::placeholders::_1, 0x7f)) == p2) #else if (std::find_if(p1, p2, std::bind2nd(std::greater< ::UChar32>(), 0x7f)) == p2) #endif { #ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS std::string s(p1, p2); #else std::string s; const char_type* p3 = p1; while (p3 != p2) s.append(1, *p3++); #endif // Try Unicode name: UErrorCode err = U_ZERO_ERROR; UChar32 c = ::u_charFromName(U_UNICODE_CHAR_NAME, s.c_str(), &err); if (U_SUCCESS(err)) { result.push_back(c); return result; } // Try Unicode-extended name: err = U_ZERO_ERROR; c = ::u_charFromName(U_EXTENDED_CHAR_NAME, s.c_str(), &err); if (U_SUCCESS(err)) { result.push_back(c); return result; } // try POSIX name: s = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(s); #ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS result.assign(s.begin(), s.end()); #else result.clear(); std::string::const_iterator si, sj; si = s.begin(); sj = s.end(); while (si != sj) result.push_back(*si++); #endif } if (result.empty() && (p2 - p1 == 1)) result.push_back(*p1); return result; } bool isctype(char_type c, char_class_type f) const { static const char_class_type mask_blank = char_class_type(1) << offset_blank; static const char_class_type mask_space = char_class_type(1) << offset_space; static const char_class_type mask_xdigit = char_class_type(1) << offset_xdigit; static const char_class_type mask_underscore = char_class_type(1) << offset_underscore; static const char_class_type mask_unicode = char_class_type(1) << offset_unicode; static const char_class_type mask_any = char_class_type(1) << offset_any; static const char_class_type mask_ascii = char_class_type(1) << offset_ascii; static const char_class_type mask_horizontal = char_class_type(1) << offset_horizontal; static const char_class_type mask_vertical = char_class_type(1) << offset_vertical; // check for standard catagories first: char_class_type m = char_class_type(static_cast(1) << u_charType(c)); if ((m & f) != 0) return true; // now check for special cases: if (((f & mask_blank) != 0) && u_isblank(c)) return true; if (((f & mask_space) != 0) && u_isspace(c)) return true; if (((f & mask_xdigit) != 0) && (u_digit(c, 16) >= 0)) return true; if (((f & mask_unicode) != 0) && (c >= 0x100)) return true; if (((f & mask_underscore) != 0) && (c == '_')) return true; if (((f & mask_any) != 0) && (c <= 0x10FFFF)) return true; if (((f & mask_ascii) != 0) && (c <= 0x7F)) return true; if (((f & mask_vertical) != 0) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == static_cast('\v')) || (m == U_GC_ZL_MASK) || (m == U_GC_ZP_MASK))) return true; if (((f & mask_horizontal) != 0) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && u_isspace(c) && (c != static_cast('\v'))) return true; return false; } boost::intmax_t toi(const char_type*& p1, const char_type* p2, int radix)const { return BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this); } int value(char_type c, int radix)const { return u_digit(c, static_cast< ::int8_t>(radix)); } locale_type imbue(locale_type l) { locale_type result(m_pimpl->getloc()); m_pimpl = BOOST_REGEX_DETAIL_NS::get_icu_regex_traits_implementation(l); return result; } locale_type getloc()const { return locale_type(); } std::string error_string(::boost::regex_constants::error_type n) const { return BOOST_REGEX_DETAIL_NS::get_default_error_string(n); } private: icu_regex_traits(const icu_regex_traits&); icu_regex_traits& operator=(const icu_regex_traits&); // // define the bitmasks offsets we need for additional character properties: // enum { offset_blank = U_CHAR_CATEGORY_COUNT, offset_space = U_CHAR_CATEGORY_COUNT + 1, offset_xdigit = U_CHAR_CATEGORY_COUNT + 2, offset_underscore = U_CHAR_CATEGORY_COUNT + 3, offset_unicode = U_CHAR_CATEGORY_COUNT + 4, offset_any = U_CHAR_CATEGORY_COUNT + 5, offset_ascii = U_CHAR_CATEGORY_COUNT + 6, offset_horizontal = U_CHAR_CATEGORY_COUNT + 7, offset_vertical = U_CHAR_CATEGORY_COUNT + 8 }; static char_class_type lookup_icu_mask(const ::UChar32* p1, const ::UChar32* p2) { static const char_class_type mask_blank = char_class_type(1) << offset_blank; static const char_class_type mask_space = char_class_type(1) << offset_space; static const char_class_type mask_xdigit = char_class_type(1) << offset_xdigit; static const char_class_type mask_underscore = char_class_type(1) << offset_underscore; static const char_class_type mask_unicode = char_class_type(1) << offset_unicode; static const char_class_type mask_any = char_class_type(1) << offset_any; static const char_class_type mask_ascii = char_class_type(1) << offset_ascii; static const char_class_type mask_horizontal = char_class_type(1) << offset_horizontal; static const char_class_type mask_vertical = char_class_type(1) << offset_vertical; static const ::UChar32 prop_name_table[] = { /* any */ 'a', 'n', 'y', /* ascii */ 'a', 's', 'c', 'i', 'i', /* assigned */ 'a', 's', 's', 'i', 'g', 'n', 'e', 'd', /* c* */ 'c', '*', /* cc */ 'c', 'c', /* cf */ 'c', 'f', /* closepunctuation */ 'c', 'l', 'o', 's', 'e', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* cn */ 'c', 'n', /* co */ 'c', 'o', /* connectorpunctuation */ 'c', 'o', 'n', 'n', 'e', 'c', 't', 'o', 'r', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* control */ 'c', 'o', 'n', 't', 'r', 'o', 'l', /* cs */ 'c', 's', /* currencysymbol */ 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 's', 'y', 'm', 'b', 'o', 'l', /* dashpunctuation */ 'd', 'a', 's', 'h', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* decimaldigitnumber */ 'd', 'e', 'c', 'i', 'm', 'a', 'l', 'd', 'i', 'g', 'i', 't', 'n', 'u', 'm', 'b', 'e', 'r', /* enclosingmark */ 'e', 'n', 'c', 'l', 'o', 's', 'i', 'n', 'g', 'm', 'a', 'r', 'k', /* finalpunctuation */ 'f', 'i', 'n', 'a', 'l', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* format */ 'f', 'o', 'r', 'm', 'a', 't', /* initialpunctuation */ 'i', 'n', 'i', 't', 'i', 'a', 'l', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* l* */ 'l', '*', /* letter */ 'l', 'e', 't', 't', 'e', 'r', /* letternumber */ 'l', 'e', 't', 't', 'e', 'r', 'n', 'u', 'm', 'b', 'e', 'r', /* lineseparator */ 'l', 'i', 'n', 'e', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', /* ll */ 'l', 'l', /* lm */ 'l', 'm', /* lo */ 'l', 'o', /* lowercaseletter */ 'l', 'o', 'w', 'e', 'r', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r', /* lt */ 'l', 't', /* lu */ 'l', 'u', /* m* */ 'm', '*', /* mark */ 'm', 'a', 'r', 'k', /* mathsymbol */ 'm', 'a', 't', 'h', 's', 'y', 'm', 'b', 'o', 'l', /* mc */ 'm', 'c', /* me */ 'm', 'e', /* mn */ 'm', 'n', /* modifierletter */ 'm', 'o', 'd', 'i', 'f', 'i', 'e', 'r', 'l', 'e', 't', 't', 'e', 'r', /* modifiersymbol */ 'm', 'o', 'd', 'i', 'f', 'i', 'e', 'r', 's', 'y', 'm', 'b', 'o', 'l', /* n* */ 'n', '*', /* nd */ 'n', 'd', /* nl */ 'n', 'l', /* no */ 'n', 'o', /* nonspacingmark */ 'n', 'o', 'n', 's', 'p', 'a', 'c', 'i', 'n', 'g', 'm', 'a', 'r', 'k', /* notassigned */ 'n', 'o', 't', 'a', 's', 's', 'i', 'g', 'n', 'e', 'd', /* number */ 'n', 'u', 'm', 'b', 'e', 'r', /* openpunctuation */ 'o', 'p', 'e', 'n', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* other */ 'o', 't', 'h', 'e', 'r', /* otherletter */ 'o', 't', 'h', 'e', 'r', 'l', 'e', 't', 't', 'e', 'r', /* othernumber */ 'o', 't', 'h', 'e', 'r', 'n', 'u', 'm', 'b', 'e', 'r', /* otherpunctuation */ 'o', 't', 'h', 'e', 'r', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* othersymbol */ 'o', 't', 'h', 'e', 'r', 's', 'y', 'm', 'b', 'o', 'l', /* p* */ 'p', '*', /* paragraphseparator */ 'p', 'a', 'r', 'a', 'g', 'r', 'a', 'p', 'h', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', /* pc */ 'p', 'c', /* pd */ 'p', 'd', /* pe */ 'p', 'e', /* pf */ 'p', 'f', /* pi */ 'p', 'i', /* po */ 'p', 'o', /* privateuse */ 'p', 'r', 'i', 'v', 'a', 't', 'e', 'u', 's', 'e', /* ps */ 'p', 's', /* punctuation */ 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* s* */ 's', '*', /* sc */ 's', 'c', /* separator */ 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', /* sk */ 's', 'k', /* sm */ 's', 'm', /* so */ 's', 'o', /* spaceseparator */ 's', 'p', 'a', 'c', 'e', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', /* spacingcombiningmark */ 's', 'p', 'a', 'c', 'i', 'n', 'g', 'c', 'o', 'm', 'b', 'i', 'n', 'i', 'n', 'g', 'm', 'a', 'r', 'k', /* surrogate */ 's', 'u', 'r', 'r', 'o', 'g', 'a', 't', 'e', /* symbol */ 's', 'y', 'm', 'b', 'o', 'l', /* titlecase */ 't', 'i', 't', 'l', 'e', 'c', 'a', 's', 'e', /* titlecaseletter */ 't', 'i', 't', 'l', 'e', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r', /* uppercaseletter */ 'u', 'p', 'p', 'e', 'r', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r', /* z* */ 'z', '*', /* zl */ 'z', 'l', /* zp */ 'z', 'p', /* zs */ 'z', 's', }; static const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32> range_data[] = { { prop_name_table + 0, prop_name_table + 3, }, // any { prop_name_table + 3, prop_name_table + 8, }, // ascii { prop_name_table + 8, prop_name_table + 16, }, // assigned { prop_name_table + 16, prop_name_table + 18, }, // c* { prop_name_table + 18, prop_name_table + 20, }, // cc { prop_name_table + 20, prop_name_table + 22, }, // cf { prop_name_table + 22, prop_name_table + 38, }, // closepunctuation { prop_name_table + 38, prop_name_table + 40, }, // cn { prop_name_table + 40, prop_name_table + 42, }, // co { prop_name_table + 42, prop_name_table + 62, }, // connectorpunctuation { prop_name_table + 62, prop_name_table + 69, }, // control { prop_name_table + 69, prop_name_table + 71, }, // cs { prop_name_table + 71, prop_name_table + 85, }, // currencysymbol { prop_name_table + 85, prop_name_table + 100, }, // dashpunctuation { prop_name_table + 100, prop_name_table + 118, }, // decimaldigitnumber { prop_name_table + 118, prop_name_table + 131, }, // enclosingmark { prop_name_table + 131, prop_name_table + 147, }, // finalpunctuation { prop_name_table + 147, prop_name_table + 153, }, // format { prop_name_table + 153, prop_name_table + 171, }, // initialpunctuation { prop_name_table + 171, prop_name_table + 173, }, // l* { prop_name_table + 173, prop_name_table + 179, }, // letter { prop_name_table + 179, prop_name_table + 191, }, // letternumber { prop_name_table + 191, prop_name_table + 204, }, // lineseparator { prop_name_table + 204, prop_name_table + 206, }, // ll { prop_name_table + 206, prop_name_table + 208, }, // lm { prop_name_table + 208, prop_name_table + 210, }, // lo { prop_name_table + 210, prop_name_table + 225, }, // lowercaseletter { prop_name_table + 225, prop_name_table + 227, }, // lt { prop_name_table + 227, prop_name_table + 229, }, // lu { prop_name_table + 229, prop_name_table + 231, }, // m* { prop_name_table + 231, prop_name_table + 235, }, // mark { prop_name_table + 235, prop_name_table + 245, }, // mathsymbol { prop_name_table + 245, prop_name_table + 247, }, // mc { prop_name_table + 247, prop_name_table + 249, }, // me { prop_name_table + 249, prop_name_table + 251, }, // mn { prop_name_table + 251, prop_name_table + 265, }, // modifierletter { prop_name_table + 265, prop_name_table + 279, }, // modifiersymbol { prop_name_table + 279, prop_name_table + 281, }, // n* { prop_name_table + 281, prop_name_table + 283, }, // nd { prop_name_table + 283, prop_name_table + 285, }, // nl { prop_name_table + 285, prop_name_table + 287, }, // no { prop_name_table + 287, prop_name_table + 301, }, // nonspacingmark { prop_name_table + 301, prop_name_table + 312, }, // notassigned { prop_name_table + 312, prop_name_table + 318, }, // number { prop_name_table + 318, prop_name_table + 333, }, // openpunctuation { prop_name_table + 333, prop_name_table + 338, }, // other { prop_name_table + 338, prop_name_table + 349, }, // otherletter { prop_name_table + 349, prop_name_table + 360, }, // othernumber { prop_name_table + 360, prop_name_table + 376, }, // otherpunctuation { prop_name_table + 376, prop_name_table + 387, }, // othersymbol { prop_name_table + 387, prop_name_table + 389, }, // p* { prop_name_table + 389, prop_name_table + 407, }, // paragraphseparator { prop_name_table + 407, prop_name_table + 409, }, // pc { prop_name_table + 409, prop_name_table + 411, }, // pd { prop_name_table + 411, prop_name_table + 413, }, // pe { prop_name_table + 413, prop_name_table + 415, }, // pf { prop_name_table + 415, prop_name_table + 417, }, // pi { prop_name_table + 417, prop_name_table + 419, }, // po { prop_name_table + 419, prop_name_table + 429, }, // privateuse { prop_name_table + 429, prop_name_table + 431, }, // ps { prop_name_table + 431, prop_name_table + 442, }, // punctuation { prop_name_table + 442, prop_name_table + 444, }, // s* { prop_name_table + 444, prop_name_table + 446, }, // sc { prop_name_table + 446, prop_name_table + 455, }, // separator { prop_name_table + 455, prop_name_table + 457, }, // sk { prop_name_table + 457, prop_name_table + 459, }, // sm { prop_name_table + 459, prop_name_table + 461, }, // so { prop_name_table + 461, prop_name_table + 475, }, // spaceseparator { prop_name_table + 475, prop_name_table + 495, }, // spacingcombiningmark { prop_name_table + 495, prop_name_table + 504, }, // surrogate { prop_name_table + 504, prop_name_table + 510, }, // symbol { prop_name_table + 510, prop_name_table + 519, }, // titlecase { prop_name_table + 519, prop_name_table + 534, }, // titlecaseletter { prop_name_table + 534, prop_name_table + 549, }, // uppercaseletter { prop_name_table + 549, prop_name_table + 551, }, // z* { prop_name_table + 551, prop_name_table + 553, }, // zl { prop_name_table + 553, prop_name_table + 555, }, // zp { prop_name_table + 555, prop_name_table + 557, }, // zs }; static const icu_regex_traits::char_class_type icu_class_map[] = { mask_any, // any mask_ascii, // ascii (0x3FFFFFFFu) & ~(U_GC_CN_MASK), // assigned U_GC_C_MASK, // c* U_GC_CC_MASK, // cc U_GC_CF_MASK, // cf U_GC_PE_MASK, // closepunctuation U_GC_CN_MASK, // cn U_GC_CO_MASK, // co U_GC_PC_MASK, // connectorpunctuation U_GC_CC_MASK, // control U_GC_CS_MASK, // cs U_GC_SC_MASK, // currencysymbol U_GC_PD_MASK, // dashpunctuation U_GC_ND_MASK, // decimaldigitnumber U_GC_ME_MASK, // enclosingmark U_GC_PF_MASK, // finalpunctuation U_GC_CF_MASK, // format U_GC_PI_MASK, // initialpunctuation U_GC_L_MASK, // l* U_GC_L_MASK, // letter U_GC_NL_MASK, // letternumber U_GC_ZL_MASK, // lineseparator U_GC_LL_MASK, // ll U_GC_LM_MASK, // lm U_GC_LO_MASK, // lo U_GC_LL_MASK, // lowercaseletter U_GC_LT_MASK, // lt U_GC_LU_MASK, // lu U_GC_M_MASK, // m* U_GC_M_MASK, // mark U_GC_SM_MASK, // mathsymbol U_GC_MC_MASK, // mc U_GC_ME_MASK, // me U_GC_MN_MASK, // mn U_GC_LM_MASK, // modifierletter U_GC_SK_MASK, // modifiersymbol U_GC_N_MASK, // n* U_GC_ND_MASK, // nd U_GC_NL_MASK, // nl U_GC_NO_MASK, // no U_GC_MN_MASK, // nonspacingmark U_GC_CN_MASK, // notassigned U_GC_N_MASK, // number U_GC_PS_MASK, // openpunctuation U_GC_C_MASK, // other U_GC_LO_MASK, // otherletter U_GC_NO_MASK, // othernumber U_GC_PO_MASK, // otherpunctuation U_GC_SO_MASK, // othersymbol U_GC_P_MASK, // p* U_GC_ZP_MASK, // paragraphseparator U_GC_PC_MASK, // pc U_GC_PD_MASK, // pd U_GC_PE_MASK, // pe U_GC_PF_MASK, // pf U_GC_PI_MASK, // pi U_GC_PO_MASK, // po U_GC_CO_MASK, // privateuse U_GC_PS_MASK, // ps U_GC_P_MASK, // punctuation U_GC_S_MASK, // s* U_GC_SC_MASK, // sc U_GC_Z_MASK, // separator U_GC_SK_MASK, // sk U_GC_SM_MASK, // sm U_GC_SO_MASK, // so U_GC_ZS_MASK, // spaceseparator U_GC_MC_MASK, // spacingcombiningmark U_GC_CS_MASK, // surrogate U_GC_S_MASK, // symbol U_GC_LT_MASK, // titlecase U_GC_LT_MASK, // titlecaseletter U_GC_LU_MASK, // uppercaseletter U_GC_Z_MASK, // z* U_GC_ZL_MASK, // zl U_GC_ZP_MASK, // zp U_GC_ZS_MASK, // zs }; const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* ranges_begin = range_data; const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* ranges_end = range_data + (sizeof(range_data) / sizeof(range_data[0])); BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32> t = { p1, p2, }; const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* p = std::lower_bound(ranges_begin, ranges_end, t); if ((p != ranges_end) && (t == *p)) return icu_class_map[p - ranges_begin]; return 0; } boost::shared_ptr< ::boost::BOOST_REGEX_DETAIL_NS::icu_regex_traits_implementation> m_pimpl; }; } // namespace boost namespace boost { // types: typedef basic_regex< ::UChar32, icu_regex_traits> u32regex; typedef match_results u32match; typedef match_results u16match; // // Construction of 32-bit regex types from UTF-8 and UTF-16 primitives: // namespace BOOST_REGEX_DETAIL_NS { #if !defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(__IBMCPP__) template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const boost::mpl::int_<1>*) { typedef boost::u8_to_u32_iterator conv_type; return u32regex(conv_type(i, i, j), conv_type(j, i, j), opt); } template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const boost::mpl::int_<2>*) { typedef boost::u16_to_u32_iterator conv_type; return u32regex(conv_type(i, i, j), conv_type(j, i, j), opt); } template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const boost::mpl::int_<4>*) { return u32regex(i, j, opt); } #else template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const boost::mpl::int_<1>*) { typedef boost::u8_to_u32_iterator conv_type; typedef std::vector vector_type; vector_type v; conv_type a(i, i, j), b(j, i, j); while (a != b) { v.push_back(*a); ++a; } if (v.size()) return u32regex(&*v.begin(), v.size(), opt); return u32regex(static_cast(0), static_cast(0), opt); } template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const boost::mpl::int_<2>*) { typedef boost::u16_to_u32_iterator conv_type; typedef std::vector vector_type; vector_type v; conv_type a(i, i, j), b(j, i, j); while (a != b) { v.push_back(*a); ++a; } if (v.size()) return u32regex(&*v.begin(), v.size(), opt); return u32regex(static_cast(0), static_cast(0), opt); } template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const boost::mpl::int_<4>*) { typedef std::vector vector_type; vector_type v; while (i != j) { v.push_back((UChar32)(*i)); ++i; } if (v.size()) return u32regex(&*v.begin(), v.size(), opt); return u32regex(static_cast(0), static_cast(0), opt); } #endif } // BOOST_REGEX_UCHAR_IS_WCHAR_T // // Source inspection of unicode/umachine.h in ICU version 59 indicates that: // // On version 59, UChar is always char16_t in C++ mode (and uint16_t in C mode) // // On earlier versions, the logic is // // #if U_SIZEOF_WCHAR_T==2 // typedef wchar_t OldUChar; // #elif defined(__CHAR16_TYPE__) // typedef __CHAR16_TYPE__ OldUChar; // #else // typedef uint16_t OldUChar; // #endif // // That is, UChar is wchar_t only on versions below 59, when U_SIZEOF_WCHAR_T==2 // // Hence, #define BOOST_REGEX_UCHAR_IS_WCHAR_T (U_ICU_VERSION_MAJOR_NUM < 59 && U_SIZEOF_WCHAR_T == 2) #if BOOST_REGEX_UCHAR_IS_WCHAR_T BOOST_STATIC_ASSERT((boost::is_same::value)); #else BOOST_STATIC_ASSERT(!(boost::is_same::value)); #endif // // Construction from an iterator pair: // template inline u32regex make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(i, j, opt, static_cast const*>(0)); } // // construction from UTF-8 nul-terminated strings: // inline u32regex make_u32regex(const char* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::strlen(p), opt, static_cast const*>(0)); } inline u32regex make_u32regex(const unsigned char* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::strlen(reinterpret_cast(p)), opt, static_cast const*>(0)); } // // construction from UTF-16 nul-terminated strings: // #ifndef BOOST_NO_WREGEX inline u32regex make_u32regex(const wchar_t* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::wcslen(p), opt, static_cast const*>(0)); } #endif #if !BOOST_REGEX_UCHAR_IS_WCHAR_T inline u32regex make_u32regex(const UChar* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + u_strlen(p), opt, static_cast const*>(0)); } #endif // // construction from basic_string class-template: // template inline u32regex make_u32regex(const std::basic_string& s, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(s.begin(), s.end(), opt, static_cast const*>(0)); } // // Construction from ICU string type: // inline u32regex make_u32regex(const U_NAMESPACE_QUALIFIER UnicodeString& s, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(s.getBuffer(), s.getBuffer() + s.length(), opt, static_cast const*>(0)); } // // regex_match overloads that widen the character type as appropriate: // namespace BOOST_REGEX_DETAIL_NS { template void copy_results(MR1& out, MR2 const& in, NSubs named_subs) { // copy results from an adapted MR2 match_results: out.set_size(in.size(), in.prefix().first.base(), in.suffix().second.base()); out.set_base(in.base().base()); out.set_named_subs(named_subs); for (int i = 0; i < (int)in.size(); ++i) { if (in[i].matched || !i) { out.set_first(in[i].first.base(), i); out.set_second(in[i].second.base(), i, in[i].matched); } } #ifdef BOOST_REGEX_MATCH_EXTRA // Copy full capture info as well: for (int i = 0; i < (int)in.size(); ++i) { if (in[i].captures().size()) { out[i].get_captures().assign(in[i].captures().size(), typename MR1::value_type()); for (int j = 0; j < (int)out[i].captures().size(); ++j) { out[i].get_captures()[j].first = in[i].captures()[j].first.base(); out[i].get_captures()[j].second = in[i].captures()[j].second.base(); out[i].get_captures()[j].matched = in[i].captures()[j].matched; } } } #endif } template inline bool do_regex_match(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, boost::mpl::int_<4> const*) { return ::boost::regex_match(first, last, m, e, flags); } template bool do_regex_match(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, boost::mpl::int_<2> const*) { typedef u16_to_u32_iterator conv_type; typedef match_results match_type; //typedef typename match_type::allocator_type alloc_type; match_type what; bool result = ::boost::regex_match(conv_type(first, first, last), conv_type(last, first, last), what, e, flags); // copy results across to m: if (result) copy_results(m, what, e.get_named_subs()); return result; } template bool do_regex_match(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, boost::mpl::int_<1> const*) { typedef u8_to_u32_iterator conv_type; typedef match_results match_type; //typedef typename match_type::allocator_type alloc_type; match_type what; bool result = ::boost::regex_match(conv_type(first, first, last), conv_type(last, first, last), what, e, flags); // copy results across to m: if (result) copy_results(m, what, e.get_named_subs()); return result; } } // namespace BOOST_REGEX_DETAIL_NS template inline bool u32regex_match(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(first, last, m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const UChar* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + u_strlen(p), m, e, flags, static_cast const*>(0)); } #if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX) inline bool u32regex_match(const wchar_t* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::wcslen(p), m, e, flags, static_cast const*>(0)); } #endif inline bool u32regex_match(const char* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::strlen(p), m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const unsigned char* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::strlen((const char*)p), m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const std::string& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast const*>(0)); } #ifndef BOOST_NO_STD_WSTRING inline bool u32regex_match(const std::wstring& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast const*>(0)); } #endif inline bool u32regex_match(const U_NAMESPACE_QUALIFIER UnicodeString& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, static_cast const*>(0)); } // // regex_match overloads that do not return what matched: // template inline bool u32regex_match(BidiIterator first, BidiIterator last, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(first, last, m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const UChar* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + u_strlen(p), m, e, flags, static_cast const*>(0)); } #if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX) inline bool u32regex_match(const wchar_t* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::wcslen(p), m, e, flags, static_cast const*>(0)); } #endif inline bool u32regex_match(const char* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::strlen(p), m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const unsigned char* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::strlen((const char*)p), m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const std::string& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast const*>(0)); } #ifndef BOOST_NO_STD_WSTRING inline bool u32regex_match(const std::wstring& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast const*>(0)); } #endif inline bool u32regex_match(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, static_cast const*>(0)); } // // regex_search overloads that widen the character type as appropriate: // namespace BOOST_REGEX_DETAIL_NS { template inline bool do_regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, BidiIterator base, boost::mpl::int_<4> const*) { return ::boost::regex_search(first, last, m, e, flags, base); } template bool do_regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, BidiIterator base, boost::mpl::int_<2> const*) { typedef u16_to_u32_iterator conv_type; typedef match_results match_type; //typedef typename match_type::allocator_type alloc_type; match_type what; bool result = ::boost::regex_search(conv_type(first, first, last), conv_type(last, first, last), what, e, flags, conv_type(base)); // copy results across to m: if (result) copy_results(m, what, e.get_named_subs()); return result; } template bool do_regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, BidiIterator base, boost::mpl::int_<1> const*) { typedef u8_to_u32_iterator conv_type; typedef match_results match_type; //typedef typename match_type::allocator_type alloc_type; match_type what; bool result = ::boost::regex_search(conv_type(first, first, last), conv_type(last, first, last), what, e, flags, conv_type(base)); // copy results across to m: if (result) copy_results(m, what, e.get_named_subs()); return result; } } template inline bool u32regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, first, static_cast const*>(0)); } template inline bool u32regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, BidiIterator base) { return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, base, static_cast const*>(0)); } inline bool u32regex_search(const UChar* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + u_strlen(p), m, e, flags, p, static_cast const*>(0)); } #if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX) inline bool u32regex_search(const wchar_t* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::wcslen(p), m, e, flags, p, static_cast const*>(0)); } #endif inline bool u32regex_search(const char* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::strlen(p), m, e, flags, p, static_cast const*>(0)); } inline bool u32regex_search(const unsigned char* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::strlen((const char*)p), m, e, flags, p, static_cast const*>(0)); } inline bool u32regex_search(const std::string& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast const*>(0)); } #ifndef BOOST_NO_STD_WSTRING inline bool u32regex_search(const std::wstring& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast const*>(0)); } #endif inline bool u32regex_search(const U_NAMESPACE_QUALIFIER UnicodeString& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, s.getBuffer(), static_cast const*>(0)); } template inline bool u32regex_search(BidiIterator first, BidiIterator last, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, first, static_cast const*>(0)); } inline bool u32regex_search(const UChar* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + u_strlen(p), m, e, flags, p, static_cast const*>(0)); } #if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX) inline bool u32regex_search(const wchar_t* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::wcslen(p), m, e, flags, p, static_cast const*>(0)); } #endif inline bool u32regex_search(const char* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::strlen(p), m, e, flags, p, static_cast const*>(0)); } inline bool u32regex_search(const unsigned char* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::strlen((const char*)p), m, e, flags, p, static_cast const*>(0)); } inline bool u32regex_search(const std::string& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast const*>(0)); } #ifndef BOOST_NO_STD_WSTRING inline bool u32regex_search(const std::wstring& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast const*>(0)); } #endif inline bool u32regex_search(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, s.getBuffer(), static_cast const*>(0)); } // // overloads for regex_replace with utf-8 and utf-16 data types: // namespace BOOST_REGEX_DETAIL_NS { template inline std::pair< boost::u8_to_u32_iterator, boost::u8_to_u32_iterator > make_utf32_seq(I i, I j, mpl::int_<1> const*) { return std::pair< boost::u8_to_u32_iterator, boost::u8_to_u32_iterator >(boost::u8_to_u32_iterator(i, i, j), boost::u8_to_u32_iterator(j, i, j)); } template inline std::pair< boost::u16_to_u32_iterator, boost::u16_to_u32_iterator > make_utf32_seq(I i, I j, mpl::int_<2> const*) { return std::pair< boost::u16_to_u32_iterator, boost::u16_to_u32_iterator >(boost::u16_to_u32_iterator(i, i, j), boost::u16_to_u32_iterator(j, i, j)); } template inline std::pair< I, I > make_utf32_seq(I i, I j, mpl::int_<4> const*) { return std::pair< I, I >(i, j); } template inline std::pair< boost::u8_to_u32_iterator, boost::u8_to_u32_iterator > make_utf32_seq(const charT* p, mpl::int_<1> const*) { std::size_t len = std::strlen((const char*)p); return std::pair< boost::u8_to_u32_iterator, boost::u8_to_u32_iterator >(boost::u8_to_u32_iterator(p, p, p + len), boost::u8_to_u32_iterator(p + len, p, p + len)); } template inline std::pair< boost::u16_to_u32_iterator, boost::u16_to_u32_iterator > make_utf32_seq(const charT* p, mpl::int_<2> const*) { std::size_t len = u_strlen((const UChar*)p); return std::pair< boost::u16_to_u32_iterator, boost::u16_to_u32_iterator >(boost::u16_to_u32_iterator(p, p, p + len), boost::u16_to_u32_iterator(p + len, p, p + len)); } template inline std::pair< const charT*, const charT* > make_utf32_seq(const charT* p, mpl::int_<4> const*) { return std::pair< const charT*, const charT* >(p, p + icu_regex_traits::length((UChar32 const*)p)); } template inline OutputIterator make_utf32_out(OutputIterator o, mpl::int_<4> const*) { return o; } template inline utf16_output_iterator make_utf32_out(OutputIterator o, mpl::int_<2> const*) { return o; } template inline utf8_output_iterator make_utf32_out(OutputIterator o, mpl::int_<1> const*) { return o; } template OutputIterator do_regex_replace(OutputIterator out, std::pair const& in, const u32regex& e, const std::pair& fmt, match_flag_type flags ) { // unfortunately we have to copy the format string in order to pass in onward: std::vector f; #ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS f.assign(fmt.first, fmt.second); #else f.clear(); I2 pos = fmt.first; while (pos != fmt.second) f.push_back(*pos++); #endif regex_iterator i(in.first, in.second, e, flags); regex_iterator j; if (i == j) { if (!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(in.first, in.second, out); } else { I1 last_m = in.first; while (i != j) { if (!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(i->prefix().first, i->prefix().second, out); if (!f.empty()) out = ::boost::BOOST_REGEX_DETAIL_NS::regex_format_imp(out, *i, &*f.begin(), &*f.begin() + f.size(), flags, e.get_traits()); else out = ::boost::BOOST_REGEX_DETAIL_NS::regex_format_imp(out, *i, static_cast(0), static_cast(0), flags, e.get_traits()); last_m = (*i)[0].second; if (flags & regex_constants::format_first_only) break; ++i; } if (!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(last_m, in.second, out); } return out; } template inline const BaseIterator& extract_output_base(const BaseIterator& b) { return b; } template inline BaseIterator extract_output_base(const utf8_output_iterator& b) { return b.base(); } template inline BaseIterator extract_output_base(const utf16_output_iterator& b) { return b.base(); } } // BOOST_REGEX_DETAIL_NS template inline OutputIterator u32regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const u32regex& e, const charT* fmt, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::extract_output_base ( BOOST_REGEX_DETAIL_NS::do_regex_replace( BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast const*>(0)), BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast const*>(0)), e, BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt, static_cast const*>(0)), flags) ); } template inline OutputIterator u32regex_replace(OutputIterator out, Iterator first, Iterator last, const u32regex& e, const std::basic_string& fmt, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::extract_output_base ( BOOST_REGEX_DETAIL_NS::do_regex_replace( BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast const*>(0)), BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast const*>(0)), e, BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.begin(), fmt.end(), static_cast const*>(0)), flags) ); } template inline OutputIterator u32regex_replace(OutputIterator out, Iterator first, Iterator last, const u32regex& e, const U_NAMESPACE_QUALIFIER UnicodeString& fmt, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::extract_output_base ( BOOST_REGEX_DETAIL_NS::do_regex_replace( BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast const*>(0)), BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast const*>(0)), e, BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.getBuffer(), fmt.getBuffer() + fmt.length(), static_cast const*>(0)), flags) ); } template std::basic_string u32regex_replace(const std::basic_string& s, const u32regex& e, const charT* fmt, match_flag_type flags = match_default) { std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); u32regex_replace(i, s.begin(), s.end(), e, fmt, flags); return result; } template std::basic_string u32regex_replace(const std::basic_string& s, const u32regex& e, const std::basic_string& fmt, match_flag_type flags = match_default) { std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); u32regex_replace(i, s.begin(), s.end(), e, fmt.c_str(), flags); return result; } namespace BOOST_REGEX_DETAIL_NS { class unicode_string_out_iterator { U_NAMESPACE_QUALIFIER UnicodeString* out; public: unicode_string_out_iterator(U_NAMESPACE_QUALIFIER UnicodeString& s) : out(&s) {} unicode_string_out_iterator& operator++() { return *this; } unicode_string_out_iterator& operator++(int) { return *this; } unicode_string_out_iterator& operator*() { return *this; } unicode_string_out_iterator& operator=(UChar v) { *out += v; return *this; } typedef std::ptrdiff_t difference_type; typedef UChar value_type; typedef value_type* pointer; typedef value_type& reference; typedef std::output_iterator_tag iterator_category; }; } inline U_NAMESPACE_QUALIFIER UnicodeString u32regex_replace(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const UChar* fmt, match_flag_type flags = match_default) { U_NAMESPACE_QUALIFIER UnicodeString result; BOOST_REGEX_DETAIL_NS::unicode_string_out_iterator i(result); u32regex_replace(i, s.getBuffer(), s.getBuffer() + s.length(), e, fmt, flags); return result; } inline U_NAMESPACE_QUALIFIER UnicodeString u32regex_replace(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const U_NAMESPACE_QUALIFIER UnicodeString& fmt, match_flag_type flags = match_default) { U_NAMESPACE_QUALIFIER UnicodeString result; BOOST_REGEX_DETAIL_NS::unicode_string_out_iterator i(result); BOOST_REGEX_DETAIL_NS::do_regex_replace( BOOST_REGEX_DETAIL_NS::make_utf32_out(i, static_cast const*>(0)), BOOST_REGEX_DETAIL_NS::make_utf32_seq(s.getBuffer(), s.getBuffer() + s.length(), static_cast const*>(0)), e, BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.getBuffer(), fmt.getBuffer() + fmt.length(), static_cast const*>(0)), flags); return result; } } // namespace boost. #ifdef BOOST_MSVC #pragma warning (pop) #endif #include #include #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/indexed_bit_flag.hpp ================================================ /* * * Copyright (c) 2020 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE basic_regex_parser.cpp * VERSION see * DESCRIPTION: Declares template class basic_regex_parser. */ #include #include #ifndef BOOST_REGEX_V4_INDEXED_BIT_FLAG_HPP #define BOOST_REGEX_V4_INDEXED_BIT_FLAG_HPP namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ class indexed_bit_flag { boost::uint64_t low_mask; std::set mask_set; public: indexed_bit_flag() : low_mask(0) {} void set(std::size_t i) { if (i < std::numeric_limits::digits - 1) low_mask |= static_cast(1u) << i; else mask_set.insert(i); } bool test(std::size_t i) { if (i < std::numeric_limits::digits - 1) return low_mask & static_cast(1u) << i ? true : false; else return mask_set.find(i) != mask_set.end(); } }; } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/iterator_category.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_match.hpp * VERSION see * DESCRIPTION: Iterator traits for selecting an iterator type as * an integral constant expression. */ #ifndef BOOST_REGEX_ITERATOR_CATEGORY_HPP #define BOOST_REGEX_ITERATOR_CATEGORY_HPP #include #include #include namespace boost{ namespace detail{ template struct is_random_imp { #ifndef BOOST_NO_STD_ITERATOR_TRAITS private: typedef typename std::iterator_traits::iterator_category cat; public: BOOST_STATIC_CONSTANT(bool, value = (::boost::is_convertible::value)); #else BOOST_STATIC_CONSTANT(bool, value = false); #endif }; template struct is_random_pointer_imp { BOOST_STATIC_CONSTANT(bool, value = true); }; template struct is_random_imp_selector { template struct rebind { typedef is_random_imp type; }; }; template <> struct is_random_imp_selector { template struct rebind { typedef is_random_pointer_imp type; }; }; } template struct is_random_access_iterator { private: typedef detail::is_random_imp_selector< ::boost::is_pointer::value> selector; typedef typename selector::template rebind bound_type; typedef typename bound_type::type answer; public: BOOST_STATIC_CONSTANT(bool, value = answer::value); }; #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION template const bool is_random_access_iterator::value; #endif } #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/iterator_traits.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE iterator_traits.cpp * VERSION see * DESCRIPTION: Declares iterator traits workarounds. */ #ifndef BOOST_REGEX_V4_ITERATOR_TRAITS_HPP #define BOOST_REGEX_V4_ITERATOR_TRAITS_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #if defined(BOOST_NO_STD_ITERATOR_TRAITS) template struct regex_iterator_traits { typedef typename T::iterator_category iterator_category; typedef typename T::value_type value_type; #if !defined(BOOST_NO_STD_ITERATOR) typedef typename T::difference_type difference_type; typedef typename T::pointer pointer; typedef typename T::reference reference; #else typedef std::ptrdiff_t difference_type; typedef value_type* pointer; typedef value_type& reference; #endif }; template struct pointer_iterator_traits { typedef std::ptrdiff_t difference_type; typedef T value_type; typedef T* pointer; typedef T& reference; typedef std::random_access_iterator_tag iterator_category; }; template struct const_pointer_iterator_traits { typedef std::ptrdiff_t difference_type; typedef T value_type; typedef const T* pointer; typedef const T& reference; typedef std::random_access_iterator_tag iterator_category; }; template<> struct regex_iterator_traits : pointer_iterator_traits{}; template<> struct regex_iterator_traits : const_pointer_iterator_traits{}; template<> struct regex_iterator_traits : pointer_iterator_traits{}; template<> struct regex_iterator_traits : const_pointer_iterator_traits{}; // // the follwoing are needed for ICU support: // template<> struct regex_iterator_traits : pointer_iterator_traits{}; template<> struct regex_iterator_traits : const_pointer_iterator_traits{}; template<> struct regex_iterator_traits : pointer_iterator_traits{}; template<> struct regex_iterator_traits : const_pointer_iterator_traits{}; #ifdef BOOST_REGEX_HAS_OTHER_WCHAR_T template<> struct regex_iterator_traits : pointer_iterator_traits{}; template<> struct regex_iterator_traits : const_pointer_iterator_traits{}; #endif #if defined(__SGI_STL_PORT) && defined(__STL_DEBUG) template<> struct regex_iterator_traits : pointer_iterator_traits{}; template<> struct regex_iterator_traits : const_pointer_iterator_traits{}; #ifndef BOOST_NO_STD_WSTRING template<> struct regex_iterator_traits : pointer_iterator_traits{}; template<> struct regex_iterator_traits : const_pointer_iterator_traits{}; #endif // BOOST_NO_WSTRING #endif // stport #else template struct regex_iterator_traits : public std::iterator_traits {}; #endif } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/match_flags.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE match_flags.hpp * VERSION see * DESCRIPTION: Declares match_flags type. */ #ifndef BOOST_REGEX_V4_MATCH_FLAGS #define BOOST_REGEX_V4_MATCH_FLAGS #ifdef __cplusplus # include #endif #ifdef __cplusplus namespace boost{ namespace regex_constants{ #endif #ifdef BOOST_MSVC #pragma warning(push) #if BOOST_MSVC >= 1800 #pragma warning(disable : 26812) #endif #endif typedef enum _match_flags { match_default = 0, match_not_bol = 1, /* first is not start of line */ match_not_eol = match_not_bol << 1, /* last is not end of line */ match_not_bob = match_not_eol << 1, /* first is not start of buffer */ match_not_eob = match_not_bob << 1, /* last is not end of buffer */ match_not_bow = match_not_eob << 1, /* first is not start of word */ match_not_eow = match_not_bow << 1, /* last is not end of word */ match_not_dot_newline = match_not_eow << 1, /* \n is not matched by '.' */ match_not_dot_null = match_not_dot_newline << 1, /* '\0' is not matched by '.' */ match_prev_avail = match_not_dot_null << 1, /* *--first is a valid expression */ match_init = match_prev_avail << 1, /* internal use */ match_any = match_init << 1, /* don't care what we match */ match_not_null = match_any << 1, /* string can't be null */ match_continuous = match_not_null << 1, /* each grep match must continue from */ /* uninterrupted from the previous one */ match_partial = match_continuous << 1, /* find partial matches */ match_stop = match_partial << 1, /* stop after first match (grep) V3 only */ match_not_initial_null = match_stop, /* don't match initial null, V4 only */ match_all = match_stop << 1, /* must find the whole of input even if match_any is set */ match_perl = match_all << 1, /* Use perl matching rules */ match_posix = match_perl << 1, /* Use POSIX matching rules */ match_nosubs = match_posix << 1, /* don't trap marked subs */ match_extra = match_nosubs << 1, /* include full capture information for repeated captures */ match_single_line = match_extra << 1, /* treat text as single line and ignore any \n's when matching ^ and $. */ match_unused1 = match_single_line << 1, /* unused */ match_unused2 = match_unused1 << 1, /* unused */ match_unused3 = match_unused2 << 1, /* unused */ match_max = match_unused3, format_perl = 0, /* perl style replacement */ format_default = 0, /* ditto. */ format_sed = match_max << 1, /* sed style replacement. */ format_all = format_sed << 1, /* enable all extensions to syntax. */ format_no_copy = format_all << 1, /* don't copy non-matching segments. */ format_first_only = format_no_copy << 1, /* Only replace first occurrence. */ format_is_if = format_first_only << 1, /* internal use only. */ format_literal = format_is_if << 1, /* treat string as a literal */ match_not_any = match_not_bol | match_not_eol | match_not_bob | match_not_eob | match_not_bow | match_not_eow | match_not_dot_newline | match_not_dot_null | match_prev_avail | match_init | match_not_null | match_continuous | match_partial | match_stop | match_not_initial_null | match_stop | match_all | match_perl | match_posix | match_nosubs | match_extra | match_single_line | match_unused1 | match_unused2 | match_unused3 | match_max | format_perl | format_default | format_sed | format_all | format_no_copy | format_first_only | format_is_if | format_literal } match_flags; #if defined(BOOST_BORLANDC) || (defined(_MSC_VER) && (_MSC_VER <= 1310)) typedef unsigned long match_flag_type; #else typedef match_flags match_flag_type; #ifdef __cplusplus inline match_flags operator&(match_flags m1, match_flags m2) { return static_cast(static_cast(m1) & static_cast(m2)); } inline match_flags operator|(match_flags m1, match_flags m2) { return static_cast(static_cast(m1) | static_cast(m2)); } inline match_flags operator^(match_flags m1, match_flags m2) { return static_cast(static_cast(m1) ^ static_cast(m2)); } inline match_flags operator~(match_flags m1) { return static_cast(~static_cast(m1)); } inline match_flags& operator&=(match_flags& m1, match_flags m2) { m1 = m1&m2; return m1; } inline match_flags& operator|=(match_flags& m1, match_flags m2) { m1 = m1|m2; return m1; } inline match_flags& operator^=(match_flags& m1, match_flags m2) { m1 = m1^m2; return m1; } #endif #endif #ifdef __cplusplus } /* namespace regex_constants */ /* * import names into boost for backwards compatibility: */ using regex_constants::match_flag_type; using regex_constants::match_default; using regex_constants::match_not_bol; using regex_constants::match_not_eol; using regex_constants::match_not_bob; using regex_constants::match_not_eob; using regex_constants::match_not_bow; using regex_constants::match_not_eow; using regex_constants::match_not_dot_newline; using regex_constants::match_not_dot_null; using regex_constants::match_prev_avail; /* using regex_constants::match_init; */ using regex_constants::match_any; using regex_constants::match_not_null; using regex_constants::match_continuous; using regex_constants::match_partial; /*using regex_constants::match_stop; */ using regex_constants::match_all; using regex_constants::match_perl; using regex_constants::match_posix; using regex_constants::match_nosubs; using regex_constants::match_extra; using regex_constants::match_single_line; /*using regex_constants::match_max; */ using regex_constants::format_all; using regex_constants::format_sed; using regex_constants::format_perl; using regex_constants::format_default; using regex_constants::format_no_copy; using regex_constants::format_first_only; /*using regex_constants::format_is_if;*/ #ifdef BOOST_MSVC #pragma warning(pop) #endif } /* namespace boost */ #endif /* __cplusplus */ #endif /* include guard */ ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/match_results.hpp ================================================ /* * * Copyright (c) 1998-2009 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE match_results.cpp * VERSION see * DESCRIPTION: Declares template class match_results. */ #ifndef BOOST_REGEX_V4_MATCH_RESULTS_HPP #define BOOST_REGEX_V4_MATCH_RESULTS_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable : 4251) #if BOOST_MSVC < 1700 # pragma warning(disable : 4231) #endif # if BOOST_MSVC < 1600 # pragma warning(disable : 4660) # endif #endif namespace BOOST_REGEX_DETAIL_NS{ class named_subexpressions; } template class match_results { private: #ifndef BOOST_NO_STD_ALLOCATOR typedef std::vector, Allocator> vector_type; #else typedef std::vector > vector_type; #endif public: typedef sub_match value_type; #ifndef BOOST_NO_CXX11_ALLOCATOR typedef typename std::allocator_traits::value_type const & const_reference; #elif !defined(BOOST_NO_STD_ALLOCATOR) && !(defined(BOOST_MSVC) && defined(_STLPORT_VERSION)) typedef typename Allocator::const_reference const_reference; #else typedef const value_type& const_reference; #endif typedef const_reference reference; typedef typename vector_type::const_iterator const_iterator; typedef const_iterator iterator; typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits< BidiIterator>::difference_type difference_type; #ifdef BOOST_NO_CXX11_ALLOCATOR typedef typename Allocator::size_type size_type; #else typedef typename std::allocator_traits::size_type size_type; #endif typedef Allocator allocator_type; typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits< BidiIterator>::value_type char_type; typedef std::basic_string string_type; typedef BOOST_REGEX_DETAIL_NS::named_subexpressions named_sub_type; // construct/copy/destroy: explicit match_results(const Allocator& a = Allocator()) #ifndef BOOST_NO_STD_ALLOCATOR : m_subs(a), m_base(), m_null(), m_last_closed_paren(0), m_is_singular(true) {} #else : m_subs(), m_base(), m_null(), m_last_closed_paren(0), m_is_singular(true) { (void)a; } #endif // // IMPORTANT: in the code below, the crazy looking checks around m_is_singular are // all required because it is illegal to copy a singular iterator. // See https://svn.boost.org/trac/boost/ticket/3632. // match_results(const match_results& m) : m_subs(m.m_subs), m_base(), m_null(), m_named_subs(m.m_named_subs), m_last_closed_paren(m.m_last_closed_paren), m_is_singular(m.m_is_singular) { if(!m_is_singular) { m_base = m.m_base; m_null = m.m_null; } } match_results& operator=(const match_results& m) { m_subs = m.m_subs; m_named_subs = m.m_named_subs; m_last_closed_paren = m.m_last_closed_paren; m_is_singular = m.m_is_singular; if(!m_is_singular) { m_base = m.m_base; m_null = m.m_null; } return *this; } ~match_results(){} // size: size_type size() const { return empty() ? 0 : m_subs.size() - 2; } size_type max_size() const { return m_subs.max_size(); } bool empty() const { return m_subs.size() < 2; } // element access: difference_type length(int sub = 0) const { if(m_is_singular) raise_logic_error(); sub += 2; if((sub < (int)m_subs.size()) && (sub > 0)) return m_subs[sub].length(); return 0; } difference_type length(const char_type* sub) const { if(m_is_singular) raise_logic_error(); const char_type* sub_end = sub; while(*sub_end) ++sub_end; return length(named_subexpression_index(sub, sub_end)); } template difference_type length(const charT* sub) const { if(m_is_singular) raise_logic_error(); const charT* sub_end = sub; while(*sub_end) ++sub_end; return length(named_subexpression_index(sub, sub_end)); } template difference_type length(const std::basic_string& sub) const { return length(sub.c_str()); } difference_type position(size_type sub = 0) const { if(m_is_singular) raise_logic_error(); sub += 2; if(sub < m_subs.size()) { const sub_match& s = m_subs[sub]; if(s.matched || (sub == 2)) { return ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)(m_base), (BidiIterator)(s.first)); } } return ~static_cast(0); } difference_type position(const char_type* sub) const { const char_type* sub_end = sub; while(*sub_end) ++sub_end; return position(named_subexpression_index(sub, sub_end)); } template difference_type position(const charT* sub) const { const charT* sub_end = sub; while(*sub_end) ++sub_end; return position(named_subexpression_index(sub, sub_end)); } template difference_type position(const std::basic_string& sub) const { return position(sub.c_str()); } string_type str(int sub = 0) const { if(m_is_singular) raise_logic_error(); sub += 2; string_type result; if(sub < (int)m_subs.size() && (sub > 0)) { const sub_match& s = m_subs[sub]; if(s.matched) { result = s.str(); } } return result; } string_type str(const char_type* sub) const { return (*this)[sub].str(); } template string_type str(const std::basic_string& sub) const { return (*this)[sub].str(); } template string_type str(const charT* sub) const { return (*this)[sub].str(); } template string_type str(const std::basic_string& sub) const { return (*this)[sub].str(); } const_reference operator[](int sub) const { if(m_is_singular && m_subs.empty()) raise_logic_error(); sub += 2; if(sub < (int)m_subs.size() && (sub >= 0)) { return m_subs[sub]; } return m_null; } // // Named sub-expressions: // const_reference named_subexpression(const char_type* i, const char_type* j) const { // // Scan for the leftmost *matched* subexpression with the specified named: // if(m_is_singular) raise_logic_error(); BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type r = m_named_subs->equal_range(i, j); while((r.first != r.second) && ((*this)[r.first->index].matched == false)) ++r.first; return r.first != r.second ? (*this)[r.first->index] : m_null; } template const_reference named_subexpression(const charT* i, const charT* j) const { BOOST_STATIC_ASSERT(sizeof(charT) <= sizeof(char_type)); if(i == j) return m_null; std::vector s; while(i != j) s.insert(s.end(), *i++); return named_subexpression(&*s.begin(), &*s.begin() + s.size()); } int named_subexpression_index(const char_type* i, const char_type* j) const { // // Scan for the leftmost *matched* subexpression with the specified named. // If none found then return the leftmost expression with that name, // otherwise an invalid index: // if(m_is_singular) raise_logic_error(); BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type s, r; s = r = m_named_subs->equal_range(i, j); while((r.first != r.second) && ((*this)[r.first->index].matched == false)) ++r.first; if(r.first == r.second) r = s; return r.first != r.second ? r.first->index : -20; } template int named_subexpression_index(const charT* i, const charT* j) const { BOOST_STATIC_ASSERT(sizeof(charT) <= sizeof(char_type)); if(i == j) return -20; std::vector s; while(i != j) s.insert(s.end(), *i++); return named_subexpression_index(&*s.begin(), &*s.begin() + s.size()); } template const_reference operator[](const std::basic_string& s) const { return named_subexpression(s.c_str(), s.c_str() + s.size()); } const_reference operator[](const char_type* p) const { const char_type* e = p; while(*e) ++e; return named_subexpression(p, e); } template const_reference operator[](const charT* p) const { BOOST_STATIC_ASSERT(sizeof(charT) <= sizeof(char_type)); if(*p == 0) return m_null; std::vector s; while(*p) s.insert(s.end(), *p++); return named_subexpression(&*s.begin(), &*s.begin() + s.size()); } template const_reference operator[](const std::basic_string& ns) const { BOOST_STATIC_ASSERT(sizeof(charT) <= sizeof(char_type)); if(ns.empty()) return m_null; std::vector s; for(unsigned i = 0; i < ns.size(); ++i) s.insert(s.end(), ns[i]); return named_subexpression(&*s.begin(), &*s.begin() + s.size()); } const_reference prefix() const { if(m_is_singular) raise_logic_error(); return (*this)[-1]; } const_reference suffix() const { if(m_is_singular) raise_logic_error(); return (*this)[-2]; } const_iterator begin() const { return (m_subs.size() > 2) ? (m_subs.begin() + 2) : m_subs.end(); } const_iterator end() const { return m_subs.end(); } // format: template OutputIterator format(OutputIterator out, Functor fmt, match_flag_type flags = format_default) const { if(m_is_singular) raise_logic_error(); typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type, OutputIterator>::type F; F func(fmt); return func(*this, out, flags); } template string_type format(Functor fmt, match_flag_type flags = format_default) const { if(m_is_singular) raise_logic_error(); std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type, BOOST_REGEX_DETAIL_NS::string_out_iterator > >::type F; F func(fmt); func(*this, i, flags); return result; } // format with locale: template OutputIterator format(OutputIterator out, Functor fmt, match_flag_type flags, const RegexT& re) const { if(m_is_singular) raise_logic_error(); typedef ::boost::regex_traits_wrapper traits_type; typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type, OutputIterator, traits_type>::type F; F func(fmt); return func(*this, out, flags, re.get_traits()); } template string_type format(Functor fmt, match_flag_type flags, const RegexT& re) const { if(m_is_singular) raise_logic_error(); typedef ::boost::regex_traits_wrapper traits_type; std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type, BOOST_REGEX_DETAIL_NS::string_out_iterator >, traits_type >::type F; F func(fmt); func(*this, i, flags, re.get_traits()); return result; } const_reference get_last_closed_paren()const { if(m_is_singular) raise_logic_error(); return m_last_closed_paren == 0 ? m_null : (*this)[m_last_closed_paren]; } allocator_type get_allocator() const { #ifndef BOOST_NO_STD_ALLOCATOR return m_subs.get_allocator(); #else return allocator_type(); #endif } void swap(match_results& that) { std::swap(m_subs, that.m_subs); std::swap(m_named_subs, that.m_named_subs); std::swap(m_last_closed_paren, that.m_last_closed_paren); if(m_is_singular) { if(!that.m_is_singular) { m_base = that.m_base; m_null = that.m_null; } } else if(that.m_is_singular) { that.m_base = m_base; that.m_null = m_null; } else { std::swap(m_base, that.m_base); std::swap(m_null, that.m_null); } std::swap(m_is_singular, that.m_is_singular); } bool operator==(const match_results& that)const { if(m_is_singular) { return that.m_is_singular; } else if(that.m_is_singular) { return false; } return (m_subs == that.m_subs) && (m_base == that.m_base) && (m_last_closed_paren == that.m_last_closed_paren); } bool operator!=(const match_results& that)const { return !(*this == that); } #ifdef BOOST_REGEX_MATCH_EXTRA typedef typename sub_match::capture_sequence_type capture_sequence_type; const capture_sequence_type& captures(int i)const { if(m_is_singular) raise_logic_error(); return (*this)[i].captures(); } #endif // // private access functions: void BOOST_REGEX_CALL set_second(BidiIterator i) { BOOST_REGEX_ASSERT(m_subs.size() > 2); m_subs[2].second = i; m_subs[2].matched = true; m_subs[0].first = i; m_subs[0].matched = (m_subs[0].first != m_subs[0].second); m_null.first = i; m_null.second = i; m_null.matched = false; m_is_singular = false; } void BOOST_REGEX_CALL set_second(BidiIterator i, size_type pos, bool m = true, bool escape_k = false) { if(pos) m_last_closed_paren = static_cast(pos); pos += 2; BOOST_REGEX_ASSERT(m_subs.size() > pos); m_subs[pos].second = i; m_subs[pos].matched = m; if((pos == 2) && !escape_k) { m_subs[0].first = i; m_subs[0].matched = (m_subs[0].first != m_subs[0].second); m_null.first = i; m_null.second = i; m_null.matched = false; m_is_singular = false; } } void BOOST_REGEX_CALL set_size(size_type n, BidiIterator i, BidiIterator j) { value_type v(j); size_type len = m_subs.size(); if(len > n + 2) { m_subs.erase(m_subs.begin()+n+2, m_subs.end()); std::fill(m_subs.begin(), m_subs.end(), v); } else { std::fill(m_subs.begin(), m_subs.end(), v); if(n+2 != len) m_subs.insert(m_subs.end(), n+2-len, v); } m_subs[1].first = i; m_last_closed_paren = 0; } void BOOST_REGEX_CALL set_base(BidiIterator pos) { m_base = pos; } BidiIterator base()const { return m_base; } void BOOST_REGEX_CALL set_first(BidiIterator i) { BOOST_REGEX_ASSERT(m_subs.size() > 2); // set up prefix: m_subs[1].second = i; m_subs[1].matched = (m_subs[1].first != i); // set up $0: m_subs[2].first = i; // zero out everything else: for(size_type n = 3; n < m_subs.size(); ++n) { m_subs[n].first = m_subs[n].second = m_subs[0].second; m_subs[n].matched = false; } } void BOOST_REGEX_CALL set_first(BidiIterator i, size_type pos, bool escape_k = false) { BOOST_REGEX_ASSERT(pos+2 < m_subs.size()); if(pos || escape_k) { m_subs[pos+2].first = i; if(escape_k) { m_subs[1].second = i; m_subs[1].matched = (m_subs[1].first != m_subs[1].second); } } else set_first(i); } void BOOST_REGEX_CALL maybe_assign(const match_results& m); void BOOST_REGEX_CALL set_named_subs(boost::shared_ptr subs) { m_named_subs = subs; } private: // // Error handler called when an uninitialized match_results is accessed: // static void raise_logic_error() { std::logic_error e("Attempt to access an uninitialized boost::match_results<> class."); boost::throw_exception(e); } vector_type m_subs; // subexpressions BidiIterator m_base; // where the search started from sub_match m_null; // a null match boost::shared_ptr m_named_subs; // Shared copy of named subs in the regex object int m_last_closed_paren; // Last ) to be seen - used for formatting bool m_is_singular; // True if our stored iterators are singular }; template void BOOST_REGEX_CALL match_results::maybe_assign(const match_results& m) { if(m_is_singular) { *this = m; return; } const_iterator p1, p2; p1 = begin(); p2 = m.begin(); // // Distances are measured from the start of *this* match, unless this isn't // a valid match in which case we use the start of the whole sequence. Note that // no subsequent match-candidate can ever be to the left of the first match found. // This ensures that when we are using bidirectional iterators, that distances // measured are as short as possible, and therefore as efficient as possible // to compute. Finally note that we don't use the "matched" data member to test // whether a sub-expression is a valid match, because partial matches set this // to false for sub-expression 0. // BidiIterator l_end = this->suffix().second; BidiIterator l_base = (p1->first == l_end) ? this->prefix().first : (*this)[0].first; difference_type len1 = 0; difference_type len2 = 0; difference_type base1 = 0; difference_type base2 = 0; std::size_t i; for(i = 0; i < size(); ++i, ++p1, ++p2) { // // Leftmost takes priority over longest; handle special cases // where distances need not be computed first (an optimisation // for bidirectional iterators: ensure that we don't accidently // compute the length of the whole sequence, as this can be really // expensive). // if(p1->first == l_end) { if(p2->first != l_end) { // p2 must be better than p1, and no need to calculate // actual distances: base1 = 1; base2 = 0; break; } else { // *p1 and *p2 are either unmatched or match end-of sequence, // either way no need to calculate distances: if((p1->matched == false) && (p2->matched == true)) break; if((p1->matched == true) && (p2->matched == false)) return; continue; } } else if(p2->first == l_end) { // p1 better than p2, and no need to calculate distances: return; } base1 = ::boost::BOOST_REGEX_DETAIL_NS::distance(l_base, p1->first); base2 = ::boost::BOOST_REGEX_DETAIL_NS::distance(l_base, p2->first); BOOST_REGEX_ASSERT(base1 >= 0); BOOST_REGEX_ASSERT(base2 >= 0); if(base1 < base2) return; if(base2 < base1) break; len1 = ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)p1->first, (BidiIterator)p1->second); len2 = ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)p2->first, (BidiIterator)p2->second); BOOST_REGEX_ASSERT(len1 >= 0); BOOST_REGEX_ASSERT(len2 >= 0); if((len1 != len2) || ((p1->matched == false) && (p2->matched == true))) break; if((p1->matched == true) && (p2->matched == false)) return; } if(i == size()) return; if(base2 < base1) *this = m; else if((len2 > len1) || ((p1->matched == false) && (p2->matched == true)) ) *this = m; } template void swap(match_results& a, match_results& b) { a.swap(b); } #ifndef BOOST_NO_STD_LOCALE template std::basic_ostream& operator << (std::basic_ostream& os, const match_results& s) { return (os << s.str()); } #else template std::ostream& operator << (std::ostream& os, const match_results& s) { return (os << s.str()); } #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/mem_block_cache.hpp ================================================ /* * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE mem_block_cache.hpp * VERSION see * DESCRIPTION: memory block cache used by the non-recursive matcher. */ #ifndef BOOST_REGEX_V4_MEM_BLOCK_CACHE_HPP #define BOOST_REGEX_V4_MEM_BLOCK_CACHE_HPP #include #ifdef BOOST_HAS_THREADS #include #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifndef BOOST_NO_CXX11_HDR_ATOMIC #include #if ATOMIC_POINTER_LOCK_FREE == 2 #define BOOST_REGEX_MEM_BLOCK_CACHE_LOCK_FREE #define BOOST_REGEX_ATOMIC_POINTER std::atomic #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_REGEX_MEM_BLOCK_CACHE_LOCK_FREE /* lock free implementation */ struct mem_block_cache { std::atomic cache[BOOST_REGEX_MAX_CACHE_BLOCKS]; ~mem_block_cache() { for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) { if (cache[i].load()) ::operator delete(cache[i].load()); } } void* get() { for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) { void* p = cache[i].load(); if (p != NULL) { if (cache[i].compare_exchange_strong(p, NULL)) return p; } } return ::operator new(BOOST_REGEX_BLOCKSIZE); } void put(void* ptr) { for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) { void* p = cache[i].load(); if (p == NULL) { if (cache[i].compare_exchange_strong(p, ptr)) return; } } ::operator delete(ptr); } static mem_block_cache& instance() { static mem_block_cache block_cache = { { {nullptr} } }; return block_cache; } }; #else /* lock-based implementation */ struct mem_block_node { mem_block_node* next; }; struct mem_block_cache { // this member has to be statically initialsed: mem_block_node* next; unsigned cached_blocks; #ifdef BOOST_HAS_THREADS boost::static_mutex mut; #endif ~mem_block_cache() { while(next) { mem_block_node* old = next; next = next->next; ::operator delete(old); } } void* get() { #ifdef BOOST_HAS_THREADS boost::static_mutex::scoped_lock g(mut); #endif if(next) { mem_block_node* result = next; next = next->next; --cached_blocks; return result; } return ::operator new(BOOST_REGEX_BLOCKSIZE); } void put(void* p) { #ifdef BOOST_HAS_THREADS boost::static_mutex::scoped_lock g(mut); #endif if(cached_blocks >= BOOST_REGEX_MAX_CACHE_BLOCKS) { ::operator delete(p); } else { mem_block_node* old = static_cast(p); old->next = next; next = old; ++cached_blocks; } } static mem_block_cache& instance() { #ifdef BOOST_HAS_THREADS static mem_block_cache block_cache = { 0, 0, BOOST_STATIC_MUTEX_INIT, }; #else static mem_block_cache block_cache = { 0, 0, }; #endif return block_cache; } }; #endif #if BOOST_REGEX_MAX_CACHE_BLOCKS == 0 inline void* BOOST_REGEX_CALL get_mem_block() { return ::operator new(BOOST_REGEX_BLOCKSIZE); } inline void BOOST_REGEX_CALL put_mem_block(void* p) { ::operator delete(p); } #else inline void* BOOST_REGEX_CALL get_mem_block() { return mem_block_cache::instance().get(); } inline void BOOST_REGEX_CALL put_mem_block(void* p) { mem_block_cache::instance().put(p); } #endif } } // namespace boost #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/object_cache.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE object_cache.hpp * VERSION see * DESCRIPTION: Implements a generic object cache. */ #ifndef BOOST_REGEX_OBJECT_CACHE_HPP #define BOOST_REGEX_OBJECT_CACHE_HPP #include #include #include #include #include #include #ifdef BOOST_HAS_THREADS #include #endif namespace boost{ template class object_cache { public: typedef std::pair< ::boost::shared_ptr, Key const*> value_type; typedef std::list list_type; typedef typename list_type::iterator list_iterator; typedef std::map map_type; typedef typename map_type::iterator map_iterator; typedef typename list_type::size_type size_type; static boost::shared_ptr get(const Key& k, size_type l_max_cache_size); private: static boost::shared_ptr do_get(const Key& k, size_type l_max_cache_size); struct data { list_type cont; map_type index; }; // Needed by compilers not implementing the resolution to DR45. For reference, // see http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#45. friend struct data; }; #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4702) #endif template boost::shared_ptr object_cache::get(const Key& k, size_type l_max_cache_size) { #ifdef BOOST_HAS_THREADS static boost::static_mutex mut = BOOST_STATIC_MUTEX_INIT; boost::static_mutex::scoped_lock l(mut); if (l) { return do_get(k, l_max_cache_size); } // // what do we do if the lock fails? // for now just throw, but we should never really get here... // ::boost::throw_exception(std::runtime_error("Error in thread safety code: could not acquire a lock")); #if defined(BOOST_NO_UNREACHABLE_RETURN_DETECTION) || defined(BOOST_NO_EXCEPTIONS) return boost::shared_ptr(); #endif #else return do_get(k, l_max_cache_size); #endif } #ifdef BOOST_MSVC #pragma warning(pop) #endif template boost::shared_ptr object_cache::do_get(const Key& k, size_type l_max_cache_size) { typedef typename object_cache::data object_data; typedef typename map_type::size_type map_size_type; static object_data s_data; // // see if the object is already in the cache: // map_iterator mpos = s_data.index.find(k); if(mpos != s_data.index.end()) { // // Eureka! // We have a cached item, bump it up the list and return it: // if(--(s_data.cont.end()) != mpos->second) { // splice out the item we want to move: list_type temp; temp.splice(temp.end(), s_data.cont, mpos->second); // and now place it at the end of the list: s_data.cont.splice(s_data.cont.end(), temp, temp.begin()); BOOST_REGEX_ASSERT(*(s_data.cont.back().second) == k); // update index with new position: mpos->second = --(s_data.cont.end()); BOOST_REGEX_ASSERT(&(mpos->first) == mpos->second->second); BOOST_REGEX_ASSERT(&(mpos->first) == s_data.cont.back().second); } return s_data.cont.back().first; } // // if we get here then the item is not in the cache, // so create it: // boost::shared_ptr result(new Object(k)); // // Add it to the list, and index it: // s_data.cont.push_back(value_type(result, static_cast(0))); s_data.index.insert(std::make_pair(k, --(s_data.cont.end()))); s_data.cont.back().second = &(s_data.index.find(k)->first); map_size_type s = s_data.index.size(); BOOST_REGEX_ASSERT(s_data.index[k]->first.get() == result.get()); BOOST_REGEX_ASSERT(&(s_data.index.find(k)->first) == s_data.cont.back().second); BOOST_REGEX_ASSERT(s_data.index.find(k)->first == k); if(s > l_max_cache_size) { // // We have too many items in the list, so we need to start // popping them off the back of the list, but only if they're // being held uniquely by us: // list_iterator pos = s_data.cont.begin(); list_iterator last = s_data.cont.end(); while((pos != last) && (s > l_max_cache_size)) { if(pos->first.unique()) { list_iterator condemmed(pos); ++pos; // now remove the items from our containers, // then order has to be as follows: BOOST_REGEX_ASSERT(s_data.index.find(*(condemmed->second)) != s_data.index.end()); s_data.index.erase(*(condemmed->second)); s_data.cont.erase(condemmed); --s; } else ++pos; } BOOST_REGEX_ASSERT(s_data.index[k]->first.get() == result.get()); BOOST_REGEX_ASSERT(&(s_data.index.find(k)->first) == s_data.cont.back().second); BOOST_REGEX_ASSERT(s_data.index.find(k)->first == k); } return result; } } #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/pattern_except.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE pattern_except.hpp * VERSION see * DESCRIPTION: Declares pattern-matching exception classes. */ #ifndef BOOST_RE_V4_PAT_EXCEPT_HPP #define BOOST_RE_V4_PAT_EXCEPT_HPP #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #include #include #include #include namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable : 4275) #if BOOST_MSVC >= 1800 #pragma warning(disable : 26812) #endif #endif class regex_error : public std::runtime_error { public: explicit regex_error(const std::string& s, regex_constants::error_type err = regex_constants::error_unknown, std::ptrdiff_t pos = 0) : std::runtime_error(s) , m_error_code(err) , m_position(pos) { } explicit regex_error(regex_constants::error_type err) : std::runtime_error(::boost::BOOST_REGEX_DETAIL_NS::get_default_error_string(err)) , m_error_code(err) , m_position(0) { } ~regex_error() BOOST_NOEXCEPT_OR_NOTHROW BOOST_OVERRIDE {} regex_constants::error_type code()const { return m_error_code; } std::ptrdiff_t position()const { return m_position; } void raise()const { #ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_REGEX_STANDALONE ::boost::throw_exception(*this); #else throw* this; #endif #endif } private: regex_constants::error_type m_error_code; std::ptrdiff_t m_position; }; typedef regex_error bad_pattern; typedef regex_error bad_expression; namespace BOOST_REGEX_DETAIL_NS{ template inline void raise_runtime_error(const E& ex) { #ifndef BOOST_REGEX_STANDALONE ::boost::throw_exception(ex); #else throw ex; #endif } template void raise_error(const traits& t, regex_constants::error_type code) { (void)t; // warning suppression regex_error e(t.error_string(code), code, 0); ::boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(e); } } #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/perl_matcher.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ #ifndef BOOST_REGEX_MATCHER_HPP #define BOOST_REGEX_MATCHER_HPP #include #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC # pragma warning(push) #pragma warning(disable : 4251) #if BOOST_MSVC < 1700 # pragma warning(disable : 4231) #endif # if BOOST_MSVC < 1600 # pragma warning(disable : 4660) # endif #if BOOST_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ // // error checking API: // inline void BOOST_REGEX_CALL verify_options(boost::regex_constants::syntax_option_type, match_flag_type mf) { // // can't mix match_extra with POSIX matching rules: // if ((mf & match_extra) && (mf & match_posix)) { std::logic_error msg("Usage Error: Can't mix regular expression captures with POSIX matching rules"); throw_exception(msg); } } // // function can_start: // template inline bool can_start(charT c, const unsigned char* map, unsigned char mask) { return ((c < static_cast(0)) ? true : ((c >= static_cast(1 << CHAR_BIT)) ? true : map[c] & mask)); } inline bool can_start(char c, const unsigned char* map, unsigned char mask) { return map[(unsigned char)c] & mask; } inline bool can_start(signed char c, const unsigned char* map, unsigned char mask) { return map[(unsigned char)c] & mask; } inline bool can_start(unsigned char c, const unsigned char* map, unsigned char mask) { return map[c] & mask; } inline bool can_start(unsigned short c, const unsigned char* map, unsigned char mask) { return ((c >= (1 << CHAR_BIT)) ? true : map[c] & mask); } #if !defined(__hpux) && !defined(__WINSCW__)// WCHAR_MIN not usable in pp-directives. #if defined(WCHAR_MIN) && (WCHAR_MIN == 0) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) inline bool can_start(wchar_t c, const unsigned char* map, unsigned char mask) { return ((c >= static_cast(1u << CHAR_BIT)) ? true : map[c] & mask); } #endif #endif #if !defined(BOOST_NO_INTRINSIC_WCHAR_T) inline bool can_start(unsigned int c, const unsigned char* map, unsigned char mask) { return (((c >= static_cast(1u << CHAR_BIT)) ? true : map[c] & mask)); } #endif // // Unfortunately Rogue Waves standard library appears to have a bug // in std::basic_string::compare that results in erroneous answers // in some cases (tested with Borland C++ 5.1, Rogue Wave lib version // 0x020101) the test case was: // {39135,0} < {0xff,0} // which succeeds when it should not. // #ifndef _RWSTD_VER template inline int string_compare(const std::basic_string& s, const C* p) { if(0 == *p) { if(s.empty() || ((s.size() == 1) && (s[0] == 0))) return 0; } return s.compare(p); } #else template inline int string_compare(const std::basic_string& s, const C* p) { if(0 == *p) { if(s.empty() || ((s.size() == 1) && (s[0] == 0))) return 0; } return s.compare(p); } inline int string_compare(const std::string& s, const char* p) { return std::strcmp(s.c_str(), p); } # ifndef BOOST_NO_WREGEX inline int string_compare(const std::wstring& s, const wchar_t* p) { return std::wcscmp(s.c_str(), p); } #endif #endif template inline int string_compare(const Seq& s, const C* p) { std::size_t i = 0; while((i < s.size()) && (p[i] == s[i])) { ++i; } return (i == s.size()) ? -(int)p[i] : (int)s[i] - (int)p[i]; } # define STR_COMP(s,p) string_compare(s,p) template inline const charT* re_skip_past_null(const charT* p) { while (*p != static_cast(0)) ++p; return ++p; } template iterator BOOST_REGEX_CALL re_is_set_member(iterator next, iterator last, const re_set_long* set_, const regex_data& e, bool icase) { const charT* p = reinterpret_cast(set_+1); iterator ptr; unsigned int i; //bool icase = e.m_flags & regex_constants::icase; if(next == last) return next; typedef typename traits_type::string_type traits_string_type; const ::boost::regex_traits_wrapper& traits_inst = *(e.m_ptraits); // dwa 9/13/00 suppress incorrect MSVC warning - it claims this is never // referenced (void)traits_inst; // try and match a single character, could be a multi-character // collating element... for(i = 0; i < set_->csingles; ++i) { ptr = next; if(*p == static_cast(0)) { // treat null string as special case: if(traits_inst.translate(*ptr, icase)) { ++p; continue; } return set_->isnot ? next : (ptr == next) ? ++next : ptr; } else { while(*p && (ptr != last)) { if(traits_inst.translate(*ptr, icase) != *p) break; ++p; ++ptr; } if(*p == static_cast(0)) // if null we've matched return set_->isnot ? next : (ptr == next) ? ++next : ptr; p = re_skip_past_null(p); // skip null } } charT col = traits_inst.translate(*next, icase); if(set_->cranges || set_->cequivalents) { traits_string_type s1; // // try and match a range, NB only a single character can match if(set_->cranges) { if((e.m_flags & regex_constants::collate) == 0) s1.assign(1, col); else { charT a[2] = { col, charT(0), }; s1 = traits_inst.transform(a, a + 1); } for(i = 0; i < set_->cranges; ++i) { if(STR_COMP(s1, p) >= 0) { do{ ++p; }while(*p); ++p; if(STR_COMP(s1, p) <= 0) return set_->isnot ? next : ++next; } else { // skip first string do{ ++p; }while(*p); ++p; } // skip second string do{ ++p; }while(*p); ++p; } } // // try and match an equivalence class, NB only a single character can match if(set_->cequivalents) { charT a[2] = { col, charT(0), }; s1 = traits_inst.transform_primary(a, a +1); for(i = 0; i < set_->cequivalents; ++i) { if(STR_COMP(s1, p) == 0) return set_->isnot ? next : ++next; // skip string do{ ++p; }while(*p); ++p; } } } if(traits_inst.isctype(col, set_->cclasses) == true) return set_->isnot ? next : ++next; if((set_->cnclasses != 0) && (traits_inst.isctype(col, set_->cnclasses) == false)) return set_->isnot ? next : ++next; return set_->isnot ? ++next : next; } template class repeater_count { repeater_count** stack; repeater_count* next; int state_id; std::size_t count; // the number of iterations so far BidiIterator start_pos; // where the last repeat started repeater_count* unwind_until(int n, repeater_count* p, int current_recursion_id) { while(p && (p->state_id != n)) { if(-2 - current_recursion_id == p->state_id) return 0; p = p->next; if(p && (p->state_id < 0)) { p = unwind_until(p->state_id, p, current_recursion_id); if(!p) return p; p = p->next; } } return p; } public: repeater_count(repeater_count** s) : stack(s), next(0), state_id(-1), count(0), start_pos() {} repeater_count(int i, repeater_count** s, BidiIterator start, int current_recursion_id) : start_pos(start) { state_id = i; stack = s; next = *stack; *stack = this; if((state_id > next->state_id) && (next->state_id >= 0)) count = 0; else { repeater_count* p = next; p = unwind_until(state_id, p, current_recursion_id); if(p) { count = p->count; start_pos = p->start_pos; } else count = 0; } } ~repeater_count() { if(next) *stack = next; } std::size_t get_count() { return count; } int get_id() { return state_id; } std::size_t operator++() { return ++count; } bool check_null_repeat(const BidiIterator& pos, std::size_t max) { // this is called when we are about to start a new repeat, // if the last one was NULL move our count to max, // otherwise save the current position. bool result = (count == 0) ? false : (pos == start_pos); if(result) count = max; else start_pos = pos; return result; } }; struct saved_state; enum saved_state_type { saved_type_end = 0, saved_type_paren = 1, saved_type_recurse = 2, saved_type_assertion = 3, saved_state_alt = 4, saved_state_repeater_count = 5, saved_state_extra_block = 6, saved_state_greedy_single_repeat = 7, saved_state_rep_slow_dot = 8, saved_state_rep_fast_dot = 9, saved_state_rep_char = 10, saved_state_rep_short_set = 11, saved_state_rep_long_set = 12, saved_state_non_greedy_long_repeat = 13, saved_state_count = 14 }; #ifdef BOOST_MSVC # pragma warning(push) #if BOOST_MSVC >= 1800 #pragma warning(disable:26495) #endif #endif template struct recursion_info { typedef typename Results::value_type value_type; typedef typename value_type::iterator iterator; int idx; const re_syntax_base* preturn_address; Results results; repeater_count* repeater_stack; iterator location_of_start; }; #ifdef BOOST_MSVC # pragma warning(pop) #endif template class perl_matcher { public: typedef typename traits::char_type char_type; typedef perl_matcher self_type; typedef bool (self_type::*matcher_proc_type)(); typedef std::size_t traits_size_type; typedef typename is_byte::width_type width_type; typedef typename regex_iterator_traits::difference_type difference_type; typedef match_results results_type; perl_matcher(BidiIterator first, BidiIterator end, match_results& what, const basic_regex& e, match_flag_type f, BidiIterator l_base) : m_result(what), base(first), last(end), position(first), backstop(l_base), re(e), traits_inst(e.get_traits()), m_independent(false), next_count(&rep_obj), rep_obj(&next_count) #ifdef BOOST_REGEX_NON_RECURSIVE , m_recursions(0) #endif { construct_init(e, f); } bool match(); bool find(); void setf(match_flag_type f) { m_match_flags |= f; } void unsetf(match_flag_type f) { m_match_flags &= ~f; } private: void construct_init(const basic_regex& e, match_flag_type f); bool find_imp(); bool match_imp(); #ifdef BOOST_REGEX_HAS_MS_STACK_GUARD typedef bool (perl_matcher::*protected_proc_type)(); bool protected_call(protected_proc_type); #endif void estimate_max_state_count(std::random_access_iterator_tag*); void estimate_max_state_count(void*); bool match_prefix(); bool match_all_states(); // match procs, stored in s_match_vtable: bool match_startmark(); bool match_endmark(); bool match_literal(); bool match_start_line(); bool match_end_line(); bool match_wild(); bool match_match(); bool match_word_boundary(); bool match_within_word(); bool match_word_start(); bool match_word_end(); bool match_buffer_start(); bool match_buffer_end(); bool match_backref(); bool match_long_set(); bool match_set(); bool match_jump(); bool match_alt(); bool match_rep(); bool match_combining(); bool match_soft_buffer_end(); bool match_restart_continue(); bool match_long_set_repeat(); bool match_set_repeat(); bool match_char_repeat(); bool match_dot_repeat_fast(); bool match_dot_repeat_slow(); bool match_dot_repeat_dispatch() { return ::boost::is_random_access_iterator::value ? match_dot_repeat_fast() : match_dot_repeat_slow(); } bool match_backstep(); bool match_assert_backref(); bool match_toggle_case(); #ifdef BOOST_REGEX_RECURSIVE bool backtrack_till_match(std::size_t count); #endif bool match_recursion(); bool match_fail(); bool match_accept(); bool match_commit(); bool match_then(); bool skip_until_paren(int index, bool match = true); // find procs stored in s_find_vtable: bool find_restart_any(); bool find_restart_word(); bool find_restart_line(); bool find_restart_buf(); bool find_restart_lit(); private: // final result structure to be filled in: match_results& m_result; // temporary result for POSIX matches: scoped_ptr > m_temp_match; // pointer to actual result structure to fill in: match_results* m_presult; // start of sequence being searched: BidiIterator base; // end of sequence being searched: BidiIterator last; // current character being examined: BidiIterator position; // where to restart next search after failed match attempt: BidiIterator restart; // where the current search started from, acts as base for $` during grep: BidiIterator search_base; // how far we can go back when matching lookbehind: BidiIterator backstop; // the expression being examined: const basic_regex& re; // the expression's traits class: const ::boost::regex_traits_wrapper& traits_inst; // the next state in the machine being matched: const re_syntax_base* pstate; // matching flags in use: match_flag_type m_match_flags; // how many states we have examined so far: std::ptrdiff_t state_count; // max number of states to examine before giving up: std::ptrdiff_t max_state_count; // whether we should ignore case or not: bool icase; // set to true when (position == last), indicates that we may have a partial match: bool m_has_partial_match; // set to true whenever we get a match: bool m_has_found_match; // set to true whenever we're inside an independent sub-expression: bool m_independent; // the current repeat being examined: repeater_count* next_count; // the first repeat being examined (top of linked list): repeater_count rep_obj; // the mask to pass when matching word boundaries: typename traits::char_class_type m_word_mask; // the bitmask to use when determining whether a match_any matches a newline or not: unsigned char match_any_mask; // recursion information: std::vector > recursion_stack; #ifdef BOOST_REGEX_RECURSIVE // Set to false by a (*COMMIT): bool m_can_backtrack; bool m_have_accept; bool m_have_then; #endif #ifdef BOOST_REGEX_NON_RECURSIVE // // additional members for non-recursive version: // typedef bool (self_type::*unwind_proc_type)(bool); void extend_stack(); bool unwind(bool); bool unwind_end(bool); bool unwind_paren(bool); bool unwind_recursion_stopper(bool); bool unwind_assertion(bool); bool unwind_alt(bool); bool unwind_repeater_counter(bool); bool unwind_extra_block(bool); bool unwind_greedy_single_repeat(bool); bool unwind_slow_dot_repeat(bool); bool unwind_fast_dot_repeat(bool); bool unwind_char_repeat(bool); bool unwind_short_set_repeat(bool); bool unwind_long_set_repeat(bool); bool unwind_non_greedy_repeat(bool); bool unwind_recursion(bool); bool unwind_recursion_pop(bool); bool unwind_commit(bool); bool unwind_then(bool); bool unwind_case(bool); void destroy_single_repeat(); void push_matched_paren(int index, const sub_match& sub); void push_recursion_stopper(); void push_assertion(const re_syntax_base* ps, bool positive); void push_alt(const re_syntax_base* ps); void push_repeater_count(int i, repeater_count** s); void push_single_repeat(std::size_t c, const re_repeat* r, BidiIterator last_position, int state_id); void push_non_greedy_repeat(const re_syntax_base* ps); void push_recursion(int idx, const re_syntax_base* p, results_type* presults, results_type* presults2); void push_recursion_pop(); void push_case_change(bool); // pointer to base of stack: saved_state* m_stack_base; // pointer to current stack position: saved_state* m_backup_state; // how many memory blocks have we used up?: unsigned used_block_count; // determines what value to return when unwinding from recursion, // allows for mixed recursive/non-recursive algorithm: bool m_recursive_result; // We have unwound to a lookahead/lookbehind, used by COMMIT/PRUNE/SKIP: bool m_unwound_lookahead; // We have unwound to an alternative, used by THEN: bool m_unwound_alt; // We are unwinding a commit - used by independent subs to determine whether to stop there or carry on unwinding: //bool m_unwind_commit; // Recursion limit: unsigned m_recursions; #endif #ifdef BOOST_MSVC # pragma warning(push) #if BOOST_MSVC >= 1800 #pragma warning(disable:26495) #endif #endif // these operations aren't allowed, so are declared private, // bodies are provided to keep explicit-instantiation requests happy: perl_matcher& operator=(const perl_matcher&) { return *this; } perl_matcher(const perl_matcher& that) : m_result(that.m_result), re(that.re), traits_inst(that.traits_inst), rep_obj(0) {} #ifdef BOOST_MSVC # pragma warning(pop) #endif }; } // namespace BOOST_REGEX_DETAIL_NS #ifdef BOOST_MSVC # pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost // // include the implementation of perl_matcher: // #ifdef BOOST_REGEX_RECURSIVE #include #else #include #endif // this one has to be last: #include #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/perl_matcher_common.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE perl_matcher_common.cpp * VERSION see * DESCRIPTION: Definitions of perl_matcher member functions that are * common to both the recursive and non-recursive versions. */ #ifndef BOOST_REGEX_V4_PERL_MATCHER_COMMON_HPP #define BOOST_REGEX_V4_PERL_MATCHER_COMMON_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #if BOOST_MSVC >= 1800 #pragma warning(disable: 26812) #endif #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_BORLANDC # pragma option push -w-8008 -w-8066 #endif #ifdef BOOST_MSVC # pragma warning(push) #if BOOST_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_MSVC # pragma warning(push) #pragma warning(disable:26812) #endif template void perl_matcher::construct_init(const basic_regex& e, match_flag_type f) { typedef typename regex_iterator_traits::iterator_category category; typedef typename basic_regex::flag_type expression_flag_type; if(e.empty()) { // precondition failure: e is not a valid regex. std::invalid_argument ex("Invalid regular expression object"); boost::throw_exception(ex); } pstate = 0; m_match_flags = f; estimate_max_state_count(static_cast(0)); expression_flag_type re_f = re.flags(); icase = re_f & regex_constants::icase; if(!(m_match_flags & (match_perl|match_posix))) { if((re_f & (regbase::main_option_type|regbase::no_perl_ex)) == 0) m_match_flags |= match_perl; else if((re_f & (regbase::main_option_type|regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex)) m_match_flags |= match_perl; else if((re_f & (regbase::main_option_type|regbase::literal)) == (regbase::literal)) m_match_flags |= match_perl; else m_match_flags |= match_posix; } if(m_match_flags & match_posix) { m_temp_match.reset(new match_results()); m_presult = m_temp_match.get(); } else m_presult = &m_result; #ifdef BOOST_REGEX_NON_RECURSIVE m_stack_base = 0; m_backup_state = 0; #elif defined(BOOST_REGEX_RECURSIVE) m_can_backtrack = true; m_have_accept = false; #endif // find the value to use for matching word boundaries: m_word_mask = re.get_data().m_word_mask; // find bitmask to use for matching '.': match_any_mask = static_cast((f & match_not_dot_newline) ? BOOST_REGEX_DETAIL_NS::test_not_newline : BOOST_REGEX_DETAIL_NS::test_newline); // Disable match_any if requested in the state machine: if(e.get_data().m_disable_match_any) m_match_flags &= regex_constants::match_not_any; } #ifdef BOOST_MSVC # pragma warning(pop) #endif template void perl_matcher::estimate_max_state_count(std::random_access_iterator_tag*) { // // How many states should we allow our machine to visit before giving up? // This is a heuristic: it takes the greater of O(N^2) and O(NS^2) // where N is the length of the string, and S is the number of states // in the machine. It's tempting to up this to O(N^2S) or even O(N^2S^2) // but these take unreasonably amounts of time to bale out in pathological // cases. // // Calculate NS^2 first: // static const std::ptrdiff_t k = 100000; std::ptrdiff_t dist = boost::BOOST_REGEX_DETAIL_NS::distance(base, last); if(dist == 0) dist = 1; std::ptrdiff_t states = re.size(); if(states == 0) states = 1; if ((std::numeric_limits::max)() / states < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states *= states; if((std::numeric_limits::max)() / dist < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states *= dist; if((std::numeric_limits::max)() - k < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states += k; max_state_count = states; // // Now calculate N^2: // states = dist; if((std::numeric_limits::max)() / dist < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states *= dist; if((std::numeric_limits::max)() - k < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states += k; // // N^2 can be a very large number indeed, to prevent things getting out // of control, cap the max states: // if(states > BOOST_REGEX_MAX_STATE_COUNT) states = BOOST_REGEX_MAX_STATE_COUNT; // // If (the possibly capped) N^2 is larger than our first estimate, // use this instead: // if(states > max_state_count) max_state_count = states; } template inline void perl_matcher::estimate_max_state_count(void*) { // we don't know how long the sequence is: max_state_count = BOOST_REGEX_MAX_STATE_COUNT; } #ifdef BOOST_REGEX_HAS_MS_STACK_GUARD template inline bool perl_matcher::protected_call( protected_proc_type proc) { ::boost::BOOST_REGEX_DETAIL_NS::concrete_protected_call > obj(this, proc); return obj.execute(); } #endif template inline bool perl_matcher::match() { #ifdef BOOST_REGEX_HAS_MS_STACK_GUARD return protected_call(&perl_matcher::match_imp); #else return match_imp(); #endif } template bool perl_matcher::match_imp() { // initialise our stack if we are non-recursive: #ifdef BOOST_REGEX_NON_RECURSIVE save_state_init init(&m_stack_base, &m_backup_state); used_block_count = BOOST_REGEX_MAX_BLOCKS; #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif #endif // reset our state machine: position = base; search_base = base; state_count = 0; m_match_flags |= regex_constants::match_all; m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast(1u + re.mark_count()), search_base, last); m_presult->set_base(base); m_presult->set_named_subs(this->re.get_named_subs()); if(m_match_flags & match_posix) m_result = *m_presult; verify_options(re.flags(), m_match_flags); if(0 == match_prefix()) return false; return (m_result[0].second == last) && (m_result[0].first == base); #if defined(BOOST_REGEX_NON_RECURSIVE) && !defined(BOOST_NO_EXCEPTIONS) } catch(...) { // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)){} throw; } #endif } template inline bool perl_matcher::find() { #ifdef BOOST_REGEX_HAS_MS_STACK_GUARD return protected_call(&perl_matcher::find_imp); #else return find_imp(); #endif } template bool perl_matcher::find_imp() { static matcher_proc_type const s_find_vtable[7] = { &perl_matcher::find_restart_any, &perl_matcher::find_restart_word, &perl_matcher::find_restart_line, &perl_matcher::find_restart_buf, &perl_matcher::match_prefix, &perl_matcher::find_restart_lit, &perl_matcher::find_restart_lit, }; // initialise our stack if we are non-recursive: #ifdef BOOST_REGEX_NON_RECURSIVE save_state_init init(&m_stack_base, &m_backup_state); used_block_count = BOOST_REGEX_MAX_BLOCKS; #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif #endif state_count = 0; if((m_match_flags & regex_constants::match_init) == 0) { // reset our state machine: search_base = position = base; pstate = re.get_first_state(); m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast(1u + re.mark_count()), base, last); m_presult->set_base(base); m_presult->set_named_subs(this->re.get_named_subs()); m_match_flags |= regex_constants::match_init; } else { // start again: search_base = position = m_result[0].second; // If last match was null and match_not_null was not set then increment // our start position, otherwise we go into an infinite loop: if(((m_match_flags & match_not_null) == 0) && (m_result.length() == 0)) { if(position == last) return false; else ++position; } // reset $` start: m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast(1u + re.mark_count()), search_base, last); //if((base != search_base) && (base == backstop)) // m_match_flags |= match_prev_avail; } if(m_match_flags & match_posix) { m_result.set_size(static_cast(1u + re.mark_count()), base, last); m_result.set_base(base); } verify_options(re.flags(), m_match_flags); // find out what kind of expression we have: unsigned type = (m_match_flags & match_continuous) ? static_cast(regbase::restart_continue) : static_cast(re.get_restart_type()); // call the appropriate search routine: matcher_proc_type proc = s_find_vtable[type]; return (this->*proc)(); #if defined(BOOST_REGEX_NON_RECURSIVE) && !defined(BOOST_NO_EXCEPTIONS) } catch(...) { // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)){} throw; } #endif } template bool perl_matcher::match_prefix() { m_has_partial_match = false; m_has_found_match = false; pstate = re.get_first_state(); m_presult->set_first(position); restart = position; match_all_states(); if(!m_has_found_match && m_has_partial_match && (m_match_flags & match_partial)) { m_has_found_match = true; m_presult->set_second(last, 0, false); position = last; if((m_match_flags & match_posix) == match_posix) { m_result.maybe_assign(*m_presult); } } #ifdef BOOST_REGEX_MATCH_EXTRA if(m_has_found_match && (match_extra & m_match_flags)) { // // we have a match, reverse the capture information: // for(unsigned i = 0; i < m_presult->size(); ++i) { typename sub_match::capture_sequence_type & seq = ((*m_presult)[i]).get_captures(); std::reverse(seq.begin(), seq.end()); } } #endif if(!m_has_found_match) position = restart; // reset search postion #ifdef BOOST_REGEX_RECURSIVE m_can_backtrack = true; // reset for further searches #endif return m_has_found_match; } template bool perl_matcher::match_literal() { unsigned int len = static_cast(pstate)->length; const char_type* what = reinterpret_cast(static_cast(pstate) + 1); // // compare string with what we stored in // our records: for(unsigned int i = 0; i < len; ++i, ++position) { if((position == last) || (traits_inst.translate(*position, icase) != what[i])) return false; } pstate = pstate->next.p; return true; } template bool perl_matcher::match_start_line() { if(position == backstop) { if((m_match_flags & match_prev_avail) == 0) { if((m_match_flags & match_not_bol) == 0) { pstate = pstate->next.p; return true; } return false; } } else if(m_match_flags & match_single_line) return false; // check the previous value character: BidiIterator t(position); --t; if(position != last) { if(is_separator(*t) && !((*t == static_cast('\r')) && (*position == static_cast('\n'))) ) { pstate = pstate->next.p; return true; } } else if(is_separator(*t)) { pstate = pstate->next.p; return true; } return false; } template bool perl_matcher::match_end_line() { if(position != last) { if(m_match_flags & match_single_line) return false; // we're not yet at the end so *first is always valid: if(is_separator(*position)) { if((position != backstop) || (m_match_flags & match_prev_avail)) { // check that we're not in the middle of \r\n sequence BidiIterator t(position); --t; if((*t == static_cast('\r')) && (*position == static_cast('\n'))) { return false; } } pstate = pstate->next.p; return true; } } else if((m_match_flags & match_not_eol) == 0) { pstate = pstate->next.p; return true; } return false; } template bool perl_matcher::match_wild() { if(position == last) return false; if(is_separator(*position) && ((match_any_mask & static_cast(pstate)->mask) == 0)) return false; if((*position == char_type(0)) && (m_match_flags & match_not_dot_null)) return false; pstate = pstate->next.p; ++position; return true; } template bool perl_matcher::match_word_boundary() { bool b; // indcates whether next character is a word character if(position != last) { // prev and this character must be opposites: b = traits_inst.isctype(*position, m_word_mask); } else { if (m_match_flags & match_not_eow) return false; b = false; } if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) { if(m_match_flags & match_not_bow) return false; else b ^= false; } else { --position; b ^= traits_inst.isctype(*position, m_word_mask); ++position; } if(b) { pstate = pstate->next.p; return true; } return false; // no match if we get to here... } template bool perl_matcher::match_within_word() { if(position == last) return false; // both prev and this character must be m_word_mask: bool prev = traits_inst.isctype(*position, m_word_mask); { bool b; if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) return false; else { --position; b = traits_inst.isctype(*position, m_word_mask); ++position; } if(b == prev) { pstate = pstate->next.p; return true; } } return false; } template bool perl_matcher::match_word_start() { if(position == last) return false; // can't be starting a word if we're already at the end of input if(!traits_inst.isctype(*position, m_word_mask)) return false; // next character isn't a word character if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) { if(m_match_flags & match_not_bow) return false; // no previous input } else { // otherwise inside buffer: BidiIterator t(position); --t; if(traits_inst.isctype(*t, m_word_mask)) return false; // previous character not non-word } // OK we have a match: pstate = pstate->next.p; return true; } template bool perl_matcher::match_word_end() { if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) return false; // start of buffer can't be end of word BidiIterator t(position); --t; if(traits_inst.isctype(*t, m_word_mask) == false) return false; // previous character wasn't a word character if(position == last) { if(m_match_flags & match_not_eow) return false; // end of buffer but not end of word } else { // otherwise inside buffer: if(traits_inst.isctype(*position, m_word_mask)) return false; // next character is a word character } pstate = pstate->next.p; return true; // if we fall through to here then we've succeeded } template bool perl_matcher::match_buffer_start() { if((position != backstop) || (m_match_flags & match_not_bob)) return false; // OK match: pstate = pstate->next.p; return true; } template bool perl_matcher::match_buffer_end() { if((position != last) || (m_match_flags & match_not_eob)) return false; // OK match: pstate = pstate->next.p; return true; } template bool perl_matcher::match_backref() { // // Compare with what we previously matched. // Note that this succeeds if the backref did not partisipate // in the match, this is in line with ECMAScript, but not Perl // or PCRE. // int index = static_cast(pstate)->index; if(index >= hash_value_mask) { named_subexpressions::range_type r = re.get_data().equal_range(index); BOOST_REGEX_ASSERT(r.first != r.second); do { index = r.first->index; ++r.first; }while((r.first != r.second) && ((*m_presult)[index].matched != true)); } if((m_match_flags & match_perl) && !(*m_presult)[index].matched) return false; BidiIterator i = (*m_presult)[index].first; BidiIterator j = (*m_presult)[index].second; while(i != j) { if((position == last) || (traits_inst.translate(*position, icase) != traits_inst.translate(*i, icase))) return false; ++i; ++position; } pstate = pstate->next.p; return true; } template bool perl_matcher::match_long_set() { typedef typename traits::char_class_type char_class_type; // let the traits class do the work: if(position == last) return false; BidiIterator t = re_is_set_member(position, last, static_cast*>(pstate), re.get_data(), icase); if(t != position) { pstate = pstate->next.p; position = t; return true; } return false; } template bool perl_matcher::match_set() { if(position == last) return false; if(static_cast(pstate)->_map[static_cast(traits_inst.translate(*position, icase))]) { pstate = pstate->next.p; ++position; return true; } return false; } template bool perl_matcher::match_jump() { pstate = static_cast(pstate)->alt.p; return true; } template bool perl_matcher::match_combining() { if(position == last) return false; if(is_combining(traits_inst.translate(*position, icase))) return false; ++position; while((position != last) && is_combining(traits_inst.translate(*position, icase))) ++position; pstate = pstate->next.p; return true; } template bool perl_matcher::match_soft_buffer_end() { if(m_match_flags & match_not_eob) return false; BidiIterator p(position); while((p != last) && is_separator(traits_inst.translate(*p, icase)))++p; if(p != last) return false; pstate = pstate->next.p; return true; } template bool perl_matcher::match_restart_continue() { if(position == search_base) { pstate = pstate->next.p; return true; } return false; } template bool perl_matcher::match_backstep() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif if( ::boost::is_random_access_iterator::value) { std::ptrdiff_t maxlen = ::boost::BOOST_REGEX_DETAIL_NS::distance(backstop, position); if(maxlen < static_cast(pstate)->index) return false; std::advance(position, -static_cast(pstate)->index); } else { int c = static_cast(pstate)->index; while(c--) { if(position == backstop) return false; --position; } } pstate = pstate->next.p; return true; #ifdef BOOST_MSVC #pragma warning(pop) #endif } template inline bool perl_matcher::match_assert_backref() { // return true if marked sub-expression N has been matched: int index = static_cast(pstate)->index; bool result = false; if(index == 9999) { // Magic value for a (DEFINE) block: return false; } else if(index > 0) { // Have we matched subexpression "index"? // Check if index is a hash value: if(index >= hash_value_mask) { named_subexpressions::range_type r = re.get_data().equal_range(index); while(r.first != r.second) { if((*m_presult)[r.first->index].matched) { result = true; break; } ++r.first; } } else { result = (*m_presult)[index].matched; } pstate = pstate->next.p; } else { // Have we recursed into subexpression "index"? // If index == 0 then check for any recursion at all, otherwise for recursion to -index-1. int idx = -(index+1); if(idx >= hash_value_mask) { named_subexpressions::range_type r = re.get_data().equal_range(idx); int stack_index = recursion_stack.empty() ? -1 : recursion_stack.back().idx; while(r.first != r.second) { result |= (stack_index == r.first->index); if(result)break; ++r.first; } } else { result = !recursion_stack.empty() && ((recursion_stack.back().idx == idx) || (index == 0)); } pstate = pstate->next.p; } return result; } template bool perl_matcher::match_fail() { // Just force a backtrack: return false; } template bool perl_matcher::match_accept() { if(!recursion_stack.empty()) { return skip_until_paren(recursion_stack.back().idx); } else { return skip_until_paren(INT_MAX); } } template bool perl_matcher::find_restart_any() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif const unsigned char* _map = re.get_map(); while(true) { // skip everything we can't match: while((position != last) && !can_start(*position, _map, (unsigned char)mask_any) ) ++position; if(position == last) { // run out of characters, try a null match if possible: if(re.can_be_null()) return match_prefix(); break; } // now try and obtain a match: if(match_prefix()) return true; if(position == last) return false; ++position; } return false; #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::find_restart_word() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif // do search optimised for word starts: const unsigned char* _map = re.get_map(); if((m_match_flags & match_prev_avail) || (position != base)) --position; else if(match_prefix()) return true; do { while((position != last) && traits_inst.isctype(*position, m_word_mask)) ++position; while((position != last) && !traits_inst.isctype(*position, m_word_mask)) ++position; if(position == last) break; if(can_start(*position, _map, (unsigned char)mask_any) ) { if(match_prefix()) return true; } if(position == last) break; } while(true); return false; #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::find_restart_line() { // do search optimised for line starts: const unsigned char* _map = re.get_map(); if(match_prefix()) return true; while(position != last) { while((position != last) && !is_separator(*position)) ++position; if(position == last) return false; ++position; if(position == last) { if(re.can_be_null() && match_prefix()) return true; return false; } if( can_start(*position, _map, (unsigned char)mask_any) ) { if(match_prefix()) return true; } if(position == last) return false; //++position; } return false; } template bool perl_matcher::find_restart_buf() { if((position == base) && ((m_match_flags & match_not_bob) == 0)) return match_prefix(); return false; } template bool perl_matcher::find_restart_lit() { #if 0 if(position == last) return false; // can't possibly match if we're at the end already unsigned type = (m_match_flags & match_continuous) ? static_cast(regbase::restart_continue) : static_cast(re.get_restart_type()); const kmp_info* info = access::get_kmp(re); int len = info->len; const char_type* x = info->pstr; int j = 0; while (position != last) { while((j > -1) && (x[j] != traits_inst.translate(*position, icase))) j = info->kmp_next[j]; ++position; ++j; if(j >= len) { if(type == regbase::restart_fixed_lit) { std::advance(position, -j); restart = position; std::advance(restart, len); m_result.set_first(position); m_result.set_second(restart); position = restart; return true; } else { restart = position; std::advance(position, -j); if(match_prefix()) return true; else { for(int k = 0; (restart != position) && (k < j); ++k, --restart) {} // dwa 10/20/2000 - warning suppression for MWCW if(restart != last) ++restart; position = restart; j = 0; //we could do better than this... } } } } if((m_match_flags & match_partial) && (position == last) && j) { // we need to check for a partial match: restart = position; std::advance(position, -j); return match_prefix(); } #endif return false; } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_MSVC # pragma warning(pop) #endif #ifdef BOOST_BORLANDC # pragma option pop #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/perl_matcher_non_recursive.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE perl_matcher_common.cpp * VERSION see * DESCRIPTION: Definitions of perl_matcher member functions that are * specific to the non-recursive implementation. */ #ifndef BOOST_REGEX_V4_PERL_MATCHER_NON_RECURSIVE_HPP #define BOOST_REGEX_V4_PERL_MATCHER_NON_RECURSIVE_HPP #include #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC # pragma warning(push) # pragma warning(disable: 4706) #if BOOST_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ template inline void inplace_destroy(T* p) { (void)p; // warning suppression p->~T(); } struct saved_state { union{ unsigned int state_id; // this padding ensures correct alignment on 64-bit platforms: std::size_t padding1; std::ptrdiff_t padding2; void* padding3; }; saved_state(unsigned i) : state_id(i) {} }; template struct saved_matched_paren : public saved_state { int index; sub_match sub; saved_matched_paren(int i, const sub_match& s) : saved_state(1), index(i), sub(s){} }; template struct saved_position : public saved_state { const re_syntax_base* pstate; BidiIterator position; saved_position(const re_syntax_base* ps, BidiIterator pos, int i) : saved_state(i), pstate(ps), position(pos){} }; template struct saved_assertion : public saved_position { bool positive; saved_assertion(bool p, const re_syntax_base* ps, BidiIterator pos) : saved_position(ps, pos, saved_type_assertion), positive(p){} }; template struct saved_repeater : public saved_state { repeater_count count; saved_repeater(int i, repeater_count** s, BidiIterator start, int current_recursion_id) : saved_state(saved_state_repeater_count), count(i, s, start, current_recursion_id){} }; struct saved_extra_block : public saved_state { saved_state *base, *end; saved_extra_block(saved_state* b, saved_state* e) : saved_state(saved_state_extra_block), base(b), end(e) {} }; struct save_state_init { saved_state** stack; save_state_init(saved_state** base, saved_state** end) : stack(base) { *base = static_cast(get_mem_block()); *end = reinterpret_cast(reinterpret_cast(*base)+BOOST_REGEX_BLOCKSIZE); --(*end); (void) new (*end)saved_state(0); BOOST_REGEX_ASSERT(*end > *base); } ~save_state_init() { put_mem_block(*stack); *stack = 0; } }; template struct saved_single_repeat : public saved_state { std::size_t count; const re_repeat* rep; BidiIterator last_position; saved_single_repeat(std::size_t c, const re_repeat* r, BidiIterator lp, int arg_id) : saved_state(arg_id), count(c), rep(r), last_position(lp){} }; template struct saved_recursion : public saved_state { saved_recursion(int idx, const re_syntax_base* p, Results* pr, Results* pr2) : saved_state(14), recursion_id(idx), preturn_address(p), internal_results(*pr), prior_results(*pr2) {} int recursion_id; const re_syntax_base* preturn_address; Results internal_results, prior_results; }; struct saved_change_case : public saved_state { bool icase; saved_change_case(bool c) : saved_state(18), icase(c) {} }; struct incrementer { incrementer(unsigned* pu) : m_pu(pu) { ++*m_pu; } ~incrementer() { --*m_pu; } bool operator > (unsigned i) { return *m_pu > i; } private: unsigned* m_pu; }; template bool perl_matcher::match_all_states() { static matcher_proc_type const s_match_vtable[34] = { (&perl_matcher::match_startmark), &perl_matcher::match_endmark, &perl_matcher::match_literal, &perl_matcher::match_start_line, &perl_matcher::match_end_line, &perl_matcher::match_wild, &perl_matcher::match_match, &perl_matcher::match_word_boundary, &perl_matcher::match_within_word, &perl_matcher::match_word_start, &perl_matcher::match_word_end, &perl_matcher::match_buffer_start, &perl_matcher::match_buffer_end, &perl_matcher::match_backref, &perl_matcher::match_long_set, &perl_matcher::match_set, &perl_matcher::match_jump, &perl_matcher::match_alt, &perl_matcher::match_rep, &perl_matcher::match_combining, &perl_matcher::match_soft_buffer_end, &perl_matcher::match_restart_continue, // Although this next line *should* be evaluated at compile time, in practice // some compilers (VC++) emit run-time initialisation which breaks thread // safety, so use a dispatch function instead: //(::boost::is_random_access_iterator::value ? &perl_matcher::match_dot_repeat_fast : &perl_matcher::match_dot_repeat_slow), &perl_matcher::match_dot_repeat_dispatch, &perl_matcher::match_char_repeat, &perl_matcher::match_set_repeat, &perl_matcher::match_long_set_repeat, &perl_matcher::match_backstep, &perl_matcher::match_assert_backref, &perl_matcher::match_toggle_case, &perl_matcher::match_recursion, &perl_matcher::match_fail, &perl_matcher::match_accept, &perl_matcher::match_commit, &perl_matcher::match_then, }; incrementer inc(&m_recursions); if(inc > 80) raise_error(traits_inst, regex_constants::error_complexity); push_recursion_stopper(); do{ while(pstate) { matcher_proc_type proc = s_match_vtable[pstate->type]; ++state_count; if(!(this->*proc)()) { if(state_count > max_state_count) raise_error(traits_inst, regex_constants::error_complexity); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; bool successful_unwind = unwind(false); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(!successful_unwind) return m_recursive_result; } } }while(unwind(true)); return m_recursive_result; } template void perl_matcher::extend_stack() { if(used_block_count) { --used_block_count; saved_state* stack_base; saved_state* backup_state; stack_base = static_cast(get_mem_block()); backup_state = reinterpret_cast(reinterpret_cast(stack_base)+BOOST_REGEX_BLOCKSIZE); saved_extra_block* block = static_cast(backup_state); --block; (void) new (block) saved_extra_block(m_stack_base, m_backup_state); m_stack_base = stack_base; m_backup_state = block; } else raise_error(traits_inst, regex_constants::error_stack); } template inline void perl_matcher::push_matched_paren(int index, const sub_match& sub) { //BOOST_REGEX_ASSERT(index); saved_matched_paren* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_matched_paren(index, sub); m_backup_state = pmp; } template inline void perl_matcher::push_case_change(bool c) { //BOOST_REGEX_ASSERT(index); saved_change_case* pmp = static_cast(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast(m_backup_state); --pmp; } (void) new (pmp)saved_change_case(c); m_backup_state = pmp; } template inline void perl_matcher::push_recursion_stopper() { saved_state* pmp = m_backup_state; --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = m_backup_state; --pmp; } (void) new (pmp)saved_state(saved_type_recurse); m_backup_state = pmp; } template inline void perl_matcher::push_assertion(const re_syntax_base* ps, bool positive) { saved_assertion* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_assertion(positive, ps, position); m_backup_state = pmp; } template inline void perl_matcher::push_alt(const re_syntax_base* ps) { saved_position* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_position(ps, position, saved_state_alt); m_backup_state = pmp; } template inline void perl_matcher::push_non_greedy_repeat(const re_syntax_base* ps) { saved_position* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_position(ps, position, saved_state_non_greedy_long_repeat); m_backup_state = pmp; } template inline void perl_matcher::push_repeater_count(int i, repeater_count** s) { saved_repeater* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_repeater(i, s, position, this->recursion_stack.empty() ? (INT_MIN + 3) : this->recursion_stack.back().idx); m_backup_state = pmp; } template inline void perl_matcher::push_single_repeat(std::size_t c, const re_repeat* r, BidiIterator last_position, int state_id) { saved_single_repeat* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_single_repeat(c, r, last_position, state_id); m_backup_state = pmp; } template inline void perl_matcher::push_recursion(int idx, const re_syntax_base* p, results_type* presults, results_type* presults2) { saved_recursion* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_recursion(idx, p, presults, presults2); m_backup_state = pmp; } template bool perl_matcher::match_toggle_case() { // change our case sensitivity: push_case_change(this->icase); this->icase = static_cast(pstate)->icase; pstate = pstate->next.p; return true; } template bool perl_matcher::match_startmark() { int index = static_cast(pstate)->index; icase = static_cast(pstate)->icase; switch(index) { case 0: pstate = pstate->next.p; break; case -1: case -2: { // forward lookahead assert: const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; push_assertion(next_pstate, index == -1); break; } case -3: { // independent sub-expression, currently this is always recursive: bool old_independent = m_independent; m_independent = true; const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; bool r = false; #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif r = match_all_states(); if(!r && !m_independent) { // Must be unwinding from a COMMIT/SKIP/PRUNE and the independent // sub failed, need to unwind everything else: while(unwind(false)); return false; } #if !defined(BOOST_NO_EXCEPTIONS) } catch(...) { pstate = next_pstate; // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)) {} throw; } #endif pstate = next_pstate; m_independent = old_independent; #ifdef BOOST_REGEX_MATCH_EXTRA if(r && (m_match_flags & match_extra)) { // // our captures have been stored in *m_presult // we need to unpack them, and insert them // back in the right order when we unwind the stack: // match_results temp_match(*m_presult); unsigned i; for(i = 0; i < temp_match.size(); ++i) (*m_presult)[i].get_captures().clear(); // match everything else: #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif r = match_all_states(); #if !defined(BOOST_NO_EXCEPTIONS) } catch(...) { pstate = next_pstate; // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)) {} throw; } #endif // now place the stored captures back: for(i = 0; i < temp_match.size(); ++i) { typedef typename sub_match::capture_sequence_type seq; seq& s1 = (*m_presult)[i].get_captures(); const seq& s2 = temp_match[i].captures(); s1.insert( s1.end(), s2.begin(), s2.end()); } } #endif return r; } case -4: { // conditional expression: const re_alt* alt = static_cast(pstate->next.p); BOOST_REGEX_ASSERT(alt->type == syntax_element_alt); pstate = alt->next.p; if(pstate->type == syntax_element_assert_backref) { if(!match_assert_backref()) pstate = alt->alt.p; break; } else { // zero width assertion, have to match this recursively: BOOST_REGEX_ASSERT(pstate->type == syntax_element_startmark); bool negated = static_cast(pstate)->index == -2; BidiIterator saved_position = position; const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif bool r = match_all_states(); position = saved_position; if(negated) r = !r; if(r) pstate = next_pstate; else pstate = alt->alt.p; #if !defined(BOOST_NO_EXCEPTIONS) } catch(...) { pstate = next_pstate; // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)){} throw; } #endif break; } } case -5: { push_matched_paren(0, (*m_presult)[0]); m_presult->set_first(position, 0, true); pstate = pstate->next.p; break; } default: { BOOST_REGEX_ASSERT(index > 0); if((m_match_flags & match_nosubs) == 0) { push_matched_paren(index, (*m_presult)[index]); m_presult->set_first(position, index); } pstate = pstate->next.p; break; } } return true; } template bool perl_matcher::match_alt() { bool take_first, take_second; const re_alt* jmp = static_cast(pstate); // find out which of these two alternatives we need to take: if(position == last) { take_first = jmp->can_be_null & mask_take; take_second = jmp->can_be_null & mask_skip; } else { take_first = can_start(*position, jmp->_map, (unsigned char)mask_take); take_second = can_start(*position, jmp->_map, (unsigned char)mask_skip); } if(take_first) { // we can take the first alternative, // see if we need to push next alternative: if(take_second) { push_alt(jmp->alt.p); } pstate = pstate->next.p; return true; } if(take_second) { pstate = jmp->alt.p; return true; } return false; // neither option is possible } template bool perl_matcher::match_rep() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127 4244) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif const re_repeat* rep = static_cast(pstate); // find out which of these two alternatives we need to take: bool take_first, take_second; if(position == last) { take_first = rep->can_be_null & mask_take; take_second = rep->can_be_null & mask_skip; } else { take_first = can_start(*position, rep->_map, (unsigned char)mask_take); take_second = can_start(*position, rep->_map, (unsigned char)mask_skip); } if((m_backup_state->state_id != saved_state_repeater_count) || (static_cast*>(m_backup_state)->count.get_id() != rep->state_id) || (next_count->get_id() != rep->state_id)) { // we're moving to a different repeat from the last // one, so set up a counter object: push_repeater_count(rep->state_id, &next_count); } // // If we've had at least one repeat already, and the last one // matched the NULL string then set the repeat count to // maximum: // next_count->check_null_repeat(position, rep->max); if(next_count->get_count() < rep->min) { // we must take the repeat: if(take_first) { // increase the counter: ++(*next_count); pstate = rep->next.p; return true; } return false; } bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); if(greedy) { // try and take the repeat if we can: if((next_count->get_count() < rep->max) && take_first) { if(take_second) { // store position in case we fail: push_alt(rep->alt.p); } // increase the counter: ++(*next_count); pstate = rep->next.p; return true; } else if(take_second) { pstate = rep->alt.p; return true; } return false; // can't take anything, fail... } else // non-greedy { // try and skip the repeat if we can: if(take_second) { if((next_count->get_count() < rep->max) && take_first) { // store position in case we fail: push_non_greedy_repeat(rep->next.p); } pstate = rep->alt.p; return true; } if((next_count->get_count() < rep->max) && take_first) { // increase the counter: ++(*next_count); pstate = rep->next.p; return true; } } return false; #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_dot_repeat_slow() { std::size_t count = 0; const re_repeat* rep = static_cast(pstate); re_syntax_base* psingle = rep->next.p; // match compulsory repeats first: while(count < rep->min) { pstate = psingle; if(!match_wild()) return false; ++count; } bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); if(greedy) { // repeat for as long as we can: while(count < rep->max) { pstate = psingle; if(!match_wild()) break; ++count; } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_slow_dot); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } } template bool perl_matcher::match_dot_repeat_fast() { if(m_match_flags & match_not_dot_null) return match_dot_repeat_slow(); if((static_cast(pstate->next.p)->mask & match_any_mask) == 0) return match_dot_repeat_slow(); const re_repeat* rep = static_cast(pstate); bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t count = static_cast((std::min)(static_cast(::boost::BOOST_REGEX_DETAIL_NS::distance(position, last)), greedy ? rep->max : rep->min)); if(rep->min > count) { position = last; return false; // not enough text left to match } std::advance(position, count); if(greedy) { if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_fast_dot); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } } template bool perl_matcher::match_char_repeat() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif const re_repeat* rep = static_cast(pstate); BOOST_REGEX_ASSERT(1 == static_cast(rep->next.p)->length); const char_type what = *reinterpret_cast(static_cast(rep->next.p) + 1); std::size_t count = 0; // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t desired = greedy ? rep->max : rep->min; if(::boost::is_random_access_iterator::value) { BidiIterator end = position; // Move end forward by "desired", preferably without using distance or advance if we can // as these can be slow for some iterator types. std::size_t len = (desired == (std::numeric_limits::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last); if(desired >= len) end = last; else std::advance(end, desired); BidiIterator origin(position); while((position != end) && (traits_inst.translate(*position, icase) == what)) { ++position; } count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position); } else { while((count < desired) && (position != last) && (traits_inst.translate(*position, icase) == what)) { ++position; ++count; } } if(count < rep->min) return false; if(greedy) { if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_char); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_set_repeat() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif const re_repeat* rep = static_cast(pstate); const unsigned char* map = static_cast(rep->next.p)->_map; std::size_t count = 0; // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t desired = greedy ? rep->max : rep->min; if(::boost::is_random_access_iterator::value) { BidiIterator end = position; // Move end forward by "desired", preferably without using distance or advance if we can // as these can be slow for some iterator types. std::size_t len = (desired == (std::numeric_limits::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last); if(desired >= len) end = last; else std::advance(end, desired); BidiIterator origin(position); while((position != end) && map[static_cast(traits_inst.translate(*position, icase))]) { ++position; } count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position); } else { while((count < desired) && (position != last) && map[static_cast(traits_inst.translate(*position, icase))]) { ++position; ++count; } } if(count < rep->min) return false; if(greedy) { if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_short_set); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_long_set_repeat() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif typedef typename traits::char_class_type m_type; const re_repeat* rep = static_cast(pstate); const re_set_long* set = static_cast*>(pstate->next.p); std::size_t count = 0; // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t desired = greedy ? rep->max : rep->min; if(::boost::is_random_access_iterator::value) { BidiIterator end = position; // Move end forward by "desired", preferably without using distance or advance if we can // as these can be slow for some iterator types. std::size_t len = (desired == (std::numeric_limits::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last); if(desired >= len) end = last; else std::advance(end, desired); BidiIterator origin(position); while((position != end) && (position != re_is_set_member(position, last, set, re.get_data(), icase))) { ++position; } count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position); } else { while((count < desired) && (position != last) && (position != re_is_set_member(position, last, set, re.get_data(), icase))) { ++position; ++count; } } if(count < rep->min) return false; if(greedy) { if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_long_set); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_recursion() { BOOST_REGEX_ASSERT(pstate->type == syntax_element_recurse); // // See if we've seen this recursion before at this location, if we have then // we need to prevent infinite recursion: // for(typename std::vector >::reverse_iterator i = recursion_stack.rbegin(); i != recursion_stack.rend(); ++i) { if(i->idx == static_cast(static_cast(pstate)->alt.p)->index) { if(i->location_of_start == position) return false; break; } } // // Backup call stack: // push_recursion_pop(); // // Set new call stack: // if(recursion_stack.capacity() == 0) { recursion_stack.reserve(50); } recursion_stack.push_back(recursion_info()); recursion_stack.back().preturn_address = pstate->next.p; recursion_stack.back().results = *m_presult; pstate = static_cast(pstate)->alt.p; recursion_stack.back().idx = static_cast(pstate)->index; recursion_stack.back().location_of_start = position; //if(static_cast(pstate)->state_id > 0) { push_repeater_count(-(2 + static_cast(pstate)->index), &next_count); } return true; } template bool perl_matcher::match_endmark() { int index = static_cast(pstate)->index; icase = static_cast(pstate)->icase; if(index > 0) { if((m_match_flags & match_nosubs) == 0) { m_presult->set_second(position, index); } if(!recursion_stack.empty()) { if(index == recursion_stack.back().idx) { pstate = recursion_stack.back().preturn_address; *m_presult = recursion_stack.back().results; push_recursion(recursion_stack.back().idx, recursion_stack.back().preturn_address, m_presult, &recursion_stack.back().results); recursion_stack.pop_back(); push_repeater_count(-(2 + index), &next_count); } } } else if((index < 0) && (index != -4)) { // matched forward lookahead: pstate = 0; return true; } pstate = pstate->next.p; return true; } template bool perl_matcher::match_match() { if(!recursion_stack.empty()) { BOOST_REGEX_ASSERT(0 == recursion_stack.back().idx); pstate = recursion_stack.back().preturn_address; push_recursion(recursion_stack.back().idx, recursion_stack.back().preturn_address, m_presult, &recursion_stack.back().results); *m_presult = recursion_stack.back().results; recursion_stack.pop_back(); return true; } if((m_match_flags & match_not_null) && (position == (*m_presult)[0].first)) return false; if((m_match_flags & match_all) && (position != last)) return false; if((m_match_flags & regex_constants::match_not_initial_null) && (position == search_base)) return false; m_presult->set_second(position); pstate = 0; m_has_found_match = true; if((m_match_flags & match_posix) == match_posix) { m_result.maybe_assign(*m_presult); if((m_match_flags & match_any) == 0) return false; } #ifdef BOOST_REGEX_MATCH_EXTRA if(match_extra & m_match_flags) { for(unsigned i = 0; i < m_presult->size(); ++i) if((*m_presult)[i].matched) ((*m_presult)[i]).get_captures().push_back((*m_presult)[i]); } #endif return true; } template bool perl_matcher::match_commit() { // Ideally we would just junk all the states that are on the stack, // however we might not unwind correctly in that case, so for now, // just mark that we don't backtrack into whatever is left (or rather // we'll unwind it unconditionally without pausing to try other matches). switch(static_cast(pstate)->action) { case commit_commit: restart = last; break; case commit_skip: if(base != position) { restart = position; // Have to decrement restart since it will get incremented again later: --restart; } break; case commit_prune: break; } saved_state* pmp = m_backup_state; --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = m_backup_state; --pmp; } (void) new (pmp)saved_state(16); m_backup_state = pmp; pstate = pstate->next.p; return true; } template bool perl_matcher::match_then() { // Just leave a mark that we need to skip to next alternative: saved_state* pmp = m_backup_state; --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = m_backup_state; --pmp; } (void) new (pmp)saved_state(17); m_backup_state = pmp; pstate = pstate->next.p; return true; } template bool perl_matcher::skip_until_paren(int index, bool have_match) { while(pstate) { if(pstate->type == syntax_element_endmark) { if(static_cast(pstate)->index == index) { if(have_match) return this->match_endmark(); pstate = pstate->next.p; return true; } else { // Unenclosed closing ), occurs when (*ACCEPT) is inside some other // parenthesis which may or may not have other side effects associated with it. const re_syntax_base* sp = pstate; match_endmark(); if(!pstate) { unwind(true); // unwind may leave pstate NULL if we've unwound a forward lookahead, in which // case just move to the next state and keep looking... if (!pstate) pstate = sp->next.p; } } continue; } else if(pstate->type == syntax_element_match) return true; else if(pstate->type == syntax_element_startmark) { int idx = static_cast(pstate)->index; pstate = pstate->next.p; skip_until_paren(idx, false); continue; } pstate = pstate->next.p; } return true; } /**************************************************************************** Unwind and associated procedures follow, these perform what normal stack unwinding does in the recursive implementation. ****************************************************************************/ template bool perl_matcher::unwind(bool have_match) { static unwind_proc_type const s_unwind_table[19] = { &perl_matcher::unwind_end, &perl_matcher::unwind_paren, &perl_matcher::unwind_recursion_stopper, &perl_matcher::unwind_assertion, &perl_matcher::unwind_alt, &perl_matcher::unwind_repeater_counter, &perl_matcher::unwind_extra_block, &perl_matcher::unwind_greedy_single_repeat, &perl_matcher::unwind_slow_dot_repeat, &perl_matcher::unwind_fast_dot_repeat, &perl_matcher::unwind_char_repeat, &perl_matcher::unwind_short_set_repeat, &perl_matcher::unwind_long_set_repeat, &perl_matcher::unwind_non_greedy_repeat, &perl_matcher::unwind_recursion, &perl_matcher::unwind_recursion_pop, &perl_matcher::unwind_commit, &perl_matcher::unwind_then, &perl_matcher::unwind_case, }; m_recursive_result = have_match; m_unwound_lookahead = false; m_unwound_alt = false; unwind_proc_type unwinder; bool cont; // // keep unwinding our stack until we have something to do: // do { unwinder = s_unwind_table[m_backup_state->state_id]; cont = (this->*unwinder)(m_recursive_result); }while(cont); // // return true if we have more states to try: // return pstate ? true : false; } template bool perl_matcher::unwind_end(bool) { pstate = 0; // nothing left to search return false; // end of stack nothing more to search } template bool perl_matcher::unwind_case(bool) { saved_change_case* pmp = static_cast(m_backup_state); icase = pmp->icase; boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; } template bool perl_matcher::unwind_paren(bool have_match) { saved_matched_paren* pmp = static_cast*>(m_backup_state); // restore previous values if no match was found: if(!have_match) { m_presult->set_first(pmp->sub.first, pmp->index, pmp->index == 0); m_presult->set_second(pmp->sub.second, pmp->index, pmp->sub.matched, pmp->index == 0); } #ifdef BOOST_REGEX_MATCH_EXTRA // // we have a match, push the capture information onto the stack: // else if(pmp->sub.matched && (match_extra & m_match_flags)) ((*m_presult)[pmp->index]).get_captures().push_back(pmp->sub); #endif // unwind stack: m_backup_state = pmp+1; boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp); return true; // keep looking } template bool perl_matcher::unwind_recursion_stopper(bool) { boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++); pstate = 0; // nothing left to search return false; // end of stack nothing more to search } template bool perl_matcher::unwind_assertion(bool r) { saved_assertion* pmp = static_cast*>(m_backup_state); pstate = pmp->pstate; position = pmp->position; bool result = (r == pmp->positive); m_recursive_result = pmp->positive ? r : !r; boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; m_unwound_lookahead = true; return !result; // return false if the assertion was matched to stop search. } template bool perl_matcher::unwind_alt(bool r) { saved_position* pmp = static_cast*>(m_backup_state); if(!r) { pstate = pmp->pstate; position = pmp->position; } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; m_unwound_alt = !r; return r; } template bool perl_matcher::unwind_repeater_counter(bool) { ++used_block_count; saved_repeater* pmp = static_cast*>(m_backup_state); boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; // keep looking } template bool perl_matcher::unwind_extra_block(bool) { saved_extra_block* pmp = static_cast(m_backup_state); void* condemmed = m_stack_base; m_stack_base = pmp->base; m_backup_state = pmp->end; boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp); put_mem_block(condemmed); return true; // keep looking } template inline void perl_matcher::destroy_single_repeat() { saved_single_repeat* p = static_cast*>(m_backup_state); boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(p++); m_backup_state = p; } template bool perl_matcher::unwind_greedy_single_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); count -= rep->min; if((m_match_flags & match_partial) && (position == last)) m_has_partial_match = true; BOOST_REGEX_ASSERT(count); position = pmp->last_position; // backtrack till we can skip out: do { --position; --count; ++state_count; }while(count && !can_start(*position, rep->_map, mask_skip)); // if we've hit base, destroy this state: if(count == 0) { destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count + rep->min; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_slow_dot_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; BOOST_REGEX_ASSERT(rep->type == syntax_element_dot_rep); BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_wild); BOOST_REGEX_ASSERT(count < rep->max); pstate = rep->next.p; position = pmp->last_position; if(position != last) { // wind forward until we can skip out of the repeat: do { if(!match_wild()) { // failed repeat match, discard this state and look for another: destroy_single_repeat(); return true; } ++count; ++state_count; pstate = rep->next.p; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_fast_dot_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; BOOST_REGEX_ASSERT(count < rep->max); position = pmp->last_position; if(position != last) { // wind forward until we can skip out of the repeat: do { ++position; ++count; ++state_count; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_char_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; pstate = rep->next.p; const char_type what = *reinterpret_cast(static_cast(pstate) + 1); position = pmp->last_position; BOOST_REGEX_ASSERT(rep->type == syntax_element_char_rep); BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_literal); BOOST_REGEX_ASSERT(count < rep->max); if(position != last) { // wind forward until we can skip out of the repeat: do { if(traits_inst.translate(*position, icase) != what) { // failed repeat match, discard this state and look for another: destroy_single_repeat(); return true; } ++count; ++ position; ++state_count; pstate = rep->next.p; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_short_set_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; pstate = rep->next.p; const unsigned char* map = static_cast(rep->next.p)->_map; position = pmp->last_position; BOOST_REGEX_ASSERT(rep->type == syntax_element_short_set_rep); BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_set); BOOST_REGEX_ASSERT(count < rep->max); if(position != last) { // wind forward until we can skip out of the repeat: do { if(!map[static_cast(traits_inst.translate(*position, icase))]) { // failed repeat match, discard this state and look for another: destroy_single_repeat(); return true; } ++count; ++ position; ++state_count; pstate = rep->next.p; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_long_set_repeat(bool r) { typedef typename traits::char_class_type m_type; saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; pstate = rep->next.p; const re_set_long* set = static_cast*>(pstate); position = pmp->last_position; BOOST_REGEX_ASSERT(rep->type == syntax_element_long_set_rep); BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_long_set); BOOST_REGEX_ASSERT(count < rep->max); if(position != last) { // wind forward until we can skip out of the repeat: do { if(position == re_is_set_member(position, last, set, re.get_data(), icase)) { // failed repeat match, discard this state and look for another: destroy_single_repeat(); return true; } ++position; ++count; ++state_count; pstate = rep->next.p; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_non_greedy_repeat(bool r) { saved_position* pmp = static_cast*>(m_backup_state); if(!r) { position = pmp->position; pstate = pmp->pstate; ++(*next_count); } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return r; } template bool perl_matcher::unwind_recursion(bool r) { // We are backtracking back inside a recursion, need to push the info // back onto the recursion stack, and do so unconditionally, otherwise // we can get mismatched pushes and pops... saved_recursion* pmp = static_cast*>(m_backup_state); if (!r) { recursion_stack.push_back(recursion_info()); recursion_stack.back().idx = pmp->recursion_id; recursion_stack.back().preturn_address = pmp->preturn_address; recursion_stack.back().results = pmp->prior_results; recursion_stack.back().location_of_start = position; *m_presult = pmp->internal_results; } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; } template bool perl_matcher::unwind_recursion_pop(bool r) { // Backtracking out of a recursion, we must pop state off the recursion // stack unconditionally to ensure matched pushes and pops: saved_state* pmp = static_cast(m_backup_state); if (!r && !recursion_stack.empty()) { *m_presult = recursion_stack.back().results; position = recursion_stack.back().location_of_start; recursion_stack.pop_back(); } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; } template void perl_matcher::push_recursion_pop() { saved_state* pmp = static_cast(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast(m_backup_state); --pmp; } (void) new (pmp)saved_state(15); m_backup_state = pmp; } template bool perl_matcher::unwind_commit(bool b) { boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++); while(unwind(b) && !m_unwound_lookahead){} if(m_unwound_lookahead && pstate) { // // If we stop because we just unwound an assertion, put the // commit state back on the stack again: // m_unwound_lookahead = false; saved_state* pmp = m_backup_state; --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = m_backup_state; --pmp; } (void) new (pmp)saved_state(16); m_backup_state = pmp; } // This prevents us from stopping when we exit from an independent sub-expression: m_independent = false; return false; } template bool perl_matcher::unwind_then(bool b) { // Unwind everything till we hit an alternative: boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++); bool result = false; while((result = unwind(b)) && !m_unwound_alt){} // We're now pointing at the next alternative, need one more backtrack // since *all* the other alternatives must fail once we've reached a THEN clause: if(result && m_unwound_alt) unwind(b); return false; } /* template bool perl_matcher::unwind_parenthesis_pop(bool r) { saved_state* pmp = static_cast(m_backup_state); if(!r) { --parenthesis_stack_position; } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; } template void perl_matcher::push_parenthesis_pop() { saved_state* pmp = static_cast(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast(m_backup_state); --pmp; } (void) new (pmp)saved_state(16); m_backup_state = pmp; } template bool perl_matcher::unwind_parenthesis_push(bool r) { saved_position* pmp = static_cast*>(m_backup_state); if(!r) { parenthesis_stack[parenthesis_stack_position++] = pmp->position; } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; } template inline void perl_matcher::push_parenthesis_push(BidiIterator p) { saved_position* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_position(0, p, 17); m_backup_state = pmp; } */ } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_MSVC # pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/perl_matcher_recursive.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE perl_matcher_common.cpp * VERSION see * DESCRIPTION: Definitions of perl_matcher member functions that are * specific to the recursive implementation. */ #ifndef BOOST_REGEX_V4_PERL_MATCHER_RECURSIVE_HPP #define BOOST_REGEX_V4_PERL_MATCHER_RECURSIVE_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4800) #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ template class backup_subex { int index; sub_match sub; public: template backup_subex(const match_results& w, int i) : index(i), sub(w[i], false) {} template void restore(match_results& w) { w.set_first(sub.first, index, index == 0); w.set_second(sub.second, index, sub.matched, index == 0); } const sub_match& get() { return sub; } }; template bool perl_matcher::match_all_states() { static matcher_proc_type const s_match_vtable[34] = { (&perl_matcher::match_startmark), &perl_matcher::match_endmark, &perl_matcher::match_literal, &perl_matcher::match_start_line, &perl_matcher::match_end_line, &perl_matcher::match_wild, &perl_matcher::match_match, &perl_matcher::match_word_boundary, &perl_matcher::match_within_word, &perl_matcher::match_word_start, &perl_matcher::match_word_end, &perl_matcher::match_buffer_start, &perl_matcher::match_buffer_end, &perl_matcher::match_backref, &perl_matcher::match_long_set, &perl_matcher::match_set, &perl_matcher::match_jump, &perl_matcher::match_alt, &perl_matcher::match_rep, &perl_matcher::match_combining, &perl_matcher::match_soft_buffer_end, &perl_matcher::match_restart_continue, // Although this next line *should* be evaluated at compile time, in practice // some compilers (VC++) emit run-time initialisation which breaks thread // safety, so use a dispatch function instead: //(::boost::is_random_access_iterator::value ? &perl_matcher::match_dot_repeat_fast : &perl_matcher::match_dot_repeat_slow), &perl_matcher::match_dot_repeat_dispatch, &perl_matcher::match_char_repeat, &perl_matcher::match_set_repeat, &perl_matcher::match_long_set_repeat, &perl_matcher::match_backstep, &perl_matcher::match_assert_backref, &perl_matcher::match_toggle_case, &perl_matcher::match_recursion, &perl_matcher::match_fail, &perl_matcher::match_accept, &perl_matcher::match_commit, &perl_matcher::match_then, }; if(state_count > max_state_count) raise_error(traits_inst, regex_constants::error_complexity); while(pstate) { matcher_proc_type proc = s_match_vtable[pstate->type]; ++state_count; if(!(this->*proc)()) { if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; return 0; } } return true; } template bool perl_matcher::match_startmark() { int index = static_cast(pstate)->index; icase = static_cast(pstate)->icase; bool r = true; switch(index) { case 0: pstate = pstate->next.p; break; case -1: case -2: { // forward lookahead assert: BidiIterator old_position(position); const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; r = match_all_states(); pstate = next_pstate; position = old_position; if((r && (index != -1)) || (!r && (index != -2))) r = false; else r = true; if(r && m_have_accept) r = skip_until_paren(INT_MAX); break; } case -3: { // independent sub-expression: bool old_independent = m_independent; m_independent = true; const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; bool can_backtrack = m_can_backtrack; r = match_all_states(); if(r) m_can_backtrack = can_backtrack; pstate = next_pstate; m_independent = old_independent; #ifdef BOOST_REGEX_MATCH_EXTRA if(r && (m_match_flags & match_extra)) { // // our captures have been stored in *m_presult // we need to unpack them, and insert them // back in the right order when we unwind the stack: // unsigned i; match_results tm(*m_presult); for(i = 0; i < tm.size(); ++i) (*m_presult)[i].get_captures().clear(); // match everything else: r = match_all_states(); // now place the stored captures back: for(i = 0; i < tm.size(); ++i) { typedef typename sub_match::capture_sequence_type seq; seq& s1 = (*m_presult)[i].get_captures(); const seq& s2 = tm[i].captures(); s1.insert( s1.end(), s2.begin(), s2.end()); } } #endif if(r && m_have_accept) r = skip_until_paren(INT_MAX); break; } case -4: { // conditional expression: const re_alt* alt = static_cast(pstate->next.p); BOOST_REGEX_ASSERT(alt->type == syntax_element_alt); pstate = alt->next.p; if(pstate->type == syntax_element_assert_backref) { if(!match_assert_backref()) pstate = alt->alt.p; break; } else { // zero width assertion, have to match this recursively: BOOST_REGEX_ASSERT(pstate->type == syntax_element_startmark); bool negated = static_cast(pstate)->index == -2; BidiIterator saved_position = position; const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; bool res = match_all_states(); position = saved_position; if(negated) res = !res; if(res) pstate = next_pstate; else pstate = alt->alt.p; break; } } case -5: { // Reset start of $0, since we have a \K escape backup_subex sub(*m_presult, 0); m_presult->set_first(position, 0, true); pstate = pstate->next.p; r = match_all_states(); if(r == false) sub.restore(*m_presult); break; } default: { BOOST_REGEX_ASSERT(index > 0); if((m_match_flags & match_nosubs) == 0) { backup_subex sub(*m_presult, index); m_presult->set_first(position, index); pstate = pstate->next.p; r = match_all_states(); if(r == false) sub.restore(*m_presult); #ifdef BOOST_REGEX_MATCH_EXTRA // // we have a match, push the capture information onto the stack: // else if(sub.get().matched && (match_extra & m_match_flags)) ((*m_presult)[index]).get_captures().push_back(sub.get()); #endif } else { pstate = pstate->next.p; } break; } } return r; } template bool perl_matcher::match_alt() { bool take_first, take_second; const re_alt* jmp = static_cast(pstate); // find out which of these two alternatives we need to take: if(position == last) { take_first = jmp->can_be_null & mask_take; take_second = jmp->can_be_null & mask_skip; } else { take_first = can_start(*position, jmp->_map, (unsigned char)mask_take); take_second = can_start(*position, jmp->_map, (unsigned char)mask_skip); } if(take_first) { // we can take the first alternative, // see if we need to push next alternative: if(take_second) { BidiIterator oldposition(position); const re_syntax_base* old_pstate = jmp->alt.p; pstate = pstate->next.p; bool oldcase = icase; m_have_then = false; if(!match_all_states()) { pstate = old_pstate; position = oldposition; icase = oldcase; if(m_have_then) { m_can_backtrack = true; m_have_then = false; return false; } } m_have_then = false; return m_can_backtrack; } pstate = pstate->next.p; return true; } if(take_second) { pstate = jmp->alt.p; return true; } return false; // neither option is possible } template bool perl_matcher::match_rep() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127 4244) #endif const re_repeat* rep = static_cast(pstate); // // Always copy the repeat count, so that the state is restored // when we exit this scope: // repeater_count r(rep->state_id, &next_count, position, this->recursion_stack.size() ? this->recursion_stack.back().idx : INT_MIN + 3); // // If we've had at least one repeat already, and the last one // matched the NULL string then set the repeat count to // maximum: // next_count->check_null_repeat(position, rep->max); // find out which of these two alternatives we need to take: bool take_first, take_second; if(position == last) { take_first = rep->can_be_null & mask_take; take_second = rep->can_be_null & mask_skip; } else { take_first = can_start(*position, rep->_map, (unsigned char)mask_take); take_second = can_start(*position, rep->_map, (unsigned char)mask_skip); } if(next_count->get_count() < rep->min) { // we must take the repeat: if(take_first) { // increase the counter: ++(*next_count); pstate = rep->next.p; return match_all_states(); } return false; } bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); if(greedy) { // try and take the repeat if we can: if((next_count->get_count() < rep->max) && take_first) { // store position in case we fail: BidiIterator pos = position; // increase the counter: ++(*next_count); pstate = rep->next.p; if(match_all_states()) return true; if(!m_can_backtrack) return false; // failed repeat, reset posistion and fall through for alternative: position = pos; } if(take_second) { pstate = rep->alt.p; return true; } return false; // can't take anything, fail... } else // non-greedy { // try and skip the repeat if we can: if(take_second) { // store position in case we fail: BidiIterator pos = position; pstate = rep->alt.p; if(match_all_states()) return true; if(!m_can_backtrack) return false; // failed alternative, reset posistion and fall through for repeat: position = pos; } if((next_count->get_count() < rep->max) && take_first) { // increase the counter: ++(*next_count); pstate = rep->next.p; return match_all_states(); } } return false; #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_dot_repeat_slow() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif std::size_t count = 0; const re_repeat* rep = static_cast(pstate); re_syntax_base* psingle = rep->next.p; // match compulsary repeats first: while(count < rep->min) { pstate = psingle; if(!match_wild()) return false; ++count; } bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); if(greedy) { // normal repeat: while(count < rep->max) { pstate = psingle; if(!match_wild()) break; ++count; } if((rep->leading) && (count < rep->max)) restart = position; pstate = rep; return backtrack_till_match(count - rep->min); } else { // non-greedy, keep trying till we get a match: BidiIterator save_pos; do { if((rep->leading) && (rep->max == UINT_MAX)) restart = position; pstate = rep->alt.p; save_pos = position; ++state_count; if(match_all_states()) return true; if((count >= rep->max) || !m_can_backtrack) return false; ++count; pstate = psingle; position = save_pos; if(!match_wild()) return false; }while(true); } #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_dot_repeat_fast() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif if(m_match_flags & match_not_dot_null) return match_dot_repeat_slow(); if((static_cast(pstate->next.p)->mask & match_any_mask) == 0) return match_dot_repeat_slow(); // // start by working out how much we can skip: // const re_repeat* rep = static_cast(pstate); #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4267) #endif bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t count = (std::min)(static_cast(::boost::BOOST_REGEX_DETAIL_NS::distance(position, last)), greedy ? rep->max : rep->min); if(rep->min > count) { position = last; return false; // not enough text left to match } std::advance(position, count); #ifdef BOOST_MSVC #pragma warning(pop) #endif if((rep->leading) && (count < rep->max) && greedy) restart = position; if(greedy) return backtrack_till_match(count - rep->min); // non-greedy, keep trying till we get a match: BidiIterator save_pos; do { while((position != last) && (count < rep->max) && !can_start(*position, rep->_map, mask_skip)) { ++position; ++count; } if((rep->leading) && (rep->max == UINT_MAX)) restart = position; pstate = rep->alt.p; save_pos = position; ++state_count; if(match_all_states()) return true; if((count >= rep->max) || !m_can_backtrack) return false; if(save_pos == last) return false; position = ++save_pos; ++count; }while(true); #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_char_repeat() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #pragma warning(disable:4267) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif const re_repeat* rep = static_cast(pstate); BOOST_REGEX_ASSERT(1 == static_cast(rep->next.p)->length); const char_type what = *reinterpret_cast(static_cast(rep->next.p) + 1); // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t count, desired; if(::boost::is_random_access_iterator::value) { desired = (std::min)( (std::size_t)(greedy ? rep->max : rep->min), (std::size_t)::boost::BOOST_REGEX_DETAIL_NS::distance(position, last)); count = desired; ++desired; if(icase) { while(--desired && (traits_inst.translate_nocase(*position) == what)) { ++position; } } else { while(--desired && (traits_inst.translate(*position) == what)) { ++position; } } count = count - desired; } else { count = 0; desired = greedy ? rep->max : rep->min; while((count < desired) && (position != last) && (traits_inst.translate(*position, icase) == what)) { ++position; ++count; } } if((rep->leading) && (count < rep->max) && greedy) restart = position; if(count < rep->min) return false; if(greedy) return backtrack_till_match(count - rep->min); // non-greedy, keep trying till we get a match: BidiIterator save_pos; do { while((position != last) && (count < rep->max) && !can_start(*position, rep->_map, mask_skip)) { if((traits_inst.translate(*position, icase) == what)) { ++position; ++count; } else return false; // counldn't repeat even though it was the only option } if((rep->leading) && (rep->max == UINT_MAX)) restart = position; pstate = rep->alt.p; save_pos = position; ++state_count; if(match_all_states()) return true; if((count >= rep->max) || !m_can_backtrack) return false; position = save_pos; if(position == last) return false; if(traits_inst.translate(*position, icase) == what) { ++position; ++count; } else { return false; } }while(true); #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_set_repeat() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif const re_repeat* rep = static_cast(pstate); const unsigned char* map = static_cast(rep->next.p)->_map; std::size_t count = 0; // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t desired = greedy ? rep->max : rep->min; if(::boost::is_random_access_iterator::value) { BidiIterator end = position; // Move end forward by "desired", preferably without using distance or advance if we can // as these can be slow for some iterator types. std::size_t len = (desired == (std::numeric_limits::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last); if(desired >= len) end = last; else std::advance(end, desired); BidiIterator origin(position); while((position != end) && map[static_cast(traits_inst.translate(*position, icase))]) { ++position; } count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position); } else { while((count < desired) && (position != last) && map[static_cast(traits_inst.translate(*position, icase))]) { ++position; ++count; } } if((rep->leading) && (count < rep->max) && greedy) restart = position; if(count < rep->min) return false; if(greedy) return backtrack_till_match(count - rep->min); // non-greedy, keep trying till we get a match: BidiIterator save_pos; do { while((position != last) && (count < rep->max) && !can_start(*position, rep->_map, mask_skip)) { if(map[static_cast(traits_inst.translate(*position, icase))]) { ++position; ++count; } else return false; // counldn't repeat even though it was the only option } if((rep->leading) && (rep->max == UINT_MAX)) restart = position; pstate = rep->alt.p; save_pos = position; ++state_count; if(match_all_states()) return true; if((count >= rep->max) || !m_can_backtrack) return false; position = save_pos; if(position == last) return false; if(map[static_cast(traits_inst.translate(*position, icase))]) { ++position; ++count; } else { return false; } }while(true); #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_long_set_repeat() { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif typedef typename traits::char_class_type char_class_type; const re_repeat* rep = static_cast(pstate); const re_set_long* set = static_cast*>(pstate->next.p); std::size_t count = 0; // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t desired = greedy ? rep->max : rep->min; if(::boost::is_random_access_iterator::value) { BidiIterator end = position; // Move end forward by "desired", preferably without using distance or advance if we can // as these can be slow for some iterator types. std::size_t len = (desired == (std::numeric_limits::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last); if(desired >= len) end = last; else std::advance(end, desired); BidiIterator origin(position); while((position != end) && (position != re_is_set_member(position, last, set, re.get_data(), icase))) { ++position; } count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position); } else { while((count < desired) && (position != last) && (position != re_is_set_member(position, last, set, re.get_data(), icase))) { ++position; ++count; } } if((rep->leading) && (count < rep->max) && greedy) restart = position; if(count < rep->min) return false; if(greedy) return backtrack_till_match(count - rep->min); // non-greedy, keep trying till we get a match: BidiIterator save_pos; do { while((position != last) && (count < rep->max) && !can_start(*position, rep->_map, mask_skip)) { if(position != re_is_set_member(position, last, set, re.get_data(), icase)) { ++position; ++count; } else return false; // counldn't repeat even though it was the only option } if((rep->leading) && (rep->max == UINT_MAX)) restart = position; pstate = rep->alt.p; save_pos = position; ++state_count; if(match_all_states()) return true; if((count >= rep->max) || !m_can_backtrack) return false; position = save_pos; if(position == last) return false; if(position != re_is_set_member(position, last, set, re.get_data(), icase)) { ++position; ++count; } else { return false; } }while(true); #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::backtrack_till_match(std::size_t count) { #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif if(!m_can_backtrack) return false; if((m_match_flags & match_partial) && (position == last)) m_has_partial_match = true; const re_repeat* rep = static_cast(pstate); BidiIterator backtrack = position; if(position == last) { if(rep->can_be_null & mask_skip) { pstate = rep->alt.p; if(match_all_states()) return true; } if(count) { position = --backtrack; --count; } else return false; } do { while(count && !can_start(*position, rep->_map, mask_skip)) { --position; --count; ++state_count; } pstate = rep->alt.p; backtrack = position; if(match_all_states()) return true; if(count == 0) return false; position = --backtrack; ++state_count; --count; }while(true); #ifdef BOOST_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_recursion() { BOOST_REGEX_ASSERT(pstate->type == syntax_element_recurse); // // Set new call stack: // if(recursion_stack.capacity() == 0) { recursion_stack.reserve(50); } // // See if we've seen this recursion before at this location, if we have then // we need to prevent infinite recursion: // for(typename std::vector >::reverse_iterator i = recursion_stack.rbegin(); i != recursion_stack.rend(); ++i) { if(i->idx == static_cast(static_cast(pstate)->alt.p)->index) { if(i->location_of_start == position) return false; break; } } // // Now get on with it: // recursion_stack.push_back(recursion_info()); recursion_stack.back().preturn_address = pstate->next.p; recursion_stack.back().results = *m_presult; recursion_stack.back().repeater_stack = next_count; recursion_stack.back().location_of_start = position; pstate = static_cast(pstate)->alt.p; recursion_stack.back().idx = static_cast(pstate)->index; repeater_count* saved = next_count; repeater_count r(&next_count); // resets all repeat counts since we're recursing and starting fresh on those next_count = &r; bool can_backtrack = m_can_backtrack; bool result = match_all_states(); m_can_backtrack = can_backtrack; next_count = saved; if(!result) { next_count = recursion_stack.back().repeater_stack; *m_presult = recursion_stack.back().results; recursion_stack.pop_back(); return false; } return true; } template bool perl_matcher::match_endmark() { int index = static_cast(pstate)->index; icase = static_cast(pstate)->icase; if(index > 0) { if((m_match_flags & match_nosubs) == 0) { m_presult->set_second(position, index); } if(!recursion_stack.empty()) { if(index == recursion_stack.back().idx) { recursion_info saved = recursion_stack.back(); recursion_stack.pop_back(); pstate = saved.preturn_address; repeater_count* saved_count = next_count; next_count = saved.repeater_stack; *m_presult = saved.results; if(!match_all_states()) { recursion_stack.push_back(saved); next_count = saved_count; return false; } } } } else if((index < 0) && (index != -4)) { // matched forward lookahead: pstate = 0; return true; } pstate = pstate ? pstate->next.p : 0; return true; } template bool perl_matcher::match_match() { if(!recursion_stack.empty()) { BOOST_REGEX_ASSERT(0 == recursion_stack.back().idx); const re_syntax_base* saved_state = pstate = recursion_stack.back().preturn_address; *m_presult = recursion_stack.back().results; recursion_stack.pop_back(); if(!match_all_states()) { recursion_stack.push_back(recursion_info()); recursion_stack.back().preturn_address = saved_state; recursion_stack.back().results = *m_presult; recursion_stack.back().location_of_start = position; return false; } return true; } if((m_match_flags & match_not_null) && (position == (*m_presult)[0].first)) return false; if((m_match_flags & match_all) && (position != last)) return false; if((m_match_flags & regex_constants::match_not_initial_null) && (position == search_base)) return false; m_presult->set_second(position); pstate = 0; m_has_found_match = true; if((m_match_flags & match_posix) == match_posix) { m_result.maybe_assign(*m_presult); if((m_match_flags & match_any) == 0) return false; } #ifdef BOOST_REGEX_MATCH_EXTRA if(match_extra & m_match_flags) { for(unsigned i = 0; i < m_presult->size(); ++i) if((*m_presult)[i].matched) ((*m_presult)[i]).get_captures().push_back((*m_presult)[i]); } #endif return true; } template bool perl_matcher::match_commit() { m_can_backtrack = false; int action = static_cast(pstate)->action; switch(action) { case commit_commit: restart = last; break; case commit_skip: restart = position; break; } pstate = pstate->next.p; return true; } template bool perl_matcher::match_then() { pstate = pstate->next.p; if(match_all_states()) return true; m_can_backtrack = false; m_have_then = true; return false; } template bool perl_matcher::match_toggle_case() { // change our case sensitivity: bool oldcase = this->icase; this->icase = static_cast(pstate)->icase; pstate = pstate->next.p; bool result = match_all_states(); this->icase = oldcase; return result; } template bool perl_matcher::skip_until_paren(int index, bool have_match) { while(pstate) { if(pstate->type == syntax_element_endmark) { if(static_cast(pstate)->index == index) { if(have_match) return this->match_endmark(); pstate = pstate->next.p; return true; } else { // Unenclosed closing ), occurs when (*ACCEPT) is inside some other // parenthesis which may or may not have other side effects associated with it. bool r = match_endmark(); m_have_accept = true; if(!pstate) return r; } continue; } else if(pstate->type == syntax_element_match) return true; else if(pstate->type == syntax_element_startmark) { int idx = static_cast(pstate)->index; pstate = pstate->next.p; skip_until_paren(idx, false); continue; } pstate = pstate->next.p; } return true; } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/primary_transform.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE: primary_transform.hpp * VERSION: see * DESCRIPTION: Heuristically determines the sort string format in use * by the current locale. */ #ifndef BOOST_REGEX_PRIMARY_TRANSFORM #define BOOST_REGEX_PRIMARY_TRANSFORM #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ enum{ sort_C, sort_fixed, sort_delim, sort_unknown }; template unsigned count_chars(const S& s, charT c) { // // Count how many occurrences of character c occur // in string s: if c is a delimeter between collation // fields, then this should be the same value for all // sort keys: // unsigned int count = 0; for(unsigned pos = 0; pos < s.size(); ++pos) { if(s[pos] == c) ++count; } return count; } template unsigned find_sort_syntax(const traits* pt, charT* delim) { // // compare 'a' with 'A' to see how similar they are, // should really use a-accute but we can't portably do that, // typedef typename traits::string_type string_type; typedef typename traits::char_type char_type; // Suppress incorrect warning for MSVC (void)pt; char_type a[2] = {'a', '\0', }; string_type sa(pt->transform(a, a+1)); if(sa == a) { *delim = 0; return sort_C; } char_type A[2] = { 'A', '\0', }; string_type sA(pt->transform(A, A+1)); char_type c[2] = { ';', '\0', }; string_type sc(pt->transform(c, c+1)); int pos = 0; while((pos <= static_cast(sa.size())) && (pos <= static_cast(sA.size())) && (sa[pos] == sA[pos])) ++pos; --pos; if(pos < 0) { *delim = 0; return sort_unknown; } // // at this point sa[pos] is either the end of a fixed width field // or the character that acts as a delimiter: // charT maybe_delim = sa[pos]; if((pos != 0) && (count_chars(sa, maybe_delim) == count_chars(sA, maybe_delim)) && (count_chars(sa, maybe_delim) == count_chars(sc, maybe_delim))) { *delim = maybe_delim; return sort_delim; } // // OK doen't look like a delimiter, try for fixed width field: // if((sa.size() == sA.size()) && (sa.size() == sc.size())) { // note assumes that the fixed width field is less than // (numeric_limits::max)(), should be true for all types // I can't imagine 127 character fields... *delim = static_cast(++pos); return sort_fixed; } // // don't know what it is: // *delim = 0; return sort_unknown; } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/protected_call.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE basic_regex_creator.cpp * VERSION see * DESCRIPTION: Declares template class basic_regex_creator which fills in * the data members of a regex_data object. */ #ifndef BOOST_REGEX_V4_PROTECTED_CALL_HPP #define BOOST_REGEX_V4_PROTECTED_CALL_HPP #include #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ class BOOST_REGEX_DECL abstract_protected_call { public: bool BOOST_REGEX_CALL execute()const; // this stops gcc-4 from complaining: virtual ~abstract_protected_call(){} private: virtual bool call()const = 0; }; template class concrete_protected_call : public abstract_protected_call { public: typedef bool (T::*proc_type)(); concrete_protected_call(T* o, proc_type p) : obj(o), proc(p) {} private: bool call()const BOOST_OVERRIDE; T* obj; proc_type proc; }; template bool concrete_protected_call::call()const { return (obj->*proc)(); } } } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regbase.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regbase.cpp * VERSION see * DESCRIPTION: Declares class regbase. */ #ifndef BOOST_REGEX_V4_REGBASE_HPP #define BOOST_REGEX_V4_REGBASE_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ // // class regbase // handles error codes and flags // class BOOST_REGEX_DECL regbase { public: enum flag_type_ { // // Divide the flags up into logical groups: // bits 0-7 indicate main synatx type. // bits 8-15 indicate syntax subtype. // bits 16-31 indicate options that are common to all // regex syntaxes. // In all cases the default is 0. // // Main synatx group: // perl_syntax_group = 0, // default basic_syntax_group = 1, // POSIX basic literal = 2, // all characters are literals main_option_type = literal | basic_syntax_group | perl_syntax_group, // everything! // // options specific to perl group: // no_bk_refs = 1 << 8, // \d not allowed no_perl_ex = 1 << 9, // disable perl extensions no_mod_m = 1 << 10, // disable Perl m modifier mod_x = 1 << 11, // Perl x modifier mod_s = 1 << 12, // force s modifier on (overrides match_not_dot_newline) no_mod_s = 1 << 13, // force s modifier off (overrides match_not_dot_newline) // // options specific to basic group: // no_char_classes = 1 << 8, // [[:CLASS:]] not allowed no_intervals = 1 << 9, // {x,y} not allowed bk_plus_qm = 1 << 10, // uses \+ and \? bk_vbar = 1 << 11, // use \| for alternatives emacs_ex = 1 << 12, // enables emacs extensions // // options common to all groups: // no_escape_in_lists = 1 << 16, // '\' not special inside [...] newline_alt = 1 << 17, // \n is the same as | no_except = 1 << 18, // no exception on error failbit = 1 << 19, // error flag icase = 1 << 20, // characters are matched regardless of case nocollate = 0, // don't use locale specific collation (deprecated) collate = 1 << 21, // use locale specific collation nosubs = 1 << 22, // don't mark sub-expressions save_subexpression_location = 1 << 23, // save subexpression locations no_empty_expressions = 1 << 24, // no empty expressions allowed optimize = 0, // not really supported basic = basic_syntax_group | collate | no_escape_in_lists, extended = no_bk_refs | collate | no_perl_ex | no_escape_in_lists, normal = 0, emacs = basic_syntax_group | collate | emacs_ex | bk_vbar, awk = no_bk_refs | collate | no_perl_ex, grep = basic | newline_alt, egrep = extended | newline_alt, sed = basic, perl = normal, ECMAScript = normal, JavaScript = normal, JScript = normal }; typedef unsigned int flag_type; enum restart_info { restart_any = 0, restart_word = 1, restart_line = 2, restart_buf = 3, restart_continue = 4, restart_lit = 5, restart_fixed_lit = 6, restart_count = 7 }; }; // // provide std lib proposal compatible constants: // namespace regex_constants{ enum flag_type_ { no_except = ::boost::regbase::no_except, failbit = ::boost::regbase::failbit, literal = ::boost::regbase::literal, icase = ::boost::regbase::icase, nocollate = ::boost::regbase::nocollate, collate = ::boost::regbase::collate, nosubs = ::boost::regbase::nosubs, optimize = ::boost::regbase::optimize, bk_plus_qm = ::boost::regbase::bk_plus_qm, bk_vbar = ::boost::regbase::bk_vbar, no_intervals = ::boost::regbase::no_intervals, no_char_classes = ::boost::regbase::no_char_classes, no_escape_in_lists = ::boost::regbase::no_escape_in_lists, no_mod_m = ::boost::regbase::no_mod_m, mod_x = ::boost::regbase::mod_x, mod_s = ::boost::regbase::mod_s, no_mod_s = ::boost::regbase::no_mod_s, save_subexpression_location = ::boost::regbase::save_subexpression_location, no_empty_expressions = ::boost::regbase::no_empty_expressions, basic = ::boost::regbase::basic, extended = ::boost::regbase::extended, normal = ::boost::regbase::normal, emacs = ::boost::regbase::emacs, awk = ::boost::regbase::awk, grep = ::boost::regbase::grep, egrep = ::boost::regbase::egrep, sed = basic, perl = normal, ECMAScript = normal, JavaScript = normal, JScript = normal }; typedef ::boost::regbase::flag_type syntax_option_type; } // namespace regex_constants } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex.cpp * VERSION see * DESCRIPTION: Declares boost::basic_regex<> and associated * functions and classes. This header is the main * entry point for the template regex code. */ #ifndef BOOST_RE_REGEX_HPP_INCLUDED #define BOOST_RE_REGEX_HPP_INCLUDED #ifdef __cplusplus // what follows is all C++ don't include in C builds!! #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #ifndef BOOST_REGEX_WORKAROUND_HPP #include #endif #ifndef BOOST_REGEX_FWD_HPP #include #endif #ifndef BOOST_REGEX_TRAITS_HPP #include #endif #ifndef BOOST_REGEX_RAW_BUFFER_HPP #include #endif #ifndef BOOST_REGEX_V4_MATCH_FLAGS #include #endif #ifndef BOOST_REGEX_RAW_BUFFER_HPP #include #endif #ifndef BOOST_RE_PAT_EXCEPT_HPP #include #endif #ifndef BOOST_REGEX_V4_CHAR_REGEX_TRAITS_HPP #include #endif #ifndef BOOST_REGEX_V4_STATES_HPP #include #endif #ifndef BOOST_REGEX_V4_REGBASE_HPP #include #endif #ifndef BOOST_REGEX_V4_ITERATOR_TRAITS_HPP #include #endif #ifndef BOOST_REGEX_V4_BASIC_REGEX_HPP #include #endif #ifndef BOOST_REGEX_V4_BASIC_REGEX_CREATOR_HPP #include #endif #ifndef BOOST_REGEX_V4_BASIC_REGEX_PARSER_HPP #include #endif #ifndef BOOST_REGEX_V4_SUB_MATCH_HPP #include #endif #ifndef BOOST_REGEX_FORMAT_HPP #include #endif #ifndef BOOST_REGEX_V4_MATCH_RESULTS_HPP #include #endif #ifndef BOOST_REGEX_V4_PROTECTED_CALL_HPP #include #endif #ifndef BOOST_REGEX_MATCHER_HPP #include #endif namespace boost{ #ifdef BOOST_REGEX_NO_FWD typedef basic_regex > regex; #ifndef BOOST_NO_WREGEX typedef basic_regex > wregex; #endif #endif typedef match_results cmatch; typedef match_results smatch; #ifndef BOOST_NO_WREGEX typedef match_results wcmatch; typedef match_results wsmatch; #endif } // namespace boost #ifndef BOOST_REGEX_MATCH_HPP #include #endif #ifndef BOOST_REGEX_V4_REGEX_SEARCH_HPP #include #endif #ifndef BOOST_REGEX_ITERATOR_HPP #include #endif #ifndef BOOST_REGEX_TOKEN_ITERATOR_HPP #include #endif #ifndef BOOST_REGEX_V4_REGEX_GREP_HPP #include #endif #ifndef BOOST_REGEX_V4_REGEX_REPLACE_HPP #include #endif #ifndef BOOST_REGEX_V4_REGEX_MERGE_HPP #include #endif #ifndef BOOST_REGEX_SPLIT_HPP #include #endif #endif // __cplusplus #endif // include ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_format.hpp ================================================ /* * * Copyright (c) 1998-2009 John Maddock * Copyright 2008 Eric Niebler. * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_format.hpp * VERSION see * DESCRIPTION: Provides formatting output routines for search and replace * operations. Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_FORMAT_HPP #define BOOST_REGEX_FORMAT_HPP #include #include #include #include #include #include #include #include #include #include #ifndef BOOST_NO_SFINAE #include #endif #include namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif // // Forward declaration: // template >::allocator_type > class match_results; namespace BOOST_REGEX_DETAIL_NS{ // // struct trivial_format_traits: // defines minimum localisation support for formatting // in the case that the actual regex traits is unavailable. // template struct trivial_format_traits { typedef charT char_type; static std::ptrdiff_t length(const charT* p) { return global_length(p); } static charT tolower(charT c) { return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c); } static charT toupper(charT c) { return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c); } static int value(const charT c, int radix) { int result = global_value(c); return result >= radix ? -1 : result; } int toi(const charT*& p1, const charT* p2, int radix)const { return (int)global_toi(p1, p2, radix, *this); } }; #ifdef BOOST_MSVC # pragma warning(push) #pragma warning(disable:26812) #endif template class basic_regex_formatter { public: typedef typename traits::char_type char_type; basic_regex_formatter(OutputIterator o, const Results& r, const traits& t) : m_traits(t), m_results(r), m_out(o), m_position(), m_end(), m_flags(), m_state(output_copy), m_restore_state(output_copy), m_have_conditional(false) {} OutputIterator format(ForwardIter p1, ForwardIter p2, match_flag_type f); OutputIterator format(ForwardIter p1, match_flag_type f) { return format(p1, p1 + m_traits.length(p1), f); } private: typedef typename Results::value_type sub_match_type; enum output_state { output_copy, output_next_lower, output_next_upper, output_lower, output_upper, output_none }; void put(char_type c); void put(const sub_match_type& sub); void format_all(); void format_perl(); void format_escape(); void format_conditional(); void format_until_scope_end(); bool handle_perl_verb(bool have_brace); inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const mpl::false_&) { std::vector v(i, j); return (i != j) ? this->m_results.named_subexpression(&v[0], &v[0] + v.size()) : this->m_results.named_subexpression(static_cast(0), static_cast(0)); } inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const mpl::true_&) { return this->m_results.named_subexpression(i, j); } inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j) { typedef typename boost::is_convertible::type tag_type; return get_named_sub(i, j, tag_type()); } inline int get_named_sub_index(ForwardIter i, ForwardIter j, const mpl::false_&) { std::vector v(i, j); return (i != j) ? this->m_results.named_subexpression_index(&v[0], &v[0] + v.size()) : this->m_results.named_subexpression_index(static_cast(0), static_cast(0)); } inline int get_named_sub_index(ForwardIter i, ForwardIter j, const mpl::true_&) { return this->m_results.named_subexpression_index(i, j); } inline int get_named_sub_index(ForwardIter i, ForwardIter j) { typedef typename boost::is_convertible::type tag_type; return get_named_sub_index(i, j, tag_type()); } #ifdef BOOST_MSVC // msvc-8.0 issues a spurious warning on the call to std::advance here: #pragma warning(push) #pragma warning(disable:4244) #endif inline int toi(ForwardIter& i, ForwardIter j, int base, const boost::mpl::false_&) { if(i != j) { std::vector v(i, j); const char_type* start = &v[0]; const char_type* pos = start; int r = (int)m_traits.toi(pos, &v[0] + v.size(), base); std::advance(i, pos - start); return r; } return -1; } #ifdef BOOST_MSVC #pragma warning(pop) #endif inline int toi(ForwardIter& i, ForwardIter j, int base, const boost::mpl::true_&) { return m_traits.toi(i, j, base); } inline int toi(ForwardIter& i, ForwardIter j, int base) { #if defined(_MSC_VER) && defined(__INTEL_COMPILER) && ((__INTEL_COMPILER == 9999) || (__INTEL_COMPILER == 1210)) // Workaround for Intel support issue #656654. // See also https://svn.boost.org/trac/boost/ticket/6359 return toi(i, j, base, mpl::false_()); #else typedef typename boost::is_convertible::type tag_type; return toi(i, j, base, tag_type()); #endif } const traits& m_traits; // the traits class for localised formatting operations const Results& m_results; // the match_results being used. OutputIterator m_out; // where to send output. ForwardIter m_position; // format string, current position ForwardIter m_end; // format string end match_flag_type m_flags; // format flags to use output_state m_state; // what to do with the next character output_state m_restore_state; // what state to restore to. bool m_have_conditional; // we are parsing a conditional private: basic_regex_formatter(const basic_regex_formatter&); basic_regex_formatter& operator=(const basic_regex_formatter&); }; #ifdef BOOST_MSVC # pragma warning(pop) #endif template OutputIterator basic_regex_formatter::format(ForwardIter p1, ForwardIter p2, match_flag_type f) { m_position = p1; m_end = p2; m_flags = f; format_all(); return m_out; } template void basic_regex_formatter::format_all() { // over and over: while(m_position != m_end) { switch(*m_position) { case '&': if(m_flags & ::boost::regex_constants::format_sed) { ++m_position; put(m_results[0]); break; } put(*m_position++); break; case '\\': format_escape(); break; case '(': if(m_flags & boost::regex_constants::format_all) { ++m_position; bool have_conditional = m_have_conditional; m_have_conditional = false; format_until_scope_end(); m_have_conditional = have_conditional; if(m_position == m_end) return; BOOST_REGEX_ASSERT(*m_position == static_cast(')')); ++m_position; // skip the closing ')' break; } put(*m_position); ++m_position; break; case ')': if(m_flags & boost::regex_constants::format_all) { return; } put(*m_position); ++m_position; break; case ':': if((m_flags & boost::regex_constants::format_all) && m_have_conditional) { return; } put(*m_position); ++m_position; break; case '?': if(m_flags & boost::regex_constants::format_all) { ++m_position; format_conditional(); break; } put(*m_position); ++m_position; break; case '$': if((m_flags & format_sed) == 0) { format_perl(); break; } // not a special character: BOOST_FALLTHROUGH; default: put(*m_position); ++m_position; break; } } } template void basic_regex_formatter::format_perl() { // // On entry *m_position points to a '$' character // output the information that goes with it: // BOOST_REGEX_ASSERT(*m_position == '$'); // // see if this is a trailing '$': // if(++m_position == m_end) { --m_position; put(*m_position); ++m_position; return; } // // OK find out what kind it is: // bool have_brace = false; ForwardIter save_position = m_position; switch(*m_position) { case '&': ++m_position; put(this->m_results[0]); break; case '`': ++m_position; put(this->m_results.prefix()); break; case '\'': ++m_position; put(this->m_results.suffix()); break; case '$': put(*m_position++); break; case '+': if((++m_position != m_end) && (*m_position == '{')) { ForwardIter base = ++m_position; while((m_position != m_end) && (*m_position != '}')) ++m_position; if(m_position != m_end) { // Named sub-expression: put(get_named_sub(base, m_position)); ++m_position; break; } else { m_position = --base; } } put((this->m_results)[this->m_results.size() > 1 ? static_cast(this->m_results.size() - 1) : 1]); break; case '{': have_brace = true; ++m_position; BOOST_FALLTHROUGH; default: // see if we have a number: { std::ptrdiff_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end); //len = (std::min)(static_cast(2), len); int v = this->toi(m_position, m_position + len, 10); if((v < 0) || (have_brace && ((m_position == m_end) || (*m_position != '}')))) { // Look for a Perl-5.10 verb: if(!handle_perl_verb(have_brace)) { // leave the $ as is, and carry on: m_position = --save_position; put(*m_position); ++m_position; } break; } // otherwise output sub v: put(this->m_results[v]); if(have_brace) ++m_position; } } } template bool basic_regex_formatter::handle_perl_verb(bool have_brace) { // // We may have a capitalised string containing a Perl action: // static const char_type MATCH[] = { 'M', 'A', 'T', 'C', 'H' }; static const char_type PREMATCH[] = { 'P', 'R', 'E', 'M', 'A', 'T', 'C', 'H' }; static const char_type POSTMATCH[] = { 'P', 'O', 'S', 'T', 'M', 'A', 'T', 'C', 'H' }; static const char_type LAST_PAREN_MATCH[] = { 'L', 'A', 'S', 'T', '_', 'P', 'A', 'R', 'E', 'N', '_', 'M', 'A', 'T', 'C', 'H' }; static const char_type LAST_SUBMATCH_RESULT[] = { 'L', 'A', 'S', 'T', '_', 'S', 'U', 'B', 'M', 'A', 'T', 'C', 'H', '_', 'R', 'E', 'S', 'U', 'L', 'T' }; static const char_type LAST_SUBMATCH_RESULT_ALT[] = { '^', 'N' }; if(m_position == m_end) return false; if(have_brace && (*m_position == '^')) ++m_position; std::ptrdiff_t max_len = m_end - m_position; if((max_len >= 5) && std::equal(m_position, m_position + 5, MATCH)) { m_position += 5; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 5; return false; } } put(this->m_results[0]); return true; } if((max_len >= 8) && std::equal(m_position, m_position + 8, PREMATCH)) { m_position += 8; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 8; return false; } } put(this->m_results.prefix()); return true; } if((max_len >= 9) && std::equal(m_position, m_position + 9, POSTMATCH)) { m_position += 9; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 9; return false; } } put(this->m_results.suffix()); return true; } if((max_len >= 16) && std::equal(m_position, m_position + 16, LAST_PAREN_MATCH)) { m_position += 16; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 16; return false; } } put((this->m_results)[this->m_results.size() > 1 ? static_cast(this->m_results.size() - 1) : 1]); return true; } if((max_len >= 20) && std::equal(m_position, m_position + 20, LAST_SUBMATCH_RESULT)) { m_position += 20; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 20; return false; } } put(this->m_results.get_last_closed_paren()); return true; } if((max_len >= 2) && std::equal(m_position, m_position + 2, LAST_SUBMATCH_RESULT_ALT)) { m_position += 2; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 2; return false; } } put(this->m_results.get_last_closed_paren()); return true; } return false; } template void basic_regex_formatter::format_escape() { // skip the escape and check for trailing escape: if(++m_position == m_end) { put(static_cast('\\')); return; } // now switch on the escape type: switch(*m_position) { case 'a': put(static_cast('\a')); ++m_position; break; case 'f': put(static_cast('\f')); ++m_position; break; case 'n': put(static_cast('\n')); ++m_position; break; case 'r': put(static_cast('\r')); ++m_position; break; case 't': put(static_cast('\t')); ++m_position; break; case 'v': put(static_cast('\v')); ++m_position; break; case 'x': if(++m_position == m_end) { put(static_cast('x')); return; } // maybe have \x{ddd} if(*m_position == static_cast('{')) { ++m_position; int val = this->toi(m_position, m_end, 16); if(val < 0) { // invalid value treat everything as literals: put(static_cast('x')); put(static_cast('{')); return; } if((m_position == m_end) || (*m_position != static_cast('}'))) { --m_position; while(*m_position != static_cast('\\')) --m_position; ++m_position; put(*m_position++); return; } ++m_position; put(static_cast(val)); return; } else { std::ptrdiff_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end); len = (std::min)(static_cast(2), len); int val = this->toi(m_position, m_position + len, 16); if(val < 0) { --m_position; put(*m_position++); return; } put(static_cast(val)); } break; case 'c': if(++m_position == m_end) { --m_position; put(*m_position++); return; } put(static_cast(*m_position++ % 32)); break; case 'e': put(static_cast(27)); ++m_position; break; default: // see if we have a perl specific escape: if((m_flags & boost::regex_constants::format_sed) == 0) { bool breakout = false; switch(*m_position) { case 'l': ++m_position; m_restore_state = m_state; m_state = output_next_lower; breakout = true; break; case 'L': ++m_position; m_state = output_lower; breakout = true; break; case 'u': ++m_position; m_restore_state = m_state; m_state = output_next_upper; breakout = true; break; case 'U': ++m_position; m_state = output_upper; breakout = true; break; case 'E': ++m_position; m_state = output_copy; breakout = true; break; } if(breakout) break; } // see if we have a \n sed style backreference: std::ptrdiff_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end); len = (std::min)(static_cast(1), len); int v = this->toi(m_position, m_position+len, 10); if((v > 0) || ((v == 0) && (m_flags & ::boost::regex_constants::format_sed))) { put(m_results[v]); break; } else if(v == 0) { // octal ecape sequence: --m_position; len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end); len = (std::min)(static_cast(4), len); v = this->toi(m_position, m_position + len, 8); BOOST_REGEX_ASSERT(v >= 0); put(static_cast(v)); break; } // Otherwise output the character "as is": put(*m_position++); break; } } template void basic_regex_formatter::format_conditional() { if(m_position == m_end) { // oops trailing '?': put(static_cast('?')); return; } int v; if(*m_position == '{') { ForwardIter base = m_position; ++m_position; v = this->toi(m_position, m_end, 10); if(v < 0) { // Try a named subexpression: while((m_position != m_end) && (*m_position != '}')) ++m_position; v = this->get_named_sub_index(base + 1, m_position); } if((v < 0) || (*m_position != '}')) { m_position = base; // oops trailing '?': put(static_cast('?')); return; } // Skip trailing '}': ++m_position; } else { std::ptrdiff_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end); len = (std::min)(static_cast(2), len); v = this->toi(m_position, m_position + len, 10); } if(v < 0) { // oops not a number: put(static_cast('?')); return; } // output varies depending upon whether sub-expression v matched or not: if(m_results[v].matched) { m_have_conditional = true; format_all(); m_have_conditional = false; if((m_position != m_end) && (*m_position == static_cast(':'))) { // skip the ':': ++m_position; // save output state, then turn it off: output_state saved_state = m_state; m_state = output_none; // format the rest of this scope: format_until_scope_end(); // restore output state: m_state = saved_state; } } else { // save output state, then turn it off: output_state saved_state = m_state; m_state = output_none; // format until ':' or ')': m_have_conditional = true; format_all(); m_have_conditional = false; // restore state: m_state = saved_state; if((m_position != m_end) && (*m_position == static_cast(':'))) { // skip the ':': ++m_position; // format the rest of this scope: format_until_scope_end(); } } } template void basic_regex_formatter::format_until_scope_end() { do { format_all(); if((m_position == m_end) || (*m_position == static_cast(')'))) return; put(*m_position++); }while(m_position != m_end); } template void basic_regex_formatter::put(char_type c) { // write a single character to output // according to which case translation mode we are in: switch(this->m_state) { case output_none: return; case output_next_lower: c = m_traits.tolower(c); this->m_state = m_restore_state; break; case output_next_upper: c = m_traits.toupper(c); this->m_state = m_restore_state; break; case output_lower: c = m_traits.tolower(c); break; case output_upper: c = m_traits.toupper(c); break; default: break; } *m_out = c; ++m_out; } template void basic_regex_formatter::put(const sub_match_type& sub) { typedef typename sub_match_type::iterator iterator_type; iterator_type i = sub.first; while(i != sub.second) { put(*i); ++i; } } template class string_out_iterator { S* out; public: string_out_iterator(S& s) : out(&s) {} string_out_iterator& operator++() { return *this; } string_out_iterator& operator++(int) { return *this; } string_out_iterator& operator*() { return *this; } string_out_iterator& operator=(typename S::value_type v) { out->append(1, v); return *this; } typedef std::ptrdiff_t difference_type; typedef typename S::value_type value_type; typedef value_type* pointer; typedef value_type& reference; typedef std::output_iterator_tag iterator_category; }; template OutputIterator regex_format_imp(OutputIterator out, const match_results& m, ForwardIter p1, ForwardIter p2, match_flag_type flags, const traits& t ) { if(flags & regex_constants::format_literal) { return BOOST_REGEX_DETAIL_NS::copy(p1, p2, out); } BOOST_REGEX_DETAIL_NS::basic_regex_formatter< OutputIterator, match_results, traits, ForwardIter> f(out, m, t); return f.format(p1, p2, flags); } #ifndef BOOST_NO_SFINAE BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator) struct any_type { template any_type(const T&); template any_type(const T&, const U&); template any_type(const T&, const U&, const V&); }; typedef char no_type; typedef char (&unary_type)[2]; typedef char (&binary_type)[3]; typedef char (&ternary_type)[4]; no_type check_is_formatter(unary_type, binary_type, ternary_type); template unary_type check_is_formatter(T const &, binary_type, ternary_type); template binary_type check_is_formatter(unary_type, T const &, ternary_type); template binary_type check_is_formatter(T const &, U const &, ternary_type); template ternary_type check_is_formatter(unary_type, binary_type, T const &); template ternary_type check_is_formatter(T const &, binary_type, U const &); template ternary_type check_is_formatter(unary_type, T const &, U const &); template ternary_type check_is_formatter(T const &, U const &, V const &); struct unary_binary_ternary { typedef unary_type (*unary_fun)(any_type); typedef binary_type (*binary_fun)(any_type, any_type); typedef ternary_type (*ternary_fun)(any_type, any_type, any_type); operator unary_fun(); operator binary_fun(); operator ternary_fun(); }; template::value> struct formatter_wrapper : Formatter , unary_binary_ternary { formatter_wrapper(){} }; template struct formatter_wrapper : unary_binary_ternary { operator Formatter *(); }; template struct formatter_wrapper : unary_binary_ternary { operator Formatter *(); }; template struct format_traits_imp { private: // // F must be a pointer, a function, or a class with a function call operator: // BOOST_STATIC_ASSERT((::boost::is_pointer::value || ::boost::is_function::value || ::boost::is_class::value)); static formatter_wrapper::type> f; static M m; static O out; static boost::regex_constants::match_flag_type flags; public: BOOST_STATIC_CONSTANT(int, value = sizeof(check_is_formatter(f(m), f(m, out), f(m, out, flags)))); }; template struct format_traits { public: // // Type is mpl::int_ where N is one of: // // 0 : F is a pointer to a presumably null-terminated string. // 1 : F is a character-container such as a std::string. // 2 : F is a Unary Functor. // 3 : F is a Binary Functor. // 4 : F is a Ternary Functor. // typedef typename boost::mpl::if_< boost::mpl::and_, boost::mpl::not_::type> > >, boost::mpl::int_<0>, typename boost::mpl::if_< has_const_iterator, boost::mpl::int_<1>, boost::mpl::int_::value> >::type >::type type; // // This static assertion will fail if the functor passed does not accept // the same type of arguments passed. // BOOST_STATIC_ASSERT( boost::is_class::value && !has_const_iterator::value ? (type::value > 1) : true); }; #else // BOOST_NO_SFINAE template struct format_traits { public: // // Type is mpl::int_ where N is one of: // // 0 : F is a pointer to a presumably null-terminated string. // 1 : F is a character-container such as a std::string. // // Other options such as F being a Functor are not supported without // SFINAE support. // typedef typename boost::mpl::if_< boost::is_pointer, boost::mpl::int_<0>, boost::mpl::int_<1> >::type type; }; #endif // BOOST_NO_SFINAE template struct format_functor3 { format_functor3(Base b) : func(b) {} template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f) { return boost::unwrap_ref(func)(m, i, f); } template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&) { return (*this)(m, i, f); } private: Base func; format_functor3(const format_functor3&); format_functor3& operator=(const format_functor3&); }; template struct format_functor2 { format_functor2(Base b) : func(b) {} template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/) { return boost::unwrap_ref(func)(m, i); } template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&) { return (*this)(m, i, f); } private: Base func; format_functor2(const format_functor2&); format_functor2& operator=(const format_functor2&); }; template struct format_functor1 { format_functor1(Base b) : func(b) {} template OutputIter do_format_string(const S& s, OutputIter i) { return BOOST_REGEX_DETAIL_NS::copy(s.begin(), s.end(), i); } template inline OutputIter do_format_string(const S* s, OutputIter i) { while(s && *s) { *i = *s; ++i; ++s; } return i; } template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/) { return do_format_string(boost::unwrap_ref(func)(m), i); } template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&) { return (*this)(m, i, f); } private: Base func; format_functor1(const format_functor1&); format_functor1& operator=(const format_functor1&); }; template struct format_functor_c_string { format_functor_c_string(const charT* ps) : func(ps) {} template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits()) { //typedef typename Match::char_type char_type; const charT* end = func; while(*end) ++end; return regex_format_imp(i, m, func, end, f, t); } private: const charT* func; format_functor_c_string(const format_functor_c_string&); format_functor_c_string& operator=(const format_functor_c_string&); }; template struct format_functor_container { format_functor_container(const Container& c) : func(c) {} template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits()) { //typedef typename Match::char_type char_type; return BOOST_REGEX_DETAIL_NS::regex_format_imp(i, m, func.begin(), func.end(), f, t); } private: const Container& func; format_functor_container(const format_functor_container&); format_functor_container& operator=(const format_functor_container&); }; template > struct compute_functor_type { typedef typename format_traits::type tag; typedef typename boost::remove_cv< typename boost::remove_pointer::type>::type maybe_char_type; typedef typename mpl::if_< ::boost::is_same >, format_functor_c_string, typename mpl::if_< ::boost::is_same >, format_functor_container, typename mpl::if_< ::boost::is_same >, format_functor1, typename mpl::if_< ::boost::is_same >, format_functor2, format_functor3 >::type >::type >::type >::type type; }; } // namespace BOOST_REGEX_DETAIL_NS template inline OutputIterator regex_format(OutputIterator out, const match_results& m, Functor fmt, match_flag_type flags = format_all ) { return m.format(out, fmt, flags); } template inline std::basic_string::char_type> regex_format(const match_results& m, Functor fmt, match_flag_type flags = format_all) { return m.format(fmt, flags); } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_FORMAT_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_fwd.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_fwd.cpp * VERSION see * DESCRIPTION: Forward declares boost::basic_regex<> and * associated typedefs. */ #ifndef BOOST_REGEX_FWD_HPP_INCLUDED #define BOOST_REGEX_FWD_HPP_INCLUDED #ifndef BOOST_REGEX_CONFIG_HPP #include #endif // // define BOOST_REGEX_NO_FWD if this // header doesn't work! // #ifdef BOOST_REGEX_NO_FWD # ifndef BOOST_RE_REGEX_HPP # include # endif #else namespace boost{ template class cpp_regex_traits; template struct c_regex_traits; template class w32_regex_traits; #ifdef BOOST_REGEX_USE_WIN32_LOCALE template > struct regex_traits; #elif defined(BOOST_REGEX_USE_CPP_LOCALE) template > struct regex_traits; #else template > struct regex_traits; #endif template > class basic_regex; typedef basic_regex > regex; #ifndef BOOST_NO_WREGEX typedef basic_regex > wregex; #endif } // namespace boost #endif // BOOST_REGEX_NO_FWD #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_grep.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_grep.hpp * VERSION see * DESCRIPTION: Provides regex_grep implementation. */ #ifndef BOOST_REGEX_V4_REGEX_GREP_HPP #define BOOST_REGEX_V4_REGEX_GREP_HPP namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif // // regex_grep: // find all non-overlapping matches within the sequence first last: // template inline unsigned int regex_grep(Predicate foo, BidiIterator first, BidiIterator last, const basic_regex& e, match_flag_type flags = match_default) { if(e.flags() & regex_constants::failbit) return false; typedef typename match_results::allocator_type match_allocator_type; match_results m; BOOST_REGEX_DETAIL_NS::perl_matcher matcher(first, last, m, e, flags, first); unsigned int count = 0; while(matcher.find()) { ++count; if(0 == foo(m)) return count; // caller doesn't want to go on if(m[0].second == last) return count; // we've reached the end, don't try and find an extra null match. if(m.length() == 0) { if(m[0].second == last) return count; // we found a NULL-match, now try to find // a non-NULL one at the same position: match_results m2(m); matcher.setf(match_not_null | match_continuous); if(matcher.find()) { ++count; if(0 == foo(m)) return count; } else { // reset match back to where it was: m = m2; } matcher.unsetf((match_not_null | match_continuous) & ~flags); } } return count; } // // regex_grep convenience interfaces: #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING // // this isn't really a partial specialisation, but template function // overloading - if the compiler doesn't support partial specialisation // then it really won't support this either: template inline unsigned int regex_grep(Predicate foo, const charT* str, const basic_regex& e, match_flag_type flags = match_default) { return regex_grep(foo, str, str + traits::length(str), e, flags); } template inline unsigned int regex_grep(Predicate foo, const std::basic_string& s, const basic_regex& e, match_flag_type flags = match_default) { return regex_grep(foo, s.begin(), s.end(), e, flags); } #else // partial specialisation inline unsigned int regex_grep(bool (*foo)(const cmatch&), const char* str, const regex& e, match_flag_type flags = match_default) { return regex_grep(foo, str, str + regex::traits_type::length(str), e, flags); } #ifndef BOOST_NO_WREGEX inline unsigned int regex_grep(bool (*foo)(const wcmatch&), const wchar_t* str, const wregex& e, match_flag_type flags = match_default) { return regex_grep(foo, str, str + wregex::traits_type::length(str), e, flags); } #endif inline unsigned int regex_grep(bool (*foo)(const match_results&), const std::string& s, const regex& e, match_flag_type flags = match_default) { return regex_grep(foo, s.begin(), s.end(), e, flags); } #if !defined(BOOST_NO_WREGEX) inline unsigned int regex_grep(bool (*foo)(const match_results::const_iterator>&), const std::basic_string& s, const wregex& e, match_flag_type flags = match_default) { return regex_grep(foo, s.begin(), s.end(), e, flags); } #endif #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_V4_REGEX_GREP_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_iterator.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_iterator.hpp * VERSION see * DESCRIPTION: Provides regex_iterator implementation. */ #ifndef BOOST_REGEX_V4_REGEX_ITERATOR_HPP #define BOOST_REGEX_V4_REGEX_ITERATOR_HPP #include namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif template class regex_iterator_implementation { typedef basic_regex regex_type; match_results what; // current match BidirectionalIterator base; // start of sequence BidirectionalIterator end; // end of sequence const regex_type re; // the expression match_flag_type flags; // flags for matching public: regex_iterator_implementation(const regex_type* p, BidirectionalIterator last, match_flag_type f) : base(), end(last), re(*p), flags(f){} regex_iterator_implementation(const regex_iterator_implementation& other) :what(other.what), base(other.base), end(other.end), re(other.re), flags(other.flags){} bool init(BidirectionalIterator first) { base = first; return regex_search(first, end, what, re, flags); } bool compare(const regex_iterator_implementation& that) { if(this == &that) return true; return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second); } const match_results& get() { return what; } bool next() { //if(what.prefix().first != what[0].second) // flags |= match_prev_avail; BidirectionalIterator next_start = what[0].second; match_flag_type f(flags); if(!what.length() || (f & regex_constants::match_posix)) f |= regex_constants::match_not_initial_null; //if(base != next_start) // f |= regex_constants::match_not_bob; bool result = regex_search(next_start, end, what, re, f, base); if(result) what.set_base(base); return result; } private: regex_iterator_implementation& operator=(const regex_iterator_implementation&); }; template ::value_type, class traits = regex_traits > class regex_iterator { private: typedef regex_iterator_implementation impl; typedef shared_ptr pimpl; public: typedef basic_regex regex_type; typedef match_results value_type; typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; regex_iterator(){} regex_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, match_flag_type m = match_default) : pdata(new impl(&re, b, m)) { if(!pdata->init(a)) { pdata.reset(); } } regex_iterator(const regex_iterator& that) : pdata(that.pdata) {} regex_iterator& operator=(const regex_iterator& that) { pdata = that.pdata; return *this; } bool operator==(const regex_iterator& that)const { if((pdata.get() == 0) || (that.pdata.get() == 0)) return pdata.get() == that.pdata.get(); return pdata->compare(*(that.pdata.get())); } bool operator!=(const regex_iterator& that)const { return !(*this == that); } const value_type& operator*()const { return pdata->get(); } const value_type* operator->()const { return &(pdata->get()); } regex_iterator& operator++() { cow(); if(0 == pdata->next()) { pdata.reset(); } return *this; } regex_iterator operator++(int) { regex_iterator result(*this); ++(*this); return result; } private: pimpl pdata; void cow() { // copy-on-write if(pdata.get() && !pdata.unique()) { pdata.reset(new impl(*(pdata.get()))); } } }; typedef regex_iterator cregex_iterator; typedef regex_iterator sregex_iterator; #ifndef BOOST_NO_WREGEX typedef regex_iterator wcregex_iterator; typedef regex_iterator wsregex_iterator; #endif // make_regex_iterator: template inline regex_iterator make_regex_iterator(const charT* p, const basic_regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_iterator(p, p+traits::length(p), e, m); } template inline regex_iterator::const_iterator, charT, traits> make_regex_iterator(const std::basic_string& p, const basic_regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_iterator::const_iterator, charT, traits>(p.begin(), p.end(), e, m); } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_V4_REGEX_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_match.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_match.hpp * VERSION see * DESCRIPTION: Regular expression matching algorithms. * Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_MATCH_HPP #define BOOST_REGEX_MATCH_HPP namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif // // proc regex_match // returns true if the specified regular expression matches // the whole of the input. Fills in what matched in m. // template bool regex_match(BidiIterator first, BidiIterator last, match_results& m, const basic_regex& e, match_flag_type flags = match_default) { BOOST_REGEX_DETAIL_NS::perl_matcher matcher(first, last, m, e, flags, first); return matcher.match(); } template bool regex_match(iterator first, iterator last, const basic_regex& e, match_flag_type flags = match_default) { match_results m; return regex_match(first, last, m, e, flags | regex_constants::match_any); } // // query_match convenience interfaces: #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING // // this isn't really a partial specialisation, but template function // overloading - if the compiler doesn't support partial specialisation // then it really won't support this either: template inline bool regex_match(const charT* str, match_results& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_match(str, str + traits::length(str), m, e, flags); } template inline bool regex_match(const std::basic_string& s, match_results::const_iterator, Allocator>& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } template inline bool regex_match(const charT* str, const basic_regex& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + traits::length(str), m, e, flags | regex_constants::match_any); } template inline bool regex_match(const std::basic_string& s, const basic_regex& e, match_flag_type flags = match_default) { typedef typename std::basic_string::const_iterator iterator; match_results m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #else // partial ordering inline bool regex_match(const char* str, cmatch& m, const regex& e, match_flag_type flags = match_default) { return regex_match(str, str + regex::traits_type::length(str), m, e, flags); } inline bool regex_match(const char* str, const regex& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #ifndef BOOST_NO_STD_LOCALE inline bool regex_match(const char* str, cmatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(str, str + regex::traits_type::length(str), m, e, flags); } inline bool regex_match(const char* str, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #endif inline bool regex_match(const char* str, cmatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(str, str + regex::traits_type::length(str), m, e, flags); } inline bool regex_match(const char* str, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32) inline bool regex_match(const char* str, cmatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(str, str + regex::traits_type::length(str), m, e, flags); } inline bool regex_match(const char* str, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #endif #ifndef BOOST_NO_WREGEX inline bool regex_match(const wchar_t* str, wcmatch& m, const wregex& e, match_flag_type flags = match_default) { return regex_match(str, str + wregex::traits_type::length(str), m, e, flags); } inline bool regex_match(const wchar_t* str, const wregex& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #ifndef BOOST_NO_STD_LOCALE inline bool regex_match(const wchar_t* str, wcmatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(str, str + wregex::traits_type::length(str), m, e, flags); } inline bool regex_match(const wchar_t* str, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #endif inline bool regex_match(const wchar_t* str, wcmatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(str, str + wregex::traits_type::length(str), m, e, flags); } inline bool regex_match(const wchar_t* str, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32) inline bool regex_match(const wchar_t* str, wcmatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(str, str + wregex::traits_type::length(str), m, e, flags); } inline bool regex_match(const wchar_t* str, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #endif #endif inline bool regex_match(const std::string& s, smatch& m, const regex& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } inline bool regex_match(const std::string& s, const regex& e, match_flag_type flags = match_default) { match_results m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #ifndef BOOST_NO_STD_LOCALE inline bool regex_match(const std::string& s, smatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } inline bool regex_match(const std::string& s, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #endif inline bool regex_match(const std::string& s, smatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } inline bool regex_match(const std::string& s, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32) inline bool regex_match(const std::string& s, smatch& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } inline bool regex_match(const std::string& s, const basic_regex >& e, match_flag_type flags = match_default) { match_results m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #endif #if !defined(BOOST_NO_WREGEX) inline bool regex_match(const std::basic_string& s, match_results::const_iterator>& m, const wregex& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } inline bool regex_match(const std::basic_string& s, const wregex& e, match_flag_type flags = match_default) { match_results::const_iterator> m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #ifndef BOOST_NO_STD_LOCALE inline bool regex_match(const std::basic_string& s, match_results::const_iterator>& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } inline bool regex_match(const std::basic_string& s, const basic_regex >& e, match_flag_type flags = match_default) { match_results::const_iterator> m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #endif inline bool regex_match(const std::basic_string& s, match_results::const_iterator>& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } inline bool regex_match(const std::basic_string& s, const basic_regex >& e, match_flag_type flags = match_default) { match_results::const_iterator> m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32) inline bool regex_match(const std::basic_string& s, match_results::const_iterator>& m, const basic_regex >& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } inline bool regex_match(const std::basic_string& s, const basic_regex >& e, match_flag_type flags = match_default) { match_results::const_iterator> m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #endif #endif #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_MATCH_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_merge.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_format.hpp * VERSION see * DESCRIPTION: Provides formatting output routines for search and replace * operations. Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_V4_REGEX_MERGE_HPP #define BOOST_REGEX_V4_REGEX_MERGE_HPP namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif template inline OutputIterator regex_merge(OutputIterator out, Iterator first, Iterator last, const basic_regex& e, const charT* fmt, match_flag_type flags = match_default) { return regex_replace(out, first, last, e, fmt, flags); } template inline OutputIterator regex_merge(OutputIterator out, Iterator first, Iterator last, const basic_regex& e, const std::basic_string& fmt, match_flag_type flags = match_default) { return regex_merge(out, first, last, e, fmt.c_str(), flags); } template inline std::basic_string regex_merge(const std::basic_string& s, const basic_regex& e, const charT* fmt, match_flag_type flags = match_default) { return regex_replace(s, e, fmt, flags); } template inline std::basic_string regex_merge(const std::basic_string& s, const basic_regex& e, const std::basic_string& fmt, match_flag_type flags = match_default) { return regex_replace(s, e, fmt, flags); } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_V4_REGEX_MERGE_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_raw_buffer.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_raw_buffer.hpp * VERSION see * DESCRIPTION: Raw character buffer for regex code. * Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_RAW_BUFFER_HPP #define BOOST_REGEX_RAW_BUFFER_HPP #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #include #include namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif struct empty_padding{}; union padding { void* p; unsigned int i; }; template struct padding3 { enum{ padding_size = 8, padding_mask = 7 }; }; template<> struct padding3<2> { enum{ padding_size = 2, padding_mask = 1 }; }; template<> struct padding3<4> { enum{ padding_size = 4, padding_mask = 3 }; }; template<> struct padding3<8> { enum{ padding_size = 8, padding_mask = 7 }; }; template<> struct padding3<16> { enum{ padding_size = 16, padding_mask = 15 }; }; enum{ padding_size = padding3::padding_size, padding_mask = padding3::padding_mask }; // // class raw_storage // basically this is a simplified vector // this is used by basic_regex for expression storage // class raw_storage { public: typedef std::size_t size_type; typedef unsigned char* pointer; private: pointer last, start, end; public: raw_storage(); raw_storage(size_type n); ~raw_storage() { ::operator delete(start); } void BOOST_REGEX_CALL resize(size_type n) { size_type newsize = start ? last - start : 1024; while (newsize < n) newsize *= 2; size_type datasize = end - start; // extend newsize to WORD/DWORD boundary: newsize = (newsize + padding_mask) & ~(padding_mask); // allocate and copy data: pointer ptr = static_cast(::operator new(newsize)); BOOST_REGEX_NOEH_ASSERT(ptr) if (start) std::memcpy(ptr, start, datasize); // get rid of old buffer: ::operator delete(start); // and set up pointers: start = ptr; end = ptr + datasize; last = ptr + newsize; } void* BOOST_REGEX_CALL extend(size_type n) { if(size_type(last - end) < n) resize(n + (end - start)); pointer result = end; end += n; return result; } void* BOOST_REGEX_CALL insert(size_type pos, size_type n) { BOOST_REGEX_ASSERT(pos <= size_type(end - start)); if (size_type(last - end) < n) resize(n + (end - start)); void* result = start + pos; std::memmove(start + pos + n, start + pos, (end - start) - pos); end += n; return result; } size_type BOOST_REGEX_CALL size() { return size_type(end - start); } size_type BOOST_REGEX_CALL capacity() { return size_type(last - start); } void* BOOST_REGEX_CALL data()const { return start; } size_type BOOST_REGEX_CALL index(void* ptr) { return size_type(static_cast(ptr) - static_cast(data())); } void BOOST_REGEX_CALL clear() { end = start; } void BOOST_REGEX_CALL align() { // move end up to a boundary: end = start + (((end - start) + padding_mask) & ~padding_mask); } void swap(raw_storage& that) { std::swap(start, that.start); std::swap(end, that.end); std::swap(last, that.last); } }; inline raw_storage::raw_storage() { last = start = end = 0; } inline raw_storage::raw_storage(size_type n) { start = end = static_cast(::operator new(n)); BOOST_REGEX_NOEH_ASSERT(start) last = start + n; } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_replace.hpp ================================================ /* * * Copyright (c) 1998-2009 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_format.hpp * VERSION see * DESCRIPTION: Provides formatting output routines for search and replace * operations. Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_V4_REGEX_REPLACE_HPP #define BOOST_REGEX_V4_REGEX_REPLACE_HPP namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif template OutputIterator regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex& e, Formatter fmt, match_flag_type flags = match_default) { regex_iterator i(first, last, e, flags); regex_iterator j; if(i == j) { if(!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(first, last, out); } else { BidirectionalIterator last_m(first); while(i != j) { if(!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(i->prefix().first, i->prefix().second, out); out = i->format(out, fmt, flags, e); last_m = (*i)[0].second; if(flags & regex_constants::format_first_only) break; ++i; } if(!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(last_m, last, out); } return out; } template std::basic_string regex_replace(const std::basic_string& s, const basic_regex& e, Formatter fmt, match_flag_type flags = match_default) { std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); regex_replace(i, s.begin(), s.end(), e, fmt, flags); return result; } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_V4_REGEX_REPLACE_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_search.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_search.hpp * VERSION see * DESCRIPTION: Provides regex_search implementation. */ #ifndef BOOST_REGEX_V4_REGEX_SEARCH_HPP #define BOOST_REGEX_V4_REGEX_SEARCH_HPP namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif template bool regex_search(BidiIterator first, BidiIterator last, match_results& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(first, last, m, e, flags, first); } template bool regex_search(BidiIterator first, BidiIterator last, match_results& m, const basic_regex& e, match_flag_type flags, BidiIterator base) { if(e.flags() & regex_constants::failbit) return false; BOOST_REGEX_DETAIL_NS::perl_matcher matcher(first, last, m, e, flags, base); return matcher.find(); } // // regex_search convenience interfaces: #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING // // this isn't really a partial specialisation, but template function // overloading - if the compiler doesn't support partial specialisation // then it really won't support this either: template inline bool regex_search(const charT* str, match_results& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(str, str + traits::length(str), m, e, flags); } template inline bool regex_search(const std::basic_string& s, match_results::const_iterator, Allocator>& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(s.begin(), s.end(), m, e, flags); } #else // partial overloads: inline bool regex_search(const char* str, cmatch& m, const regex& e, match_flag_type flags = match_default) { return regex_search(str, str + regex::traits_type::length(str), m, e, flags); } inline bool regex_search(const char* first, const char* last, const regex& e, match_flag_type flags = match_default) { cmatch m; return regex_search(first, last, m, e, flags | regex_constants::match_any); } #ifndef BOOST_NO_WREGEX inline bool regex_search(const wchar_t* str, wcmatch& m, const wregex& e, match_flag_type flags = match_default) { return regex_search(str, str + wregex::traits_type::length(str), m, e, flags); } inline bool regex_search(const wchar_t* first, const wchar_t* last, const wregex& e, match_flag_type flags = match_default) { wcmatch m; return regex_search(first, last, m, e, flags | regex_constants::match_any); } #endif inline bool regex_search(const std::string& s, smatch& m, const regex& e, match_flag_type flags = match_default) { return regex_search(s.begin(), s.end(), m, e, flags); } #if !defined(BOOST_NO_WREGEX) inline bool regex_search(const std::basic_string& s, wsmatch& m, const wregex& e, match_flag_type flags = match_default) { return regex_search(s.begin(), s.end(), m, e, flags); } #endif #endif template bool regex_search(BidiIterator first, BidiIterator last, const basic_regex& e, match_flag_type flags = match_default) { if(e.flags() & regex_constants::failbit) return false; match_results m; typedef typename match_results::allocator_type match_alloc_type; BOOST_REGEX_DETAIL_NS::perl_matcher matcher(first, last, m, e, flags | regex_constants::match_any, first); return matcher.find(); } #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING template inline bool regex_search(const charT* str, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(str, str + traits::length(str), e, flags); } template inline bool regex_search(const std::basic_string& s, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(s.begin(), s.end(), e, flags); } #else // non-template function overloads inline bool regex_search(const char* str, const regex& e, match_flag_type flags = match_default) { cmatch m; return regex_search(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #ifndef BOOST_NO_WREGEX inline bool regex_search(const wchar_t* str, const wregex& e, match_flag_type flags = match_default) { wcmatch m; return regex_search(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any); } #endif inline bool regex_search(const std::string& s, const regex& e, match_flag_type flags = match_default) { smatch m; return regex_search(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #if !defined(BOOST_NO_WREGEX) inline bool regex_search(const std::basic_string& s, const wregex& e, match_flag_type flags = match_default) { wsmatch m; return regex_search(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } #endif // BOOST_NO_WREGEX #endif // partial overload #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_V4_REGEX_SEARCH_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_split.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_split.hpp * VERSION see * DESCRIPTION: Implements regex_split and associated functions. * Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_SPLIT_HPP #define BOOST_REGEX_SPLIT_HPP namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC # pragma warning(push) #if BOOST_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace BOOST_REGEX_DETAIL_NS{ template const basic_regex& get_default_expression(charT) { static const charT expression_text[4] = { '\\', 's', '+', '\00', }; static const basic_regex e(expression_text); return e; } template class split_pred { typedef std::basic_string string_type; typedef typename string_type::const_iterator iterator_type; iterator_type* p_last; OutputIterator* p_out; std::size_t* p_max; std::size_t initial_max; public: split_pred(iterator_type* a, OutputIterator* b, std::size_t* c) : p_last(a), p_out(b), p_max(c), initial_max(*c) {} bool operator()(const match_results& what); }; template bool split_pred::operator() (const match_results& what) { *p_last = what[0].second; if(what.size() > 1) { // output sub-expressions only: for(unsigned i = 1; i < what.size(); ++i) { *(*p_out) = what.str(i); ++(*p_out); if(0 == --*p_max) return false; } return *p_max != 0; } else { // output $` only if it's not-null or not at the start of the input: const sub_match& sub = what[-1]; if((sub.first != sub.second) || (*p_max != initial_max)) { *(*p_out) = sub.str(); ++(*p_out); return --*p_max; } } // // initial null, do nothing: return true; } } // namespace BOOST_REGEX_DETAIL_NS template std::size_t regex_split(OutputIterator out, std::basic_string& s, const basic_regex& e, match_flag_type flags, std::size_t max_split) { typedef typename std::basic_string::const_iterator ci_t; //typedef typename match_results::allocator_type match_allocator; ci_t last = s.begin(); std::size_t init_size = max_split; BOOST_REGEX_DETAIL_NS::split_pred pred(&last, &out, &max_split); ci_t i, j; i = s.begin(); j = s.end(); regex_grep(pred, i, j, e, flags); // // if there is still input left, do a final push as long as max_split // is not exhausted, and we're not splitting sub-expressions rather // than whitespace: if(max_split && (last != s.end()) && (e.mark_count() == 0)) { *out = std::basic_string((ci_t)last, (ci_t)s.end()); ++out; last = s.end(); --max_split; } // // delete from the string everything that has been processed so far: s.erase(0, last - s.begin()); // // return the number of new records pushed: return init_size - max_split; } template inline std::size_t regex_split(OutputIterator out, std::basic_string& s, const basic_regex& e, match_flag_type flags = match_default) { return regex_split(out, s, e, flags, UINT_MAX); } template inline std::size_t regex_split(OutputIterator out, std::basic_string& s) { return regex_split(out, s, BOOST_REGEX_DETAIL_NS::get_default_expression(charT(0)), match_default, UINT_MAX); } #ifdef BOOST_MSVC # pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_token_iterator.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_token_iterator.hpp * VERSION see * DESCRIPTION: Provides regex_token_iterator implementation. */ #ifndef BOOST_REGEX_V4_REGEX_TOKEN_ITERATOR_HPP #define BOOST_REGEX_V4_REGEX_TOKEN_ITERATOR_HPP #include #include #if (BOOST_WORKAROUND(BOOST_BORLANDC, >= 0x560) && BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x570)))\ || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) // // Borland C++ Builder 6, and Visual C++ 6, // can't cope with the array template constructor // so we have a template member that will accept any type as // argument, and then assert that is really is an array: // #include #include #endif namespace boost{ #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #pragma warning(push) #pragma warning(disable:4700) #endif template class regex_token_iterator_implementation { typedef basic_regex regex_type; typedef sub_match value_type; match_results what; // current match BidirectionalIterator base; // start of search area BidirectionalIterator end; // end of search area const regex_type re; // the expression match_flag_type flags; // match flags value_type result; // the current string result int N; // the current sub-expression being enumerated std::vector subs; // the sub-expressions to enumerate public: regex_token_iterator_implementation(const regex_token_iterator_implementation& other) : what(other.what), base(other.base), end(other.end), re(other.re), flags(other.flags), result(other.result), N(other.N), subs(other.subs) {} regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, int sub, match_flag_type f) : end(last), re(*p), flags(f), N(0){ subs.push_back(sub); } regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const std::vector& v, match_flag_type f) : end(last), re(*p), flags(f), N(0), subs(v){} #if !BOOST_WORKAROUND(__HP_aCC, < 60700) #if (BOOST_WORKAROUND(BOOST_BORLANDC, >= 0x560) && BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x570)))\ || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) \ || BOOST_WORKAROUND(__HP_aCC, < 60700) template regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const T& submatches, match_flag_type f) : end(last), re(*p), flags(f), N(0) { // assert that T really is an array: BOOST_STATIC_ASSERT(::boost::is_array::value); const std::size_t array_size = sizeof(T) / sizeof(submatches[0]); for(std::size_t i = 0; i < array_size; ++i) { subs.push_back(submatches[i]); } } #else template regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const int (&submatches)[CN], match_flag_type f) : end(last), re(*p), flags(f), N(0) { for(std::size_t i = 0; i < CN; ++i) { subs.push_back(submatches[i]); } } #endif #endif bool init(BidirectionalIterator first) { N = 0; base = first; if(regex_search(first, end, what, re, flags, base) == true) { N = 0; result = ((subs[N] == -1) ? what.prefix() : what[(int)subs[N]]); return true; } else if((subs[N] == -1) && (first != end)) { result.first = first; result.second = end; result.matched = (first != end); N = -1; return true; } return false; } bool compare(const regex_token_iterator_implementation& that) { if(this == &that) return true; return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (N == that.N) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second); } const value_type& get() { return result; } bool next() { if(N == -1) return false; if(N+1 < (int)subs.size()) { ++N; result =((subs[N] == -1) ? what.prefix() : what[subs[N]]); return true; } //if(what.prefix().first != what[0].second) // flags |= /*match_prev_avail |*/ regex_constants::match_not_bob; BidirectionalIterator last_end(what[0].second); if(regex_search(last_end, end, what, re, ((what[0].first == what[0].second) ? flags | regex_constants::match_not_initial_null : flags), base)) { N =0; result =((subs[N] == -1) ? what.prefix() : what[subs[N]]); return true; } else if((last_end != end) && (subs[0] == -1)) { N =-1; result.first = last_end; result.second = end; result.matched = (last_end != end); return true; } return false; } private: regex_token_iterator_implementation& operator=(const regex_token_iterator_implementation&); }; template ::value_type, class traits = regex_traits > class regex_token_iterator { private: typedef regex_token_iterator_implementation impl; typedef shared_ptr pimpl; public: typedef basic_regex regex_type; typedef sub_match value_type; typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; regex_token_iterator(){} regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, int submatch = 0, match_flag_type m = match_default) : pdata(new impl(&re, b, submatch, m)) { if(!pdata->init(a)) pdata.reset(); } regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const std::vector& submatches, match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } #if !BOOST_WORKAROUND(__HP_aCC, < 60700) #if (BOOST_WORKAROUND(BOOST_BORLANDC, >= 0x560) && BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x570)))\ || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) \ || BOOST_WORKAROUND(__HP_aCC, < 60700) template regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const T& submatches, match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } #else template regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const int (&submatches)[N], match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } #endif #endif regex_token_iterator(const regex_token_iterator& that) : pdata(that.pdata) {} regex_token_iterator& operator=(const regex_token_iterator& that) { pdata = that.pdata; return *this; } bool operator==(const regex_token_iterator& that)const { if((pdata.get() == 0) || (that.pdata.get() == 0)) return pdata.get() == that.pdata.get(); return pdata->compare(*(that.pdata.get())); } bool operator!=(const regex_token_iterator& that)const { return !(*this == that); } const value_type& operator*()const { return pdata->get(); } const value_type* operator->()const { return &(pdata->get()); } regex_token_iterator& operator++() { cow(); if(0 == pdata->next()) { pdata.reset(); } return *this; } regex_token_iterator operator++(int) { regex_token_iterator result(*this); ++(*this); return result; } private: pimpl pdata; void cow() { // copy-on-write if(pdata.get() && !pdata.unique()) { pdata.reset(new impl(*(pdata.get()))); } } }; typedef regex_token_iterator cregex_token_iterator; typedef regex_token_iterator sregex_token_iterator; #ifndef BOOST_NO_WREGEX typedef regex_token_iterator wcregex_token_iterator; typedef regex_token_iterator wsregex_token_iterator; #endif template inline regex_token_iterator make_regex_token_iterator(const charT* p, const basic_regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator(p, p+traits::length(p), e, submatch, m); } template inline regex_token_iterator::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string& p, const basic_regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m); } template inline regex_token_iterator make_regex_token_iterator(const charT* p, const basic_regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator(p, p+traits::length(p), e, submatch, m); } template inline regex_token_iterator::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string& p, const basic_regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m); } template inline regex_token_iterator make_regex_token_iterator(const charT* p, const basic_regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator(p, p+traits::length(p), e, submatch, m); } template inline regex_token_iterator::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string& p, const basic_regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m); } #ifdef BOOST_MSVC #pragma warning(pop) #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_V4_REGEX_TOKEN_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_traits.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits classes. */ #ifndef BOOST_REGEX_TRAITS_HPP_INCLUDED #define BOOST_REGEX_TRAITS_HPP_INCLUDED #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #ifndef BOOST_REGEX_WORKAROUND_HPP #include #endif #ifndef BOOST_REGEX_SYNTAX_TYPE_HPP #include #endif #ifndef BOOST_REGEX_ERROR_TYPE_HPP #include #endif #ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED #include #endif #ifndef BOOST_NO_STD_LOCALE # ifndef BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED # include # endif #endif #if !BOOST_WORKAROUND(BOOST_BORLANDC, < 0x560) # ifndef BOOST_C_REGEX_TRAITS_HPP_INCLUDED # include # endif #endif #if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32) # ifndef BOOST_W32_REGEX_TRAITS_HPP_INCLUDED # include # endif #endif #ifndef BOOST_REGEX_FWD_HPP_INCLUDED #include #endif #include "boost/mpl/has_xxx.hpp" #include #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ template struct regex_traits : public implementationT { regex_traits() : implementationT() {} }; // // class regex_traits_wrapper. // this is what our implementation will actually store; // it provides default implementations of the "optional" // interfaces that we support, in addition to the // required "standard" ones: // namespace BOOST_REGEX_DETAIL_NS{ #if !BOOST_WORKAROUND(__HP_aCC, < 60000) BOOST_MPL_HAS_XXX_TRAIT_DEF(boost_extensions_tag) #else template struct has_boost_extensions_tag { BOOST_STATIC_CONSTANT(bool, value = false); }; #endif template struct default_wrapper : public BaseT { typedef typename BaseT::char_type char_type; std::string error_string(::boost::regex_constants::error_type e)const { return ::boost::BOOST_REGEX_DETAIL_NS::get_default_error_string(e); } ::boost::regex_constants::syntax_type syntax_type(char_type c)const { return ((c & 0x7f) == c) ? get_default_syntax_type(static_cast(c)) : ::boost::regex_constants::syntax_char; } ::boost::regex_constants::escape_syntax_type escape_syntax_type(char_type c)const { return ((c & 0x7f) == c) ? get_default_escape_syntax_type(static_cast(c)) : ::boost::regex_constants::escape_type_identity; } boost::intmax_t toi(const char_type*& p1, const char_type* p2, int radix)const { return ::boost::BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this); } char_type translate(char_type c, bool icase)const { return (icase ? this->translate_nocase(c) : this->translate(c)); } char_type translate(char_type c)const { return BaseT::translate(c); } char_type tolower(char_type c)const { return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c); } char_type toupper(char_type c)const { return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c); } }; template struct compute_wrapper_base { typedef BaseT type; }; #if !BOOST_WORKAROUND(__HP_aCC, < 60000) template struct compute_wrapper_base { typedef default_wrapper type; }; #else template <> struct compute_wrapper_base, false> { typedef default_wrapper > type; }; #ifndef BOOST_NO_WREGEX template <> struct compute_wrapper_base, false> { typedef default_wrapper > type; }; #endif #endif } // namespace BOOST_REGEX_DETAIL_NS template struct regex_traits_wrapper : public ::boost::BOOST_REGEX_DETAIL_NS::compute_wrapper_base< BaseT, ::boost::BOOST_REGEX_DETAIL_NS::has_boost_extensions_tag::value >::type { regex_traits_wrapper(){} private: regex_traits_wrapper(const regex_traits_wrapper&); regex_traits_wrapper& operator=(const regex_traits_wrapper&); }; } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif // include ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_traits_defaults.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_traits_defaults.hpp * VERSION see * DESCRIPTION: Declares API's for access to regex_traits default properties. */ #ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED #define BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #include #include #include #include #include #ifndef BOOST_REGEX_SYNTAX_TYPE_HPP #include #endif #ifndef BOOST_REGEX_ERROR_TYPE_HPP #include #endif #include #include #include #ifdef BOOST_NO_STDC_NAMESPACE namespace std{ using ::strlen; } #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ // // helpers to suppress warnings: // template inline bool is_extended(charT c) { typedef typename make_unsigned::type unsigned_type; return (sizeof(charT) > 1) && (static_cast(c) >= 256u); } inline bool is_extended(char) { return false; } inline const char* BOOST_REGEX_CALL get_default_syntax(regex_constants::syntax_type n) { // if the user hasn't supplied a message catalog, then this supplies // default "messages" for us to load in the range 1-100. const char* messages[] = { "", "(", ")", "$", "^", ".", "*", "+", "?", "[", "]", "|", "\\", "#", "-", "{", "}", "0123456789", "b", "B", "<", ">", "", "", "A`", "z'", "\n", ",", "a", "f", "n", "r", "t", "v", "x", "c", ":", "=", "e", "", "", "", "", "", "", "", "", "E", "Q", "X", "C", "Z", "G", "!", "p", "P", "N", "gk", "K", "R", }; return ((n >= (sizeof(messages) / sizeof(messages[1]))) ? "" : messages[n]); } inline const char* BOOST_REGEX_CALL get_default_error_string(regex_constants::error_type n) { static const char* const s_default_error_messages[] = { "Success", /* REG_NOERROR 0 error_ok */ "No match", /* REG_NOMATCH 1 error_no_match */ "Invalid regular expression.", /* REG_BADPAT 2 error_bad_pattern */ "Invalid collation character.", /* REG_ECOLLATE 3 error_collate */ "Invalid character class name, collating name, or character range.", /* REG_ECTYPE 4 error_ctype */ "Invalid or unterminated escape sequence.", /* REG_EESCAPE 5 error_escape */ "Invalid back reference: specified capturing group does not exist.", /* REG_ESUBREG 6 error_backref */ "Unmatched [ or [^ in character class declaration.", /* REG_EBRACK 7 error_brack */ "Unmatched marking parenthesis ( or \\(.", /* REG_EPAREN 8 error_paren */ "Unmatched quantified repeat operator { or \\{.", /* REG_EBRACE 9 error_brace */ "Invalid content of repeat range.", /* REG_BADBR 10 error_badbrace */ "Invalid range end in character class", /* REG_ERANGE 11 error_range */ "Out of memory.", /* REG_ESPACE 12 error_space NOT USED */ "Invalid preceding regular expression prior to repetition operator.", /* REG_BADRPT 13 error_badrepeat */ "Premature end of regular expression", /* REG_EEND 14 error_end NOT USED */ "Regular expression is too large.", /* REG_ESIZE 15 error_size NOT USED */ "Unmatched ) or \\)", /* REG_ERPAREN 16 error_right_paren NOT USED */ "Empty regular expression.", /* REG_EMPTY 17 error_empty */ "The complexity of matching the regular expression exceeded predefined bounds. " "Try refactoring the regular expression to make each choice made by the state machine unambiguous. " "This exception is thrown to prevent \"eternal\" matches that take an " "indefinite period time to locate.", /* REG_ECOMPLEXITY 18 error_complexity */ "Ran out of stack space trying to match the regular expression.", /* REG_ESTACK 19 error_stack */ "Invalid or unterminated Perl (?...) sequence.", /* REG_E_PERL 20 error_perl */ "Unknown error.", /* REG_E_UNKNOWN 21 error_unknown */ }; return (n > ::boost::regex_constants::error_unknown) ? s_default_error_messages[::boost::regex_constants::error_unknown] : s_default_error_messages[n]; } inline regex_constants::syntax_type BOOST_REGEX_CALL get_default_syntax_type(char c) { // // char_syntax determines how the compiler treats a given character // in a regular expression. // static regex_constants::syntax_type char_syntax[] = { regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_newline, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /* */ // 32 regex_constants::syntax_not, /*!*/ regex_constants::syntax_char, /*"*/ regex_constants::syntax_hash, /*#*/ regex_constants::syntax_dollar, /*$*/ regex_constants::syntax_char, /*%*/ regex_constants::syntax_char, /*&*/ regex_constants::escape_type_end_buffer, /*'*/ regex_constants::syntax_open_mark, /*(*/ regex_constants::syntax_close_mark, /*)*/ regex_constants::syntax_star, /***/ regex_constants::syntax_plus, /*+*/ regex_constants::syntax_comma, /*,*/ regex_constants::syntax_dash, /*-*/ regex_constants::syntax_dot, /*.*/ regex_constants::syntax_char, /*/*/ regex_constants::syntax_digit, /*0*/ regex_constants::syntax_digit, /*1*/ regex_constants::syntax_digit, /*2*/ regex_constants::syntax_digit, /*3*/ regex_constants::syntax_digit, /*4*/ regex_constants::syntax_digit, /*5*/ regex_constants::syntax_digit, /*6*/ regex_constants::syntax_digit, /*7*/ regex_constants::syntax_digit, /*8*/ regex_constants::syntax_digit, /*9*/ regex_constants::syntax_colon, /*:*/ regex_constants::syntax_char, /*;*/ regex_constants::escape_type_left_word, /*<*/ regex_constants::syntax_equal, /*=*/ regex_constants::escape_type_right_word, /*>*/ regex_constants::syntax_question, /*?*/ regex_constants::syntax_char, /*@*/ regex_constants::syntax_char, /*A*/ regex_constants::syntax_char, /*B*/ regex_constants::syntax_char, /*C*/ regex_constants::syntax_char, /*D*/ regex_constants::syntax_char, /*E*/ regex_constants::syntax_char, /*F*/ regex_constants::syntax_char, /*G*/ regex_constants::syntax_char, /*H*/ regex_constants::syntax_char, /*I*/ regex_constants::syntax_char, /*J*/ regex_constants::syntax_char, /*K*/ regex_constants::syntax_char, /*L*/ regex_constants::syntax_char, /*M*/ regex_constants::syntax_char, /*N*/ regex_constants::syntax_char, /*O*/ regex_constants::syntax_char, /*P*/ regex_constants::syntax_char, /*Q*/ regex_constants::syntax_char, /*R*/ regex_constants::syntax_char, /*S*/ regex_constants::syntax_char, /*T*/ regex_constants::syntax_char, /*U*/ regex_constants::syntax_char, /*V*/ regex_constants::syntax_char, /*W*/ regex_constants::syntax_char, /*X*/ regex_constants::syntax_char, /*Y*/ regex_constants::syntax_char, /*Z*/ regex_constants::syntax_open_set, /*[*/ regex_constants::syntax_escape, /*\*/ regex_constants::syntax_close_set, /*]*/ regex_constants::syntax_caret, /*^*/ regex_constants::syntax_char, /*_*/ regex_constants::syntax_char, /*`*/ regex_constants::syntax_char, /*a*/ regex_constants::syntax_char, /*b*/ regex_constants::syntax_char, /*c*/ regex_constants::syntax_char, /*d*/ regex_constants::syntax_char, /*e*/ regex_constants::syntax_char, /*f*/ regex_constants::syntax_char, /*g*/ regex_constants::syntax_char, /*h*/ regex_constants::syntax_char, /*i*/ regex_constants::syntax_char, /*j*/ regex_constants::syntax_char, /*k*/ regex_constants::syntax_char, /*l*/ regex_constants::syntax_char, /*m*/ regex_constants::syntax_char, /*n*/ regex_constants::syntax_char, /*o*/ regex_constants::syntax_char, /*p*/ regex_constants::syntax_char, /*q*/ regex_constants::syntax_char, /*r*/ regex_constants::syntax_char, /*s*/ regex_constants::syntax_char, /*t*/ regex_constants::syntax_char, /*u*/ regex_constants::syntax_char, /*v*/ regex_constants::syntax_char, /*w*/ regex_constants::syntax_char, /*x*/ regex_constants::syntax_char, /*y*/ regex_constants::syntax_char, /*z*/ regex_constants::syntax_open_brace, /*{*/ regex_constants::syntax_or, /*|*/ regex_constants::syntax_close_brace, /*}*/ regex_constants::syntax_char, /*~*/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ }; return char_syntax[(unsigned char)c]; } inline regex_constants::escape_syntax_type BOOST_REGEX_CALL get_default_escape_syntax_type(char c) { // // char_syntax determines how the compiler treats a given character // in a regular expression. // static regex_constants::escape_syntax_type char_syntax[] = { regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /* */ // 32 regex_constants::escape_type_identity, /*!*/ regex_constants::escape_type_identity, /*"*/ regex_constants::escape_type_identity, /*#*/ regex_constants::escape_type_identity, /*$*/ regex_constants::escape_type_identity, /*%*/ regex_constants::escape_type_identity, /*&*/ regex_constants::escape_type_end_buffer, /*'*/ regex_constants::syntax_open_mark, /*(*/ regex_constants::syntax_close_mark, /*)*/ regex_constants::escape_type_identity, /***/ regex_constants::syntax_plus, /*+*/ regex_constants::escape_type_identity, /*,*/ regex_constants::escape_type_identity, /*-*/ regex_constants::escape_type_identity, /*.*/ regex_constants::escape_type_identity, /*/*/ regex_constants::escape_type_decimal, /*0*/ regex_constants::escape_type_backref, /*1*/ regex_constants::escape_type_backref, /*2*/ regex_constants::escape_type_backref, /*3*/ regex_constants::escape_type_backref, /*4*/ regex_constants::escape_type_backref, /*5*/ regex_constants::escape_type_backref, /*6*/ regex_constants::escape_type_backref, /*7*/ regex_constants::escape_type_backref, /*8*/ regex_constants::escape_type_backref, /*9*/ regex_constants::escape_type_identity, /*:*/ regex_constants::escape_type_identity, /*;*/ regex_constants::escape_type_left_word, /*<*/ regex_constants::escape_type_identity, /*=*/ regex_constants::escape_type_right_word, /*>*/ regex_constants::syntax_question, /*?*/ regex_constants::escape_type_identity, /*@*/ regex_constants::escape_type_start_buffer, /*A*/ regex_constants::escape_type_not_word_assert, /*B*/ regex_constants::escape_type_C, /*C*/ regex_constants::escape_type_not_class, /*D*/ regex_constants::escape_type_E, /*E*/ regex_constants::escape_type_not_class, /*F*/ regex_constants::escape_type_G, /*G*/ regex_constants::escape_type_not_class, /*H*/ regex_constants::escape_type_not_class, /*I*/ regex_constants::escape_type_not_class, /*J*/ regex_constants::escape_type_reset_start_mark, /*K*/ regex_constants::escape_type_not_class, /*L*/ regex_constants::escape_type_not_class, /*M*/ regex_constants::escape_type_named_char, /*N*/ regex_constants::escape_type_not_class, /*O*/ regex_constants::escape_type_not_property, /*P*/ regex_constants::escape_type_Q, /*Q*/ regex_constants::escape_type_line_ending, /*R*/ regex_constants::escape_type_not_class, /*S*/ regex_constants::escape_type_not_class, /*T*/ regex_constants::escape_type_not_class, /*U*/ regex_constants::escape_type_not_class, /*V*/ regex_constants::escape_type_not_class, /*W*/ regex_constants::escape_type_X, /*X*/ regex_constants::escape_type_not_class, /*Y*/ regex_constants::escape_type_Z, /*Z*/ regex_constants::escape_type_identity, /*[*/ regex_constants::escape_type_identity, /*\*/ regex_constants::escape_type_identity, /*]*/ regex_constants::escape_type_identity, /*^*/ regex_constants::escape_type_identity, /*_*/ regex_constants::escape_type_start_buffer, /*`*/ regex_constants::escape_type_control_a, /*a*/ regex_constants::escape_type_word_assert, /*b*/ regex_constants::escape_type_ascii_control, /*c*/ regex_constants::escape_type_class, /*d*/ regex_constants::escape_type_e, /*e*/ regex_constants::escape_type_control_f, /*f*/ regex_constants::escape_type_extended_backref, /*g*/ regex_constants::escape_type_class, /*h*/ regex_constants::escape_type_class, /*i*/ regex_constants::escape_type_class, /*j*/ regex_constants::escape_type_extended_backref, /*k*/ regex_constants::escape_type_class, /*l*/ regex_constants::escape_type_class, /*m*/ regex_constants::escape_type_control_n, /*n*/ regex_constants::escape_type_class, /*o*/ regex_constants::escape_type_property, /*p*/ regex_constants::escape_type_class, /*q*/ regex_constants::escape_type_control_r, /*r*/ regex_constants::escape_type_class, /*s*/ regex_constants::escape_type_control_t, /*t*/ regex_constants::escape_type_class, /*u*/ regex_constants::escape_type_control_v, /*v*/ regex_constants::escape_type_class, /*w*/ regex_constants::escape_type_hex, /*x*/ regex_constants::escape_type_class, /*y*/ regex_constants::escape_type_end_buffer, /*z*/ regex_constants::syntax_open_brace, /*{*/ regex_constants::syntax_or, /*|*/ regex_constants::syntax_close_brace, /*}*/ regex_constants::escape_type_identity, /*~*/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ }; return char_syntax[(unsigned char)c]; } // is charT c a combining character? inline bool BOOST_REGEX_CALL is_combining_implementation(boost::uint_least16_t c) { const boost::uint_least16_t combining_ranges[] = { 0x0300, 0x0361, 0x0483, 0x0486, 0x0903, 0x0903, 0x093E, 0x0940, 0x0949, 0x094C, 0x0982, 0x0983, 0x09BE, 0x09C0, 0x09C7, 0x09CC, 0x09D7, 0x09D7, 0x0A3E, 0x0A40, 0x0A83, 0x0A83, 0x0ABE, 0x0AC0, 0x0AC9, 0x0ACC, 0x0B02, 0x0B03, 0x0B3E, 0x0B3E, 0x0B40, 0x0B40, 0x0B47, 0x0B4C, 0x0B57, 0x0B57, 0x0B83, 0x0B83, 0x0BBE, 0x0BBF, 0x0BC1, 0x0BCC, 0x0BD7, 0x0BD7, 0x0C01, 0x0C03, 0x0C41, 0x0C44, 0x0C82, 0x0C83, 0x0CBE, 0x0CBE, 0x0CC0, 0x0CC4, 0x0CC7, 0x0CCB, 0x0CD5, 0x0CD6, 0x0D02, 0x0D03, 0x0D3E, 0x0D40, 0x0D46, 0x0D4C, 0x0D57, 0x0D57, 0x0F7F, 0x0F7F, 0x20D0, 0x20E1, 0x3099, 0x309A, 0xFE20, 0xFE23, 0xffff, 0xffff, }; const boost::uint_least16_t* p = combining_ranges + 1; while (*p < c) p += 2; --p; if ((c >= *p) && (c <= *(p + 1))) return true; return false; } template inline bool is_combining(charT c) { return (c <= static_cast(0)) ? false : ((c >= static_cast((std::numeric_limits::max)())) ? false : is_combining_implementation(static_cast(c))); } template <> inline bool is_combining(char) { return false; } template <> inline bool is_combining(signed char) { return false; } template <> inline bool is_combining(unsigned char) { return false; } #if !defined(__hpux) && !defined(__WINSCW__) // can't use WCHAR_MAX/MIN in pp-directives #ifdef _MSC_VER template<> inline bool is_combining(wchar_t c) { return is_combining_implementation(static_cast(c)); } #elif !defined(__DECCXX) && !defined(__osf__) && !defined(__OSF__) && defined(WCHAR_MIN) && (WCHAR_MIN == 0) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) #if defined(WCHAR_MAX) && (WCHAR_MAX <= USHRT_MAX) template<> inline bool is_combining(wchar_t c) { return is_combining_implementation(static_cast(c)); } #else template<> inline bool is_combining(wchar_t c) { return (c >= (std::numeric_limits::max)()) ? false : is_combining_implementation(static_cast(c)); } #endif #endif #endif // // is a charT c a line separator? // template inline bool is_separator(charT c) { return BOOST_REGEX_MAKE_BOOL( (c == static_cast('\n')) || (c == static_cast('\r')) || (c == static_cast('\f')) || (static_cast(c) == 0x2028u) || (static_cast(c) == 0x2029u) || (static_cast(c) == 0x85u)); } template <> inline bool is_separator(char c) { return BOOST_REGEX_MAKE_BOOL((c == '\n') || (c == '\r') || (c == '\f')); } // // get a default collating element: // inline std::string BOOST_REGEX_CALL lookup_default_collate_name(const std::string& name) { // // these are the POSIX collating names: // static const char* def_coll_names[] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "alert", "backspace", "tab", "newline", "vertical-tab", "form-feed", "carriage-return", "SO", "SI", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "IS4", "IS3", "IS2", "IS1", "space", "exclamation-mark", "quotation-mark", "number-sign", "dollar-sign", "percent-sign", "ampersand", "apostrophe", "left-parenthesis", "right-parenthesis", "asterisk", "plus-sign", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less-than-sign", "equals-sign", "greater-than-sign", "question-mark", "commercial-at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "left-square-bracket", "backslash", "right-square-bracket", "circumflex", "underscore", "grave-accent", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "left-curly-bracket", "vertical-line", "right-curly-bracket", "tilde", "DEL", "", }; // these multi-character collating elements // should keep most Western-European locales // happy - we should really localise these a // little more - but this will have to do for // now: static const char* def_multi_coll[] = { "ae", "Ae", "AE", "ch", "Ch", "CH", "ll", "Ll", "LL", "ss", "Ss", "SS", "nj", "Nj", "NJ", "dz", "Dz", "DZ", "lj", "Lj", "LJ", "", }; unsigned int i = 0; while (*def_coll_names[i]) { if (def_coll_names[i] == name) { return std::string(1, char(i)); } ++i; } i = 0; while (*def_multi_coll[i]) { if (def_multi_coll[i] == name) { return def_multi_coll[i]; } ++i; } return std::string(); } // // get the state_id of a character classification, the individual // traits classes then transform that state_id into a bitmask: // template struct character_pointer_range { const charT* p1; const charT* p2; bool operator < (const character_pointer_range& r)const { return std::lexicographical_compare(p1, p2, r.p1, r.p2); } bool operator == (const character_pointer_range& r)const { // Not only do we check that the ranges are of equal size before // calling std::equal, but there is no other algorithm available: // not even a non-standard MS one. So forward to unchecked_equal // in the MS case. return ((p2 - p1) == (r.p2 - r.p1)) && BOOST_REGEX_DETAIL_NS::equal(p1, p2, r.p1); } }; template int get_default_class_id(const charT* p1, const charT* p2) { static const charT data[73] = { 'a', 'l', 'n', 'u', 'm', 'a', 'l', 'p', 'h', 'a', 'b', 'l', 'a', 'n', 'k', 'c', 'n', 't', 'r', 'l', 'd', 'i', 'g', 'i', 't', 'g', 'r', 'a', 'p', 'h', 'l', 'o', 'w', 'e', 'r', 'p', 'r', 'i', 'n', 't', 'p', 'u', 'n', 'c', 't', 's', 'p', 'a', 'c', 'e', 'u', 'n', 'i', 'c', 'o', 'd', 'e', 'u', 'p', 'p', 'e', 'r', 'v', 'w', 'o', 'r', 'd', 'x', 'd', 'i', 'g', 'i', 't', }; static const character_pointer_range ranges[21] = { {data+0, data+5,}, // alnum {data+5, data+10,}, // alpha {data+10, data+15,}, // blank {data+15, data+20,}, // cntrl {data+20, data+21,}, // d {data+20, data+25,}, // digit {data+25, data+30,}, // graph {data+29, data+30,}, // h {data+30, data+31,}, // l {data+30, data+35,}, // lower {data+35, data+40,}, // print {data+40, data+45,}, // punct {data+45, data+46,}, // s {data+45, data+50,}, // space {data+57, data+58,}, // u {data+50, data+57,}, // unicode {data+57, data+62,}, // upper {data+62, data+63,}, // v {data+63, data+64,}, // w {data+63, data+67,}, // word {data+67, data+73,}, // xdigit }; const character_pointer_range* ranges_begin = ranges; const character_pointer_range* ranges_end = ranges + (sizeof(ranges)/sizeof(ranges[0])); character_pointer_range t = { p1, p2, }; const character_pointer_range* p = std::lower_bound(ranges_begin, ranges_end, t); if((p != ranges_end) && (t == *p)) return static_cast(p - ranges); return -1; } // // helper functions: // template std::ptrdiff_t global_length(const charT* p) { std::ptrdiff_t n = 0; while(*p) { ++p; ++n; } return n; } template<> inline std::ptrdiff_t global_length(const char* p) { return (std::strlen)(p); } #ifndef BOOST_NO_WREGEX template<> inline std::ptrdiff_t global_length(const wchar_t* p) { return (std::ptrdiff_t)(std::wcslen)(p); } #endif template inline charT BOOST_REGEX_CALL global_lower(charT c) { return c; } template inline charT BOOST_REGEX_CALL global_upper(charT c) { return c; } inline char BOOST_REGEX_CALL do_global_lower(char c) { return static_cast((std::tolower)((unsigned char)c)); } inline char BOOST_REGEX_CALL do_global_upper(char c) { return static_cast((std::toupper)((unsigned char)c)); } #ifndef BOOST_NO_WREGEX inline wchar_t BOOST_REGEX_CALL do_global_lower(wchar_t c) { return (std::towlower)(c); } inline wchar_t BOOST_REGEX_CALL do_global_upper(wchar_t c) { return (std::towupper)(c); } #endif // // This sucks: declare template specialisations of global_lower/global_upper // that just forward to the non-template implementation functions. We do // this because there is one compiler (Compaq Tru64 C++) that doesn't seem // to differentiate between templates and non-template overloads.... // what's more, the primary template, plus all overloads have to be // defined in the same translation unit (if one is inline they all must be) // otherwise the "local template instantiation" compiler option can pick // the wrong instantiation when linking: // template<> inline char BOOST_REGEX_CALL global_lower(char c) { return do_global_lower(c); } template<> inline char BOOST_REGEX_CALL global_upper(char c) { return do_global_upper(c); } #ifndef BOOST_NO_WREGEX template<> inline wchar_t BOOST_REGEX_CALL global_lower(wchar_t c) { return do_global_lower(c); } template<> inline wchar_t BOOST_REGEX_CALL global_upper(wchar_t c) { return do_global_upper(c); } #endif template int global_value(charT c) { static const charT zero = '0'; static const charT nine = '9'; static const charT a = 'a'; static const charT f = 'f'; static const charT A = 'A'; static const charT F = 'F'; if(c > f) return -1; if(c >= a) return 10 + (c - a); if(c > F) return -1; if(c >= A) return 10 + (c - A); if(c > nine) return -1; if(c >= zero) return c - zero; return -1; } template boost::intmax_t global_toi(const charT*& p1, const charT* p2, int radix, const traits& t) { (void)t; // warning suppression boost::intmax_t limit = (std::numeric_limits::max)() / radix; boost::intmax_t next_value = t.value(*p1, radix); if((p1 == p2) || (next_value < 0) || (next_value >= radix)) return -1; boost::intmax_t result = 0; while(p1 != p2) { next_value = t.value(*p1, radix); if((next_value < 0) || (next_value >= radix)) break; result *= radix; result += next_value; ++p1; if (result > limit) return -1; } return result; } template inline typename boost::enable_if_c<(sizeof(charT) > 1), const charT*>::type get_escape_R_string() { #ifdef BOOST_MSVC # pragma warning(push) # pragma warning(disable:4309 4245) #endif static const charT e1[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\x0D', '\x0A', '?', '|', '[', '\x0A', '\x0B', '\x0C', static_cast(0x85), static_cast(0x2028), static_cast(0x2029), ']', ')', ')', '\0' }; static const charT e2[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\x0D', '\x0A', '?', '|', '[', '\x0A', '\x0B', '\x0C', static_cast(0x85), ']', ')', ')', '\0' }; charT c = static_cast(0x2029u); bool b = (static_cast(c) == 0x2029u); return (b ? e1 : e2); #ifdef BOOST_MSVC # pragma warning(pop) #endif } template inline typename boost::disable_if_c<(sizeof(charT) > 1), const charT*>::type get_escape_R_string() { #ifdef BOOST_MSVC # pragma warning(push) # pragma warning(disable:4309) #endif static const charT e2[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\x0D', '\x0A', '?', '|', '[', '\x0A', '\x0B', '\x0C', '\x85', ']', ')', ')', '\0' }; return e2; #ifdef BOOST_MSVC # pragma warning(pop) #endif } } // BOOST_REGEX_DETAIL_NS } // boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/regex_workaround.hpp ================================================ /* * * Copyright (c) 1998-2005 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_workarounds.cpp * VERSION see * DESCRIPTION: Declares Misc workarounds. */ #ifndef BOOST_REGEX_WORKAROUND_HPP #define BOOST_REGEX_WORKAROUND_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef BOOST_NO_STD_LOCALE # include #endif #if defined(BOOST_NO_STDC_NAMESPACE) namespace std{ using ::sprintf; using ::strcpy; using ::strcat; using ::strlen; } #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_NO_STD_DISTANCE template std::ptrdiff_t distance(const T& x, const T& y) { return y - x; } #else using std::distance; #endif }} #ifdef BOOST_REGEX_NO_BOOL # define BOOST_REGEX_MAKE_BOOL(x) static_cast((x) ? true : false) #else # define BOOST_REGEX_MAKE_BOOL(x) static_cast(x) #endif /***************************************************************************** * * Fix broken namespace support: * ****************************************************************************/ #if defined(BOOST_NO_STDC_NAMESPACE) && defined(__cplusplus) namespace std{ using ::ptrdiff_t; using ::size_t; using ::abs; using ::memset; using ::memcpy; } #endif /***************************************************************************** * * helper functions pointer_construct/pointer_destroy: * ****************************************************************************/ #ifdef __cplusplus namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_MSVC #pragma warning (push) #pragma warning (disable : 4100) #endif template inline void pointer_destroy(T* p) { p->~T(); (void)p; } #ifdef BOOST_MSVC #pragma warning (pop) #endif template inline void pointer_construct(T* p, const T& t) { new (p) T(t); } }} // namespaces #endif /***************************************************************************** * * helper function copy: * ****************************************************************************/ #ifdef __cplusplus namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #if BOOST_WORKAROUND(BOOST_MSVC,>=1400) && BOOST_WORKAROUND(BOOST_MSVC, <1600) && defined(_CPPLIB_VER) && defined(BOOST_DINKUMWARE_STDLIB) && !(defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)) // // MSVC 8 will either emit warnings or else refuse to compile // code that makes perfectly legitimate use of std::copy, when // the OutputIterator type is a user-defined class (apparently all user // defined iterators are "unsafe"). This code works around that: // template inline OutputIterator copy( InputIterator first, InputIterator last, OutputIterator dest ) { return stdext::unchecked_copy(first, last, dest); } template inline bool equal( InputIterator1 first, InputIterator1 last, InputIterator2 with ) { return stdext::unchecked_equal(first, last, with); } #elif BOOST_WORKAROUND(BOOST_MSVC, > 1500) // // MSVC 10 will either emit warnings or else refuse to compile // code that makes perfectly legitimate use of std::copy, when // the OutputIterator type is a user-defined class (apparently all user // defined iterators are "unsafe"). What's more Microsoft have removed their // non-standard "unchecked" versions, even though their still in the MS // documentation!! Work around this as best we can: // template inline OutputIterator copy( InputIterator first, InputIterator last, OutputIterator dest ) { while(first != last) *dest++ = *first++; return dest; } template inline bool equal( InputIterator1 first, InputIterator1 last, InputIterator2 with ) { while(first != last) if(*first++ != *with++) return false; return true; } #else using std::copy; using std::equal; #endif #if BOOST_WORKAROUND(BOOST_MSVC,>=1400) && defined(__STDC_WANT_SECURE_LIB__) && __STDC_WANT_SECURE_LIB__ // use safe versions of strcpy etc: using ::strcpy_s; using ::strcat_s; #else inline std::size_t strcpy_s( char *strDestination, std::size_t sizeInBytes, const char *strSource ) { std::size_t lenSourceWithNull = std::strlen(strSource) + 1; if (lenSourceWithNull > sizeInBytes) return 1; std::memcpy(strDestination, strSource, lenSourceWithNull); return 0; } inline std::size_t strcat_s( char *strDestination, std::size_t sizeInBytes, const char *strSource ) { std::size_t lenSourceWithNull = std::strlen(strSource) + 1; std::size_t lenDestination = std::strlen(strDestination); if (lenSourceWithNull + lenDestination > sizeInBytes) return 1; std::memcpy(strDestination + lenDestination, strSource, lenSourceWithNull); return 0; } #endif inline void overflow_error_if_not_zero(std::size_t i) { if(i) { std::overflow_error e("String buffer too small"); boost::throw_exception(e); } } }} // namespaces #endif // __cplusplus #endif // include guard ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/states.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE states.cpp * VERSION see * DESCRIPTION: Declares internal state machine structures. */ #ifndef BOOST_REGEX_V4_STATES_HPP #define BOOST_REGEX_V4_STATES_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ /*** mask_type ******************************************************* Whenever we have a choice of two alternatives, we use an array of bytes to indicate which of the two alternatives it is possible to take for any given input character. If mask_take is set, then we can take the next state, and if mask_skip is set then we can take the alternative. ***********************************************************************/ enum mask_type { mask_take = 1, mask_skip = 2, mask_init = 4, mask_any = mask_skip | mask_take, mask_all = mask_any }; /*** helpers ********************************************************** These helpers let us use function overload resolution to detect whether we have narrow or wide character strings: ***********************************************************************/ struct _narrow_type{}; struct _wide_type{}; template struct is_byte; template<> struct is_byte { typedef _narrow_type width_type; }; template<> struct is_byte{ typedef _narrow_type width_type; }; template<> struct is_byte { typedef _narrow_type width_type; }; template struct is_byte { typedef _wide_type width_type; }; /*** enum syntax_element_type ****************************************** Every record in the state machine falls into one of the following types: ***********************************************************************/ enum syntax_element_type { // start of a marked sub-expression, or perl-style (?...) extension syntax_element_startmark = 0, // end of a marked sub-expression, or perl-style (?...) extension syntax_element_endmark = syntax_element_startmark + 1, // any sequence of literal characters syntax_element_literal = syntax_element_endmark + 1, // start of line assertion: ^ syntax_element_start_line = syntax_element_literal + 1, // end of line assertion $ syntax_element_end_line = syntax_element_start_line + 1, // match any character: . syntax_element_wild = syntax_element_end_line + 1, // end of expression: we have a match when we get here syntax_element_match = syntax_element_wild + 1, // perl style word boundary: \b syntax_element_word_boundary = syntax_element_match + 1, // perl style within word boundary: \B syntax_element_within_word = syntax_element_word_boundary + 1, // start of word assertion: \< syntax_element_word_start = syntax_element_within_word + 1, // end of word assertion: \> syntax_element_word_end = syntax_element_word_start + 1, // start of buffer assertion: \` syntax_element_buffer_start = syntax_element_word_end + 1, // end of buffer assertion: \' syntax_element_buffer_end = syntax_element_buffer_start + 1, // backreference to previously matched sub-expression syntax_element_backref = syntax_element_buffer_end + 1, // either a wide character set [..] or one with multicharacter collating elements: syntax_element_long_set = syntax_element_backref + 1, // narrow character set: [...] syntax_element_set = syntax_element_long_set + 1, // jump to a new state in the machine: syntax_element_jump = syntax_element_set + 1, // choose between two production states: syntax_element_alt = syntax_element_jump + 1, // a repeat syntax_element_rep = syntax_element_alt + 1, // match a combining character sequence syntax_element_combining = syntax_element_rep + 1, // perl style soft buffer end: \z syntax_element_soft_buffer_end = syntax_element_combining + 1, // perl style continuation: \G syntax_element_restart_continue = syntax_element_soft_buffer_end + 1, // single character repeats: syntax_element_dot_rep = syntax_element_restart_continue + 1, syntax_element_char_rep = syntax_element_dot_rep + 1, syntax_element_short_set_rep = syntax_element_char_rep + 1, syntax_element_long_set_rep = syntax_element_short_set_rep + 1, // a backstep for lookbehind repeats: syntax_element_backstep = syntax_element_long_set_rep + 1, // an assertion that a mark was matched: syntax_element_assert_backref = syntax_element_backstep + 1, syntax_element_toggle_case = syntax_element_assert_backref + 1, // a recursive expression: syntax_element_recurse = syntax_element_toggle_case + 1, // Verbs: syntax_element_fail = syntax_element_recurse + 1, syntax_element_accept = syntax_element_fail + 1, syntax_element_commit = syntax_element_accept + 1, syntax_element_then = syntax_element_commit + 1 }; #ifdef BOOST_REGEX_DEBUG // dwa 09/26/00 - This is needed to suppress warnings about an ambiguous conversion std::ostream& operator<<(std::ostream&, syntax_element_type); #endif struct re_syntax_base; /*** union offset_type ************************************************ Points to another state in the machine. During machine construction we use integral offsets, but these are converted to pointers before execution of the machine. ***********************************************************************/ union offset_type { re_syntax_base* p; std::ptrdiff_t i; }; /*** struct re_syntax_base ******************************************** Base class for all states in the machine. ***********************************************************************/ struct re_syntax_base { syntax_element_type type; // what kind of state this is offset_type next; // next state in the machine }; /*** struct re_brace ************************************************** A marked parenthesis. ***********************************************************************/ struct re_brace : public re_syntax_base { // The index to match, can be zero (don't mark the sub-expression) // or negative (for perl style (?...) extensions): int index; bool icase; }; /*** struct re_dot ************************************************** Match anything. ***********************************************************************/ enum { dont_care = 1, force_not_newline = 0, force_newline = 2, test_not_newline = 2, test_newline = 3 }; struct re_dot : public re_syntax_base { unsigned char mask; }; /*** struct re_literal ************************************************ A string of literals, following this structure will be an array of characters: charT[length] ***********************************************************************/ struct re_literal : public re_syntax_base { unsigned int length; }; /*** struct re_case ************************************************ Indicates whether we are moving to a case insensive block or not ***********************************************************************/ struct re_case : public re_syntax_base { bool icase; }; /*** struct re_set_long *********************************************** A wide character set of characters, following this structure will be an array of type charT: First csingles null-terminated strings Then 2 * cranges NULL terminated strings Then cequivalents NULL terminated strings ***********************************************************************/ template struct re_set_long : public re_syntax_base { unsigned int csingles, cranges, cequivalents; mask_type cclasses; mask_type cnclasses; bool isnot; bool singleton; }; /*** struct re_set **************************************************** A set of narrow-characters, matches any of _map which is none-zero ***********************************************************************/ struct re_set : public re_syntax_base { unsigned char _map[1 << CHAR_BIT]; }; /*** struct re_jump *************************************************** Jump to a new location in the machine (not next). ***********************************************************************/ struct re_jump : public re_syntax_base { offset_type alt; // location to jump to }; /*** struct re_alt *************************************************** Jump to a new location in the machine (possibly next). ***********************************************************************/ struct re_alt : public re_jump { unsigned char _map[1 << CHAR_BIT]; // which characters can take the jump unsigned int can_be_null; // true if we match a NULL string }; /*** struct re_repeat ************************************************* Repeat a section of the machine ***********************************************************************/ struct re_repeat : public re_alt { std::size_t min, max; // min and max allowable repeats int state_id; // Unique identifier for this repeat bool leading; // True if this repeat is at the start of the machine (lets us optimize some searches) bool greedy; // True if this is a greedy repeat }; /*** struct re_recurse ************************************************ Recurse to a particular subexpression. **********************************************************************/ struct re_recurse : public re_jump { int state_id; // identifier of first nested repeat within the recursion. }; /*** struct re_commit ************************************************* Used for the PRUNE, SKIP and COMMIT verbs which basically differ only in what happens if no match is found and we start searching forward. **********************************************************************/ enum commit_type { commit_prune, commit_skip, commit_commit }; struct re_commit : public re_syntax_base { commit_type action; }; /*** enum re_jump_size_type ******************************************* Provides compiled size of re_jump structure (allowing for trailing alignment). We provide this so we know how manybytes to insert when constructing the machine (The value of padding_mask is defined in regex_raw_buffer.hpp). ***********************************************************************/ enum re_jump_size_type { re_jump_size = (sizeof(re_jump) + padding_mask) & ~(padding_mask), re_repeater_size = (sizeof(re_repeat) + padding_mask) & ~(padding_mask), re_alt_size = (sizeof(re_alt) + padding_mask) & ~(padding_mask) }; /*** proc re_is_set_member ********************************************* Forward declaration: we'll need this one later... ***********************************************************************/ template struct regex_data; template iterator BOOST_REGEX_CALL re_is_set_member(iterator next, iterator last, const re_set_long* set_, const regex_data& e, bool icase); } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/sub_match.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE sub_match.cpp * VERSION see * DESCRIPTION: Declares template class sub_match. */ #ifndef BOOST_REGEX_V4_SUB_MATCH_HPP #define BOOST_REGEX_V4_SUB_MATCH_HPP #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif namespace boost{ template struct sub_match : public std::pair { typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type value_type; #if defined(BOOST_NO_STD_ITERATOR_TRAITS) typedef std::ptrdiff_t difference_type; #else typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::difference_type difference_type; #endif typedef BidiIterator iterator_type; typedef BidiIterator iterator; typedef BidiIterator const_iterator; bool matched; sub_match() : std::pair(), matched(false) {} sub_match(BidiIterator i) : std::pair(i, i), matched(false) {} #if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\ && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551)\ && !BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) template operator std::basic_string ()const { return matched ? std::basic_string(this->first, this->second) : std::basic_string(); } #else operator std::basic_string ()const { return str(); } #endif difference_type BOOST_REGEX_CALL length()const { difference_type n = matched ? ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)this->first, (BidiIterator)this->second) : 0; return n; } std::basic_string str()const { std::basic_string result; if(matched) { std::size_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)this->first, (BidiIterator)this->second); result.reserve(len); BidiIterator i = this->first; while(i != this->second) { result.append(1, *i); ++i; } } return result; } int compare(const sub_match& s)const { if(matched != s.matched) return static_cast(matched) - static_cast(s.matched); return str().compare(s.str()); } int compare(const std::basic_string& s)const { return str().compare(s); } int compare(const value_type* p)const { return str().compare(p); } bool operator==(const sub_match& that)const { return compare(that) == 0; } bool BOOST_REGEX_CALL operator !=(const sub_match& that)const { return compare(that) != 0; } bool operator<(const sub_match& that)const { return compare(that) < 0; } bool operator>(const sub_match& that)const { return compare(that) > 0; } bool operator<=(const sub_match& that)const { return compare(that) <= 0; } bool operator>=(const sub_match& that)const { return compare(that) >= 0; } #ifdef BOOST_REGEX_MATCH_EXTRA typedef std::vector > capture_sequence_type; const capture_sequence_type& captures()const { if(!m_captures) m_captures.reset(new capture_sequence_type()); return *m_captures; } // // Private implementation API: DO NOT USE! // capture_sequence_type& get_captures()const { if(!m_captures) m_captures.reset(new capture_sequence_type()); return *m_captures; } private: mutable boost::scoped_ptr m_captures; public: #endif sub_match(const sub_match& that, bool #ifdef BOOST_REGEX_MATCH_EXTRA deep_copy #endif = true ) : std::pair(that), matched(that.matched) { #ifdef BOOST_REGEX_MATCH_EXTRA if(that.m_captures) if(deep_copy) m_captures.reset(new capture_sequence_type(*(that.m_captures))); #endif } sub_match& operator=(const sub_match& that) { this->first = that.first; this->second = that.second; matched = that.matched; #ifdef BOOST_REGEX_MATCH_EXTRA if(that.m_captures) get_captures() = *(that.m_captures); #endif return *this; } // // Make this type a range, for both Boost.Range, and C++11: // BidiIterator begin()const { return this->first; } BidiIterator end()const { return this->second; } #ifdef BOOST_OLD_REGEX_H // // the following are deprecated, do not use!! // operator int()const; operator unsigned int()const; operator short()const { return (short)(int)(*this); } operator unsigned short()const { return (unsigned short)(unsigned int)(*this); } #endif }; typedef sub_match csub_match; typedef sub_match ssub_match; #ifndef BOOST_NO_WREGEX typedef sub_match wcsub_match; typedef sub_match wssub_match; #endif // comparison to std::basic_string<> part 1: template inline bool operator == (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) == 0; } template inline bool operator != (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) != 0; } template inline bool operator < (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) < 0; } template inline bool operator <= (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) <= 0; } template inline bool operator >= (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) >= 0; } template inline bool operator > (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) > 0; } // comparison to std::basic_string<> part 2: template inline bool operator == (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) == 0; } template inline bool operator != (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) != 0; } template inline bool operator < (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) < 0; } template inline bool operator > (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) > 0; } template inline bool operator <= (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) <= 0; } template inline bool operator >= (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) >= 0; } // comparison to const charT* part 1: template inline bool operator == (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s) { return m.str().compare(s) == 0; } template inline bool operator != (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s) { return m.str().compare(s) != 0; } template inline bool operator > (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s) { return m.str().compare(s) > 0; } template inline bool operator < (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s) { return m.str().compare(s) < 0; } template inline bool operator >= (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s) { return m.str().compare(s) >= 0; } template inline bool operator <= (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s) { return m.str().compare(s) <= 0; } // comparison to const charT* part 2: template inline bool operator == (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) == 0; } template inline bool operator != (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) != 0; } template inline bool operator < (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) > 0; } template inline bool operator > (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) < 0; } template inline bool operator <= (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) >= 0; } template inline bool operator >= (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) <= 0; } // comparison to const charT& part 1: template inline bool operator == (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) == 0; } template inline bool operator != (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) != 0; } template inline bool operator > (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) > 0; } template inline bool operator < (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) < 0; } template inline bool operator >= (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) >= 0; } template inline bool operator <= (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) <= 0; } // comparison to const charT* part 2: template inline bool operator == (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) == 0; } template inline bool operator != (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) != 0; } template inline bool operator < (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) > 0; } template inline bool operator > (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) < 0; } template inline bool operator <= (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) >= 0; } template inline bool operator >= (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) <= 0; } // addition operators: template inline std::basic_string::value_type, traits, Allocator> operator + (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { std::basic_string::value_type, traits, Allocator> result; result.reserve(s.size() + m.length() + 1); return result.append(s).append(m.first, m.second); } template inline std::basic_string::value_type, traits, Allocator> operator + (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { std::basic_string::value_type, traits, Allocator> result; result.reserve(s.size() + m.length() + 1); return result.append(m.first, m.second).append(s); } #if !(defined(__GNUC__) && defined(BOOST_NO_STD_LOCALE)) template inline std::basic_string::value_type> operator + (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s, const sub_match& m) { std::basic_string::value_type> result; result.reserve(std::char_traits::value_type>::length(s) + m.length() + 1); return result.append(s).append(m.first, m.second); } template inline std::basic_string::value_type> operator + (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const * s) { std::basic_string::value_type> result; result.reserve(std::char_traits::value_type>::length(s) + m.length() + 1); return result.append(m.first, m.second).append(s); } #else // worwaround versions: template inline std::basic_string::value_type> operator + (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const* s, const sub_match& m) { return s + m.str(); } template inline std::basic_string::value_type> operator + (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const * s) { return m.str() + s; } #endif template inline std::basic_string::value_type> operator + (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s, const sub_match& m) { std::basic_string::value_type> result; result.reserve(m.length() + 2); return result.append(1, s).append(m.first, m.second); } template inline std::basic_string::value_type> operator + (const sub_match& m, typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::value_type const& s) { std::basic_string::value_type> result; result.reserve(m.length() + 2); return result.append(m.first, m.second).append(1, s); } template inline std::basic_string::value_type> operator + (const sub_match& m1, const sub_match& m2) { std::basic_string::value_type> result; result.reserve(m1.length() + m2.length() + 1); return result.append(m1.first, m1.second).append(m2.first, m2.second); } #ifndef BOOST_NO_STD_LOCALE template std::basic_ostream& operator << (std::basic_ostream& os, const sub_match& s) { return (os << s.str()); } #else template std::ostream& operator << (std::ostream& os, const sub_match& s) { return (os << s.str()); } #endif #ifdef BOOST_OLD_REGEX_H namespace BOOST_REGEX_DETAIL_NS{ template int do_toi(BidiIterator i, BidiIterator j, char c, int radix) { std::string s(i, j); char* p; int result = std::strtol(s.c_str(), &p, radix); if(*p)raise_regex_exception("Bad sub-expression"); return result; } // // helper: template int do_toi(I& i, I j, charT c) { int result = 0; while((i != j) && (isdigit(*i))) { result = result*10 + (*i - '0'); ++i; } return result; } } template sub_match::operator int()const { BidiIterator i = first; BidiIterator j = second; if(i == j)raise_regex_exception("Bad sub-expression"); int neg = 1; if((i != j) && (*i == '-')) { neg = -1; ++i; } neg *= BOOST_REGEX_DETAIL_NS::do_toi(i, j, *i); if(i != j)raise_regex_exception("Bad sub-expression"); return neg; } template sub_match::operator unsigned int()const { BidiIterator i = first; BidiIterator j = second; if(i == j) raise_regex_exception("Bad sub-expression"); return BOOST_REGEX_DETAIL_NS::do_toi(i, j, *first); } #endif } // namespace boost #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/syntax_type.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE syntax_type.hpp * VERSION see * DESCRIPTION: Declares regular expression synatx type enumerator. */ #ifndef BOOST_REGEX_SYNTAX_TYPE_HPP #define BOOST_REGEX_SYNTAX_TYPE_HPP namespace boost{ namespace regex_constants{ typedef unsigned char syntax_type; // // values chosen are binary compatible with previous version: // static const syntax_type syntax_char = 0; static const syntax_type syntax_open_mark = 1; static const syntax_type syntax_close_mark = 2; static const syntax_type syntax_dollar = 3; static const syntax_type syntax_caret = 4; static const syntax_type syntax_dot = 5; static const syntax_type syntax_star = 6; static const syntax_type syntax_plus = 7; static const syntax_type syntax_question = 8; static const syntax_type syntax_open_set = 9; static const syntax_type syntax_close_set = 10; static const syntax_type syntax_or = 11; static const syntax_type syntax_escape = 12; static const syntax_type syntax_dash = 14; static const syntax_type syntax_open_brace = 15; static const syntax_type syntax_close_brace = 16; static const syntax_type syntax_digit = 17; static const syntax_type syntax_comma = 27; static const syntax_type syntax_equal = 37; static const syntax_type syntax_colon = 36; static const syntax_type syntax_not = 53; // extensions: static const syntax_type syntax_hash = 13; static const syntax_type syntax_newline = 26; // escapes: typedef syntax_type escape_syntax_type; static const escape_syntax_type escape_type_word_assert = 18; static const escape_syntax_type escape_type_not_word_assert = 19; static const escape_syntax_type escape_type_control_f = 29; static const escape_syntax_type escape_type_control_n = 30; static const escape_syntax_type escape_type_control_r = 31; static const escape_syntax_type escape_type_control_t = 32; static const escape_syntax_type escape_type_control_v = 33; static const escape_syntax_type escape_type_ascii_control = 35; static const escape_syntax_type escape_type_hex = 34; static const escape_syntax_type escape_type_unicode = 0; // not used static const escape_syntax_type escape_type_identity = 0; // not used static const escape_syntax_type escape_type_backref = syntax_digit; static const escape_syntax_type escape_type_decimal = syntax_digit; // not used static const escape_syntax_type escape_type_class = 22; static const escape_syntax_type escape_type_not_class = 23; // extensions: static const escape_syntax_type escape_type_left_word = 20; static const escape_syntax_type escape_type_right_word = 21; static const escape_syntax_type escape_type_start_buffer = 24; // for \` static const escape_syntax_type escape_type_end_buffer = 25; // for \' static const escape_syntax_type escape_type_control_a = 28; // for \a static const escape_syntax_type escape_type_e = 38; // for \e static const escape_syntax_type escape_type_E = 47; // for \Q\E static const escape_syntax_type escape_type_Q = 48; // for \Q\E static const escape_syntax_type escape_type_X = 49; // for \X static const escape_syntax_type escape_type_C = 50; // for \C static const escape_syntax_type escape_type_Z = 51; // for \Z static const escape_syntax_type escape_type_G = 52; // for \G static const escape_syntax_type escape_type_property = 54; // for \p static const escape_syntax_type escape_type_not_property = 55; // for \P static const escape_syntax_type escape_type_named_char = 56; // for \N static const escape_syntax_type escape_type_extended_backref = 57; // for \g static const escape_syntax_type escape_type_reset_start_mark = 58; // for \K static const escape_syntax_type escape_type_line_ending = 59; // for \R static const escape_syntax_type syntax_max = 60; } } #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/u32regex_iterator.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE u32regex_iterator.hpp * VERSION see * DESCRIPTION: Provides u32regex_iterator implementation. */ #ifndef BOOST_REGEX_V4_U32REGEX_ITERATOR_HPP #define BOOST_REGEX_V4_U32REGEX_ITERATOR_HPP namespace boost{ #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif template class u32regex_iterator_implementation { typedef u32regex regex_type; match_results what; // current match BidirectionalIterator base; // start of sequence BidirectionalIterator end; // end of sequence const regex_type re; // the expression match_flag_type flags; // flags for matching public: u32regex_iterator_implementation(const regex_type* p, BidirectionalIterator last, match_flag_type f) : base(), end(last), re(*p), flags(f){} bool init(BidirectionalIterator first) { base = first; return u32regex_search(first, end, what, re, flags, base); } bool compare(const u32regex_iterator_implementation& that) { if(this == &that) return true; return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second); } const match_results& get() { return what; } bool next() { //if(what.prefix().first != what[0].second) // flags |= match_prev_avail; BidirectionalIterator next_start = what[0].second; match_flag_type f(flags); if(!what.length()) f |= regex_constants::match_not_initial_null; //if(base != next_start) // f |= regex_constants::match_not_bob; bool result = u32regex_search(next_start, end, what, re, f, base); if(result) what.set_base(base); return result; } private: u32regex_iterator_implementation& operator=(const u32regex_iterator_implementation&); }; template class u32regex_iterator { private: typedef u32regex_iterator_implementation impl; typedef shared_ptr pimpl; public: typedef u32regex regex_type; typedef match_results value_type; typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; u32regex_iterator(){} u32regex_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, match_flag_type m = match_default) : pdata(new impl(&re, b, m)) { if(!pdata->init(a)) { pdata.reset(); } } u32regex_iterator(const u32regex_iterator& that) : pdata(that.pdata) {} u32regex_iterator& operator=(const u32regex_iterator& that) { pdata = that.pdata; return *this; } bool operator==(const u32regex_iterator& that)const { if((pdata.get() == 0) || (that.pdata.get() == 0)) return pdata.get() == that.pdata.get(); return pdata->compare(*(that.pdata.get())); } bool operator!=(const u32regex_iterator& that)const { return !(*this == that); } const value_type& operator*()const { return pdata->get(); } const value_type* operator->()const { return &(pdata->get()); } u32regex_iterator& operator++() { cow(); if(0 == pdata->next()) { pdata.reset(); } return *this; } u32regex_iterator operator++(int) { u32regex_iterator result(*this); ++(*this); return result; } private: pimpl pdata; void cow() { // copy-on-write if(pdata.get() && !pdata.unique()) { pdata.reset(new impl(*(pdata.get()))); } } }; typedef u32regex_iterator utf8regex_iterator; typedef u32regex_iterator utf16regex_iterator; typedef u32regex_iterator utf32regex_iterator; inline u32regex_iterator make_u32regex_iterator(const char* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_iterator(p, p+std::strlen(p), e, m); } #ifndef BOOST_NO_WREGEX inline u32regex_iterator make_u32regex_iterator(const wchar_t* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_iterator(p, p+std::wcslen(p), e, m); } #endif #if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T) inline u32regex_iterator make_u32regex_iterator(const UChar* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_iterator(p, p+u_strlen(p), e, m); } #endif template inline u32regex_iterator::const_iterator> make_u32regex_iterator(const std::basic_string& p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { typedef typename std::basic_string::const_iterator iter_type; return u32regex_iterator(p.begin(), p.end(), e, m); } inline u32regex_iterator make_u32regex_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_iterator(s.getBuffer(), s.getBuffer() + s.length(), e, m); } #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif } // namespace boost #endif // BOOST_REGEX_V4_REGEX_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/u32regex_token_iterator.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE u32regex_token_iterator.hpp * VERSION see * DESCRIPTION: Provides u32regex_token_iterator implementation. */ #ifndef BOOST_REGEX_V4_U32REGEX_TOKEN_ITERATOR_HPP #define BOOST_REGEX_V4_U32REGEX_TOKEN_ITERATOR_HPP #if (BOOST_WORKAROUND(BOOST_BORLANDC, >= 0x560) && BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x570)))\ || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) // // Borland C++ Builder 6, and Visual C++ 6, // can't cope with the array template constructor // so we have a template member that will accept any type as // argument, and then assert that is really is an array: // #include #include #endif namespace boost{ #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC # pragma warning(push) # pragma warning(disable:4700) #endif template class u32regex_token_iterator_implementation { typedef u32regex regex_type; typedef sub_match value_type; match_results what; // current match BidirectionalIterator end; // end of search area BidirectionalIterator base; // start of search area const regex_type re; // the expression match_flag_type flags; // match flags value_type result; // the current string result int N; // the current sub-expression being enumerated std::vector subs; // the sub-expressions to enumerate public: u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, int sub, match_flag_type f) : end(last), re(*p), flags(f){ subs.push_back(sub); } u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const std::vector& v, match_flag_type f) : end(last), re(*p), flags(f), subs(v){} #if (BOOST_WORKAROUND(__BORLANDC__, >= 0x560) && BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570)))\ || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) \ || BOOST_WORKAROUND(__HP_aCC, < 60700) template u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const T& submatches, match_flag_type f) : end(last), re(*p), flags(f) { // assert that T really is an array: BOOST_STATIC_ASSERT(::boost::is_array::value); const std::size_t array_size = sizeof(T) / sizeof(submatches[0]); for(std::size_t i = 0; i < array_size; ++i) { subs.push_back(submatches[i]); } } #else template u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const int (&submatches)[CN], match_flag_type f) : end(last), re(*p), flags(f) { for(std::size_t i = 0; i < CN; ++i) { subs.push_back(submatches[i]); } } #endif bool init(BidirectionalIterator first) { base = first; N = 0; if(u32regex_search(first, end, what, re, flags, base) == true) { N = 0; result = ((subs[N] == -1) ? what.prefix() : what[(int)subs[N]]); return true; } else if((subs[N] == -1) && (first != end)) { result.first = first; result.second = end; result.matched = (first != end); N = -1; return true; } return false; } bool compare(const u32regex_token_iterator_implementation& that) { if(this == &that) return true; return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (N == that.N) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second); } const value_type& get() { return result; } bool next() { if(N == -1) return false; if(N+1 < (int)subs.size()) { ++N; result =((subs[N] == -1) ? what.prefix() : what[subs[N]]); return true; } //if(what.prefix().first != what[0].second) // flags |= match_prev_avail | regex_constants::match_not_bob; BidirectionalIterator last_end(what[0].second); if(u32regex_search(last_end, end, what, re, ((what[0].first == what[0].second) ? flags | regex_constants::match_not_initial_null : flags), base)) { N =0; result =((subs[N] == -1) ? what.prefix() : what[subs[N]]); return true; } else if((last_end != end) && (subs[0] == -1)) { N =-1; result.first = last_end; result.second = end; result.matched = (last_end != end); return true; } return false; } private: u32regex_token_iterator_implementation& operator=(const u32regex_token_iterator_implementation&); }; template class u32regex_token_iterator { private: typedef u32regex_token_iterator_implementation impl; typedef shared_ptr pimpl; public: typedef u32regex regex_type; typedef sub_match value_type; typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; u32regex_token_iterator(){} u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, int submatch = 0, match_flag_type m = match_default) : pdata(new impl(&re, b, submatch, m)) { if(!pdata->init(a)) pdata.reset(); } u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const std::vector& submatches, match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } #if (BOOST_WORKAROUND(__BORLANDC__, >= 0x560) && BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570)))\ || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) \ || BOOST_WORKAROUND(__HP_aCC, < 60700) template u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const T& submatches, match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } #else template u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const int (&submatches)[N], match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } #endif u32regex_token_iterator(const u32regex_token_iterator& that) : pdata(that.pdata) {} u32regex_token_iterator& operator=(const u32regex_token_iterator& that) { pdata = that.pdata; return *this; } bool operator==(const u32regex_token_iterator& that)const { if((pdata.get() == 0) || (that.pdata.get() == 0)) return pdata.get() == that.pdata.get(); return pdata->compare(*(that.pdata.get())); } bool operator!=(const u32regex_token_iterator& that)const { return !(*this == that); } const value_type& operator*()const { return pdata->get(); } const value_type* operator->()const { return &(pdata->get()); } u32regex_token_iterator& operator++() { cow(); if(0 == pdata->next()) { pdata.reset(); } return *this; } u32regex_token_iterator operator++(int) { u32regex_token_iterator result(*this); ++(*this); return result; } private: pimpl pdata; void cow() { // copy-on-write if(pdata.get() && !pdata.unique()) { pdata.reset(new impl(*(pdata.get()))); } } }; typedef u32regex_token_iterator utf8regex_token_iterator; typedef u32regex_token_iterator utf16regex_token_iterator; typedef u32regex_token_iterator utf32regex_token_iterator; // construction from an integral sub_match state_id: inline u32regex_token_iterator make_u32regex_token_iterator(const char* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::strlen(p), e, submatch, m); } #ifndef BOOST_NO_WREGEX inline u32regex_token_iterator make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::wcslen(p), e, submatch, m); } #endif #if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T) inline u32regex_token_iterator make_u32regex_token_iterator(const UChar* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+u_strlen(p), e, submatch, m); } #endif template inline u32regex_token_iterator::const_iterator> make_u32regex_token_iterator(const std::basic_string& p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { typedef typename std::basic_string::const_iterator iter_type; return u32regex_token_iterator(p.begin(), p.end(), e, submatch, m); } inline u32regex_token_iterator make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m); } // construction from a reference to an array: template inline u32regex_token_iterator make_u32regex_token_iterator(const char* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::strlen(p), e, submatch, m); } #ifndef BOOST_NO_WREGEX template inline u32regex_token_iterator make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::wcslen(p), e, submatch, m); } #endif #if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T) template inline u32regex_token_iterator make_u32regex_token_iterator(const UChar* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+u_strlen(p), e, submatch, m); } #endif template inline u32regex_token_iterator::const_iterator> make_u32regex_token_iterator(const std::basic_string& p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { typedef typename std::basic_string::const_iterator iter_type; return u32regex_token_iterator(p.begin(), p.end(), e, submatch, m); } template inline u32regex_token_iterator make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m); } // construction from a vector of sub_match state_id's: inline u32regex_token_iterator make_u32regex_token_iterator(const char* p, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::strlen(p), e, submatch, m); } #ifndef BOOST_NO_WREGEX inline u32regex_token_iterator make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::wcslen(p), e, submatch, m); } #endif #if !defined(U_WCHAR_IS_UTF16) && (U_SIZEOF_WCHAR_T != 2) inline u32regex_token_iterator make_u32regex_token_iterator(const UChar* p, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+u_strlen(p), e, submatch, m); } #endif template inline u32regex_token_iterator::const_iterator> make_u32regex_token_iterator(const std::basic_string& p, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { typedef typename std::basic_string::const_iterator iter_type; return u32regex_token_iterator(p.begin(), p.end(), e, submatch, m); } inline u32regex_token_iterator make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m); } #ifdef BOOST_MSVC # pragma warning(pop) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif } // namespace boost #endif // BOOST_REGEX_V4_REGEX_TOKEN_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/unicode_iterator.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE unicode_iterator.hpp * VERSION see * DESCRIPTION: Iterator adapters for converting between different Unicode encodings. */ /**************************************************************************** Contents: ~~~~~~~~~ 1) Read Only, Input Adapters: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ template class u32_to_u8_iterator; Adapts sequence of UTF-32 code points to "look like" a sequence of UTF-8. template class u8_to_u32_iterator; Adapts sequence of UTF-8 code points to "look like" a sequence of UTF-32. template class u32_to_u16_iterator; Adapts sequence of UTF-32 code points to "look like" a sequence of UTF-16. template class u16_to_u32_iterator; Adapts sequence of UTF-16 code points to "look like" a sequence of UTF-32. 2) Single pass output iterator adapters: template class utf8_output_iterator; Accepts UTF-32 code points and forwards them on as UTF-8 code points. template class utf16_output_iterator; Accepts UTF-32 code points and forwards them on as UTF-16 code points. ****************************************************************************/ #ifndef BOOST_REGEX_V4_UNICODE_ITERATOR_HPP #define BOOST_REGEX_V4_UNICODE_ITERATOR_HPP #include #include #include #include #include #ifndef BOOST_NO_STD_LOCALE #include #include #endif #include // CHAR_BIT #ifdef BOOST_REGEX_CXX03 #else #endif namespace boost{ namespace detail{ static const ::boost::uint16_t high_surrogate_base = 0xD7C0u; static const ::boost::uint16_t low_surrogate_base = 0xDC00u; static const ::boost::uint32_t ten_bit_mask = 0x3FFu; inline bool is_high_surrogate(::boost::uint16_t v) { return (v & 0xFFFFFC00u) == 0xd800u; } inline bool is_low_surrogate(::boost::uint16_t v) { return (v & 0xFFFFFC00u) == 0xdc00u; } template inline bool is_surrogate(T v) { return (v & 0xFFFFF800u) == 0xd800; } inline unsigned utf8_byte_count(boost::uint8_t c) { // if the most significant bit with a zero in it is in position // 8-N then there are N bytes in this UTF-8 sequence: boost::uint8_t mask = 0x80u; unsigned result = 0; while(c & mask) { ++result; mask >>= 1; } return (result == 0) ? 1 : ((result > 4) ? 4 : result); } inline unsigned utf8_trailing_byte_count(boost::uint8_t c) { return utf8_byte_count(c) - 1; } #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4100) #endif #ifndef BOOST_NO_EXCEPTIONS BOOST_NORETURN #endif inline void invalid_utf32_code_point(::boost::uint32_t val) { #ifndef BOOST_NO_STD_LOCALE std::stringstream ss; ss << "Invalid UTF-32 code point U+" << std::showbase << std::hex << val << " encountered while trying to encode UTF-16 sequence"; std::out_of_range e(ss.str()); #else std::out_of_range e("Invalid UTF-32 code point encountered while trying to encode UTF-16 sequence"); #endif boost::throw_exception(e); } #ifdef BOOST_MSVC #pragma warning(pop) #endif } // namespace detail template class u32_to_u16_iterator { #if !defined(BOOST_NO_STD_ITERATOR_TRAITS) typedef typename std::iterator_traits::value_type base_value_type; BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 32); BOOST_STATIC_ASSERT(sizeof(U16Type)*CHAR_BIT == 16); #endif public: typedef std::ptrdiff_t difference_type; typedef U16Type value_type; typedef value_type const* pointer; typedef value_type const reference; typedef std::bidirectional_iterator_tag iterator_category; reference operator*()const { if(m_current == 2) extract_current(); return m_values[m_current]; } bool operator==(const u32_to_u16_iterator& that)const { if(m_position == that.m_position) { // Both m_currents must be equal, or both even // this is the same as saying their sum must be even: return (m_current + that.m_current) & 1u ? false : true; } return false; } bool operator!=(const u32_to_u16_iterator& that)const { return !(*this == that); } u32_to_u16_iterator& operator++() { // if we have a pending read then read now, so that we know whether // to skip a position, or move to a low-surrogate: if(m_current == 2) { // pending read: extract_current(); } // move to the next surrogate position: ++m_current; // if we've reached the end skip a position: if(m_values[m_current] == 0) { m_current = 2; ++m_position; } return *this; } u32_to_u16_iterator operator++(int) { u32_to_u16_iterator r(*this); ++(*this); return r; } u32_to_u16_iterator& operator--() { if(m_current != 1) { // decrementing an iterator always leads to a valid position: --m_position; extract_current(); m_current = m_values[1] ? 1 : 0; } else { m_current = 0; } return *this; } u32_to_u16_iterator operator--(int) { u32_to_u16_iterator r(*this); --(*this); return r; } BaseIterator base()const { return m_position; } // construct: u32_to_u16_iterator() : m_position(), m_current(0) { m_values[0] = 0; m_values[1] = 0; m_values[2] = 0; } u32_to_u16_iterator(BaseIterator b) : m_position(b), m_current(2) { m_values[0] = 0; m_values[1] = 0; m_values[2] = 0; } private: void extract_current()const { // begin by checking for a code point out of range: ::boost::uint32_t v = *m_position; if(v >= 0x10000u) { if(v > 0x10FFFFu) detail::invalid_utf32_code_point(*m_position); // split into two surrogates: m_values[0] = static_cast(v >> 10) + detail::high_surrogate_base; m_values[1] = static_cast(v & detail::ten_bit_mask) + detail::low_surrogate_base; m_current = 0; BOOST_REGEX_ASSERT(detail::is_high_surrogate(m_values[0])); BOOST_REGEX_ASSERT(detail::is_low_surrogate(m_values[1])); } else { // 16-bit code point: m_values[0] = static_cast(*m_position); m_values[1] = 0; m_current = 0; // value must not be a surrogate: if(detail::is_surrogate(m_values[0])) detail::invalid_utf32_code_point(*m_position); } } BaseIterator m_position; mutable U16Type m_values[3]; mutable unsigned m_current; }; template class u16_to_u32_iterator { // special values for pending iterator reads: BOOST_STATIC_CONSTANT(U32Type, pending_read = 0xffffffffu); #if !defined(BOOST_NO_STD_ITERATOR_TRAITS) typedef typename std::iterator_traits::value_type base_value_type; BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 16); BOOST_STATIC_ASSERT(sizeof(U32Type)*CHAR_BIT == 32); #endif public: typedef std::ptrdiff_t difference_type; typedef U32Type value_type; typedef value_type const* pointer; typedef value_type const reference; typedef std::bidirectional_iterator_tag iterator_category; reference operator*()const { if(m_value == pending_read) extract_current(); return m_value; } bool operator==(const u16_to_u32_iterator& that)const { return m_position == that.m_position; } bool operator!=(const u16_to_u32_iterator& that)const { return !(*this == that); } u16_to_u32_iterator& operator++() { // skip high surrogate first if there is one: if(detail::is_high_surrogate(*m_position)) ++m_position; ++m_position; m_value = pending_read; return *this; } u16_to_u32_iterator operator++(int) { u16_to_u32_iterator r(*this); ++(*this); return r; } u16_to_u32_iterator& operator--() { --m_position; // if we have a low surrogate then go back one more: if(detail::is_low_surrogate(*m_position)) --m_position; m_value = pending_read; return *this; } u16_to_u32_iterator operator--(int) { u16_to_u32_iterator r(*this); --(*this); return r; } BaseIterator base()const { return m_position; } // construct: u16_to_u32_iterator() : m_position() { m_value = pending_read; } u16_to_u32_iterator(BaseIterator b) : m_position(b) { m_value = pending_read; } // // Range checked version: // u16_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b) { m_value = pending_read; // // The range must not start with a low surrogate, or end in a high surrogate, // otherwise we run the risk of running outside the underlying input range. // Likewise b must not be located at a low surrogate. // boost::uint16_t val; if(start != end) { if((b != start) && (b != end)) { val = *b; if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u)) invalid_code_point(val); } val = *start; if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u)) invalid_code_point(val); val = *--end; if(detail::is_high_surrogate(val)) invalid_code_point(val); } } private: static void invalid_code_point(::boost::uint16_t val) { #ifndef BOOST_NO_STD_LOCALE std::stringstream ss; ss << "Misplaced UTF-16 surrogate U+" << std::showbase << std::hex << val << " encountered while trying to encode UTF-32 sequence"; std::out_of_range e(ss.str()); #else std::out_of_range e("Misplaced UTF-16 surrogate encountered while trying to encode UTF-32 sequence"); #endif boost::throw_exception(e); } void extract_current()const { m_value = static_cast(static_cast< ::boost::uint16_t>(*m_position)); // if the last value is a high surrogate then adjust m_position and m_value as needed: if(detail::is_high_surrogate(*m_position)) { // precondition; next value must have be a low-surrogate: BaseIterator next(m_position); ::boost::uint16_t t = *++next; if((t & 0xFC00u) != 0xDC00u) invalid_code_point(t); m_value = (m_value - detail::high_surrogate_base) << 10; m_value |= (static_cast(static_cast< ::boost::uint16_t>(t)) & detail::ten_bit_mask); } // postcondition; result must not be a surrogate: if(detail::is_surrogate(m_value)) invalid_code_point(static_cast< ::boost::uint16_t>(m_value)); } BaseIterator m_position; mutable U32Type m_value; }; template class u32_to_u8_iterator { #if !defined(BOOST_NO_STD_ITERATOR_TRAITS) typedef typename std::iterator_traits::value_type base_value_type; BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 32); BOOST_STATIC_ASSERT(sizeof(U8Type)*CHAR_BIT == 8); #endif public: typedef std::ptrdiff_t difference_type; typedef U8Type value_type; typedef value_type const* pointer; typedef value_type const reference; typedef std::bidirectional_iterator_tag iterator_category; reference operator*()const { if(m_current == 4) extract_current(); return m_values[m_current]; } bool operator==(const u32_to_u8_iterator& that)const { if(m_position == that.m_position) { // either the m_current's must be equal, or one must be 0 and // the other 4: which means neither must have bits 1 or 2 set: return (m_current == that.m_current) || (((m_current | that.m_current) & 3) == 0); } return false; } bool operator!=(const u32_to_u8_iterator& that)const { return !(*this == that); } u32_to_u8_iterator& operator++() { // if we have a pending read then read now, so that we know whether // to skip a position, or move to a low-surrogate: if(m_current == 4) { // pending read: extract_current(); } // move to the next surrogate position: ++m_current; // if we've reached the end skip a position: if(m_values[m_current] == 0) { m_current = 4; ++m_position; } return *this; } u32_to_u8_iterator operator++(int) { u32_to_u8_iterator r(*this); ++(*this); return r; } u32_to_u8_iterator& operator--() { if((m_current & 3) == 0) { --m_position; extract_current(); m_current = 3; while(m_current && (m_values[m_current] == 0)) --m_current; } else --m_current; return *this; } u32_to_u8_iterator operator--(int) { u32_to_u8_iterator r(*this); --(*this); return r; } BaseIterator base()const { return m_position; } // construct: u32_to_u8_iterator() : m_position(), m_current(0) { m_values[0] = 0; m_values[1] = 0; m_values[2] = 0; m_values[3] = 0; m_values[4] = 0; } u32_to_u8_iterator(BaseIterator b) : m_position(b), m_current(4) { m_values[0] = 0; m_values[1] = 0; m_values[2] = 0; m_values[3] = 0; m_values[4] = 0; } private: void extract_current()const { boost::uint32_t c = *m_position; if(c > 0x10FFFFu) detail::invalid_utf32_code_point(c); if(c < 0x80u) { m_values[0] = static_cast(c); m_values[1] = static_cast(0u); m_values[2] = static_cast(0u); m_values[3] = static_cast(0u); } else if(c < 0x800u) { m_values[0] = static_cast(0xC0u + (c >> 6)); m_values[1] = static_cast(0x80u + (c & 0x3Fu)); m_values[2] = static_cast(0u); m_values[3] = static_cast(0u); } else if(c < 0x10000u) { m_values[0] = static_cast(0xE0u + (c >> 12)); m_values[1] = static_cast(0x80u + ((c >> 6) & 0x3Fu)); m_values[2] = static_cast(0x80u + (c & 0x3Fu)); m_values[3] = static_cast(0u); } else { m_values[0] = static_cast(0xF0u + (c >> 18)); m_values[1] = static_cast(0x80u + ((c >> 12) & 0x3Fu)); m_values[2] = static_cast(0x80u + ((c >> 6) & 0x3Fu)); m_values[3] = static_cast(0x80u + (c & 0x3Fu)); } m_current= 0; } BaseIterator m_position; mutable U8Type m_values[5]; mutable unsigned m_current; }; template class u8_to_u32_iterator { // special values for pending iterator reads: BOOST_STATIC_CONSTANT(U32Type, pending_read = 0xffffffffu); #if !defined(BOOST_NO_STD_ITERATOR_TRAITS) typedef typename std::iterator_traits::value_type base_value_type; BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 8); BOOST_STATIC_ASSERT(sizeof(U32Type)*CHAR_BIT == 32); #endif public: typedef std::ptrdiff_t difference_type; typedef U32Type value_type; typedef value_type const* pointer; typedef value_type const reference; typedef std::bidirectional_iterator_tag iterator_category; reference operator*()const { if(m_value == pending_read) extract_current(); return m_value; } bool operator==(const u8_to_u32_iterator& that)const { return m_position == that.m_position; } bool operator!=(const u8_to_u32_iterator& that)const { return !(*this == that); } u8_to_u32_iterator& operator++() { // We must not start with a continuation character: if((static_cast(*m_position) & 0xC0) == 0x80) invalid_sequence(); // skip high surrogate first if there is one: unsigned c = detail::utf8_byte_count(*m_position); if(m_value == pending_read) { // Since we haven't read in a value, we need to validate the code points: for(unsigned i = 0; i < c; ++i) { ++m_position; // We must have a continuation byte: if((i != c - 1) && ((static_cast(*m_position) & 0xC0) != 0x80)) invalid_sequence(); } } else { std::advance(m_position, c); } m_value = pending_read; return *this; } u8_to_u32_iterator operator++(int) { u8_to_u32_iterator r(*this); ++(*this); return r; } u8_to_u32_iterator& operator--() { // Keep backtracking until we don't have a trailing character: unsigned count = 0; while((*--m_position & 0xC0u) == 0x80u) ++count; // now check that the sequence was valid: if(count != detail::utf8_trailing_byte_count(*m_position)) invalid_sequence(); m_value = pending_read; return *this; } u8_to_u32_iterator operator--(int) { u8_to_u32_iterator r(*this); --(*this); return r; } BaseIterator base()const { return m_position; } // construct: u8_to_u32_iterator() : m_position() { m_value = pending_read; } u8_to_u32_iterator(BaseIterator b) : m_position(b) { m_value = pending_read; } // // Checked constructor: // u8_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b) { m_value = pending_read; // // We must not start with a continuation character, or end with a // truncated UTF-8 sequence otherwise we run the risk of going past // the start/end of the underlying sequence: // if(start != end) { unsigned char v = *start; if((v & 0xC0u) == 0x80u) invalid_sequence(); if((b != start) && (b != end) && ((*b & 0xC0u) == 0x80u)) invalid_sequence(); BaseIterator pos = end; do { v = *--pos; } while((start != pos) && ((v & 0xC0u) == 0x80u)); std::ptrdiff_t extra = detail::utf8_byte_count(v); if(std::distance(pos, end) < extra) invalid_sequence(); } } private: static void invalid_sequence() { std::out_of_range e("Invalid UTF-8 sequence encountered while trying to encode UTF-32 character"); boost::throw_exception(e); } void extract_current()const { m_value = static_cast(static_cast< ::boost::uint8_t>(*m_position)); // we must not have a continuation character: if((m_value & 0xC0u) == 0x80u) invalid_sequence(); // see how many extra bytes we have: unsigned extra = detail::utf8_trailing_byte_count(*m_position); // extract the extra bits, 6 from each extra byte: BaseIterator next(m_position); for(unsigned c = 0; c < extra; ++c) { ++next; m_value <<= 6; // We must have a continuation byte: if((static_cast(*next) & 0xC0) != 0x80) invalid_sequence(); m_value += static_cast(*next) & 0x3Fu; } // we now need to remove a few of the leftmost bits, but how many depends // upon how many extra bytes we've extracted: static const boost::uint32_t masks[4] = { 0x7Fu, 0x7FFu, 0xFFFFu, 0x1FFFFFu, }; m_value &= masks[extra]; // check the result is in range: if(m_value > static_cast(0x10FFFFu)) invalid_sequence(); // The result must not be a surrogate: if((m_value >= static_cast(0xD800)) && (m_value <= static_cast(0xDFFF))) invalid_sequence(); // We should not have had an invalidly encoded UTF8 sequence: if((extra > 0) && (m_value <= static_cast(masks[extra - 1]))) invalid_sequence(); } BaseIterator m_position; mutable U32Type m_value; }; template class utf16_output_iterator { public: typedef void difference_type; typedef void value_type; typedef boost::uint32_t* pointer; typedef boost::uint32_t& reference; typedef std::output_iterator_tag iterator_category; utf16_output_iterator(const BaseIterator& b) : m_position(b){} utf16_output_iterator(const utf16_output_iterator& that) : m_position(that.m_position){} utf16_output_iterator& operator=(const utf16_output_iterator& that) { m_position = that.m_position; return *this; } const utf16_output_iterator& operator*()const { return *this; } void operator=(boost::uint32_t val)const { push(val); } utf16_output_iterator& operator++() { return *this; } utf16_output_iterator& operator++(int) { return *this; } BaseIterator base()const { return m_position; } private: void push(boost::uint32_t v)const { if(v >= 0x10000u) { // begin by checking for a code point out of range: if(v > 0x10FFFFu) detail::invalid_utf32_code_point(v); // split into two surrogates: *m_position++ = static_cast(v >> 10) + detail::high_surrogate_base; *m_position++ = static_cast(v & detail::ten_bit_mask) + detail::low_surrogate_base; } else { // 16-bit code point: // value must not be a surrogate: if(detail::is_surrogate(v)) detail::invalid_utf32_code_point(v); *m_position++ = static_cast(v); } } mutable BaseIterator m_position; }; template class utf8_output_iterator { public: typedef void difference_type; typedef void value_type; typedef boost::uint32_t* pointer; typedef boost::uint32_t& reference; typedef std::output_iterator_tag iterator_category; utf8_output_iterator(const BaseIterator& b) : m_position(b){} utf8_output_iterator(const utf8_output_iterator& that) : m_position(that.m_position){} utf8_output_iterator& operator=(const utf8_output_iterator& that) { m_position = that.m_position; return *this; } const utf8_output_iterator& operator*()const { return *this; } void operator=(boost::uint32_t val)const { push(val); } utf8_output_iterator& operator++() { return *this; } utf8_output_iterator& operator++(int) { return *this; } BaseIterator base()const { return m_position; } private: void push(boost::uint32_t c)const { if(c > 0x10FFFFu) detail::invalid_utf32_code_point(c); if(c < 0x80u) { *m_position++ = static_cast(c); } else if(c < 0x800u) { *m_position++ = static_cast(0xC0u + (c >> 6)); *m_position++ = static_cast(0x80u + (c & 0x3Fu)); } else if(c < 0x10000u) { *m_position++ = static_cast(0xE0u + (c >> 12)); *m_position++ = static_cast(0x80u + ((c >> 6) & 0x3Fu)); *m_position++ = static_cast(0x80u + (c & 0x3Fu)); } else { *m_position++ = static_cast(0xF0u + (c >> 18)); *m_position++ = static_cast(0x80u + ((c >> 12) & 0x3Fu)); *m_position++ = static_cast(0x80u + ((c >> 6) & 0x3Fu)); *m_position++ = static_cast(0x80u + (c & 0x3Fu)); } } mutable BaseIterator m_position; }; } // namespace boost #endif // BOOST_REGEX_UNICODE_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v4/w32_regex_traits.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE w32_regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits class w32_regex_traits. */ #ifndef BOOST_W32_REGEX_TRAITS_HPP_INCLUDED #define BOOST_W32_REGEX_TRAITS_HPP_INCLUDED #ifndef BOOST_REGEX_NO_WIN32_LOCALE #ifndef BOOST_RE_PAT_EXCEPT_HPP #include #endif #ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED #include #endif #ifdef BOOST_HAS_THREADS #include #endif #ifndef BOOST_REGEX_PRIMARY_TRANSFORM #include #endif #ifndef BOOST_REGEX_OBJECT_CACHE_HPP #include #endif #define VC_EXTRALEAN #define WIN32_LEAN_AND_MEAN #include #if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(UNDER_CE) #pragma comment(lib, "user32.lib") #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable:4786) #if BOOST_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace boost{ // // forward declaration is needed by some compilers: // template class w32_regex_traits; namespace BOOST_REGEX_DETAIL_NS{ // // start by typedeffing the types we'll need: // typedef ::boost::uint32_t lcid_type; // placeholder for LCID. typedef ::boost::shared_ptr cat_type; // placeholder for dll HANDLE. // // then add wrappers around the actual Win32 API's (ie implementation hiding): // lcid_type BOOST_REGEX_CALL w32_get_default_locale(); bool BOOST_REGEX_CALL w32_is_lower(char, lcid_type); #ifndef BOOST_NO_WREGEX bool BOOST_REGEX_CALL w32_is_lower(wchar_t, lcid_type); #endif bool BOOST_REGEX_CALL w32_is_upper(char, lcid_type); #ifndef BOOST_NO_WREGEX bool BOOST_REGEX_CALL w32_is_upper(wchar_t, lcid_type); #endif cat_type BOOST_REGEX_CALL w32_cat_open(const std::string& name); std::string BOOST_REGEX_CALL w32_cat_get(const cat_type& cat, lcid_type state_id, int i, const std::string& def); #ifndef BOOST_NO_WREGEX std::wstring BOOST_REGEX_CALL w32_cat_get(const cat_type& cat, lcid_type state_id, int i, const std::wstring& def); #endif std::string BOOST_REGEX_CALL w32_transform(lcid_type state_id, const char* p1, const char* p2); #ifndef BOOST_NO_WREGEX std::wstring BOOST_REGEX_CALL w32_transform(lcid_type state_id, const wchar_t* p1, const wchar_t* p2); #endif char BOOST_REGEX_CALL w32_tolower(char c, lcid_type); #ifndef BOOST_NO_WREGEX wchar_t BOOST_REGEX_CALL w32_tolower(wchar_t c, lcid_type); #endif char BOOST_REGEX_CALL w32_toupper(char c, lcid_type); #ifndef BOOST_NO_WREGEX wchar_t BOOST_REGEX_CALL w32_toupper(wchar_t c, lcid_type); #endif bool BOOST_REGEX_CALL w32_is(lcid_type, boost::uint32_t mask, char c); #ifndef BOOST_NO_WREGEX bool BOOST_REGEX_CALL w32_is(lcid_type, boost::uint32_t mask, wchar_t c); #endif // // class w32_regex_traits_base: // acts as a container for locale and the facets we are using. // template struct w32_regex_traits_base { w32_regex_traits_base(lcid_type l) { imbue(l); } lcid_type imbue(lcid_type l); lcid_type m_locale; }; template inline lcid_type w32_regex_traits_base::imbue(lcid_type l) { lcid_type result(m_locale); m_locale = l; return result; } // // class w32_regex_traits_char_layer: // implements methods that require specialisation for narrow characters: // template class w32_regex_traits_char_layer : public w32_regex_traits_base { typedef std::basic_string string_type; typedef std::map map_type; typedef typename map_type::const_iterator map_iterator_type; public: w32_regex_traits_char_layer(const lcid_type l); regex_constants::syntax_type syntax_type(charT c)const { map_iterator_type i = m_char_map.find(c); return ((i == m_char_map.end()) ? 0 : i->second); } regex_constants::escape_syntax_type escape_syntax_type(charT c) const { map_iterator_type i = m_char_map.find(c); if(i == m_char_map.end()) { if(::boost::BOOST_REGEX_DETAIL_NS::w32_is_lower(c, this->m_locale)) return regex_constants::escape_type_class; if(::boost::BOOST_REGEX_DETAIL_NS::w32_is_upper(c, this->m_locale)) return regex_constants::escape_type_not_class; return 0; } return i->second; } charT tolower(charT c)const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_tolower(c, this->m_locale); } bool isctype(boost::uint32_t mask, charT c)const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, mask, c); } private: string_type get_default_message(regex_constants::syntax_type); // TODO: use a hash table when available! map_type m_char_map; }; template w32_regex_traits_char_layer::w32_regex_traits_char_layer(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) : w32_regex_traits_base(l) { // we need to start by initialising our syntax map so we know which // character is used for which purpose: cat_type cat; std::string cat_name(w32_regex_traits::get_catalog_name()); if(cat_name.size()) { cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name); if(!cat) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if(cat) { for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { string_type mss = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i, get_default_message(i)); for(typename string_type::size_type j = 0; j < mss.size(); ++j) { this->m_char_map[mss[j]] = i; } } } else { for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { const char* ptr = get_default_syntax(i); while(ptr && *ptr) { this->m_char_map[static_cast(*ptr)] = i; ++ptr; } } } } template typename w32_regex_traits_char_layer::string_type w32_regex_traits_char_layer::get_default_message(regex_constants::syntax_type i) { const char* ptr = get_default_syntax(i); string_type result; while(ptr && *ptr) { result.append(1, static_cast(*ptr)); ++ptr; } return result; } // // specialised version for narrow characters: // template <> class w32_regex_traits_char_layer : public w32_regex_traits_base { typedef std::string string_type; public: w32_regex_traits_char_layer(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) : w32_regex_traits_base(l) { init(); } regex_constants::syntax_type syntax_type(char c)const { return m_char_map[static_cast(c)]; } regex_constants::escape_syntax_type escape_syntax_type(char c) const { return m_char_map[static_cast(c)]; } char tolower(char c)const { return m_lower_map[static_cast(c)]; } bool isctype(boost::uint32_t mask, char c)const { return m_type_map[static_cast(c)] & mask; } private: regex_constants::syntax_type m_char_map[1u << CHAR_BIT]; char m_lower_map[1u << CHAR_BIT]; boost::uint16_t m_type_map[1u << CHAR_BIT]; template void init(); }; // // class w32_regex_traits_implementation: // provides pimpl implementation for w32_regex_traits. // template class w32_regex_traits_implementation : public w32_regex_traits_char_layer { public: typedef typename w32_regex_traits::char_class_type char_class_type; BOOST_STATIC_CONSTANT(char_class_type, mask_word = 0x0400); // must be C1_DEFINED << 1 BOOST_STATIC_CONSTANT(char_class_type, mask_unicode = 0x0800); // must be C1_DEFINED << 2 BOOST_STATIC_CONSTANT(char_class_type, mask_horizontal = 0x1000); // must be C1_DEFINED << 3 BOOST_STATIC_CONSTANT(char_class_type, mask_vertical = 0x2000); // must be C1_DEFINED << 4 BOOST_STATIC_CONSTANT(char_class_type, mask_base = 0x3ff); // all the masks used by the CT_CTYPE1 group typedef std::basic_string string_type; typedef charT char_type; w32_regex_traits_implementation(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l); std::string error_string(regex_constants::error_type n) const { if(!m_error_strings.empty()) { std::map::const_iterator p = m_error_strings.find(n); return (p == m_error_strings.end()) ? std::string(get_default_error_string(n)) : p->second; } return get_default_error_string(n); } char_class_type lookup_classname(const charT* p1, const charT* p2) const { char_class_type result = lookup_classname_imp(p1, p2); if(result == 0) { typedef typename string_type::size_type size_type; string_type temp(p1, p2); for(size_type i = 0; i < temp.size(); ++i) temp[i] = this->tolower(temp[i]); result = lookup_classname_imp(&*temp.begin(), &*temp.begin() + temp.size()); } return result; } string_type lookup_collatename(const charT* p1, const charT* p2) const; string_type transform_primary(const charT* p1, const charT* p2) const; string_type transform(const charT* p1, const charT* p2) const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_transform(this->m_locale, p1, p2); } private: std::map m_error_strings; // error messages indexed by numberic ID std::map m_custom_class_names; // character class names std::map m_custom_collate_names; // collating element names unsigned m_collate_type; // the form of the collation string charT m_collate_delim; // the collation group delimiter // // helpers: // char_class_type lookup_classname_imp(const charT* p1, const charT* p2) const; }; template typename w32_regex_traits_implementation::string_type w32_regex_traits_implementation::transform_primary(const charT* p1, const charT* p2) const { string_type result; // // What we do here depends upon the format of the sort key returned by // sort key returned by this->transform: // switch(m_collate_type) { case sort_C: case sort_unknown: // the best we can do is translate to lower case, then get a regular sort key: { result.assign(p1, p2); typedef typename string_type::size_type size_type; for(size_type i = 0; i < result.size(); ++i) result[i] = this->tolower(result[i]); result = this->transform(&*result.begin(), &*result.begin() + result.size()); break; } case sort_fixed: { // get a regular sort key, and then truncate it: result.assign(this->transform(p1, p2)); result.erase(this->m_collate_delim); break; } case sort_delim: // get a regular sort key, and then truncate everything after the delim: result.assign(this->transform(p1, p2)); std::size_t i; for(i = 0; i < result.size(); ++i) { if(result[i] == m_collate_delim) break; } result.erase(i); break; } if(result.empty()) result = string_type(1, charT(0)); return result; } template typename w32_regex_traits_implementation::string_type w32_regex_traits_implementation::lookup_collatename(const charT* p1, const charT* p2) const { typedef typename std::map::const_iterator iter_type; if(m_custom_collate_names.size()) { iter_type pos = m_custom_collate_names.find(string_type(p1, p2)); if(pos != m_custom_collate_names.end()) return pos->second; } #if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\ && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551) std::string name(p1, p2); #else std::string name; const charT* p0 = p1; while(p0 != p2) name.append(1, char(*p0++)); #endif name = lookup_default_collate_name(name); #if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\ && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551) if(name.size()) return string_type(name.begin(), name.end()); #else if(name.size()) { string_type result; typedef std::string::const_iterator iter; iter b = name.begin(); iter e = name.end(); while(b != e) result.append(1, charT(*b++)); return result; } #endif if(p2 - p1 == 1) return string_type(1, *p1); return string_type(); } template w32_regex_traits_implementation::w32_regex_traits_implementation(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) : w32_regex_traits_char_layer(l) { cat_type cat; std::string cat_name(w32_regex_traits::get_catalog_name()); if(cat_name.size()) { cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name); if(!cat) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if(cat) { // // Error messages: // for(boost::regex_constants::error_type i = static_cast(0); i <= boost::regex_constants::error_unknown; i = static_cast(i + 1)) { const char* p = get_default_error_string(i); string_type default_message; while(*p) { default_message.append(1, static_cast(*p)); ++p; } string_type s = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i+200, default_message); std::string result; for(std::string::size_type j = 0; j < s.size(); ++j) { result.append(1, static_cast(s[j])); } m_error_strings[i] = result; } // // Custom class names: // static const char_class_type masks[14] = { 0x0104u, // C1_ALPHA | C1_DIGIT 0x0100u, // C1_ALPHA 0x0020u, // C1_CNTRL 0x0004u, // C1_DIGIT (~(0x0020u|0x0008u) & 0x01ffu) | 0x0400u, // not C1_CNTRL or C1_SPACE 0x0002u, // C1_LOWER (~0x0020u & 0x01ffu) | 0x0400, // not C1_CNTRL 0x0010u, // C1_PUNCT 0x0008u, // C1_SPACE 0x0001u, // C1_UPPER 0x0080u, // C1_XDIGIT 0x0040u, // C1_BLANK w32_regex_traits_implementation::mask_word, w32_regex_traits_implementation::mask_unicode, }; static const string_type null_string; for(unsigned int j = 0; j <= 13; ++j) { string_type s(::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, j+300, null_string)); if(s.size()) this->m_custom_class_names[s] = masks[j]; } } // // get the collation format used by m_pcollate: // m_collate_type = BOOST_REGEX_DETAIL_NS::find_sort_syntax(this, &m_collate_delim); } template typename w32_regex_traits_implementation::char_class_type w32_regex_traits_implementation::lookup_classname_imp(const charT* p1, const charT* p2) const { static const char_class_type masks[22] = { 0, 0x0104u, // C1_ALPHA | C1_DIGIT 0x0100u, // C1_ALPHA 0x0040u, // C1_BLANK 0x0020u, // C1_CNTRL 0x0004u, // C1_DIGIT 0x0004u, // C1_DIGIT (~(0x0020u|0x0008u|0x0040) & 0x01ffu) | 0x0400u, // not C1_CNTRL or C1_SPACE or C1_BLANK w32_regex_traits_implementation::mask_horizontal, 0x0002u, // C1_LOWER 0x0002u, // C1_LOWER (~0x0020u & 0x01ffu) | 0x0400, // not C1_CNTRL 0x0010u, // C1_PUNCT 0x0008u, // C1_SPACE 0x0008u, // C1_SPACE 0x0001u, // C1_UPPER w32_regex_traits_implementation::mask_unicode, 0x0001u, // C1_UPPER w32_regex_traits_implementation::mask_vertical, 0x0104u | w32_regex_traits_implementation::mask_word, 0x0104u | w32_regex_traits_implementation::mask_word, 0x0080u, // C1_XDIGIT }; if(m_custom_class_names.size()) { typedef typename std::map, char_class_type>::const_iterator map_iter; map_iter pos = m_custom_class_names.find(string_type(p1, p2)); if(pos != m_custom_class_names.end()) return pos->second; } std::size_t state_id = 1u + (std::size_t)BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); if(state_id < sizeof(masks) / sizeof(masks[0])) return masks[state_id]; return masks[0]; } template boost::shared_ptr > create_w32_regex_traits(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) { // TODO: create a cache for previously constructed objects. return boost::object_cache< ::boost::BOOST_REGEX_DETAIL_NS::lcid_type, w32_regex_traits_implementation >::get(l, 5); } } // BOOST_REGEX_DETAIL_NS template class w32_regex_traits { public: typedef charT char_type; typedef std::size_t size_type; typedef std::basic_string string_type; typedef ::boost::BOOST_REGEX_DETAIL_NS::lcid_type locale_type; typedef boost::uint_least32_t char_class_type; struct boost_extensions_tag{}; w32_regex_traits() : m_pimpl(BOOST_REGEX_DETAIL_NS::create_w32_regex_traits(::boost::BOOST_REGEX_DETAIL_NS::w32_get_default_locale())) { } static size_type length(const char_type* p) { return std::char_traits::length(p); } regex_constants::syntax_type syntax_type(charT c)const { return m_pimpl->syntax_type(c); } regex_constants::escape_syntax_type escape_syntax_type(charT c) const { return m_pimpl->escape_syntax_type(c); } charT translate(charT c) const { return c; } charT translate_nocase(charT c) const { return this->m_pimpl->tolower(c); } charT translate(charT c, bool icase) const { return icase ? this->m_pimpl->tolower(c) : c; } charT tolower(charT c) const { return this->m_pimpl->tolower(c); } charT toupper(charT c) const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_toupper(c, this->m_pimpl->m_locale); } string_type transform(const charT* p1, const charT* p2) const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_transform(this->m_pimpl->m_locale, p1, p2); } string_type transform_primary(const charT* p1, const charT* p2) const { return m_pimpl->transform_primary(p1, p2); } char_class_type lookup_classname(const charT* p1, const charT* p2) const { return m_pimpl->lookup_classname(p1, p2); } string_type lookup_collatename(const charT* p1, const charT* p2) const { return m_pimpl->lookup_collatename(p1, p2); } bool isctype(charT c, char_class_type f) const { if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_base) && (this->m_pimpl->isctype(f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_base, c))) return true; else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_unicode) && BOOST_REGEX_DETAIL_NS::is_extended(c)) return true; else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_word) && (c == '_')) return true; else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\v'))) return true; else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_horizontal) && this->isctype(c, 0x0008u) && !this->isctype(c, BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_vertical)) return true; return false; } boost::intmax_t toi(const charT*& p1, const charT* p2, int radix)const { return ::boost::BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this); } int value(charT c, int radix)const { int result = (int)::boost::BOOST_REGEX_DETAIL_NS::global_value(c); return result < radix ? result : -1; } locale_type imbue(locale_type l) { ::boost::BOOST_REGEX_DETAIL_NS::lcid_type result(getloc()); m_pimpl = BOOST_REGEX_DETAIL_NS::create_w32_regex_traits(l); return result; } locale_type getloc()const { return m_pimpl->m_locale; } std::string error_string(regex_constants::error_type n) const { return m_pimpl->error_string(n); } // // extension: // set the name of the message catalog in use (defaults to "boost_regex"). // static std::string catalog_name(const std::string& name); static std::string get_catalog_name(); private: boost::shared_ptr > m_pimpl; // // catalog name handler: // static std::string& get_catalog_name_inst(); #ifdef BOOST_HAS_THREADS static static_mutex& get_mutex_inst(); #endif }; template std::string w32_regex_traits::catalog_name(const std::string& name) { #ifdef BOOST_HAS_THREADS static_mutex::scoped_lock lk(get_mutex_inst()); #endif std::string result(get_catalog_name_inst()); get_catalog_name_inst() = name; return result; } template std::string& w32_regex_traits::get_catalog_name_inst() { static std::string s_name; return s_name; } template std::string w32_regex_traits::get_catalog_name() { #ifdef BOOST_HAS_THREADS static_mutex::scoped_lock lk(get_mutex_inst()); #endif std::string result(get_catalog_name_inst()); return result; } #ifdef BOOST_HAS_THREADS template static_mutex& w32_regex_traits::get_mutex_inst() { static static_mutex s_mutex = BOOST_STATIC_MUTEX_INIT; return s_mutex; } #endif namespace BOOST_REGEX_DETAIL_NS { #ifdef BOOST_NO_ANSI_APIS inline UINT get_code_page_for_locale_id(lcid_type idx) { WCHAR code_page_string[7]; if (::GetLocaleInfoW(idx, LOCALE_IDEFAULTANSICODEPAGE, code_page_string, 7) == 0) return 0; return static_cast(_wtol(code_page_string)); } #endif template inline void w32_regex_traits_char_layer::init() { // we need to start by initialising our syntax map so we know which // character is used for which purpose: std::memset(m_char_map, 0, sizeof(m_char_map)); cat_type cat; std::string cat_name(w32_regex_traits::get_catalog_name()); if (cat_name.size()) { cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name); if (!cat) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); ::boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if (cat) { for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { string_type mss = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i, get_default_syntax(i)); for (string_type::size_type j = 0; j < mss.size(); ++j) { m_char_map[static_cast(mss[j])] = i; } } } else { for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { const char* ptr = get_default_syntax(i); while (ptr && *ptr) { m_char_map[static_cast(*ptr)] = i; ++ptr; } } } // // finish off by calculating our escape types: // unsigned char i = 'A'; do { if (m_char_map[i] == 0) { if (::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, 0x0002u, (char)i)) m_char_map[i] = regex_constants::escape_type_class; else if (::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, 0x0001u, (char)i)) m_char_map[i] = regex_constants::escape_type_not_class; } } while (0xFF != i++); // // fill in lower case map: // char char_map[1 << CHAR_BIT]; for (int ii = 0; ii < (1 << CHAR_BIT); ++ii) char_map[ii] = static_cast(ii); #ifndef BOOST_NO_ANSI_APIS int r = ::LCMapStringA(this->m_locale, LCMAP_LOWERCASE, char_map, 1 << CHAR_BIT, this->m_lower_map, 1 << CHAR_BIT); BOOST_REGEX_ASSERT(r != 0); #else UINT code_page = get_code_page_for_locale_id(this->m_locale); BOOST_REGEX_ASSERT(code_page != 0); WCHAR wide_char_map[1 << CHAR_BIT]; int conv_r = ::MultiByteToWideChar(code_page, 0, char_map, 1 << CHAR_BIT, wide_char_map, 1 << CHAR_BIT); BOOST_REGEX_ASSERT(conv_r != 0); WCHAR wide_lower_map[1 << CHAR_BIT]; int r = ::LCMapStringW(this->m_locale, LCMAP_LOWERCASE, wide_char_map, 1 << CHAR_BIT, wide_lower_map, 1 << CHAR_BIT); BOOST_REGEX_ASSERT(r != 0); conv_r = ::WideCharToMultiByte(code_page, 0, wide_lower_map, r, this->m_lower_map, 1 << CHAR_BIT, NULL, NULL); BOOST_REGEX_ASSERT(conv_r != 0); #endif if (r < (1 << CHAR_BIT)) { // if we have multibyte characters then not all may have been given // a lower case mapping: for (int jj = r; jj < (1 << CHAR_BIT); ++jj) this->m_lower_map[jj] = static_cast(jj); } #ifndef BOOST_NO_ANSI_APIS r = ::GetStringTypeExA(this->m_locale, CT_CTYPE1, char_map, 1 << CHAR_BIT, this->m_type_map); #else r = ::GetStringTypeExW(this->m_locale, CT_CTYPE1, wide_char_map, 1 << CHAR_BIT, this->m_type_map); #endif BOOST_REGEX_ASSERT(0 != r); } inline lcid_type BOOST_REGEX_CALL w32_get_default_locale() { return ::GetUserDefaultLCID(); } inline bool BOOST_REGEX_CALL w32_is_lower(char c, lcid_type idx) { #ifndef BOOST_NO_ANSI_APIS WORD mask; if (::GetStringTypeExA(idx, CT_CTYPE1, &c, 1, &mask) && (mask & C1_LOWER)) return true; return false; #else UINT code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return false; WCHAR wide_c; if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return false; WORD mask; if (::GetStringTypeExW(idx, CT_CTYPE1, &wide_c, 1, &mask) && (mask & C1_LOWER)) return true; return false; #endif } inline bool BOOST_REGEX_CALL w32_is_lower(wchar_t c, lcid_type idx) { WORD mask; if (::GetStringTypeExW(idx, CT_CTYPE1, &c, 1, &mask) && (mask & C1_LOWER)) return true; return false; } inline bool BOOST_REGEX_CALL w32_is_upper(char c, lcid_type idx) { #ifndef BOOST_NO_ANSI_APIS WORD mask; if (::GetStringTypeExA(idx, CT_CTYPE1, &c, 1, &mask) && (mask & C1_UPPER)) return true; return false; #else UINT code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return false; WCHAR wide_c; if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return false; WORD mask; if (::GetStringTypeExW(idx, CT_CTYPE1, &wide_c, 1, &mask) && (mask & C1_UPPER)) return true; return false; #endif } inline bool BOOST_REGEX_CALL w32_is_upper(wchar_t c, lcid_type idx) { WORD mask; if (::GetStringTypeExW(idx, CT_CTYPE1, &c, 1, &mask) && (mask & C1_UPPER)) return true; return false; } inline void free_module(void* mod) { ::FreeLibrary(static_cast(mod)); } inline cat_type BOOST_REGEX_CALL w32_cat_open(const std::string& name) { #ifndef BOOST_NO_ANSI_APIS cat_type result(::LoadLibraryA(name.c_str()), &free_module); return result; #else LPWSTR wide_name = (LPWSTR)_alloca((name.size() + 1) * sizeof(WCHAR)); if (::MultiByteToWideChar(CP_ACP, 0, name.c_str(), name.size(), wide_name, name.size() + 1) == 0) return cat_type(); cat_type result(::LoadLibraryW(wide_name), &free_module); return result; #endif } inline std::string BOOST_REGEX_CALL w32_cat_get(const cat_type& cat, lcid_type, int i, const std::string& def) { #ifndef BOOST_NO_ANSI_APIS char buf[256]; if (0 == ::LoadStringA( static_cast(cat.get()), i, buf, 256 )) { return def; } #else WCHAR wbuf[256]; int r = ::LoadStringW( static_cast(cat.get()), i, wbuf, 256 ); if (r == 0) return def; int buf_size = 1 + ::WideCharToMultiByte(CP_ACP, 0, wbuf, r, NULL, 0, NULL, NULL); LPSTR buf = (LPSTR)_alloca(buf_size); if (::WideCharToMultiByte(CP_ACP, 0, wbuf, r, buf, buf_size, NULL, NULL) == 0) return def; // failed conversion. #endif return std::string(buf); } #ifndef BOOST_NO_WREGEX inline std::wstring BOOST_REGEX_CALL w32_cat_get(const cat_type& cat, lcid_type, int i, const std::wstring& def) { wchar_t buf[256]; if (0 == ::LoadStringW( static_cast(cat.get()), i, buf, 256 )) { return def; } return std::wstring(buf); } #endif inline std::string BOOST_REGEX_CALL w32_transform(lcid_type idx, const char* p1, const char* p2) { #ifndef BOOST_NO_ANSI_APIS int bytes = ::LCMapStringA( idx, // locale identifier LCMAP_SORTKEY, // mapping transformation type p1, // source string static_cast(p2 - p1), // number of characters in source string 0, // destination buffer 0 // size of destination buffer ); if (!bytes) return std::string(p1, p2); std::string result(++bytes, '\0'); bytes = ::LCMapStringA( idx, // locale identifier LCMAP_SORTKEY, // mapping transformation type p1, // source string static_cast(p2 - p1), // number of characters in source string &*result.begin(), // destination buffer bytes // size of destination buffer ); #else UINT code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return std::string(p1, p2); int src_len = static_cast(p2 - p1); LPWSTR wide_p1 = (LPWSTR)_alloca((src_len + 1) * 2); if (::MultiByteToWideChar(code_page, 0, p1, src_len, wide_p1, src_len + 1) == 0) return std::string(p1, p2); int bytes = ::LCMapStringW( idx, // locale identifier LCMAP_SORTKEY, // mapping transformation type wide_p1, // source string src_len, // number of characters in source string 0, // destination buffer 0 // size of destination buffer ); if (!bytes) return std::string(p1, p2); std::string result(++bytes, '\0'); bytes = ::LCMapStringW( idx, // locale identifier LCMAP_SORTKEY, // mapping transformation type wide_p1, // source string src_len, // number of characters in source string (LPWSTR) & *result.begin(), // destination buffer bytes // size of destination buffer ); #endif if (bytes > static_cast(result.size())) return std::string(p1, p2); while (result.size() && result[result.size() - 1] == '\0') { result.erase(result.size() - 1); } return result; } #ifndef BOOST_NO_WREGEX inline std::wstring BOOST_REGEX_CALL w32_transform(lcid_type idx, const wchar_t* p1, const wchar_t* p2) { int bytes = ::LCMapStringW( idx, // locale identifier LCMAP_SORTKEY, // mapping transformation type p1, // source string static_cast(p2 - p1), // number of characters in source string 0, // destination buffer 0 // size of destination buffer ); if (!bytes) return std::wstring(p1, p2); std::string result(++bytes, '\0'); bytes = ::LCMapStringW( idx, // locale identifier LCMAP_SORTKEY, // mapping transformation type p1, // source string static_cast(p2 - p1), // number of characters in source string reinterpret_cast(&*result.begin()), // destination buffer *of bytes* bytes // size of destination buffer ); if (bytes > static_cast(result.size())) return std::wstring(p1, p2); while (result.size() && result[result.size() - 1] == L'\0') { result.erase(result.size() - 1); } std::wstring r2; for (std::string::size_type i = 0; i < result.size(); ++i) r2.append(1, static_cast(static_cast(result[i]))); return r2; } #endif inline char BOOST_REGEX_CALL w32_tolower(char c, lcid_type idx) { char result[2]; #ifndef BOOST_NO_ANSI_APIS int b = ::LCMapStringA( idx, // locale identifier LCMAP_LOWERCASE, // mapping transformation type &c, // source string 1, // number of characters in source string result, // destination buffer 1); // size of destination buffer if (b == 0) return c; #else UINT code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return c; WCHAR wide_c; if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return c; WCHAR wide_result; int b = ::LCMapStringW( idx, // locale identifier LCMAP_LOWERCASE, // mapping transformation type &wide_c, // source string 1, // number of characters in source string &wide_result, // destination buffer 1); // size of destination buffer if (b == 0) return c; if (::WideCharToMultiByte(code_page, 0, &wide_result, 1, result, 2, NULL, NULL) == 0) return c; // No single byte lower case equivalent available #endif return result[0]; } #ifndef BOOST_NO_WREGEX inline wchar_t BOOST_REGEX_CALL w32_tolower(wchar_t c, lcid_type idx) { wchar_t result[2]; int b = ::LCMapStringW( idx, // locale identifier LCMAP_LOWERCASE, // mapping transformation type &c, // source string 1, // number of characters in source string result, // destination buffer 1); // size of destination buffer if (b == 0) return c; return result[0]; } #endif inline char BOOST_REGEX_CALL w32_toupper(char c, lcid_type idx) { char result[2]; #ifndef BOOST_NO_ANSI_APIS int b = ::LCMapStringA( idx, // locale identifier LCMAP_UPPERCASE, // mapping transformation type &c, // source string 1, // number of characters in source string result, // destination buffer 1); // size of destination buffer if (b == 0) return c; #else UINT code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return c; WCHAR wide_c; if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return c; WCHAR wide_result; int b = ::LCMapStringW( idx, // locale identifier LCMAP_UPPERCASE, // mapping transformation type &wide_c, // source string 1, // number of characters in source string &wide_result, // destination buffer 1); // size of destination buffer if (b == 0) return c; if (::WideCharToMultiByte(code_page, 0, &wide_result, 1, result, 2, NULL, NULL) == 0) return c; // No single byte upper case equivalent available. #endif return result[0]; } #ifndef BOOST_NO_WREGEX inline wchar_t BOOST_REGEX_CALL w32_toupper(wchar_t c, lcid_type idx) { wchar_t result[2]; int b = ::LCMapStringW( idx, // locale identifier LCMAP_UPPERCASE, // mapping transformation type &c, // source string 1, // number of characters in source string result, // destination buffer 1); // size of destination buffer if (b == 0) return c; return result[0]; } #endif inline bool BOOST_REGEX_CALL w32_is(lcid_type idx, boost::uint32_t m, char c) { WORD mask; #ifndef BOOST_NO_ANSI_APIS if (::GetStringTypeExA(idx, CT_CTYPE1, &c, 1, &mask) && (mask & m & w32_regex_traits_implementation::mask_base)) return true; #else UINT code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return false; WCHAR wide_c; if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return false; if (::GetStringTypeExW(idx, CT_CTYPE1, &wide_c, 1, &mask) && (mask & m & w32_regex_traits_implementation::mask_base)) return true; #endif if ((m & w32_regex_traits_implementation::mask_word) && (c == '_')) return true; return false; } #ifndef BOOST_NO_WREGEX inline bool BOOST_REGEX_CALL w32_is(lcid_type idx, boost::uint32_t m, wchar_t c) { WORD mask; if (::GetStringTypeExW(idx, CT_CTYPE1, &c, 1, &mask) && (mask & m & w32_regex_traits_implementation::mask_base)) return true; if ((m & w32_regex_traits_implementation::mask_word) && (c == '_')) return true; if ((m & w32_regex_traits_implementation::mask_unicode) && (c > 0xff)) return true; return false; } #endif } // BOOST_REGEX_DETAIL_NS } // boost #ifdef BOOST_MSVC #pragma warning(pop) #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4103) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #endif // BOOST_REGEX_NO_WIN32_LOCALE #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/basic_regex.hpp ================================================ /* * * Copyright (c) 1998-2004 John Maddock * Copyright 2011 Garmin Ltd. or its subsidiaries * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org/ for most recent version. * FILE basic_regex.cpp * VERSION see * DESCRIPTION: Declares template class basic_regex. */ #ifndef BOOST_REGEX_V5_BASIC_REGEX_HPP #define BOOST_REGEX_V5_BASIC_REGEX_HPP #include namespace boost{ #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable : 4251) #if BOOST_REGEX_MSVC < 1700 # pragma warning(disable : 4231) #endif #if BOOST_REGEX_MSVC < 1600 #pragma warning(disable : 4660) #endif #if BOOST_REGEX_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace BOOST_REGEX_DETAIL_NS{ // // forward declaration, we will need this one later: // template class basic_regex_parser; template void bubble_down_one(I first, I last) { if(first != last) { I next = last - 1; while((next != first) && (*next < *(next-1))) { (next-1)->swap(*next); --next; } } } static const int hash_value_mask = 1 << (std::numeric_limits::digits - 1); template inline int hash_value_from_capture_name(Iterator i, Iterator j) { std::size_t r = 0; while (i != j) { r ^= *i + 0x9e3779b9 + (r << 6) + (r >> 2); ++i; } r %= ((std::numeric_limits::max)()); return static_cast(r) | hash_value_mask; } class named_subexpressions { public: struct name { template name(const charT* i, const charT* j, int idx) : index(idx) { hash = hash_value_from_capture_name(i, j); } name(int h, int idx) : index(idx), hash(h) { } int index; int hash; bool operator < (const name& other)const { return hash < other.hash; } bool operator == (const name& other)const { return hash == other.hash; } void swap(name& other) { std::swap(index, other.index); std::swap(hash, other.hash); } }; typedef std::vector::const_iterator const_iterator; typedef std::pair range_type; named_subexpressions(){} template void set_name(const charT* i, const charT* j, int index) { m_sub_names.push_back(name(i, j, index)); bubble_down_one(m_sub_names.begin(), m_sub_names.end()); } template int get_id(const charT* i, const charT* j)const { name t(i, j, 0); typename std::vector::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t); if((pos != m_sub_names.end()) && (*pos == t)) { return pos->index; } return -1; } template range_type equal_range(const charT* i, const charT* j)const { name t(i, j, 0); return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t); } int get_id(int h)const { name t(h, 0); std::vector::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t); if((pos != m_sub_names.end()) && (*pos == t)) { return pos->index; } return -1; } range_type equal_range(int h)const { name t(h, 0); return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t); } private: std::vector m_sub_names; }; // // class regex_data: // represents the data we wish to expose to the matching algorithms. // template struct regex_data : public named_subexpressions { typedef regex_constants::syntax_option_type flag_type; typedef std::size_t size_type; regex_data(const ::std::shared_ptr< ::boost::regex_traits_wrapper >& t) : m_ptraits(t), m_flags(0), m_status(0), m_expression(0), m_expression_len(0), m_mark_count(0), m_first_state(0), m_restart_type(0), m_startmap{ 0 }, m_can_be_null(0), m_word_mask(0), m_has_recursions(false), m_disable_match_any(false) {} regex_data() : m_ptraits(new ::boost::regex_traits_wrapper()), m_flags(0), m_status(0), m_expression(0), m_expression_len(0), m_mark_count(0), m_first_state(0), m_restart_type(0), m_startmap{ 0 }, m_can_be_null(0), m_word_mask(0), m_has_recursions(false), m_disable_match_any(false) {} ::std::shared_ptr< ::boost::regex_traits_wrapper > m_ptraits; // traits class instance flag_type m_flags; // flags with which we were compiled int m_status; // error code (0 implies OK). const charT* m_expression; // the original expression std::ptrdiff_t m_expression_len; // the length of the original expression size_type m_mark_count; // the number of marked sub-expressions BOOST_REGEX_DETAIL_NS::re_syntax_base* m_first_state; // the first state of the machine unsigned m_restart_type; // search optimisation type unsigned char m_startmap[1 << CHAR_BIT]; // which characters can start a match unsigned int m_can_be_null; // whether we can match a null string BOOST_REGEX_DETAIL_NS::raw_storage m_data; // the buffer in which our states are constructed typename traits::char_class_type m_word_mask; // mask used to determine if a character is a word character std::vector< std::pair< std::size_t, std::size_t> > m_subs; // Position of sub-expressions within the *string*. bool m_has_recursions; // whether we have recursive expressions; bool m_disable_match_any; // when set we need to disable the match_any flag as it causes different/buggy behaviour. }; // // class basic_regex_implementation // pimpl implementation class for basic_regex. // template class basic_regex_implementation : public regex_data { public: typedef regex_constants::syntax_option_type flag_type; typedef std::ptrdiff_t difference_type; typedef std::size_t size_type; typedef typename traits::locale_type locale_type; typedef const charT* const_iterator; basic_regex_implementation(){} basic_regex_implementation(const ::std::shared_ptr< ::boost::regex_traits_wrapper >& t) : regex_data(t) {} void assign(const charT* arg_first, const charT* arg_last, flag_type f) { regex_data* pdat = this; basic_regex_parser parser(pdat); parser.parse(arg_first, arg_last, f); } locale_type imbue(locale_type l) { return this->m_ptraits->imbue(l); } locale_type getloc()const { return this->m_ptraits->getloc(); } std::basic_string str()const { std::basic_string result; if(this->m_status == 0) result = std::basic_string(this->m_expression, this->m_expression_len); return result; } const_iterator expression()const { return this->m_expression; } std::pair subexpression(std::size_t n)const { const std::pair& pi = this->m_subs.at(n); std::pair p(expression() + pi.first, expression() + pi.second); return p; } // // begin, end: const_iterator begin()const { return (this->m_status ? 0 : this->m_expression); } const_iterator end()const { return (this->m_status ? 0 : this->m_expression + this->m_expression_len); } flag_type flags()const { return this->m_flags; } size_type size()const { return this->m_expression_len; } int status()const { return this->m_status; } size_type mark_count()const { return this->m_mark_count - 1; } const BOOST_REGEX_DETAIL_NS::re_syntax_base* get_first_state()const { return this->m_first_state; } unsigned get_restart_type()const { return this->m_restart_type; } const unsigned char* get_map()const { return this->m_startmap; } const ::boost::regex_traits_wrapper& get_traits()const { return *(this->m_ptraits); } bool can_be_null()const { return this->m_can_be_null; } const regex_data& get_data()const { basic_regex_implementation const* p = this; return *static_cast*>(p); } }; } // namespace BOOST_REGEX_DETAIL_NS // // class basic_regex: // represents the compiled // regular expression: // #ifdef BOOST_REGEX_NO_FWD template > #else template #endif class basic_regex : public regbase { public: // typedefs: typedef std::size_t traits_size_type; typedef typename traits::string_type traits_string_type; typedef charT char_type; typedef traits traits_type; typedef charT value_type; typedef charT& reference; typedef const charT& const_reference; typedef const charT* const_iterator; typedef const_iterator iterator; typedef std::ptrdiff_t difference_type; typedef std::size_t size_type; typedef regex_constants::syntax_option_type flag_type; // locale_type // placeholder for actual locale type used by the // traits class to localise *this. typedef typename traits::locale_type locale_type; public: explicit basic_regex(){} explicit basic_regex(const charT* p, flag_type f = regex_constants::normal) { assign(p, f); } basic_regex(const charT* p1, const charT* p2, flag_type f = regex_constants::normal) { assign(p1, p2, f); } basic_regex(const charT* p, size_type len, flag_type f) { assign(p, len, f); } basic_regex(const basic_regex& that) : m_pimpl(that.m_pimpl) {} ~basic_regex(){} basic_regex& operator=(const basic_regex& that) { return assign(that); } basic_regex& operator=(const charT* ptr) { return assign(ptr); } // // assign: basic_regex& assign(const basic_regex& that) { m_pimpl = that.m_pimpl; return *this; } basic_regex& assign(const charT* p, flag_type f = regex_constants::normal) { return assign(p, p + traits::length(p), f); } basic_regex& assign(const charT* p, size_type len, flag_type f) { return assign(p, p + len, f); } private: basic_regex& do_assign(const charT* p1, const charT* p2, flag_type f); public: basic_regex& assign(const charT* p1, const charT* p2, flag_type f = regex_constants::normal) { return do_assign(p1, p2, f); } template unsigned int set_expression(const std::basic_string& p, flag_type f = regex_constants::normal) { return set_expression(p.data(), p.data() + p.size(), f); } template explicit basic_regex(const std::basic_string& p, flag_type f = regex_constants::normal) { assign(p, f); } template basic_regex(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal) { typedef typename traits::string_type seq_type; seq_type a(arg_first, arg_last); if(!a.empty()) assign(static_cast(&*a.begin()), static_cast(&*a.begin() + a.size()), f); else assign(static_cast(0), static_cast(0), f); } template basic_regex& operator=(const std::basic_string& p) { return assign(p.data(), p.data() + p.size(), regex_constants::normal); } template basic_regex& assign( const std::basic_string& s, flag_type f = regex_constants::normal) { return assign(s.data(), s.data() + s.size(), f); } template basic_regex& assign(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal) { typedef typename traits::string_type seq_type; seq_type a(arg_first, arg_last); if(a.size()) { const charT* p1 = &*a.begin(); const charT* p2 = &*a.begin() + a.size(); return assign(p1, p2, f); } return assign(static_cast(0), static_cast(0), f); } // // locale: locale_type imbue(locale_type l); locale_type getloc()const { return m_pimpl.get() ? m_pimpl->getloc() : locale_type(); } // // getflags: // retained for backwards compatibility only, "flags" // is now the preferred name: flag_type getflags()const { return flags(); } flag_type flags()const { return m_pimpl.get() ? m_pimpl->flags() : 0; } // // str: std::basic_string str()const { return m_pimpl.get() ? m_pimpl->str() : std::basic_string(); } // // begin, end, subexpression: std::pair subexpression(std::size_t n)const { #ifdef BOOST_REGEX_STANDALONE if (!m_pimpl.get()) throw std::logic_error("Can't access subexpressions in an invalid regex."); #else if(!m_pimpl.get()) boost::throw_exception(std::logic_error("Can't access subexpressions in an invalid regex.")); #endif return m_pimpl->subexpression(n); } const_iterator begin()const { return (m_pimpl.get() ? m_pimpl->begin() : 0); } const_iterator end()const { return (m_pimpl.get() ? m_pimpl->end() : 0); } // // swap: void swap(basic_regex& that)throw() { m_pimpl.swap(that.m_pimpl); } // // size: size_type size()const { return (m_pimpl.get() ? m_pimpl->size() : 0); } // // max_size: size_type max_size()const { return UINT_MAX; } // // empty: bool empty()const { return (m_pimpl.get() ? 0 != m_pimpl->status() : true); } size_type mark_count()const { return (m_pimpl.get() ? m_pimpl->mark_count() : 0); } int status()const { return (m_pimpl.get() ? m_pimpl->status() : regex_constants::error_empty); } int compare(const basic_regex& that) const { if(m_pimpl.get() == that.m_pimpl.get()) return 0; if(!m_pimpl.get()) return -1; if(!that.m_pimpl.get()) return 1; if(status() != that.status()) return status() - that.status(); if(flags() != that.flags()) return flags() - that.flags(); return str().compare(that.str()); } bool operator==(const basic_regex& e)const { return compare(e) == 0; } bool operator != (const basic_regex& e)const { return compare(e) != 0; } bool operator<(const basic_regex& e)const { return compare(e) < 0; } bool operator>(const basic_regex& e)const { return compare(e) > 0; } bool operator<=(const basic_regex& e)const { return compare(e) <= 0; } bool operator>=(const basic_regex& e)const { return compare(e) >= 0; } // // The following are deprecated as public interfaces // but are available for compatibility with earlier versions. const charT* expression()const { return (m_pimpl.get() && !m_pimpl->status() ? m_pimpl->expression() : 0); } unsigned int set_expression(const charT* p1, const charT* p2, flag_type f = regex_constants::normal) { assign(p1, p2, f | regex_constants::no_except); return status(); } unsigned int set_expression(const charT* p, flag_type f = regex_constants::normal) { assign(p, f | regex_constants::no_except); return status(); } unsigned int error_code()const { return status(); } // // private access methods: // const BOOST_REGEX_DETAIL_NS::re_syntax_base* get_first_state()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_first_state(); } unsigned get_restart_type()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_restart_type(); } const unsigned char* get_map()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_map(); } const ::boost::regex_traits_wrapper& get_traits()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_traits(); } bool can_be_null()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->can_be_null(); } const BOOST_REGEX_DETAIL_NS::regex_data& get_data()const { BOOST_REGEX_ASSERT(0 != m_pimpl.get()); return m_pimpl->get_data(); } std::shared_ptr get_named_subs()const { return m_pimpl; } private: std::shared_ptr > m_pimpl; }; // // out of line members; // these are the only members that mutate the basic_regex object, // and are designed to provide the strong exception guarantee // (in the event of a throw, the state of the object remains unchanged). // template basic_regex& basic_regex::do_assign(const charT* p1, const charT* p2, flag_type f) { std::shared_ptr > temp; if(!m_pimpl.get()) { temp = std::shared_ptr >(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation()); } else { temp = std::shared_ptr >(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation(m_pimpl->m_ptraits)); } temp->assign(p1, p2, f); temp.swap(m_pimpl); return *this; } template typename basic_regex::locale_type basic_regex::imbue(locale_type l) { std::shared_ptr > temp(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation()); locale_type result = temp->imbue(l); temp.swap(m_pimpl); return result; } // // non-members: // template void swap(basic_regex& e1, basic_regex& e2) { e1.swap(e2); } template std::basic_ostream& operator << (std::basic_ostream& os, const basic_regex& e) { return (os << e.str()); } // // class reg_expression: // this is provided for backwards compatibility only, // it is deprecated, no not use! // #ifdef BOOST_REGEX_NO_FWD template > #else template #endif class reg_expression : public basic_regex { public: typedef typename basic_regex::flag_type flag_type; typedef typename basic_regex::size_type size_type; explicit reg_expression(){} explicit reg_expression(const charT* p, flag_type f = regex_constants::normal) : basic_regex(p, f){} reg_expression(const charT* p1, const charT* p2, flag_type f = regex_constants::normal) : basic_regex(p1, p2, f){} reg_expression(const charT* p, size_type len, flag_type f) : basic_regex(p, len, f){} reg_expression(const reg_expression& that) : basic_regex(that) {} ~reg_expression(){} reg_expression& operator=(const reg_expression& that) { return this->assign(that); } template explicit reg_expression(const std::basic_string& p, flag_type f = regex_constants::normal) : basic_regex(p, f) { } template reg_expression(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal) : basic_regex(arg_first, arg_last, f) { } template reg_expression& operator=(const std::basic_string& p) { this->assign(p); return *this; } }; #ifdef BOOST_REGEX_MSVC #pragma warning (pop) #endif } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/basic_regex_creator.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE basic_regex_creator.cpp * VERSION see * DESCRIPTION: Declares template class basic_regex_creator which fills in * the data members of a regex_data object. */ #ifndef BOOST_REGEX_V5_BASIC_REGEX_CREATOR_HPP #define BOOST_REGEX_V5_BASIC_REGEX_CREATOR_HPP #ifdef BOOST_REGEX_MSVC # pragma warning(push) #pragma warning(disable:4459) #if BOOST_REGEX_MSVC < 1910 #pragma warning(disable:4800) #endif #endif #include namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ template struct digraph : public std::pair { digraph() : std::pair(charT(0), charT(0)){} digraph(charT c1) : std::pair(c1, charT(0)){} digraph(charT c1, charT c2) : std::pair(c1, c2) {} digraph(const digraph& d) : std::pair(d.first, d.second){} digraph& operator=(const digraph&) = default; template digraph(const Seq& s) : std::pair() { BOOST_REGEX_ASSERT(s.size() <= 2); BOOST_REGEX_ASSERT(s.size()); this->first = s[0]; this->second = (s.size() > 1) ? s[1] : 0; } }; template class basic_char_set { public: typedef digraph digraph_type; typedef typename traits::string_type string_type; typedef typename traits::char_class_type m_type; basic_char_set() { m_negate = false; m_has_digraphs = false; m_classes = 0; m_negated_classes = 0; m_empty = true; } void add_single(const digraph_type& s) { m_singles.insert(s); if(s.second) m_has_digraphs = true; m_empty = false; } void add_range(const digraph_type& first, const digraph_type& end) { m_ranges.push_back(first); m_ranges.push_back(end); if(first.second) { m_has_digraphs = true; add_single(first); } if(end.second) { m_has_digraphs = true; add_single(end); } m_empty = false; } void add_class(m_type m) { m_classes |= m; m_empty = false; } void add_negated_class(m_type m) { m_negated_classes |= m; m_empty = false; } void add_equivalent(const digraph_type& s) { m_equivalents.insert(s); if(s.second) { m_has_digraphs = true; add_single(s); } m_empty = false; } void negate() { m_negate = true; //m_empty = false; } // // accessor functions: // bool has_digraphs()const { return m_has_digraphs; } bool is_negated()const { return m_negate; } typedef typename std::vector::const_iterator list_iterator; typedef typename std::set::const_iterator set_iterator; set_iterator singles_begin()const { return m_singles.begin(); } set_iterator singles_end()const { return m_singles.end(); } list_iterator ranges_begin()const { return m_ranges.begin(); } list_iterator ranges_end()const { return m_ranges.end(); } set_iterator equivalents_begin()const { return m_equivalents.begin(); } set_iterator equivalents_end()const { return m_equivalents.end(); } m_type classes()const { return m_classes; } m_type negated_classes()const { return m_negated_classes; } bool empty()const { return m_empty; } private: std::set m_singles; // a list of single characters to match std::vector m_ranges; // a list of end points of our ranges bool m_negate; // true if the set is to be negated bool m_has_digraphs; // true if we have digraphs present m_type m_classes; // character classes to match m_type m_negated_classes; // negated character classes to match bool m_empty; // whether we've added anything yet std::set m_equivalents; // a list of equivalence classes }; template class basic_regex_creator { public: basic_regex_creator(regex_data* data); std::ptrdiff_t getoffset(void* addr) { return getoffset(addr, m_pdata->m_data.data()); } std::ptrdiff_t getoffset(const void* addr, const void* base) { return static_cast(addr) - static_cast(base); } re_syntax_base* getaddress(std::ptrdiff_t off) { return getaddress(off, m_pdata->m_data.data()); } re_syntax_base* getaddress(std::ptrdiff_t off, void* base) { return static_cast(static_cast(static_cast(base) + off)); } void init(unsigned l_flags) { m_pdata->m_flags = l_flags; m_icase = l_flags & regex_constants::icase; } regbase::flag_type flags() { return m_pdata->m_flags; } void flags(regbase::flag_type f) { m_pdata->m_flags = f; if(m_icase != static_cast(f & regbase::icase)) { m_icase = static_cast(f & regbase::icase); } } re_syntax_base* append_state(syntax_element_type t, std::size_t s = sizeof(re_syntax_base)); re_syntax_base* insert_state(std::ptrdiff_t pos, syntax_element_type t, std::size_t s = sizeof(re_syntax_base)); re_literal* append_literal(charT c); re_syntax_base* append_set(const basic_char_set& char_set); re_syntax_base* append_set(const basic_char_set& char_set, std::integral_constant*); re_syntax_base* append_set(const basic_char_set& char_set, std::integral_constant*); void finalize(const charT* p1, const charT* p2); protected: regex_data* m_pdata; // pointer to the basic_regex_data struct we are filling in const ::boost::regex_traits_wrapper& m_traits; // convenience reference to traits class re_syntax_base* m_last_state; // the last state we added bool m_icase; // true for case insensitive matches unsigned m_repeater_id; // the state_id of the next repeater bool m_has_backrefs; // true if there are actually any backrefs std::uintmax_t m_bad_repeats; // bitmask of repeats we can't deduce a startmap for; bool m_has_recursions; // set when we have recursive expressions to fixup std::vector m_recursion_checks; // notes which recursions we've followed while analysing this expression typename traits::char_class_type m_word_mask; // mask used to determine if a character is a word character typename traits::char_class_type m_mask_space; // mask used to determine if a character is a word character typename traits::char_class_type m_lower_mask; // mask used to determine if a character is a lowercase character typename traits::char_class_type m_upper_mask; // mask used to determine if a character is an uppercase character typename traits::char_class_type m_alpha_mask; // mask used to determine if a character is an alphabetic character private: basic_regex_creator& operator=(const basic_regex_creator&); basic_regex_creator(const basic_regex_creator&); void fixup_pointers(re_syntax_base* state); void fixup_recursions(re_syntax_base* state); void create_startmaps(re_syntax_base* state); int calculate_backstep(re_syntax_base* state); void create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask); unsigned get_restart_type(re_syntax_base* state); void set_all_masks(unsigned char* bits, unsigned char); bool is_bad_repeat(re_syntax_base* pt); void set_bad_repeat(re_syntax_base* pt); syntax_element_type get_repeat_type(re_syntax_base* state); void probe_leading_repeat(re_syntax_base* state); }; template basic_regex_creator::basic_regex_creator(regex_data* data) : m_pdata(data), m_traits(*(data->m_ptraits)), m_last_state(0), m_icase(false), m_repeater_id(0), m_has_backrefs(false), m_bad_repeats(0), m_has_recursions(false), m_word_mask(0), m_mask_space(0), m_lower_mask(0), m_upper_mask(0), m_alpha_mask(0) { m_pdata->m_data.clear(); m_pdata->m_status = ::boost::regex_constants::error_ok; static const charT w = 'w'; static const charT s = 's'; static const charT l[5] = { 'l', 'o', 'w', 'e', 'r', }; static const charT u[5] = { 'u', 'p', 'p', 'e', 'r', }; static const charT a[5] = { 'a', 'l', 'p', 'h', 'a', }; m_word_mask = m_traits.lookup_classname(&w, &w +1); m_mask_space = m_traits.lookup_classname(&s, &s +1); m_lower_mask = m_traits.lookup_classname(l, l + 5); m_upper_mask = m_traits.lookup_classname(u, u + 5); m_alpha_mask = m_traits.lookup_classname(a, a + 5); m_pdata->m_word_mask = m_word_mask; BOOST_REGEX_ASSERT(m_word_mask != 0); BOOST_REGEX_ASSERT(m_mask_space != 0); BOOST_REGEX_ASSERT(m_lower_mask != 0); BOOST_REGEX_ASSERT(m_upper_mask != 0); BOOST_REGEX_ASSERT(m_alpha_mask != 0); } template re_syntax_base* basic_regex_creator::append_state(syntax_element_type t, std::size_t s) { // if the state is a backref then make a note of it: if(t == syntax_element_backref) this->m_has_backrefs = true; // append a new state, start by aligning our last one: m_pdata->m_data.align(); // set the offset to the next state in our last one: if(m_last_state) m_last_state->next.i = m_pdata->m_data.size() - getoffset(m_last_state); // now actually extend our data: m_last_state = static_cast(m_pdata->m_data.extend(s)); // fill in boilerplate options in the new state: m_last_state->next.i = 0; m_last_state->type = t; return m_last_state; } template re_syntax_base* basic_regex_creator::insert_state(std::ptrdiff_t pos, syntax_element_type t, std::size_t s) { // append a new state, start by aligning our last one: m_pdata->m_data.align(); // set the offset to the next state in our last one: if(m_last_state) m_last_state->next.i = m_pdata->m_data.size() - getoffset(m_last_state); // remember the last state position: std::ptrdiff_t off = getoffset(m_last_state) + s; // now actually insert our data: re_syntax_base* new_state = static_cast(m_pdata->m_data.insert(pos, s)); // fill in boilerplate options in the new state: new_state->next.i = s; new_state->type = t; m_last_state = getaddress(off); return new_state; } template re_literal* basic_regex_creator::append_literal(charT c) { re_literal* result; // start by seeing if we have an existing re_literal we can extend: if((0 == m_last_state) || (m_last_state->type != syntax_element_literal)) { // no existing re_literal, create a new one: result = static_cast(append_state(syntax_element_literal, sizeof(re_literal) + sizeof(charT))); result->length = 1; *static_cast(static_cast(result+1)) = m_traits.translate(c, m_icase); } else { // we have an existing re_literal, extend it: std::ptrdiff_t off = getoffset(m_last_state); m_pdata->m_data.extend(sizeof(charT)); m_last_state = result = static_cast(getaddress(off)); charT* characters = static_cast(static_cast(result+1)); characters[result->length] = m_traits.translate(c, m_icase); result->length += 1; } return result; } template inline re_syntax_base* basic_regex_creator::append_set( const basic_char_set& char_set) { typedef std::integral_constant truth_type; return char_set.has_digraphs() ? append_set(char_set, static_cast*>(0)) : append_set(char_set, static_cast(0)); } template re_syntax_base* basic_regex_creator::append_set( const basic_char_set& char_set, std::integral_constant*) { typedef typename traits::string_type string_type; typedef typename basic_char_set::list_iterator item_iterator; typedef typename basic_char_set::set_iterator set_iterator; typedef typename traits::char_class_type m_type; re_set_long* result = static_cast*>(append_state(syntax_element_long_set, sizeof(re_set_long))); // // fill in the basics: // result->csingles = static_cast(std::distance(char_set.singles_begin(), char_set.singles_end())); result->cranges = static_cast(std::distance(char_set.ranges_begin(), char_set.ranges_end())) / 2; result->cequivalents = static_cast(std::distance(char_set.equivalents_begin(), char_set.equivalents_end())); result->cclasses = char_set.classes(); result->cnclasses = char_set.negated_classes(); if(flags() & regbase::icase) { // adjust classes as needed: if(((result->cclasses & m_lower_mask) == m_lower_mask) || ((result->cclasses & m_upper_mask) == m_upper_mask)) result->cclasses |= m_alpha_mask; if(((result->cnclasses & m_lower_mask) == m_lower_mask) || ((result->cnclasses & m_upper_mask) == m_upper_mask)) result->cnclasses |= m_alpha_mask; } result->isnot = char_set.is_negated(); result->singleton = !char_set.has_digraphs(); // // remember where the state is for later: // std::ptrdiff_t offset = getoffset(result); // // now extend with all the singles: // item_iterator first, last; set_iterator sfirst, slast; sfirst = char_set.singles_begin(); slast = char_set.singles_end(); while(sfirst != slast) { charT* p = static_cast(this->m_pdata->m_data.extend(sizeof(charT) * (sfirst->first == static_cast(0) ? 1 : sfirst->second ? 3 : 2))); p[0] = m_traits.translate(sfirst->first, m_icase); if(sfirst->first == static_cast(0)) { p[0] = 0; } else if(sfirst->second) { p[1] = m_traits.translate(sfirst->second, m_icase); p[2] = 0; } else p[1] = 0; ++sfirst; } // // now extend with all the ranges: // first = char_set.ranges_begin(); last = char_set.ranges_end(); while(first != last) { // first grab the endpoints of the range: digraph c1 = *first; c1.first = this->m_traits.translate(c1.first, this->m_icase); c1.second = this->m_traits.translate(c1.second, this->m_icase); ++first; digraph c2 = *first; c2.first = this->m_traits.translate(c2.first, this->m_icase); c2.second = this->m_traits.translate(c2.second, this->m_icase); ++first; string_type s1, s2; // different actions now depending upon whether collation is turned on: if(flags() & regex_constants::collate) { // we need to transform our range into sort keys: charT a1[3] = { c1.first, c1.second, charT(0), }; charT a2[3] = { c2.first, c2.second, charT(0), }; s1 = this->m_traits.transform(a1, (a1[1] ? a1+2 : a1+1)); s2 = this->m_traits.transform(a2, (a2[1] ? a2+2 : a2+1)); if(s1.empty()) s1 = string_type(1, charT(0)); if(s2.empty()) s2 = string_type(1, charT(0)); } else { if(c1.second) { s1.insert(s1.end(), c1.first); s1.insert(s1.end(), c1.second); } else s1 = string_type(1, c1.first); if(c2.second) { s2.insert(s2.end(), c2.first); s2.insert(s2.end(), c2.second); } else s2.insert(s2.end(), c2.first); } if(s1 > s2) { // Oops error: return 0; } charT* p = static_cast(this->m_pdata->m_data.extend(sizeof(charT) * (s1.size() + s2.size() + 2) ) ); BOOST_REGEX_DETAIL_NS::copy(s1.begin(), s1.end(), p); p[s1.size()] = charT(0); p += s1.size() + 1; BOOST_REGEX_DETAIL_NS::copy(s2.begin(), s2.end(), p); p[s2.size()] = charT(0); } // // now process the equivalence classes: // sfirst = char_set.equivalents_begin(); slast = char_set.equivalents_end(); while(sfirst != slast) { string_type s; if(sfirst->second) { charT cs[3] = { sfirst->first, sfirst->second, charT(0), }; s = m_traits.transform_primary(cs, cs+2); } else s = m_traits.transform_primary(&sfirst->first, &sfirst->first+1); if(s.empty()) return 0; // invalid or unsupported equivalence class charT* p = static_cast(this->m_pdata->m_data.extend(sizeof(charT) * (s.size()+1) ) ); BOOST_REGEX_DETAIL_NS::copy(s.begin(), s.end(), p); p[s.size()] = charT(0); ++sfirst; } // // finally reset the address of our last state: // m_last_state = result = static_cast*>(getaddress(offset)); return result; } template inline bool char_less(T t1, T t2) { return t1 < t2; } inline bool char_less(char t1, char t2) { return static_cast(t1) < static_cast(t2); } inline bool char_less(signed char t1, signed char t2) { return static_cast(t1) < static_cast(t2); } template re_syntax_base* basic_regex_creator::append_set( const basic_char_set& char_set, std::integral_constant*) { typedef typename traits::string_type string_type; typedef typename basic_char_set::list_iterator item_iterator; typedef typename basic_char_set::set_iterator set_iterator; re_set* result = static_cast(append_state(syntax_element_set, sizeof(re_set))); bool negate = char_set.is_negated(); std::memset(result->_map, 0, sizeof(result->_map)); // // handle singles first: // item_iterator first, last; set_iterator sfirst, slast; sfirst = char_set.singles_begin(); slast = char_set.singles_end(); while(sfirst != slast) { for(unsigned int i = 0; i < (1 << CHAR_BIT); ++i) { if(this->m_traits.translate(static_cast(i), this->m_icase) == this->m_traits.translate(sfirst->first, this->m_icase)) result->_map[i] = true; } ++sfirst; } // // OK now handle ranges: // first = char_set.ranges_begin(); last = char_set.ranges_end(); while(first != last) { // first grab the endpoints of the range: charT c1 = this->m_traits.translate(first->first, this->m_icase); ++first; charT c2 = this->m_traits.translate(first->first, this->m_icase); ++first; // different actions now depending upon whether collation is turned on: if(flags() & regex_constants::collate) { // we need to transform our range into sort keys: charT c3[2] = { c1, charT(0), }; string_type s1 = this->m_traits.transform(c3, c3+1); c3[0] = c2; string_type s2 = this->m_traits.transform(c3, c3+1); if(s1 > s2) { // Oops error: return 0; } BOOST_REGEX_ASSERT(c3[1] == charT(0)); for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { c3[0] = static_cast(i); string_type s3 = this->m_traits.transform(c3, c3 +1); if((s1 <= s3) && (s3 <= s2)) result->_map[i] = true; } } else { if(char_less(c2, c1)) { // Oops error: return 0; } // everything in range matches: std::memset(result->_map + static_cast(c1), true, static_cast(1u) + static_cast(static_cast(c2) - static_cast(c1))); } } // // and now the classes: // typedef typename traits::char_class_type m_type; m_type m = char_set.classes(); if(flags() & regbase::icase) { // adjust m as needed: if(((m & m_lower_mask) == m_lower_mask) || ((m & m_upper_mask) == m_upper_mask)) m |= m_alpha_mask; } if(m != 0) { for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { if(this->m_traits.isctype(static_cast(i), m)) result->_map[i] = true; } } // // and now the negated classes: // m = char_set.negated_classes(); if(flags() & regbase::icase) { // adjust m as needed: if(((m & m_lower_mask) == m_lower_mask) || ((m & m_upper_mask) == m_upper_mask)) m |= m_alpha_mask; } if(m != 0) { for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { if(0 == this->m_traits.isctype(static_cast(i), m)) result->_map[i] = true; } } // // now process the equivalence classes: // sfirst = char_set.equivalents_begin(); slast = char_set.equivalents_end(); while(sfirst != slast) { string_type s; BOOST_REGEX_ASSERT(static_cast(0) == sfirst->second); s = m_traits.transform_primary(&sfirst->first, &sfirst->first+1); if(s.empty()) return 0; // invalid or unsupported equivalence class for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { charT c[2] = { (static_cast(i)), charT(0), }; string_type s2 = this->m_traits.transform_primary(c, c+1); if(s == s2) result->_map[i] = true; } ++sfirst; } if(negate) { for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) { result->_map[i] = !(result->_map[i]); } } return result; } template void basic_regex_creator::finalize(const charT* p1, const charT* p2) { if(this->m_pdata->m_status) return; // we've added all the states we need, now finish things off. // start by adding a terminating state: append_state(syntax_element_match); // extend storage to store original expression: std::ptrdiff_t len = p2 - p1; m_pdata->m_expression_len = len; charT* ps = static_cast(m_pdata->m_data.extend(sizeof(charT) * (1 + (p2 - p1)))); m_pdata->m_expression = ps; BOOST_REGEX_DETAIL_NS::copy(p1, p2, ps); ps[p2 - p1] = 0; // fill in our other data... // successful parsing implies a zero status: m_pdata->m_status = 0; // get the first state of the machine: m_pdata->m_first_state = static_cast(m_pdata->m_data.data()); // fixup pointers in the machine: fixup_pointers(m_pdata->m_first_state); if(m_has_recursions) { m_pdata->m_has_recursions = true; fixup_recursions(m_pdata->m_first_state); if(this->m_pdata->m_status) return; } else m_pdata->m_has_recursions = false; // create nested startmaps: create_startmaps(m_pdata->m_first_state); // create main startmap: std::memset(m_pdata->m_startmap, 0, sizeof(m_pdata->m_startmap)); m_pdata->m_can_be_null = 0; m_bad_repeats = 0; if(m_has_recursions) m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u); create_startmap(m_pdata->m_first_state, m_pdata->m_startmap, &(m_pdata->m_can_be_null), mask_all); // get the restart type: m_pdata->m_restart_type = get_restart_type(m_pdata->m_first_state); // optimise a leading repeat if there is one: probe_leading_repeat(m_pdata->m_first_state); } template void basic_regex_creator::fixup_pointers(re_syntax_base* state) { while(state) { switch(state->type) { case syntax_element_recurse: m_has_recursions = true; if(state->next.i) state->next.p = getaddress(state->next.i, state); else state->next.p = 0; break; case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: // set the state_id of this repeat: static_cast(state)->state_id = m_repeater_id++; BOOST_REGEX_FALLTHROUGH; case syntax_element_alt: std::memset(static_cast(state)->_map, 0, sizeof(static_cast(state)->_map)); static_cast(state)->can_be_null = 0; BOOST_REGEX_FALLTHROUGH; case syntax_element_jump: static_cast(state)->alt.p = getaddress(static_cast(state)->alt.i, state); BOOST_REGEX_FALLTHROUGH; default: if(state->next.i) state->next.p = getaddress(state->next.i, state); else state->next.p = 0; } state = state->next.p; } } template void basic_regex_creator::fixup_recursions(re_syntax_base* state) { re_syntax_base* base = state; while(state) { switch(state->type) { case syntax_element_assert_backref: { // just check that the index is valid: int idx = static_cast(state)->index; if(idx < 0) { idx = -idx-1; if(idx >= hash_value_mask) { idx = m_pdata->get_id(idx); if(idx <= 0) { // check of sub-expression that doesn't exist: if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = boost::regex_constants::error_bad_pattern; // // clear the expression, we should be empty: // this->m_pdata->m_expression = 0; this->m_pdata->m_expression_len = 0; // // and throw if required: // if(0 == (this->flags() & regex_constants::no_except)) { std::string message = "Encountered a forward reference to a marked sub-expression that does not exist."; boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0); e.raise(); } } } } } break; case syntax_element_recurse: { bool ok = false; re_syntax_base* p = base; std::ptrdiff_t idx = static_cast(state)->alt.i; if(idx >= hash_value_mask) { // // There may be more than one capture group with this hash, just do what Perl // does and recurse to the leftmost: // idx = m_pdata->get_id(static_cast(idx)); } if(idx < 0) { ok = false; } else { while(p) { if((p->type == syntax_element_startmark) && (static_cast(p)->index == idx)) { // // We've found the target of the recursion, set the jump target: // static_cast(state)->alt.p = p; ok = true; // // Now scan the target for nested repeats: // p = p->next.p; int next_rep_id = 0; while(p) { switch(p->type) { case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: next_rep_id = static_cast(p)->state_id; break; case syntax_element_endmark: if(static_cast(p)->index == idx) next_rep_id = -1; break; default: break; } if(next_rep_id) break; p = p->next.p; } if(next_rep_id > 0) { static_cast(state)->state_id = next_rep_id - 1; } break; } p = p->next.p; } } if(!ok) { // recursion to sub-expression that doesn't exist: if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = boost::regex_constants::error_bad_pattern; // // clear the expression, we should be empty: // this->m_pdata->m_expression = 0; this->m_pdata->m_expression_len = 0; // // and throw if required: // if(0 == (this->flags() & regex_constants::no_except)) { std::string message = "Encountered a forward reference to a recursive sub-expression that does not exist."; boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0); e.raise(); } } } break; default: break; } state = state->next.p; } } template void basic_regex_creator::create_startmaps(re_syntax_base* state) { // non-recursive implementation: // create the last map in the machine first, so that earlier maps // can make use of the result... // // This was originally a recursive implementation, but that caused stack // overflows with complex expressions on small stacks (think COM+). // start by saving the case setting: bool l_icase = m_icase; std::vector > v; while(state) { switch(state->type) { case syntax_element_toggle_case: // we need to track case changes here: m_icase = static_cast(state)->icase; state = state->next.p; continue; case syntax_element_alt: case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: // just push the state onto our stack for now: v.push_back(std::pair(m_icase, state)); state = state->next.p; break; case syntax_element_backstep: // we need to calculate how big the backstep is: static_cast(state)->index = this->calculate_backstep(state->next.p); if(static_cast(state)->index < 0) { // Oops error: if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = boost::regex_constants::error_bad_pattern; // // clear the expression, we should be empty: // this->m_pdata->m_expression = 0; this->m_pdata->m_expression_len = 0; // // and throw if required: // if(0 == (this->flags() & regex_constants::no_except)) { std::string message = "Invalid lookbehind assertion encountered in the regular expression."; boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0); e.raise(); } } BOOST_REGEX_FALLTHROUGH; default: state = state->next.p; } } // now work through our list, building all the maps as we go: while(!v.empty()) { // Initialize m_recursion_checks if we need it: if(m_has_recursions) m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u); const std::pair& p = v.back(); m_icase = p.first; state = p.second; v.pop_back(); // Build maps: m_bad_repeats = 0; create_startmap(state->next.p, static_cast(state)->_map, &static_cast(state)->can_be_null, mask_take); m_bad_repeats = 0; if(m_has_recursions) m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u); create_startmap(static_cast(state)->alt.p, static_cast(state)->_map, &static_cast(state)->can_be_null, mask_skip); // adjust the type of the state to allow for faster matching: state->type = this->get_repeat_type(state); } // restore case sensitivity: m_icase = l_icase; } template int basic_regex_creator::calculate_backstep(re_syntax_base* state) { typedef typename traits::char_class_type m_type; int result = 0; while(state) { switch(state->type) { case syntax_element_startmark: if((static_cast(state)->index == -1) || (static_cast(state)->index == -2)) { state = static_cast(state->next.p)->alt.p->next.p; continue; } else if(static_cast(state)->index == -3) { state = state->next.p->next.p; continue; } break; case syntax_element_endmark: if((static_cast(state)->index == -1) || (static_cast(state)->index == -2)) return result; break; case syntax_element_literal: result += static_cast(state)->length; break; case syntax_element_wild: case syntax_element_set: result += 1; break; case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_backref: case syntax_element_rep: case syntax_element_combining: case syntax_element_long_set_rep: case syntax_element_backstep: { re_repeat* rep = static_cast(state); // adjust the type of the state to allow for faster matching: state->type = this->get_repeat_type(state); if((state->type == syntax_element_dot_rep) || (state->type == syntax_element_char_rep) || (state->type == syntax_element_short_set_rep)) { if(rep->max != rep->min) return -1; if (static_cast((std::numeric_limits::max)() - result) < rep->min) return -1; // protection against overflow, we can't calculate a backstep in this case and the expression is probably ill-formed. result += static_cast(rep->min); state = rep->alt.p; continue; } else if(state->type == syntax_element_long_set_rep) { BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_long_set); if(static_cast*>(rep->next.p)->singleton == 0) return -1; if(rep->max != rep->min) return -1; result += static_cast(rep->min); state = rep->alt.p; continue; } } return -1; case syntax_element_long_set: if(static_cast*>(state)->singleton == 0) return -1; result += 1; break; case syntax_element_jump: state = static_cast(state)->alt.p; continue; case syntax_element_alt: { int r1 = calculate_backstep(state->next.p); int r2 = calculate_backstep(static_cast(state)->alt.p); if((r1 < 0) || (r1 != r2)) return -1; return result + r1; } default: break; } state = state->next.p; } return -1; } struct recursion_saver { std::vector saved_state; std::vector* state; recursion_saver(std::vector* p) : saved_state(*p), state(p) {} ~recursion_saver() { state->swap(saved_state); } }; template void basic_regex_creator::create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask) { recursion_saver saved_recursions(&m_recursion_checks); int not_last_jump = 1; re_syntax_base* recursion_start = 0; int recursion_sub = 0; re_syntax_base* recursion_restart = 0; // track case sensitivity: bool l_icase = m_icase; while(state) { switch(state->type) { case syntax_element_toggle_case: l_icase = static_cast(state)->icase; state = state->next.p; break; case syntax_element_literal: { // don't set anything in *pnull, set each element in l_map // that could match the first character in the literal: if(l_map) { l_map[0] |= mask_init; charT first_char = *static_cast(static_cast(static_cast(state) + 1)); for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { if(m_traits.translate(static_cast(i), l_icase) == first_char) l_map[i] |= mask; } } return; } case syntax_element_end_line: { // next character must be a line separator (if there is one): if(l_map) { l_map[0] |= mask_init; l_map[static_cast('\n')] |= mask; l_map[static_cast('\r')] |= mask; l_map[static_cast('\f')] |= mask; l_map[0x85] |= mask; } // now figure out if we can match a NULL string at this point: if(pnull) create_startmap(state->next.p, 0, pnull, mask); return; } case syntax_element_recurse: { BOOST_REGEX_ASSERT(static_cast(state)->alt.p->type == syntax_element_startmark); recursion_sub = static_cast(static_cast(state)->alt.p)->index; if(m_recursion_checks[recursion_sub] & 1u) { // Infinite recursion!! if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = boost::regex_constants::error_bad_pattern; // // clear the expression, we should be empty: // this->m_pdata->m_expression = 0; this->m_pdata->m_expression_len = 0; // // and throw if required: // if(0 == (this->flags() & regex_constants::no_except)) { std::string message = "Encountered an infinite recursion."; boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0); e.raise(); } } else if(recursion_start == 0) { recursion_start = state; recursion_restart = state->next.p; state = static_cast(state)->alt.p; m_recursion_checks[recursion_sub] |= 1u; break; } m_recursion_checks[recursion_sub] |= 1u; // can't handle nested recursion here... BOOST_REGEX_FALLTHROUGH; } case syntax_element_backref: // can be null, and any character can match: if(pnull) *pnull |= mask; BOOST_REGEX_FALLTHROUGH; case syntax_element_wild: { // can't be null, any character can match: set_all_masks(l_map, mask); return; } case syntax_element_accept: case syntax_element_match: { // must be null, any character can match: set_all_masks(l_map, mask); if(pnull) *pnull |= mask; return; } case syntax_element_word_start: { // recurse, then AND with all the word characters: create_startmap(state->next.p, l_map, pnull, mask); if(l_map) { l_map[0] |= mask_init; for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { if(!m_traits.isctype(static_cast(i), m_word_mask)) l_map[i] &= static_cast(~mask); } } return; } case syntax_element_word_end: { // recurse, then AND with all the word characters: create_startmap(state->next.p, l_map, pnull, mask); if(l_map) { l_map[0] |= mask_init; for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { if(m_traits.isctype(static_cast(i), m_word_mask)) l_map[i] &= static_cast(~mask); } } return; } case syntax_element_buffer_end: { // we *must be null* : if(pnull) *pnull |= mask; return; } case syntax_element_long_set: if(l_map) { typedef typename traits::char_class_type m_type; if(static_cast*>(state)->singleton) { l_map[0] |= mask_init; for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { charT c = static_cast(i); if(&c != re_is_set_member(&c, &c + 1, static_cast*>(state), *m_pdata, l_icase)) l_map[i] |= mask; } } else set_all_masks(l_map, mask); } return; case syntax_element_set: if(l_map) { l_map[0] |= mask_init; for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i) { if(static_cast(state)->_map[ static_cast(m_traits.translate(static_cast(i), l_icase))]) l_map[i] |= mask; } } return; case syntax_element_jump: // take the jump: state = static_cast(state)->alt.p; not_last_jump = -1; break; case syntax_element_alt: case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: { re_alt* rep = static_cast(state); if(rep->_map[0] & mask_init) { if(l_map) { // copy previous results: l_map[0] |= mask_init; for(unsigned int i = 0; i <= UCHAR_MAX; ++i) { if(rep->_map[i] & mask_any) l_map[i] |= mask; } } if(pnull) { if(rep->can_be_null & mask_any) *pnull |= mask; } } else { // we haven't created a startmap for this alternative yet // so take the union of the two options: if(is_bad_repeat(state)) { set_all_masks(l_map, mask); if(pnull) *pnull |= mask; return; } set_bad_repeat(state); create_startmap(state->next.p, l_map, pnull, mask); if((state->type == syntax_element_alt) || (static_cast(state)->min == 0) || (not_last_jump == 0)) create_startmap(rep->alt.p, l_map, pnull, mask); } } return; case syntax_element_soft_buffer_end: // match newline or null: if(l_map) { l_map[0] |= mask_init; l_map[static_cast('\n')] |= mask; l_map[static_cast('\r')] |= mask; } if(pnull) *pnull |= mask; return; case syntax_element_endmark: // need to handle independent subs as a special case: if(static_cast(state)->index < 0) { // can be null, any character can match: set_all_masks(l_map, mask); if(pnull) *pnull |= mask; return; } else if(recursion_start && (recursion_sub != 0) && (recursion_sub == static_cast(state)->index)) { // recursion termination: recursion_start = 0; state = recursion_restart; break; } // // Normally we just go to the next state... but if this sub-expression is // the target of a recursion, then we might be ending a recursion, in which // case we should check whatever follows that recursion, as well as whatever // follows this state: // if(m_pdata->m_has_recursions && static_cast(state)->index) { bool ok = false; re_syntax_base* p = m_pdata->m_first_state; while(p) { if(p->type == syntax_element_recurse) { re_brace* p2 = static_cast(static_cast(p)->alt.p); if((p2->type == syntax_element_startmark) && (p2->index == static_cast(state)->index)) { ok = true; break; } } p = p->next.p; } if(ok && ((m_recursion_checks[static_cast(state)->index] & 2u) == 0)) { m_recursion_checks[static_cast(state)->index] |= 2u; create_startmap(p->next.p, l_map, pnull, mask); } } state = state->next.p; break; case syntax_element_commit: set_all_masks(l_map, mask); // Continue scanning so we can figure out whether we can be null: state = state->next.p; break; case syntax_element_startmark: // need to handle independent subs as a special case: if(static_cast(state)->index == -3) { state = state->next.p->next.p; break; } BOOST_REGEX_FALLTHROUGH; default: state = state->next.p; } ++not_last_jump; } } template unsigned basic_regex_creator::get_restart_type(re_syntax_base* state) { // // find out how the machine starts, so we can optimise the search: // while(state) { switch(state->type) { case syntax_element_startmark: case syntax_element_endmark: state = state->next.p; continue; case syntax_element_start_line: return regbase::restart_line; case syntax_element_word_start: return regbase::restart_word; case syntax_element_buffer_start: return regbase::restart_buf; case syntax_element_restart_continue: return regbase::restart_continue; default: state = 0; continue; } } return regbase::restart_any; } template void basic_regex_creator::set_all_masks(unsigned char* bits, unsigned char mask) { // // set mask in all of bits elements, // if bits[0] has mask_init not set then we can // optimise this to a call to memset: // if(bits) { if(bits[0] == 0) (std::memset)(bits, mask, 1u << CHAR_BIT); else { for(unsigned i = 0; i < (1u << CHAR_BIT); ++i) bits[i] |= mask; } bits[0] |= mask_init; } } template bool basic_regex_creator::is_bad_repeat(re_syntax_base* pt) { switch(pt->type) { case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: { unsigned state_id = static_cast(pt)->state_id; if(state_id >= sizeof(m_bad_repeats) * CHAR_BIT) return true; // run out of bits, assume we can't traverse this one. static const std::uintmax_t one = 1uL; return m_bad_repeats & (one << state_id); } default: return false; } } template void basic_regex_creator::set_bad_repeat(re_syntax_base* pt) { switch(pt->type) { case syntax_element_rep: case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: { unsigned state_id = static_cast(pt)->state_id; static const std::uintmax_t one = 1uL; if(state_id <= sizeof(m_bad_repeats) * CHAR_BIT) m_bad_repeats |= (one << state_id); } break; default: break; } } template syntax_element_type basic_regex_creator::get_repeat_type(re_syntax_base* state) { typedef typename traits::char_class_type m_type; if(state->type == syntax_element_rep) { // check to see if we are repeating a single state: if(state->next.p->next.p->next.p == static_cast(state)->alt.p) { switch(state->next.p->type) { case BOOST_REGEX_DETAIL_NS::syntax_element_wild: return BOOST_REGEX_DETAIL_NS::syntax_element_dot_rep; case BOOST_REGEX_DETAIL_NS::syntax_element_literal: return BOOST_REGEX_DETAIL_NS::syntax_element_char_rep; case BOOST_REGEX_DETAIL_NS::syntax_element_set: return BOOST_REGEX_DETAIL_NS::syntax_element_short_set_rep; case BOOST_REGEX_DETAIL_NS::syntax_element_long_set: if(static_cast*>(state->next.p)->singleton) return BOOST_REGEX_DETAIL_NS::syntax_element_long_set_rep; break; default: break; } } } return state->type; } template void basic_regex_creator::probe_leading_repeat(re_syntax_base* state) { // enumerate our states, and see if we have a leading repeat // for which failed search restarts can be optimized; do { switch(state->type) { case syntax_element_startmark: if(static_cast(state)->index >= 0) { state = state->next.p; continue; } #ifdef BOOST_REGEX_MSVC # pragma warning(push) #pragma warning(disable:6011) #endif if((static_cast(state)->index == -1) || (static_cast(state)->index == -2)) { // skip past the zero width assertion: state = static_cast(state->next.p)->alt.p->next.p; continue; } #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif if(static_cast(state)->index == -3) { // Have to skip the leading jump state: state = state->next.p->next.p; continue; } return; case syntax_element_endmark: case syntax_element_start_line: case syntax_element_end_line: case syntax_element_word_boundary: case syntax_element_within_word: case syntax_element_word_start: case syntax_element_word_end: case syntax_element_buffer_start: case syntax_element_buffer_end: case syntax_element_restart_continue: state = state->next.p; break; case syntax_element_dot_rep: case syntax_element_char_rep: case syntax_element_short_set_rep: case syntax_element_long_set_rep: if(this->m_has_backrefs == 0) static_cast(state)->leading = true; BOOST_REGEX_FALLTHROUGH; default: return; } }while(state); } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/basic_regex_parser.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE basic_regex_parser.cpp * VERSION see * DESCRIPTION: Declares template class basic_regex_parser. */ #ifndef BOOST_REGEX_V5_BASIC_REGEX_PARSER_HPP #define BOOST_REGEX_V5_BASIC_REGEX_PARSER_HPP namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4244 4459) #if BOOST_REGEX_MSVC < 1910 #pragma warning(disable:4800) #endif #endif inline std::intmax_t umax(std::integral_constant const&) { // Get out clause here, just in case numeric_limits is unspecialized: return std::numeric_limits::is_specialized ? (std::numeric_limits::max)() : INT_MAX; } inline std::intmax_t umax(std::integral_constant const&) { return (std::numeric_limits::max)(); } inline std::intmax_t umax() { return umax(std::integral_constant::digits >= std::numeric_limits::digits>()); } template class basic_regex_parser : public basic_regex_creator { public: basic_regex_parser(regex_data* data); void parse(const charT* p1, const charT* p2, unsigned flags); void fail(regex_constants::error_type error_code, std::ptrdiff_t position); void fail(regex_constants::error_type error_code, std::ptrdiff_t position, std::string message, std::ptrdiff_t start_pos); void fail(regex_constants::error_type error_code, std::ptrdiff_t position, const std::string& message) { fail(error_code, position, message, position); } bool parse_all(); bool parse_basic(); bool parse_extended(); bool parse_literal(); bool parse_open_paren(); bool parse_basic_escape(); bool parse_extended_escape(); bool parse_match_any(); bool parse_repeat(std::size_t low = 0, std::size_t high = (std::numeric_limits::max)()); bool parse_repeat_range(bool isbasic); bool parse_alt(); bool parse_set(); bool parse_backref(); void parse_set_literal(basic_char_set& char_set); bool parse_inner_set(basic_char_set& char_set); bool parse_QE(); bool parse_perl_extension(); bool parse_perl_verb(); bool match_verb(const char*); bool add_emacs_code(bool negate); bool unwind_alts(std::ptrdiff_t last_paren_start); digraph get_next_set_literal(basic_char_set& char_set); charT unescape_character(); regex_constants::syntax_option_type parse_options(); private: typedef bool (basic_regex_parser::*parser_proc_type)(); typedef typename traits::string_type string_type; typedef typename traits::char_class_type char_class_type; parser_proc_type m_parser_proc; // the main parser to use const charT* m_base; // the start of the string being parsed const charT* m_end; // the end of the string being parsed const charT* m_position; // our current parser position unsigned m_mark_count; // how many sub-expressions we have int m_mark_reset; // used to indicate that we're inside a (?|...) block. unsigned m_max_mark; // largest mark count seen inside a (?|...) block. std::ptrdiff_t m_paren_start; // where the last seen ')' began (where repeats are inserted). std::ptrdiff_t m_alt_insert_point; // where to insert the next alternative bool m_has_case_change; // true if somewhere in the current block the case has changed unsigned m_recursion_count; // How many times we've called parse_all. unsigned m_max_backref; // Largest index of any backref. #if defined(BOOST_REGEX_MSVC) && defined(_M_IX86) // This is an ugly warning suppression workaround (for warnings *inside* std::vector // that can not otherwise be suppressed)... static_assert(sizeof(long) >= sizeof(void*), "Long isn't long enough!"); std::vector m_alt_jumps; // list of alternative in the current scope. #else std::vector m_alt_jumps; // list of alternative in the current scope. #endif basic_regex_parser& operator=(const basic_regex_parser&); basic_regex_parser(const basic_regex_parser&); }; template basic_regex_parser::basic_regex_parser(regex_data* data) : basic_regex_creator(data), m_parser_proc(), m_base(0), m_end(0), m_position(0), m_mark_count(0), m_mark_reset(-1), m_max_mark(0), m_paren_start(0), m_alt_insert_point(0), m_has_case_change(false), m_recursion_count(0), m_max_backref(0) { } template void basic_regex_parser::parse(const charT* p1, const charT* p2, unsigned l_flags) { // pass l_flags on to base class: this->init(l_flags); // set up pointers: m_position = m_base = p1; m_end = p2; // empty strings are errors: if((p1 == p2) && ( ((l_flags & regbase::main_option_type) != regbase::perl_syntax_group) || (l_flags & regbase::no_empty_expressions) ) ) { fail(regex_constants::error_empty, 0); return; } // select which parser to use: switch(l_flags & regbase::main_option_type) { case regbase::perl_syntax_group: { m_parser_proc = &basic_regex_parser::parse_extended; // // Add a leading paren with index zero to give recursions a target: // re_brace* br = static_cast(this->append_state(syntax_element_startmark, sizeof(re_brace))); br->index = 0; br->icase = this->flags() & regbase::icase; break; } case regbase::basic_syntax_group: m_parser_proc = &basic_regex_parser::parse_basic; break; case regbase::literal: m_parser_proc = &basic_regex_parser::parse_literal; break; default: // Oops, someone has managed to set more than one of the main option flags, // so this must be an error: fail(regex_constants::error_unknown, 0, "An invalid combination of regular expression syntax flags was used."); return; } // parse all our characters: bool result = parse_all(); // // Unwind our alternatives: // unwind_alts(-1); // reset l_flags as a global scope (?imsx) may have altered them: this->flags(l_flags); // if we haven't gobbled up all the characters then we must // have had an unexpected ')' : if(!result) { fail(regex_constants::error_paren, std::distance(m_base, m_position), "Found a closing ) with no corresponding opening parenthesis."); return; } // if an error has been set then give up now: if(this->m_pdata->m_status) return; // fill in our sub-expression count: this->m_pdata->m_mark_count = 1u + (std::size_t)m_mark_count; // // Check we don't have backreferences to sub-expressions which don't exist: // if (m_max_backref > m_mark_count) { fail(regex_constants::error_backref, std::distance(m_base, m_position), "Found a backreference to a non-existant sub-expression."); } this->finalize(p1, p2); } template void basic_regex_parser::fail(regex_constants::error_type error_code, std::ptrdiff_t position) { // get the error message: std::string message = this->m_pdata->m_ptraits->error_string(error_code); fail(error_code, position, message); } template void basic_regex_parser::fail(regex_constants::error_type error_code, std::ptrdiff_t position, std::string message, std::ptrdiff_t start_pos) { if(0 == this->m_pdata->m_status) // update the error code if not already set this->m_pdata->m_status = error_code; m_position = m_end; // don't bother parsing anything else // // Augment error message with the regular expression text: // if(start_pos == position) start_pos = (std::max)(static_cast(0), position - static_cast(10)); std::ptrdiff_t end_pos = (std::min)(position + static_cast(10), static_cast(m_end - m_base)); if(error_code != regex_constants::error_empty) { if((start_pos != 0) || (end_pos != (m_end - m_base))) message += " The error occurred while parsing the regular expression fragment: '"; else message += " The error occurred while parsing the regular expression: '"; if(start_pos != end_pos) { message += std::string(m_base + start_pos, m_base + position); message += ">>>HERE>>>"; message += std::string(m_base + position, m_base + end_pos); } message += "'."; } #ifndef BOOST_NO_EXCEPTIONS if(0 == (this->flags() & regex_constants::no_except)) { boost::regex_error e(message, error_code, position); e.raise(); } #else (void)position; // suppress warnings. #endif } template bool basic_regex_parser::parse_all() { if (++m_recursion_count > 400) { // exceeded internal limits fail(boost::regex_constants::error_complexity, m_position - m_base, "Exceeded nested brace limit."); } bool result = true; while(result && (m_position != m_end)) { result = (this->*m_parser_proc)(); } --m_recursion_count; return result; } #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4702) #endif template bool basic_regex_parser::parse_basic() { switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_escape: return parse_basic_escape(); case regex_constants::syntax_dot: return parse_match_any(); case regex_constants::syntax_caret: ++m_position; this->append_state(syntax_element_start_line); break; case regex_constants::syntax_dollar: ++m_position; this->append_state(syntax_element_end_line); break; case regex_constants::syntax_star: if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line)) return parse_literal(); else { ++m_position; return parse_repeat(); } case regex_constants::syntax_plus: if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line) || !(this->flags() & regbase::emacs_ex)) return parse_literal(); else { ++m_position; return parse_repeat(1); } case regex_constants::syntax_question: if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line) || !(this->flags() & regbase::emacs_ex)) return parse_literal(); else { ++m_position; return parse_repeat(0, 1); } case regex_constants::syntax_open_set: return parse_set(); case regex_constants::syntax_newline: if(this->flags() & regbase::newline_alt) return parse_alt(); else return parse_literal(); default: return parse_literal(); } return true; } #ifdef BOOST_REGEX_MSVC # pragma warning(push) #if BOOST_REGEX_MSVC >= 1800 #pragma warning(disable:26812) #endif #endif template bool basic_regex_parser::parse_extended() { bool result = true; switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_open_mark: return parse_open_paren(); case regex_constants::syntax_close_mark: return false; case regex_constants::syntax_escape: return parse_extended_escape(); case regex_constants::syntax_dot: return parse_match_any(); case regex_constants::syntax_caret: ++m_position; this->append_state( (this->flags() & regex_constants::no_mod_m ? syntax_element_buffer_start : syntax_element_start_line)); break; case regex_constants::syntax_dollar: ++m_position; this->append_state( (this->flags() & regex_constants::no_mod_m ? syntax_element_buffer_end : syntax_element_end_line)); break; case regex_constants::syntax_star: if(m_position == this->m_base) { fail(regex_constants::error_badrepeat, 0, "The repeat operator \"*\" cannot start a regular expression."); return false; } ++m_position; return parse_repeat(); case regex_constants::syntax_question: if(m_position == this->m_base) { fail(regex_constants::error_badrepeat, 0, "The repeat operator \"?\" cannot start a regular expression."); return false; } ++m_position; return parse_repeat(0,1); case regex_constants::syntax_plus: if(m_position == this->m_base) { fail(regex_constants::error_badrepeat, 0, "The repeat operator \"+\" cannot start a regular expression."); return false; } ++m_position; return parse_repeat(1); case regex_constants::syntax_open_brace: ++m_position; return parse_repeat_range(false); case regex_constants::syntax_close_brace: if((this->flags() & regbase::no_perl_ex) == regbase::no_perl_ex) { fail(regex_constants::error_brace, this->m_position - this->m_base, "Found a closing repetition operator } with no corresponding {."); return false; } result = parse_literal(); break; case regex_constants::syntax_or: return parse_alt(); case regex_constants::syntax_open_set: return parse_set(); case regex_constants::syntax_newline: if(this->flags() & regbase::newline_alt) return parse_alt(); else return parse_literal(); case regex_constants::syntax_hash: // // If we have a mod_x flag set, then skip until // we get to a newline character: // if((this->flags() & (regbase::no_perl_ex|regbase::mod_x)) == regbase::mod_x) { while((m_position != m_end) && !is_separator(*m_position++)){} return true; } BOOST_REGEX_FALLTHROUGH; default: result = parse_literal(); break; } return result; } #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif template bool basic_regex_parser::parse_literal() { // append this as a literal provided it's not a space character // or the perl option regbase::mod_x is not set: if( ((this->flags() & (regbase::main_option_type|regbase::mod_x|regbase::no_perl_ex)) != regbase::mod_x) || !this->m_traits.isctype(*m_position, this->m_mask_space)) this->append_literal(*m_position); ++m_position; return true; } template bool basic_regex_parser::parse_open_paren() { // // skip the '(' and error check: // if(++m_position == m_end) { fail(regex_constants::error_paren, m_position - m_base); return false; } // // begin by checking for a perl-style (?...) extension: // if( ((this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) == 0) || ((this->flags() & (regbase::main_option_type | regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex)) ) { if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_question) return parse_perl_extension(); if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_star) return parse_perl_verb(); } // // update our mark count, and append the required state: // unsigned markid = 0; if(0 == (this->flags() & regbase::nosubs)) { markid = ++m_mark_count; if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.push_back(std::pair(std::distance(m_base, m_position) - 1, 0)); } re_brace* pb = static_cast(this->append_state(syntax_element_startmark, sizeof(re_brace))); pb->index = markid; pb->icase = this->flags() & regbase::icase; std::ptrdiff_t last_paren_start = this->getoffset(pb); // back up insertion point for alternations, and set new point: std::ptrdiff_t last_alt_point = m_alt_insert_point; this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); // // back up the current flags in case we have a nested (?imsx) group: // regex_constants::syntax_option_type opts = this->flags(); bool old_case_change = m_has_case_change; m_has_case_change = false; // no changes to this scope as yet... // // Back up branch reset data in case we have a nested (?|...) // int mark_reset = m_mark_reset; m_mark_reset = -1; // // now recursively add more states, this will terminate when we get to a // matching ')' : // parse_all(); // // Unwind pushed alternatives: // if(0 == unwind_alts(last_paren_start)) return false; // // restore flags: // if(m_has_case_change) { // the case has changed in one or more of the alternatives // within the scoped (...) block: we have to add a state // to reset the case sensitivity: static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = opts & regbase::icase; } this->flags(opts); m_has_case_change = old_case_change; // // restore branch reset: // m_mark_reset = mark_reset; // // we either have a ')' or we have run out of characters prematurely: // if(m_position == m_end) { this->fail(regex_constants::error_paren, std::distance(m_base, m_end)); return false; } if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) return false; if(markid && (this->flags() & regbase::save_subexpression_location)) this->m_pdata->m_subs.at(markid - 1).second = std::distance(m_base, m_position); ++m_position; // // append closing parenthesis state: // pb = static_cast(this->append_state(syntax_element_endmark, sizeof(re_brace))); pb->index = markid; pb->icase = this->flags() & regbase::icase; this->m_paren_start = last_paren_start; // // restore the alternate insertion point: // this->m_alt_insert_point = last_alt_point; return true; } template bool basic_regex_parser::parse_basic_escape() { if(++m_position == m_end) { fail(regex_constants::error_paren, m_position - m_base); return false; } bool result = true; switch(this->m_traits.escape_syntax_type(*m_position)) { case regex_constants::syntax_open_mark: return parse_open_paren(); case regex_constants::syntax_close_mark: return false; case regex_constants::syntax_plus: if(this->flags() & regex_constants::bk_plus_qm) { ++m_position; return parse_repeat(1); } else return parse_literal(); case regex_constants::syntax_question: if(this->flags() & regex_constants::bk_plus_qm) { ++m_position; return parse_repeat(0, 1); } else return parse_literal(); case regex_constants::syntax_open_brace: if(this->flags() & regbase::no_intervals) return parse_literal(); ++m_position; return parse_repeat_range(true); case regex_constants::syntax_close_brace: if(this->flags() & regbase::no_intervals) return parse_literal(); fail(regex_constants::error_brace, this->m_position - this->m_base, "Found a closing repetition operator } with no corresponding {."); return false; case regex_constants::syntax_or: if(this->flags() & regbase::bk_vbar) return parse_alt(); else result = parse_literal(); break; case regex_constants::syntax_digit: return parse_backref(); case regex_constants::escape_type_start_buffer: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_buffer_start); } else result = parse_literal(); break; case regex_constants::escape_type_end_buffer: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_buffer_end); } else result = parse_literal(); break; case regex_constants::escape_type_word_assert: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_word_boundary); } else result = parse_literal(); break; case regex_constants::escape_type_not_word_assert: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_within_word); } else result = parse_literal(); break; case regex_constants::escape_type_left_word: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_word_start); } else result = parse_literal(); break; case regex_constants::escape_type_right_word: if(this->flags() & regbase::emacs_ex) { ++m_position; this->append_state(syntax_element_word_end); } else result = parse_literal(); break; default: if(this->flags() & regbase::emacs_ex) { bool negate = true; switch(*m_position) { case 'w': negate = false; BOOST_REGEX_FALLTHROUGH; case 'W': { basic_char_set char_set; if(negate) char_set.negate(); char_set.add_class(this->m_word_mask); if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } ++m_position; return true; } case 's': negate = false; BOOST_REGEX_FALLTHROUGH; case 'S': return add_emacs_code(negate); case 'c': case 'C': // not supported yet: fail(regex_constants::error_escape, m_position - m_base, "The \\c and \\C escape sequences are not supported by POSIX basic regular expressions: try the Perl syntax instead."); return false; default: break; } } result = parse_literal(); break; } return result; } template bool basic_regex_parser::parse_extended_escape() { ++m_position; if(m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, "Incomplete escape sequence found."); return false; } bool negate = false; // in case this is a character class escape: \w \d etc switch(this->m_traits.escape_syntax_type(*m_position)) { case regex_constants::escape_type_not_class: negate = true; BOOST_REGEX_FALLTHROUGH; case regex_constants::escape_type_class: { escape_type_class_jump: typedef typename traits::char_class_type m_type; m_type m = this->m_traits.lookup_classname(m_position, m_position+1); if(m != 0) { basic_char_set char_set; if(negate) char_set.negate(); char_set.add_class(m); if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } ++m_position; return true; } // // not a class, just a regular unknown escape: // this->append_literal(unescape_character()); break; } case regex_constants::syntax_digit: return parse_backref(); case regex_constants::escape_type_left_word: ++m_position; this->append_state(syntax_element_word_start); break; case regex_constants::escape_type_right_word: ++m_position; this->append_state(syntax_element_word_end); break; case regex_constants::escape_type_start_buffer: ++m_position; this->append_state(syntax_element_buffer_start); break; case regex_constants::escape_type_end_buffer: ++m_position; this->append_state(syntax_element_buffer_end); break; case regex_constants::escape_type_word_assert: ++m_position; this->append_state(syntax_element_word_boundary); break; case regex_constants::escape_type_not_word_assert: ++m_position; this->append_state(syntax_element_within_word); break; case regex_constants::escape_type_Z: ++m_position; this->append_state(syntax_element_soft_buffer_end); break; case regex_constants::escape_type_Q: return parse_QE(); case regex_constants::escape_type_C: return parse_match_any(); case regex_constants::escape_type_X: ++m_position; this->append_state(syntax_element_combining); break; case regex_constants::escape_type_G: ++m_position; this->append_state(syntax_element_restart_continue); break; case regex_constants::escape_type_not_property: negate = true; BOOST_REGEX_FALLTHROUGH; case regex_constants::escape_type_property: { ++m_position; char_class_type m; if(m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, "Incomplete property escape found."); return false; } // maybe have \p{ddd} if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace) { const charT* base = m_position; // skip forward until we find enclosing brace: while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace)) ++m_position; if(m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, "Closing } missing from property escape sequence."); return false; } m = this->m_traits.lookup_classname(++base, m_position++); } else { m = this->m_traits.lookup_classname(m_position, m_position+1); ++m_position; } if(m != 0) { basic_char_set char_set; if(negate) char_set.negate(); char_set.add_class(m); if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } return true; } fail(regex_constants::error_ctype, m_position - m_base, "Escape sequence was neither a valid property nor a valid character class name."); return false; } case regex_constants::escape_type_reset_start_mark: if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) { re_brace* pb = static_cast(this->append_state(syntax_element_startmark, sizeof(re_brace))); pb->index = -5; pb->icase = this->flags() & regbase::icase; this->m_pdata->m_data.align(); ++m_position; return true; } goto escape_type_class_jump; case regex_constants::escape_type_line_ending: if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) { const charT* e = get_escape_R_string(); const charT* old_position = m_position; const charT* old_end = m_end; const charT* old_base = m_base; m_position = e; m_base = e; m_end = e + traits::length(e); bool r = parse_all(); m_position = ++old_position; m_end = old_end; m_base = old_base; return r; } goto escape_type_class_jump; case regex_constants::escape_type_extended_backref: if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) { bool have_brace = false; bool negative = false; static const char incomplete_message[] = "Incomplete \\g escape found."; if(++m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, incomplete_message); return false; } // maybe have \g{ddd} regex_constants::syntax_type syn = this->m_traits.syntax_type(*m_position); regex_constants::syntax_type syn_end = 0; if((syn == regex_constants::syntax_open_brace) || (syn == regex_constants::escape_type_left_word) || (syn == regex_constants::escape_type_end_buffer)) { if(++m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, incomplete_message); return false; } have_brace = true; switch(syn) { case regex_constants::syntax_open_brace: syn_end = regex_constants::syntax_close_brace; break; case regex_constants::escape_type_left_word: syn_end = regex_constants::escape_type_right_word; break; default: syn_end = regex_constants::escape_type_end_buffer; break; } } negative = (*m_position == static_cast('-')); if((negative) && (++m_position == m_end)) { fail(regex_constants::error_escape, m_position - m_base, incomplete_message); return false; } const charT* pc = m_position; std::intmax_t i = this->m_traits.toi(pc, m_end, 10); if((i < 0) && syn_end) { // Check for a named capture, get the leftmost one if there is more than one: const charT* base = m_position; while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != syn_end)) { ++m_position; } i = hash_value_from_capture_name(base, m_position); pc = m_position; } if(negative) i = 1 + (static_cast(m_mark_count) - i); if(((i < hash_value_mask) && (i > 0)) || ((i >= hash_value_mask) && (this->m_pdata->get_id((int)i) > 0))) { m_position = pc; re_brace* pb = static_cast(this->append_state(syntax_element_backref, sizeof(re_brace))); pb->index = (int)i; pb->icase = this->flags() & regbase::icase; if ((i > m_max_backref) && (i < hash_value_mask)) m_max_backref = i; } else { fail(regex_constants::error_backref, m_position - m_base); return false; } m_position = pc; if(have_brace) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != syn_end)) { fail(regex_constants::error_escape, m_position - m_base, incomplete_message); return false; } ++m_position; } return true; } goto escape_type_class_jump; case regex_constants::escape_type_control_v: if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) goto escape_type_class_jump; BOOST_REGEX_FALLTHROUGH; default: this->append_literal(unescape_character()); break; } return true; } template bool basic_regex_parser::parse_match_any() { // // we have a '.' that can match any character: // ++m_position; static_cast( this->append_state(syntax_element_wild, sizeof(re_dot)) )->mask = static_cast(this->flags() & regbase::no_mod_s ? BOOST_REGEX_DETAIL_NS::force_not_newline : this->flags() & regbase::mod_s ? BOOST_REGEX_DETAIL_NS::force_newline : BOOST_REGEX_DETAIL_NS::dont_care); return true; } template bool basic_regex_parser::parse_repeat(std::size_t low, std::size_t high) { bool greedy = true; bool possessive = false; std::size_t insert_point; // // when we get to here we may have a non-greedy ? mark still to come: // if((m_position != m_end) && ( (0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex))) || ((regbase::basic_syntax_group|regbase::emacs_ex) == (this->flags() & (regbase::main_option_type | regbase::emacs_ex))) ) ) { // OK we have a perl or emacs regex, check for a '?': if ((this->flags() & (regbase::main_option_type | regbase::mod_x | regbase::no_perl_ex)) == regbase::mod_x) { // whitespace skip: while ((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; } if((m_position != m_end) && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_question)) { greedy = false; ++m_position; } // for perl regexes only check for possessive ++ repeats. if((m_position != m_end) && (0 == (this->flags() & regbase::main_option_type)) && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_plus)) { possessive = true; ++m_position; } } if(0 == this->m_last_state) { fail(regex_constants::error_badrepeat, std::distance(m_base, m_position), "Nothing to repeat."); return false; } if(this->m_last_state->type == syntax_element_endmark) { // insert a repeat before the '(' matching the last ')': insert_point = this->m_paren_start; } else if((this->m_last_state->type == syntax_element_literal) && (static_cast(this->m_last_state)->length > 1)) { // the last state was a literal with more than one character, split it in two: re_literal* lit = static_cast(this->m_last_state); charT c = (static_cast(static_cast(lit+1)))[lit->length - 1]; lit->length -= 1; // now append new state: lit = static_cast(this->append_state(syntax_element_literal, sizeof(re_literal) + sizeof(charT))); lit->length = 1; (static_cast(static_cast(lit+1)))[0] = c; insert_point = this->getoffset(this->m_last_state); } else { // repeat the last state whatever it was, need to add some error checking here: switch(this->m_last_state->type) { case syntax_element_start_line: case syntax_element_end_line: case syntax_element_word_boundary: case syntax_element_within_word: case syntax_element_word_start: case syntax_element_word_end: case syntax_element_buffer_start: case syntax_element_buffer_end: case syntax_element_alt: case syntax_element_soft_buffer_end: case syntax_element_restart_continue: case syntax_element_jump: case syntax_element_startmark: case syntax_element_backstep: case syntax_element_toggle_case: // can't legally repeat any of the above: fail(regex_constants::error_badrepeat, m_position - m_base); return false; default: // do nothing... break; } insert_point = this->getoffset(this->m_last_state); } // // OK we now know what to repeat, so insert the repeat around it: // re_repeat* rep = static_cast(this->insert_state(insert_point, syntax_element_rep, re_repeater_size)); rep->min = low; rep->max = high; rep->greedy = greedy; rep->leading = false; // store our repeater position for later: std::ptrdiff_t rep_off = this->getoffset(rep); // and append a back jump to the repeat: re_jump* jmp = static_cast(this->append_state(syntax_element_jump, sizeof(re_jump))); jmp->alt.i = rep_off - this->getoffset(jmp); this->m_pdata->m_data.align(); // now fill in the alt jump for the repeat: rep = static_cast(this->getaddress(rep_off)); rep->alt.i = this->m_pdata->m_data.size() - rep_off; // // If the repeat is possessive then bracket the repeat with a (?>...) // independent sub-expression construct: // if(possessive) { if(m_position != m_end) { // // Check for illegal following quantifier, we have to do this here, because // the extra states we insert below circumvents our usual error checking :-( // bool contin = false; do { if ((this->flags() & (regbase::main_option_type | regbase::mod_x | regbase::no_perl_ex)) == regbase::mod_x) { // whitespace skip: while ((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; } if (m_position != m_end) { switch (this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_star: case regex_constants::syntax_plus: case regex_constants::syntax_question: case regex_constants::syntax_open_brace: fail(regex_constants::error_badrepeat, m_position - m_base); return false; case regex_constants::syntax_open_mark: // Do we have a comment? If so we need to skip it here... if ((m_position + 2 < m_end) && this->m_traits.syntax_type(*(m_position + 1)) == regex_constants::syntax_question && this->m_traits.syntax_type(*(m_position + 2)) == regex_constants::syntax_hash) { while ((m_position != m_end) && (this->m_traits.syntax_type(*m_position++) != regex_constants::syntax_close_mark)) { } contin = true; } else contin = false; break; default: contin = false; } } else contin = false; } while (contin); } re_brace* pb = static_cast(this->insert_state(insert_point, syntax_element_startmark, sizeof(re_brace))); pb->index = -3; pb->icase = this->flags() & regbase::icase; jmp = static_cast(this->insert_state(insert_point + sizeof(re_brace), syntax_element_jump, sizeof(re_jump))); this->m_pdata->m_data.align(); jmp->alt.i = this->m_pdata->m_data.size() - this->getoffset(jmp); pb = static_cast(this->append_state(syntax_element_endmark, sizeof(re_brace))); pb->index = -3; pb->icase = this->flags() & regbase::icase; } return true; } template bool basic_regex_parser::parse_repeat_range(bool isbasic) { static const char incomplete_message[] = "Missing } in quantified repetition."; // // parse a repeat-range: // std::size_t min, max; std::intmax_t v; // skip whitespace: while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; if(this->m_position == this->m_end) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } // get min: v = this->m_traits.toi(m_position, m_end, 10); // skip whitespace: if((v < 0) || (v > umax())) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; if(this->m_position == this->m_end) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } min = static_cast(v); // see if we have a comma: if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_comma) { // move on and error check: ++m_position; // skip whitespace: while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; if(this->m_position == this->m_end) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } // get the value if any: v = this->m_traits.toi(m_position, m_end, 10); max = ((v >= 0) && (v < umax())) ? (std::size_t)v : (std::numeric_limits::max)(); } else { // no comma, max = min: max = min; } // skip whitespace: while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space)) ++m_position; // OK now check trailing }: if(this->m_position == this->m_end) { if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } if(isbasic) { if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_escape) { ++m_position; if(this->m_position == this->m_end) { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } } else { fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message); return false; } } if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_brace) ++m_position; else { // Treat the opening '{' as a literal character, rewind to start of error: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position; return parse_literal(); } // // finally go and add the repeat, unless error: // if(min > max) { // Backtrack to error location: m_position -= 2; while(this->m_traits.isctype(*m_position, this->m_word_mask)) --m_position; ++m_position; fail(regex_constants::error_badbrace, m_position - m_base); return false; } return parse_repeat(min, max); } template bool basic_regex_parser::parse_alt() { // // error check: if there have been no previous states, // or if the last state was a '(' then error: // if( ((this->m_last_state == 0) || (this->m_last_state->type == syntax_element_startmark)) && !( ((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group) && ((this->flags() & regbase::no_empty_expressions) == 0) ) ) { fail(regex_constants::error_empty, this->m_position - this->m_base, "A regular expression cannot start with the alternation operator |."); return false; } // // Reset mark count if required: // if(m_max_mark < m_mark_count) m_max_mark = m_mark_count; if(m_mark_reset >= 0) m_mark_count = m_mark_reset; ++m_position; // // we need to append a trailing jump: // re_syntax_base* pj = this->append_state(BOOST_REGEX_DETAIL_NS::syntax_element_jump, sizeof(re_jump)); std::ptrdiff_t jump_offset = this->getoffset(pj); // // now insert the alternative: // re_alt* palt = static_cast(this->insert_state(this->m_alt_insert_point, syntax_element_alt, re_alt_size)); jump_offset += re_alt_size; this->m_pdata->m_data.align(); palt->alt.i = this->m_pdata->m_data.size() - this->getoffset(palt); // // update m_alt_insert_point so that the next alternate gets // inserted at the start of the second of the two we've just created: // this->m_alt_insert_point = this->m_pdata->m_data.size(); // // the start of this alternative must have a case changes state // if the current block has messed around with case changes: // if(m_has_case_change) { static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = this->m_icase; } // // push the alternative onto our stack, a recursive // implementation here is easier to understand (and faster // as it happens), but causes all kinds of stack overflow problems // on programs with small stacks (COM+). // m_alt_jumps.push_back(jump_offset); return true; } template bool basic_regex_parser::parse_set() { static const char incomplete_message[] = "Character set declaration starting with [ terminated prematurely - either no ] was found or the set had no content."; ++m_position; if(m_position == m_end) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } basic_char_set char_set; const charT* base = m_position; // where the '[' was const charT* item_base = m_position; // where the '[' or '^' was while(m_position != m_end) { switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_caret: if(m_position == base) { char_set.negate(); ++m_position; item_base = m_position; } else parse_set_literal(char_set); break; case regex_constants::syntax_close_set: if(m_position == item_base) { parse_set_literal(char_set); break; } else { ++m_position; if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } } return true; case regex_constants::syntax_open_set: if(parse_inner_set(char_set)) break; return true; case regex_constants::syntax_escape: { // // look ahead and see if this is a character class shortcut // \d \w \s etc... // ++m_position; if(this->m_traits.escape_syntax_type(*m_position) == regex_constants::escape_type_class) { char_class_type m = this->m_traits.lookup_classname(m_position, m_position+1); if(m != 0) { char_set.add_class(m); ++m_position; break; } } else if(this->m_traits.escape_syntax_type(*m_position) == regex_constants::escape_type_not_class) { // negated character class: char_class_type m = this->m_traits.lookup_classname(m_position, m_position+1); if(m != 0) { char_set.add_negated_class(m); ++m_position; break; } } // not a character class, just a regular escape: --m_position; parse_set_literal(char_set); break; } default: parse_set_literal(char_set); break; } } return m_position != m_end; } template bool basic_regex_parser::parse_inner_set(basic_char_set& char_set) { static const char incomplete_message[] = "Character class declaration starting with [ terminated prematurely - either no ] was found or the set had no content."; // // we have either a character class [:name:] // a collating element [.name.] // or an equivalence class [=name=] // if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_dot: // // a collating element is treated as a literal: // --m_position; parse_set_literal(char_set); return true; case regex_constants::syntax_colon: { // check that character classes are actually enabled: if((this->flags() & (regbase::main_option_type | regbase::no_char_classes)) == (regbase::basic_syntax_group | regbase::no_char_classes)) { --m_position; parse_set_literal(char_set); return true; } // skip the ':' if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } const charT* name_first = m_position; // skip at least one character, then find the matching ':]' if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_colon)) ++m_position; const charT* name_last = m_position; if(m_end == m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } if((m_end == ++m_position) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } // // check for negated class: // bool negated = false; if(this->m_traits.syntax_type(*name_first) == regex_constants::syntax_caret) { ++name_first; negated = true; } typedef typename traits::char_class_type m_type; m_type m = this->m_traits.lookup_classname(name_first, name_last); if(m == 0) { if(char_set.empty() && (name_last - name_first == 1)) { // maybe a special case: ++m_position; if( (m_position != m_end) && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_set)) { if(this->m_traits.escape_syntax_type(*name_first) == regex_constants::escape_type_left_word) { ++m_position; this->append_state(syntax_element_word_start); return false; } if(this->m_traits.escape_syntax_type(*name_first) == regex_constants::escape_type_right_word) { ++m_position; this->append_state(syntax_element_word_end); return false; } } } fail(regex_constants::error_ctype, name_first - m_base); return false; } if(!negated) char_set.add_class(m); else char_set.add_negated_class(m); ++m_position; break; } case regex_constants::syntax_equal: { // skip the '=' if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } const charT* name_first = m_position; // skip at least one character, then find the matching '=]' if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal)) ++m_position; const charT* name_last = m_position; if(m_end == m_position) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } if((m_end == ++m_position) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)) { fail(regex_constants::error_brack, m_position - m_base, incomplete_message); return false; } string_type m = this->m_traits.lookup_collatename(name_first, name_last); if(m.empty() || (m.size() > 2)) { fail(regex_constants::error_collate, name_first - m_base); return false; } digraph d; d.first = m[0]; if(m.size() > 1) d.second = m[1]; else d.second = 0; char_set.add_equivalent(d); ++m_position; break; } default: --m_position; parse_set_literal(char_set); break; } return true; } template void basic_regex_parser::parse_set_literal(basic_char_set& char_set) { digraph start_range(get_next_set_literal(char_set)); if(m_end == m_position) { fail(regex_constants::error_brack, m_position - m_base); return; } if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_dash) { // we have a range: if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base); return; } if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set) { digraph end_range = get_next_set_literal(char_set); char_set.add_range(start_range, end_range); if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_dash) { if(m_end == ++m_position) { fail(regex_constants::error_brack, m_position - m_base); return; } if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_set) { // trailing - : --m_position; return; } fail(regex_constants::error_range, m_position - m_base); return; } return; } --m_position; } char_set.add_single(start_range); } template digraph basic_regex_parser::get_next_set_literal(basic_char_set& char_set) { digraph result; switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_dash: if(!char_set.empty()) { // see if we are at the end of the set: if((++m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)) { fail(regex_constants::error_range, m_position - m_base); return result; } --m_position; } result.first = *m_position++; return result; case regex_constants::syntax_escape: // check to see if escapes are supported first: if(this->flags() & regex_constants::no_escape_in_lists) { result = *m_position++; break; } ++m_position; result = unescape_character(); break; case regex_constants::syntax_open_set: { if(m_end == ++m_position) { fail(regex_constants::error_collate, m_position - m_base); return result; } if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_dot) { --m_position; result.first = *m_position; ++m_position; return result; } if(m_end == ++m_position) { fail(regex_constants::error_collate, m_position - m_base); return result; } const charT* name_first = m_position; // skip at least one character, then find the matching ':]' if(m_end == ++m_position) { fail(regex_constants::error_collate, name_first - m_base); return result; } while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_dot)) ++m_position; const charT* name_last = m_position; if(m_end == m_position) { fail(regex_constants::error_collate, name_first - m_base); return result; } if((m_end == ++m_position) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)) { fail(regex_constants::error_collate, name_first - m_base); return result; } ++m_position; string_type s = this->m_traits.lookup_collatename(name_first, name_last); if(s.empty() || (s.size() > 2)) { fail(regex_constants::error_collate, name_first - m_base); return result; } result.first = s[0]; if(s.size() > 1) result.second = s[1]; else result.second = 0; return result; } default: result = *m_position++; } return result; } // // does a value fit in the specified charT type? // template bool valid_value(charT, std::intmax_t v, const std::integral_constant&) { return (v >> (sizeof(charT) * CHAR_BIT)) == 0; } template bool valid_value(charT, std::intmax_t, const std::integral_constant&) { return true; // v will alsways fit in a charT } template bool valid_value(charT c, std::intmax_t v) { return valid_value(c, v, std::integral_constant()); } template charT basic_regex_parser::unescape_character() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif charT result(0); if(m_position == m_end) { fail(regex_constants::error_escape, m_position - m_base, "Escape sequence terminated prematurely."); return false; } switch(this->m_traits.escape_syntax_type(*m_position)) { case regex_constants::escape_type_control_a: result = charT('\a'); break; case regex_constants::escape_type_e: result = charT(27); break; case regex_constants::escape_type_control_f: result = charT('\f'); break; case regex_constants::escape_type_control_n: result = charT('\n'); break; case regex_constants::escape_type_control_r: result = charT('\r'); break; case regex_constants::escape_type_control_t: result = charT('\t'); break; case regex_constants::escape_type_control_v: result = charT('\v'); break; case regex_constants::escape_type_word_assert: result = charT('\b'); break; case regex_constants::escape_type_ascii_control: ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "ASCII escape sequence terminated prematurely."); return result; } result = static_cast(*m_position % 32); break; case regex_constants::escape_type_hex: ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "Hexadecimal escape sequence terminated prematurely."); return result; } // maybe have \x{ddd} if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace) { ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "Missing } in hexadecimal escape sequence."); return result; } std::intmax_t i = this->m_traits.toi(m_position, m_end, 16); if((m_position == m_end) || (i < 0) || ((std::numeric_limits::is_specialized) && (i > (std::intmax_t)(std::numeric_limits::max)())) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace)) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_badbrace, m_position - m_base, "Hexadecimal escape sequence was invalid."); return result; } ++m_position; result = charT(i); } else { std::ptrdiff_t len = (std::min)(static_cast(2), static_cast(m_end - m_position)); std::intmax_t i = this->m_traits.toi(m_position, m_position + len, 16); if((i < 0) || !valid_value(charT(0), i)) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "Escape sequence did not encode a valid character."); return result; } result = charT(i); } return result; case regex_constants::syntax_digit: { // an octal escape sequence, the first character must be a zero // followed by up to 3 octal digits: std::ptrdiff_t len = (std::min)(std::distance(m_position, m_end), static_cast(4)); const charT* bp = m_position; std::intmax_t val = this->m_traits.toi(bp, bp + 1, 8); if(val != 0) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; // Oops not an octal escape after all: fail(regex_constants::error_escape, m_position - m_base, "Invalid octal escape sequence."); return result; } val = this->m_traits.toi(m_position, m_position + len, 8); if((val < 0) || (val > (std::intmax_t)(std::numeric_limits::max)())) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base, "Octal escape sequence is invalid."); return result; } return static_cast(val); } case regex_constants::escape_type_named_char: { ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base); return false; } // maybe have \N{name} if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace) { const charT* base = m_position; // skip forward until we find enclosing brace: while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace)) ++m_position; if(m_position == m_end) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base); return false; } string_type s = this->m_traits.lookup_collatename(++base, m_position++); if(s.empty()) { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_collate, m_position - m_base); return false; } if(s.size() == 1) { return s[0]; } } // fall through is a failure: // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base); return false; } default: result = *m_position; break; } ++m_position; return result; #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template bool basic_regex_parser::parse_backref() { BOOST_REGEX_ASSERT(m_position != m_end); const charT* pc = m_position; std::intmax_t i = this->m_traits.toi(pc, pc + 1, 10); if((i == 0) || (((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group) && (this->flags() & regbase::no_bk_refs))) { // not a backref at all but an octal escape sequence: charT c = unescape_character(); this->append_literal(c); } else if((i > 0)) { m_position = pc; re_brace* pb = static_cast(this->append_state(syntax_element_backref, sizeof(re_brace))); pb->index = (int)i; pb->icase = this->flags() & regbase::icase; if(i > m_max_backref) m_max_backref = i; } else { // Rewind to start of escape: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_backref, m_position - m_base); return false; } return true; } template bool basic_regex_parser::parse_QE() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif // // parse a \Q...\E sequence: // ++m_position; // skip the Q const charT* start = m_position; const charT* end; do { while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape)) ++m_position; if(m_position == m_end) { // a \Q...\E sequence may terminate with the end of the expression: end = m_position; break; } if(++m_position == m_end) // skip the escape { fail(regex_constants::error_escape, m_position - m_base, "Unterminated \\Q...\\E sequence."); return false; } // check to see if it's a \E: if(this->m_traits.escape_syntax_type(*m_position) == regex_constants::escape_type_E) { ++m_position; end = m_position - 2; break; } // otherwise go round again: }while(true); // // now add all the character between the two escapes as literals: // while(start != end) { this->append_literal(*start); ++start; } return true; #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template bool basic_regex_parser::parse_perl_extension() { if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } // // treat comments as a special case, as these // are the only ones that don't start with a leading // startmark state: // if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_hash) { while((m_position != m_end) && (this->m_traits.syntax_type(*m_position++) != regex_constants::syntax_close_mark)) {} return true; } // // backup some state, and prepare the way: // int markid = 0; std::ptrdiff_t jump_offset = 0; re_brace* pb = static_cast(this->append_state(syntax_element_startmark, sizeof(re_brace))); pb->icase = this->flags() & regbase::icase; std::ptrdiff_t last_paren_start = this->getoffset(pb); // back up insertion point for alternations, and set new point: std::ptrdiff_t last_alt_point = m_alt_insert_point; this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); std::ptrdiff_t expected_alt_point = m_alt_insert_point; bool restore_flags = true; regex_constants::syntax_option_type old_flags = this->flags(); bool old_case_change = m_has_case_change; m_has_case_change = false; charT name_delim; int mark_reset = m_mark_reset; int max_mark = m_max_mark; m_mark_reset = -1; m_max_mark = m_mark_count; std::intmax_t v; // // select the actual extension used: // switch(this->m_traits.syntax_type(*m_position)) { case regex_constants::syntax_or: m_mark_reset = m_mark_count; BOOST_REGEX_FALLTHROUGH; case regex_constants::syntax_colon: // // a non-capturing mark: // pb->index = markid = 0; ++m_position; break; case regex_constants::syntax_digit: { // // a recursive subexpression: // v = this->m_traits.toi(m_position, m_end, 10); if((v < 0) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "The recursive sub-expression refers to an invalid marking group, or is unterminated."); return false; } insert_recursion: pb->index = markid = 0; re_recurse* pr = static_cast(this->append_state(syntax_element_recurse, sizeof(re_recurse))); pr->alt.i = (std::ptrdiff_t)v; pr->state_id = 0; static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = this->flags() & regbase::icase; break; } case regex_constants::syntax_plus: // // A forward-relative recursive subexpression: // ++m_position; v = this->m_traits.toi(m_position, m_end, 10); if((v <= 0) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "An invalid or unterminated recursive sub-expression."); return false; } if ((std::numeric_limits::max)() - m_mark_count < v) { fail(regex_constants::error_perl_extension, m_position - m_base, "An invalid or unterminated recursive sub-expression."); return false; } v += m_mark_count; goto insert_recursion; case regex_constants::syntax_dash: // // Possibly a backward-relative recursive subexpression: // ++m_position; v = this->m_traits.toi(m_position, m_end, 10); if(v <= 0) { --m_position; // Oops not a relative recursion at all, but a (?-imsx) group: goto option_group_jump; } v = static_cast(m_mark_count) + 1 - v; if(v <= 0) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "An invalid or unterminated recursive sub-expression."); return false; } goto insert_recursion; case regex_constants::syntax_equal: pb->index = markid = -1; ++m_position; jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump))); this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); break; case regex_constants::syntax_not: pb->index = markid = -2; ++m_position; jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump))); this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); break; case regex_constants::escape_type_left_word: { // a lookbehind assertion: if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } regex_constants::syntax_type t = this->m_traits.syntax_type(*m_position); if(t == regex_constants::syntax_not) pb->index = markid = -2; else if(t == regex_constants::syntax_equal) pb->index = markid = -1; else { // Probably a named capture which also starts (?< : name_delim = '>'; --m_position; goto named_capture_jump; } ++m_position; jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump))); this->append_state(syntax_element_backstep, sizeof(re_brace)); this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); break; } case regex_constants::escape_type_right_word: // // an independent sub-expression: // pb->index = markid = -3; ++m_position; jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump))); this->m_pdata->m_data.align(); m_alt_insert_point = this->m_pdata->m_data.size(); break; case regex_constants::syntax_open_mark: { // a conditional expression: pb->index = markid = -4; if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = this->m_traits.toi(m_position, m_end, 10); if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(*m_position == charT('R')) { if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(*m_position == charT('&')) { const charT* base = ++m_position; while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = -static_cast(hash_value_from_capture_name(base, m_position)); } else { v = -this->m_traits.toi(m_position, m_end, 10); } re_brace* br = static_cast(this->append_state(syntax_element_assert_backref, sizeof(re_brace))); br->index = v < 0 ? (int)(v - 1) : 0; if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } } else if((*m_position == charT('\'')) || (*m_position == charT('<'))) { const charT* base = ++m_position; while((m_position != m_end) && (*m_position != charT('>')) && (*m_position != charT('\''))) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = static_cast(hash_value_from_capture_name(base, m_position)); re_brace* br = static_cast(this->append_state(syntax_element_assert_backref, sizeof(re_brace))); br->index = (int)v; if(((*m_position != charT('>')) && (*m_position != charT('\''))) || (++m_position == m_end)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "Unterminated named capture."); return false; } if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } } else if(*m_position == charT('D')) { const char* def = "DEFINE"; while(*def && (m_position != m_end) && (*m_position == charT(*def))) ++m_position, ++def; if((m_position == m_end) || *def) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } re_brace* br = static_cast(this->append_state(syntax_element_assert_backref, sizeof(re_brace))); br->index = 9999; // special magic value! if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } } else if(v > 0) { re_brace* br = static_cast(this->append_state(syntax_element_assert_backref, sizeof(re_brace))); br->index = (int)v; if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } } else { // verify that we have a lookahead or lookbehind assert: if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_question) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(this->m_traits.syntax_type(*m_position) == regex_constants::escape_type_left_word) { if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if((this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_not)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } m_position -= 3; } else { if((this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_not)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } m_position -= 2; } } break; } case regex_constants::syntax_close_mark: // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; case regex_constants::escape_type_end_buffer: { name_delim = *m_position; named_capture_jump: markid = 0; if(0 == (this->flags() & regbase::nosubs)) { markid = ++m_mark_count; if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.push_back(std::pair(std::distance(m_base, m_position) - 2, 0)); } pb->index = markid; const charT* base = ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } while((m_position != m_end) && (*m_position != name_delim)) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } this->m_pdata->set_name(base, m_position, markid); ++m_position; break; } default: if(*m_position == charT('R')) { ++m_position; v = 0; if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } goto insert_recursion; } if(*m_position == charT('&')) { ++m_position; const charT* base = m_position; while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = static_cast(hash_value_from_capture_name(base, m_position)); goto insert_recursion; } if(*m_position == charT('P')) { ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(*m_position == charT('>')) { ++m_position; const charT* base = m_position; while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) ++m_position; if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } v = static_cast(hash_value_from_capture_name(base, m_position)); goto insert_recursion; } } // // lets assume that we have a (?imsx) group and try and parse it: // option_group_jump: regex_constants::syntax_option_type opts = parse_options(); if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } // make a note of whether we have a case change: m_has_case_change = ((opts & regbase::icase) != (this->flags() & regbase::icase)); pb->index = markid = 0; if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark) { // update flags and carry on as normal: this->flags(opts); restore_flags = false; old_case_change |= m_has_case_change; // defer end of scope by one ')' } else if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_colon) { // update flags and carry on until the matching ')' is found: this->flags(opts); ++m_position; } else { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } // finally append a case change state if we need it: if(m_has_case_change) { static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = opts & regbase::icase; } } // // now recursively add more states, this will terminate when we get to a // matching ')' : // parse_all(); // // Unwind alternatives: // if(0 == unwind_alts(last_paren_start)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "Invalid alternation operators within (?...) block."); return false; } // // we either have a ')' or we have run out of characters prematurely: // if(m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; this->fail(regex_constants::error_paren, std::distance(m_base, m_end)); return false; } BOOST_REGEX_ASSERT(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark); ++m_position; // // restore the flags: // if(restore_flags) { // append a case change state if we need it: if(m_has_case_change) { static_cast( this->append_state(syntax_element_toggle_case, sizeof(re_case)) )->icase = old_flags & regbase::icase; } this->flags(old_flags); } // // set up the jump pointer if we have one: // if(jump_offset) { this->m_pdata->m_data.align(); re_jump* jmp = static_cast(this->getaddress(jump_offset)); jmp->alt.i = this->m_pdata->m_data.size() - this->getoffset(jmp); if((this->m_last_state == jmp) && (markid != -2)) { // Oops... we didn't have anything inside the assertion. // Note we don't get here for negated forward lookahead as (?!) // does have some uses. // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base, "Invalid or empty zero width assertion."); return false; } } // // verify that if this is conditional expression, that we do have // an alternative, if not add one: // if(markid == -4) { re_syntax_base* b = this->getaddress(expected_alt_point); // Make sure we have exactly one alternative following this state: if(b->type != syntax_element_alt) { re_alt* alt = static_cast(this->insert_state(expected_alt_point, syntax_element_alt, sizeof(re_alt))); alt->alt.i = this->m_pdata->m_data.size() - this->getoffset(alt); } else if(((std::ptrdiff_t)this->m_pdata->m_data.size() > (static_cast(b)->alt.i + this->getoffset(b))) && (static_cast(b)->alt.i > 0) && this->getaddress(static_cast(b)->alt.i, b)->type == syntax_element_alt) { // Can't have seen more than one alternative: // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_bad_pattern, m_position - m_base, "More than one alternation operator | was encountered inside a conditional expression."); return false; } else { // We must *not* have seen an alternative inside a (DEFINE) block: b = this->getaddress(b->next.i, b); if((b->type == syntax_element_assert_backref) && (static_cast(b)->index == 9999)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_bad_pattern, m_position - m_base, "Alternation operators are not allowed inside a DEFINE block."); return false; } } // check for invalid repetition of next state: b = this->getaddress(expected_alt_point); b = this->getaddress(static_cast(b)->next.i, b); if((b->type != syntax_element_assert_backref) && (b->type != syntax_element_startmark)) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_badrepeat, m_position - m_base, "A repetition operator cannot be applied to a zero-width assertion."); return false; } } // // append closing parenthesis state: // pb = static_cast(this->append_state(syntax_element_endmark, sizeof(re_brace))); pb->index = markid; pb->icase = this->flags() & regbase::icase; this->m_paren_start = last_paren_start; // // restore the alternate insertion point: // this->m_alt_insert_point = last_alt_point; // // and the case change data: // m_has_case_change = old_case_change; // // And the mark_reset data: // if(m_max_mark > m_mark_count) { m_mark_count = m_max_mark; } m_mark_reset = mark_reset; m_max_mark = max_mark; if(markid > 0) { if(this->flags() & regbase::save_subexpression_location) this->m_pdata->m_subs.at((std::size_t)markid - 1).second = std::distance(m_base, m_position) - 1; } return true; } template bool basic_regex_parser::match_verb(const char* verb) { while(*verb) { if(static_cast(*verb) != *m_position) { while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(++m_position == m_end) { --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++verb; } return true; } #ifdef BOOST_REGEX_MSVC # pragma warning(push) #if BOOST_REGEX_MSVC >= 1800 #pragma warning(disable:26812) #endif #endif template bool basic_regex_parser::parse_perl_verb() { if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } switch(*m_position) { case 'F': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if((this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark) || match_verb("AIL")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; this->append_state(syntax_element_fail); return true; } break; case 'A': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("CCEPT")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; this->append_state(syntax_element_accept); return true; } break; case 'C': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("OMMIT")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; static_cast(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_commit; this->m_pdata->m_disable_match_any = true; return true; } break; case 'P': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("RUNE")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; static_cast(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_prune; this->m_pdata->m_disable_match_any = true; return true; } break; case 'S': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("KIP")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; static_cast(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_skip; this->m_pdata->m_disable_match_any = true; return true; } break; case 'T': if(++m_position == m_end) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } if(match_verb("HEN")) { if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)) { // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } ++m_position; this->append_state(syntax_element_then); this->m_pdata->m_disable_match_any = true; return true; } break; } // Rewind to start of (* sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_perl_extension, m_position - m_base); return false; } #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif template bool basic_regex_parser::add_emacs_code(bool negate) { // // parses an emacs style \sx or \Sx construct. // if(++m_position == m_end) { // Rewind to start of sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position; fail(regex_constants::error_escape, m_position - m_base); return false; } basic_char_set char_set; if(negate) char_set.negate(); static const charT s_punct[5] = { 'p', 'u', 'n', 'c', 't', }; switch(*m_position) { case 's': case ' ': char_set.add_class(this->m_mask_space); break; case 'w': char_set.add_class(this->m_word_mask); break; case '_': char_set.add_single(digraph(charT('$'))); char_set.add_single(digraph(charT('&'))); char_set.add_single(digraph(charT('*'))); char_set.add_single(digraph(charT('+'))); char_set.add_single(digraph(charT('-'))); char_set.add_single(digraph(charT('_'))); char_set.add_single(digraph(charT('<'))); char_set.add_single(digraph(charT('>'))); break; case '.': char_set.add_class(this->m_traits.lookup_classname(s_punct, s_punct+5)); break; case '(': char_set.add_single(digraph(charT('('))); char_set.add_single(digraph(charT('['))); char_set.add_single(digraph(charT('{'))); break; case ')': char_set.add_single(digraph(charT(')'))); char_set.add_single(digraph(charT(']'))); char_set.add_single(digraph(charT('}'))); break; case '"': char_set.add_single(digraph(charT('"'))); char_set.add_single(digraph(charT('\''))); char_set.add_single(digraph(charT('`'))); break; case '\'': char_set.add_single(digraph(charT('\''))); char_set.add_single(digraph(charT(','))); char_set.add_single(digraph(charT('#'))); break; case '<': char_set.add_single(digraph(charT(';'))); break; case '>': char_set.add_single(digraph(charT('\n'))); char_set.add_single(digraph(charT('\f'))); break; default: fail(regex_constants::error_ctype, m_position - m_base); return false; } if(0 == this->append_set(char_set)) { fail(regex_constants::error_ctype, m_position - m_base); return false; } ++m_position; return true; } template regex_constants::syntax_option_type basic_regex_parser::parse_options() { // we have a (?imsx-imsx) group, convert it into a set of flags: regex_constants::syntax_option_type f = this->flags(); bool breakout = false; do { switch(*m_position) { case 's': f |= regex_constants::mod_s; f &= ~regex_constants::no_mod_s; break; case 'm': f &= ~regex_constants::no_mod_m; break; case 'i': f |= regex_constants::icase; break; case 'x': f |= regex_constants::mod_x; break; default: breakout = true; continue; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_paren, m_position - m_base); return false; } } while(!breakout); breakout = false; if(*m_position == static_cast('-')) { if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_paren, m_position - m_base); return false; } do { switch(*m_position) { case 's': f &= ~regex_constants::mod_s; f |= regex_constants::no_mod_s; break; case 'm': f |= regex_constants::no_mod_m; break; case 'i': f &= ~regex_constants::icase; break; case 'x': f &= ~regex_constants::mod_x; break; default: breakout = true; continue; } if(++m_position == m_end) { // Rewind to start of (? sequence: --m_position; while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position; fail(regex_constants::error_paren, m_position - m_base); return false; } } while(!breakout); } return f; } template bool basic_regex_parser::unwind_alts(std::ptrdiff_t last_paren_start) { // // If we didn't actually add any states after the last // alternative then that's an error: // if((this->m_alt_insert_point == static_cast(this->m_pdata->m_data.size())) && (!m_alt_jumps.empty()) && (m_alt_jumps.back() > last_paren_start) && !( ((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group) && ((this->flags() & regbase::no_empty_expressions) == 0) ) ) { fail(regex_constants::error_empty, this->m_position - this->m_base, "Can't terminate a sub-expression with an alternation operator |."); return false; } // // Fix up our alternatives: // while((!m_alt_jumps.empty()) && (m_alt_jumps.back() > last_paren_start)) { // // fix up the jump to point to the end of the states // that we've just added: // std::ptrdiff_t jump_offset = m_alt_jumps.back(); m_alt_jumps.pop_back(); this->m_pdata->m_data.align(); re_jump* jmp = static_cast(this->getaddress(jump_offset)); if (jmp->type != syntax_element_jump) { // Something really bad happened, this used to be an assert, // but we'll make it an error just in case we should ever get here. fail(regex_constants::error_unknown, this->m_position - this->m_base, "Internal logic failed while compiling the expression, probably you added a repeat to something non-repeatable!"); return false; } jmp->alt.i = this->m_pdata->m_data.size() - jump_offset; } return true; } #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/c_regex_traits.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE c_regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits class that wraps the global C locale. */ #ifndef BOOST_C_REGEX_TRAITS_HPP_INCLUDED #define BOOST_C_REGEX_TRAITS_HPP_INCLUDED #include #include #include namespace boost{ namespace BOOST_REGEX_DETAIL_NS { enum { char_class_space = 1 << 0, char_class_print = 1 << 1, char_class_cntrl = 1 << 2, char_class_upper = 1 << 3, char_class_lower = 1 << 4, char_class_alpha = 1 << 5, char_class_digit = 1 << 6, char_class_punct = 1 << 7, char_class_xdigit = 1 << 8, char_class_alnum = char_class_alpha | char_class_digit, char_class_graph = char_class_alnum | char_class_punct, char_class_blank = 1 << 9, char_class_word = 1 << 10, char_class_unicode = 1 << 11, char_class_horizontal = 1 << 12, char_class_vertical = 1 << 13 }; } template struct c_regex_traits; template<> struct c_regex_traits { c_regex_traits(){} typedef char char_type; typedef std::size_t size_type; typedef std::string string_type; struct locale_type{}; typedef std::uint32_t char_class_type; static size_type length(const char_type* p) { return (std::strlen)(p); } char translate(char c) const { return c; } char translate_nocase(char c) const { return static_cast((std::tolower)(static_cast(c))); } static string_type transform(const char* p1, const char* p2); static string_type transform_primary(const char* p1, const char* p2); static char_class_type lookup_classname(const char* p1, const char* p2); static string_type lookup_collatename(const char* p1, const char* p2); static bool isctype(char, char_class_type); static int value(char, int); locale_type imbue(locale_type l) { return l; } locale_type getloc()const { return locale_type(); } private: // this type is not copyable: c_regex_traits(const c_regex_traits&); c_regex_traits& operator=(const c_regex_traits&); }; #ifndef BOOST_NO_WREGEX template<> struct c_regex_traits { c_regex_traits(){} typedef wchar_t char_type; typedef std::size_t size_type; typedef std::wstring string_type; struct locale_type{}; typedef std::uint32_t char_class_type; static size_type length(const char_type* p) { return (std::wcslen)(p); } wchar_t translate(wchar_t c) const { return c; } wchar_t translate_nocase(wchar_t c) const { return (std::towlower)(c); } static string_type transform(const wchar_t* p1, const wchar_t* p2); static string_type transform_primary(const wchar_t* p1, const wchar_t* p2); static char_class_type lookup_classname(const wchar_t* p1, const wchar_t* p2); static string_type lookup_collatename(const wchar_t* p1, const wchar_t* p2); static bool isctype(wchar_t, char_class_type); static int value(wchar_t, int); locale_type imbue(locale_type l) { return l; } locale_type getloc()const { return locale_type(); } private: // this type is not copyable: c_regex_traits(const c_regex_traits&); c_regex_traits& operator=(const c_regex_traits&); }; #endif // BOOST_NO_WREGEX inline c_regex_traits::string_type c_regex_traits::transform(const char* p1, const char* p2) { std::string result(10, ' '); std::size_t s = result.size(); std::size_t r; std::string src(p1, p2); while (s < (r = std::strxfrm(&*result.begin(), src.c_str(), s))) { #if defined(_CPPLIB_VER) // // A bug in VC11 and 12 causes the program to hang if we pass a null-string // to std::strxfrm, but only for certain locales :-( // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware). // if (r == INT_MAX) { result.erase(); result.insert(result.begin(), static_cast(0)); return result; } #endif result.append(r - s + 3, ' '); s = result.size(); } result.erase(r); return result; } inline c_regex_traits::string_type c_regex_traits::transform_primary(const char* p1, const char* p2) { static char s_delim; static const int s_collate_type = ::boost::BOOST_REGEX_DETAIL_NS::find_sort_syntax(static_cast*>(0), &s_delim); std::string result; // // What we do here depends upon the format of the sort key returned by // sort key returned by this->transform: // switch (s_collate_type) { case ::boost::BOOST_REGEX_DETAIL_NS::sort_C: case ::boost::BOOST_REGEX_DETAIL_NS::sort_unknown: // the best we can do is translate to lower case, then get a regular sort key: { result.assign(p1, p2); for (std::string::size_type i = 0; i < result.size(); ++i) result[i] = static_cast((std::tolower)(static_cast(result[i]))); result = transform(&*result.begin(), &*result.begin() + result.size()); break; } case ::boost::BOOST_REGEX_DETAIL_NS::sort_fixed: { // get a regular sort key, and then truncate it: result = transform(p1, p2); result.erase(s_delim); break; } case ::boost::BOOST_REGEX_DETAIL_NS::sort_delim: // get a regular sort key, and then truncate everything after the delim: result = transform(p1, p2); if ((!result.empty()) && (result[0] == s_delim)) break; std::size_t i; for (i = 0; i < result.size(); ++i) { if (result[i] == s_delim) break; } result.erase(i); break; } if (result.empty()) result = std::string(1, char(0)); return result; } inline c_regex_traits::char_class_type c_regex_traits::lookup_classname(const char* p1, const char* p2) { using namespace BOOST_REGEX_DETAIL_NS; static const char_class_type masks[] = { 0, char_class_alnum, char_class_alpha, char_class_blank, char_class_cntrl, char_class_digit, char_class_digit, char_class_graph, char_class_horizontal, char_class_lower, char_class_lower, char_class_print, char_class_punct, char_class_space, char_class_space, char_class_upper, char_class_unicode, char_class_upper, char_class_vertical, char_class_alnum | char_class_word, char_class_alnum | char_class_word, char_class_xdigit, }; int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); if (idx < 0) { std::string s(p1, p2); for (std::string::size_type i = 0; i < s.size(); ++i) s[i] = static_cast((std::tolower)(static_cast(s[i]))); idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size()); } BOOST_REGEX_ASSERT(std::size_t(idx) + 1u < sizeof(masks) / sizeof(masks[0])); return masks[idx + 1]; } inline bool c_regex_traits::isctype(char c, char_class_type mask) { using namespace BOOST_REGEX_DETAIL_NS; return ((mask & char_class_space) && (std::isspace)(static_cast(c))) || ((mask & char_class_print) && (std::isprint)(static_cast(c))) || ((mask & char_class_cntrl) && (std::iscntrl)(static_cast(c))) || ((mask & char_class_upper) && (std::isupper)(static_cast(c))) || ((mask & char_class_lower) && (std::islower)(static_cast(c))) || ((mask & char_class_alpha) && (std::isalpha)(static_cast(c))) || ((mask & char_class_digit) && (std::isdigit)(static_cast(c))) || ((mask & char_class_punct) && (std::ispunct)(static_cast(c))) || ((mask & char_class_xdigit) && (std::isxdigit)(static_cast(c))) || ((mask & char_class_blank) && (std::isspace)(static_cast(c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c)) || ((mask & char_class_word) && (c == '_')) || ((mask & char_class_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\v'))) || ((mask & char_class_horizontal) && (std::isspace)(static_cast(c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && (c != '\v')); } inline c_regex_traits::string_type c_regex_traits::lookup_collatename(const char* p1, const char* p2) { std::string s(p1, p2); s = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(s); if (s.empty() && (p2 - p1 == 1)) s.append(1, *p1); return s; } inline int c_regex_traits::value(char c, int radix) { char b[2] = { c, '\0', }; char* ep; int result = std::strtol(b, &ep, radix); if (ep == b) return -1; return result; } #ifndef BOOST_NO_WREGEX inline c_regex_traits::string_type c_regex_traits::transform(const wchar_t* p1, const wchar_t* p2) { std::size_t r; std::size_t s = 10; std::wstring src(p1, p2); std::wstring result(s, L' '); while (s < (r = std::wcsxfrm(&*result.begin(), src.c_str(), s))) { #if defined(_CPPLIB_VER) // // A bug in VC11 and 12 causes the program to hang if we pass a null-string // to std::strxfrm, but only for certain locales :-( // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware). // if (r == INT_MAX) { result.erase(); result.insert(result.begin(), static_cast(0)); return result; } #endif result.append(r - s + 3, L' '); s = result.size(); } result.erase(r); return result; } inline c_regex_traits::string_type c_regex_traits::transform_primary(const wchar_t* p1, const wchar_t* p2) { static wchar_t s_delim; static const int s_collate_type = ::boost::BOOST_REGEX_DETAIL_NS::find_sort_syntax(static_cast*>(0), &s_delim); std::wstring result; // // What we do here depends upon the format of the sort key returned by // sort key returned by this->transform: // switch (s_collate_type) { case ::boost::BOOST_REGEX_DETAIL_NS::sort_C: case ::boost::BOOST_REGEX_DETAIL_NS::sort_unknown: // the best we can do is translate to lower case, then get a regular sort key: { result.assign(p1, p2); for (std::wstring::size_type i = 0; i < result.size(); ++i) result[i] = (std::towlower)(result[i]); result = c_regex_traits::transform(&*result.begin(), &*result.begin() + result.size()); break; } case ::boost::BOOST_REGEX_DETAIL_NS::sort_fixed: { // get a regular sort key, and then truncate it: result = c_regex_traits::transform(&*result.begin(), &*result.begin() + result.size()); result.erase(s_delim); break; } case ::boost::BOOST_REGEX_DETAIL_NS::sort_delim: // get a regular sort key, and then truncate everything after the delim: result = c_regex_traits::transform(&*result.begin(), &*result.begin() + result.size()); if ((!result.empty()) && (result[0] == s_delim)) break; std::size_t i; for (i = 0; i < result.size(); ++i) { if (result[i] == s_delim) break; } result.erase(i); break; } if (result.empty()) result = std::wstring(1, char(0)); return result; } inline c_regex_traits::char_class_type c_regex_traits::lookup_classname(const wchar_t* p1, const wchar_t* p2) { using namespace BOOST_REGEX_DETAIL_NS; static const char_class_type masks[] = { 0, char_class_alnum, char_class_alpha, char_class_blank, char_class_cntrl, char_class_digit, char_class_digit, char_class_graph, char_class_horizontal, char_class_lower, char_class_lower, char_class_print, char_class_punct, char_class_space, char_class_space, char_class_upper, char_class_unicode, char_class_upper, char_class_vertical, char_class_alnum | char_class_word, char_class_alnum | char_class_word, char_class_xdigit, }; int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); if (idx < 0) { std::wstring s(p1, p2); for (std::wstring::size_type i = 0; i < s.size(); ++i) s[i] = (std::towlower)(s[i]); idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size()); } BOOST_REGEX_ASSERT(idx + 1 < static_cast(sizeof(masks) / sizeof(masks[0]))); return masks[idx + 1]; } inline bool c_regex_traits::isctype(wchar_t c, char_class_type mask) { using namespace BOOST_REGEX_DETAIL_NS; return ((mask & char_class_space) && (std::iswspace)(c)) || ((mask & char_class_print) && (std::iswprint)(c)) || ((mask & char_class_cntrl) && (std::iswcntrl)(c)) || ((mask & char_class_upper) && (std::iswupper)(c)) || ((mask & char_class_lower) && (std::iswlower)(c)) || ((mask & char_class_alpha) && (std::iswalpha)(c)) || ((mask & char_class_digit) && (std::iswdigit)(c)) || ((mask & char_class_punct) && (std::iswpunct)(c)) || ((mask & char_class_xdigit) && (std::iswxdigit)(c)) || ((mask & char_class_blank) && (std::iswspace)(c) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c)) || ((mask & char_class_word) && (c == '_')) || ((mask & char_class_unicode) && (c & ~static_cast(0xff))) || ((mask & char_class_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == L'\v'))) || ((mask & char_class_horizontal) && (std::iswspace)(c) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && (c != L'\v')); } inline c_regex_traits::string_type c_regex_traits::lookup_collatename(const wchar_t* p1, const wchar_t* p2) { std::string name; // Usual msvc warning suppression does not work here with std::string template constructor.... use a workaround instead: for (const wchar_t* pos = p1; pos != p2; ++pos) name.push_back((char)*pos); name = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(name); if (!name.empty()) return string_type(name.begin(), name.end()); if (p2 - p1 == 1) return string_type(1, *p1); return string_type(); } inline int c_regex_traits::value(wchar_t c, int radix) { #ifdef BOOST_BORLANDC // workaround for broken wcstol: if ((std::iswxdigit)(c) == 0) return -1; #endif wchar_t b[2] = { c, '\0', }; wchar_t* ep; int result = std::wcstol(b, &ep, radix); if (ep == b) return -1; return result; } #endif } #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/char_regex_traits.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE char_regex_traits.cpp * VERSION see * DESCRIPTION: Declares deprecated traits classes char_regex_traits<>. */ #ifndef BOOST_REGEX_V5_CHAR_REGEX_TRAITS_HPP #define BOOST_REGEX_V5_CHAR_REGEX_TRAITS_HPP namespace boost{ namespace deprecated{ // // class char_regex_traits_i // provides case insensitive traits classes (deprecated): template class char_regex_traits_i : public regex_traits {}; template<> class char_regex_traits_i : public regex_traits { public: typedef char char_type; typedef unsigned char uchar_type; typedef unsigned int size_type; typedef regex_traits base_type; }; #ifndef BOOST_NO_WREGEX template<> class char_regex_traits_i : public regex_traits { public: typedef wchar_t char_type; typedef unsigned short uchar_type; typedef unsigned int size_type; typedef regex_traits base_type; }; #endif } // namespace deprecated } // namespace boost #endif // include ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/cpp_regex_traits.hpp ================================================ /* * * Copyright (c) 2004 John Maddock * Copyright 2011 Garmin Ltd. or its subsidiaries * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE cpp_regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits class cpp_regex_traits. */ #ifndef BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED #define BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED #include #include #include #include #include #include #ifdef BOOST_HAS_THREADS #include #endif #include #include #include #include #include #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4786 4251) #endif namespace boost{ // // forward declaration is needed by some compilers: // template class cpp_regex_traits; namespace BOOST_REGEX_DETAIL_NS{ // // class parser_buf: // acts as a stream buffer which wraps around a pair of pointers: // template > class parser_buf : public ::std::basic_streambuf { typedef ::std::basic_streambuf base_type; typedef typename base_type::int_type int_type; typedef typename base_type::char_type char_type; typedef typename base_type::pos_type pos_type; typedef ::std::streamsize streamsize; typedef typename base_type::off_type off_type; public: parser_buf() : base_type() { setbuf(0, 0); } const charT* getnext() { return this->gptr(); } protected: std::basic_streambuf* setbuf(char_type* s, streamsize n) override; typename parser_buf::pos_type seekpos(pos_type sp, ::std::ios_base::openmode which) override; typename parser_buf::pos_type seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which) override; private: parser_buf& operator=(const parser_buf&); parser_buf(const parser_buf&); }; template std::basic_streambuf* parser_buf::setbuf(char_type* s, streamsize n) { this->setg(s, s, s + n); return this; } template typename parser_buf::pos_type parser_buf::seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which) { if(which & ::std::ios_base::out) return pos_type(off_type(-1)); std::ptrdiff_t size = this->egptr() - this->eback(); std::ptrdiff_t pos = this->gptr() - this->eback(); charT* g = this->eback(); switch(static_cast(way)) { case ::std::ios_base::beg: if((off < 0) || (off > size)) return pos_type(off_type(-1)); else this->setg(g, g + off, g + size); break; case ::std::ios_base::end: if((off < 0) || (off > size)) return pos_type(off_type(-1)); else this->setg(g, g + size - off, g + size); break; case ::std::ios_base::cur: { std::ptrdiff_t newpos = static_cast(pos + off); if((newpos < 0) || (newpos > size)) return pos_type(off_type(-1)); else this->setg(g, g + newpos, g + size); break; } default: ; } #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4244) #endif return static_cast(this->gptr() - this->eback()); #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template typename parser_buf::pos_type parser_buf::seekpos(pos_type sp, ::std::ios_base::openmode which) { if(which & ::std::ios_base::out) return pos_type(off_type(-1)); off_type size = static_cast(this->egptr() - this->eback()); charT* g = this->eback(); if(off_type(sp) <= size) { this->setg(g, g + off_type(sp), g + size); } return pos_type(off_type(-1)); } // // class cpp_regex_traits_base: // acts as a container for locale and the facets we are using. // template struct cpp_regex_traits_base { cpp_regex_traits_base(const std::locale& l) { (void)imbue(l); } std::locale imbue(const std::locale& l); std::locale m_locale; std::ctype const* m_pctype; std::messages const* m_pmessages; std::collate const* m_pcollate; bool operator<(const cpp_regex_traits_base& b)const { if(m_pctype == b.m_pctype) { if(m_pmessages == b.m_pmessages) { return m_pcollate < b.m_pcollate; } return m_pmessages < b.m_pmessages; } return m_pctype < b.m_pctype; } bool operator==(const cpp_regex_traits_base& b)const { return (m_pctype == b.m_pctype) && (m_pmessages == b.m_pmessages) && (m_pcollate == b.m_pcollate); } }; template std::locale cpp_regex_traits_base::imbue(const std::locale& l) { std::locale result(m_locale); m_locale = l; m_pctype = &std::use_facet>(l); m_pmessages = std::has_facet >(l) ? &std::use_facet >(l) : 0; m_pcollate = &std::use_facet >(l); return result; } // // class cpp_regex_traits_char_layer: // implements methods that require specialization for narrow characters: // template class cpp_regex_traits_char_layer : public cpp_regex_traits_base { typedef std::basic_string string_type; typedef std::map map_type; typedef typename map_type::const_iterator map_iterator_type; public: cpp_regex_traits_char_layer(const std::locale& l) : cpp_regex_traits_base(l) { init(); } cpp_regex_traits_char_layer(const cpp_regex_traits_base& b) : cpp_regex_traits_base(b) { init(); } void init(); regex_constants::syntax_type syntax_type(charT c)const { map_iterator_type i = m_char_map.find(c); return ((i == m_char_map.end()) ? 0 : i->second); } regex_constants::escape_syntax_type escape_syntax_type(charT c) const { map_iterator_type i = m_char_map.find(c); if(i == m_char_map.end()) { if(this->m_pctype->is(std::ctype_base::lower, c)) return regex_constants::escape_type_class; if(this->m_pctype->is(std::ctype_base::upper, c)) return regex_constants::escape_type_not_class; return 0; } return i->second; } private: string_type get_default_message(regex_constants::syntax_type); // TODO: use a hash table when available! map_type m_char_map; }; template void cpp_regex_traits_char_layer::init() { // we need to start by initialising our syntax map so we know which // character is used for which purpose: #ifndef __IBMCPP__ typename std::messages::catalog cat = static_cast::catalog>(-1); #else typename std::messages::catalog cat = reinterpret_cast::catalog>(-1); #endif std::string cat_name(cpp_regex_traits::get_catalog_name()); if((!cat_name.empty()) && (this->m_pmessages != 0)) { cat = this->m_pmessages->open( cat_name, this->m_locale); if((int)cat < 0) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if((int)cat >= 0) { #ifndef BOOST_NO_EXCEPTIONS try{ #endif for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { string_type mss = this->m_pmessages->get(cat, 0, i, get_default_message(i)); for(typename string_type::size_type j = 0; j < mss.size(); ++j) { m_char_map[mss[j]] = i; } } this->m_pmessages->close(cat); #ifndef BOOST_NO_EXCEPTIONS } catch(...) { if(this->m_pmessages) this->m_pmessages->close(cat); throw; } #endif } else { for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { const char* ptr = get_default_syntax(i); while(ptr && *ptr) { m_char_map[this->m_pctype->widen(*ptr)] = i; ++ptr; } } } } template typename cpp_regex_traits_char_layer::string_type cpp_regex_traits_char_layer::get_default_message(regex_constants::syntax_type i) { const char* ptr = get_default_syntax(i); string_type result; while(ptr && *ptr) { result.append(1, this->m_pctype->widen(*ptr)); ++ptr; } return result; } // // specialized version for narrow characters: // template <> class cpp_regex_traits_char_layer : public cpp_regex_traits_base { typedef std::string string_type; public: cpp_regex_traits_char_layer(const std::locale& l) : cpp_regex_traits_base(l) { init(); } cpp_regex_traits_char_layer(const cpp_regex_traits_base& l) : cpp_regex_traits_base(l) { init(); } regex_constants::syntax_type syntax_type(char c)const { return m_char_map[static_cast(c)]; } regex_constants::escape_syntax_type escape_syntax_type(char c) const { return m_char_map[static_cast(c)]; } private: regex_constants::syntax_type m_char_map[1u << CHAR_BIT]; void init(); }; // // class cpp_regex_traits_implementation: // provides pimpl implementation for cpp_regex_traits. // template class cpp_regex_traits_implementation : public cpp_regex_traits_char_layer { public: typedef typename cpp_regex_traits::char_class_type char_class_type; typedef typename std::ctype::mask native_mask_type; typedef typename std::make_unsigned::type unsigned_native_mask_type; static const char_class_type mask_blank = 1u << 24; static const char_class_type mask_word = 1u << 25; static const char_class_type mask_unicode = 1u << 26; static const char_class_type mask_horizontal = 1u << 27; static const char_class_type mask_vertical = 1u << 28; typedef std::basic_string string_type; typedef charT char_type; //cpp_regex_traits_implementation(); cpp_regex_traits_implementation(const std::locale& l) : cpp_regex_traits_char_layer(l) { init(); } cpp_regex_traits_implementation(const cpp_regex_traits_base& l) : cpp_regex_traits_char_layer(l) { init(); } std::string error_string(regex_constants::error_type n) const { if(!m_error_strings.empty()) { std::map::const_iterator p = m_error_strings.find(n); return (p == m_error_strings.end()) ? std::string(get_default_error_string(n)) : p->second; } return get_default_error_string(n); } char_class_type lookup_classname(const charT* p1, const charT* p2) const { char_class_type result = lookup_classname_imp(p1, p2); if(result == 0) { string_type temp(p1, p2); this->m_pctype->tolower(&*temp.begin(), &*temp.begin() + temp.size()); result = lookup_classname_imp(&*temp.begin(), &*temp.begin() + temp.size()); } return result; } string_type lookup_collatename(const charT* p1, const charT* p2) const; string_type transform_primary(const charT* p1, const charT* p2) const; string_type transform(const charT* p1, const charT* p2) const; private: std::map m_error_strings; // error messages indexed by numberic ID std::map m_custom_class_names; // character class names std::map m_custom_collate_names; // collating element names unsigned m_collate_type; // the form of the collation string charT m_collate_delim; // the collation group delimiter // // helpers: // char_class_type lookup_classname_imp(const charT* p1, const charT* p2) const; void init(); }; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_blank; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_word; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_unicode; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_vertical; template typename cpp_regex_traits_implementation::char_class_type const cpp_regex_traits_implementation::mask_horizontal; template typename cpp_regex_traits_implementation::string_type cpp_regex_traits_implementation::transform_primary(const charT* p1, const charT* p2) const { // // PRECONDITIONS: // // A bug in gcc 3.2 (and maybe other versions as well) treats // p1 as a null terminated string, for efficiency reasons // we work around this elsewhere, but just assert here that // we adhere to gcc's (buggy) preconditions... // BOOST_REGEX_ASSERT(*p2 == 0); string_type result; #if defined(_CPPLIB_VER) // // A bug in VC11 and 12 causes the program to hang if we pass a null-string // to std::collate::transform, but only for certain locales :-( // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware). // if(*p1 == 0) { return string_type(1, charT(0)); } #endif // // swallowing all exceptions here is a bad idea // however at least one std lib will always throw // std::bad_alloc for certain arguments... // #ifndef BOOST_NO_EXCEPTIONS try{ #endif // // What we do here depends upon the format of the sort key returned by // sort key returned by this->transform: // switch(m_collate_type) { case sort_C: case sort_unknown: // the best we can do is translate to lower case, then get a regular sort key: { result.assign(p1, p2); this->m_pctype->tolower(&*result.begin(), &*result.begin() + result.size()); result = this->m_pcollate->transform(&*result.begin(), &*result.begin() + result.size()); break; } case sort_fixed: { // get a regular sort key, and then truncate it: result.assign(this->m_pcollate->transform(p1, p2)); result.erase(this->m_collate_delim); break; } case sort_delim: // get a regular sort key, and then truncate everything after the delim: result.assign(this->m_pcollate->transform(p1, p2)); std::size_t i; for(i = 0; i < result.size(); ++i) { if(result[i] == m_collate_delim) break; } result.erase(i); break; } #ifndef BOOST_NO_EXCEPTIONS }catch(...){} #endif while((!result.empty()) && (charT(0) == *result.rbegin())) result.erase(result.size() - 1); if(result.empty()) { // character is ignorable at the primary level: result = string_type(1, charT(0)); } return result; } template typename cpp_regex_traits_implementation::string_type cpp_regex_traits_implementation::transform(const charT* p1, const charT* p2) const { // // PRECONDITIONS: // // A bug in gcc 3.2 (and maybe other versions as well) treats // p1 as a null terminated string, for efficiency reasons // we work around this elsewhere, but just assert here that // we adhere to gcc's (buggy) preconditions... // BOOST_REGEX_ASSERT(*p2 == 0); // // swallowing all exceptions here is a bad idea // however at least one std lib will always throw // std::bad_alloc for certain arguments... // string_type result, result2; #if defined(_CPPLIB_VER) // // A bug in VC11 and 12 causes the program to hang if we pass a null-string // to std::collate::transform, but only for certain locales :-( // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware). // if(*p1 == 0) { return result; } #endif #ifndef BOOST_NO_EXCEPTIONS try{ #endif result = this->m_pcollate->transform(p1, p2); // // some implementations (Dinkumware) append unnecessary trailing \0's: while((!result.empty()) && (charT(0) == *result.rbegin())) result.erase(result.size() - 1); // // We may have NULL's used as separators between sections of the collate string, // an example would be Boost.Locale. We have no way to detect this case via // #defines since this can be used with any compiler/platform combination. // Unfortunately our state machine (which was devised when all implementations // used underlying C language API's) can't cope with that case. One workaround // is to replace each character with 2, fortunately this code isn't used that // much as this is now slower than before :-( // typedef typename std::make_unsigned::type uchar_type; result2.reserve(result.size() * 2 + 2); for(unsigned i = 0; i < result.size(); ++i) { if(static_cast(result[i]) == (std::numeric_limits::max)()) { result2.append(1, charT((std::numeric_limits::max)())).append(1, charT('b')); } else { result2.append(1, static_cast(1 + static_cast(result[i]))).append(1, charT('b') - 1); } } BOOST_REGEX_ASSERT(std::find(result2.begin(), result2.end(), charT(0)) == result2.end()); #ifndef BOOST_NO_EXCEPTIONS } catch(...) { } #endif return result2; } template typename cpp_regex_traits_implementation::string_type cpp_regex_traits_implementation::lookup_collatename(const charT* p1, const charT* p2) const { typedef typename std::map::const_iterator iter_type; if(!m_custom_collate_names.empty()) { iter_type pos = m_custom_collate_names.find(string_type(p1, p2)); if(pos != m_custom_collate_names.end()) return pos->second; } std::string name(p1, p2); name = lookup_default_collate_name(name); if(!name.empty()) return string_type(name.begin(), name.end()); if(p2 - p1 == 1) return string_type(1, *p1); return string_type(); } template void cpp_regex_traits_implementation::init() { #ifndef __IBMCPP__ typename std::messages::catalog cat = static_cast::catalog>(-1); #else typename std::messages::catalog cat = reinterpret_cast::catalog>(-1); #endif std::string cat_name(cpp_regex_traits::get_catalog_name()); if((!cat_name.empty()) && (this->m_pmessages != 0)) { cat = this->m_pmessages->open( cat_name, this->m_locale); if((int)cat < 0) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if((int)cat >= 0) { // // Error messages: // for(boost::regex_constants::error_type i = static_cast(0); i <= boost::regex_constants::error_unknown; i = static_cast(i + 1)) { const char* p = get_default_error_string(i); string_type default_message; while(*p) { default_message.append(1, this->m_pctype->widen(*p)); ++p; } string_type s = this->m_pmessages->get(cat, 0, i+200, default_message); std::string result; for(std::string::size_type j = 0; j < s.size(); ++j) { result.append(1, this->m_pctype->narrow(s[j], 0)); } m_error_strings[i] = result; } // // Custom class names: // static const char_class_type masks[16] = { static_cast(std::ctype::alnum), static_cast(std::ctype::alpha), static_cast(std::ctype::cntrl), static_cast(std::ctype::digit), static_cast(std::ctype::graph), cpp_regex_traits_implementation::mask_horizontal, static_cast(std::ctype::lower), static_cast(std::ctype::print), static_cast(std::ctype::punct), static_cast(std::ctype::space), static_cast(std::ctype::upper), cpp_regex_traits_implementation::mask_vertical, static_cast(std::ctype::xdigit), cpp_regex_traits_implementation::mask_blank, cpp_regex_traits_implementation::mask_word, cpp_regex_traits_implementation::mask_unicode, }; static const string_type null_string; for(unsigned int j = 0; j <= 13; ++j) { string_type s(this->m_pmessages->get(cat, 0, j+300, null_string)); if(!s.empty()) this->m_custom_class_names[s] = masks[j]; } } // // get the collation format used by m_pcollate: // m_collate_type = BOOST_REGEX_DETAIL_NS::find_sort_syntax(this, &m_collate_delim); } template typename cpp_regex_traits_implementation::char_class_type cpp_regex_traits_implementation::lookup_classname_imp(const charT* p1, const charT* p2) const { static const char_class_type masks[22] = { 0, static_cast(std::ctype::alnum), static_cast(std::ctype::alpha), cpp_regex_traits_implementation::mask_blank, static_cast(std::ctype::cntrl), static_cast(std::ctype::digit), static_cast(std::ctype::digit), static_cast(std::ctype::graph), cpp_regex_traits_implementation::mask_horizontal, static_cast(std::ctype::lower), static_cast(std::ctype::lower), static_cast(std::ctype::print), static_cast(std::ctype::punct), static_cast(std::ctype::space), static_cast(std::ctype::space), static_cast(std::ctype::upper), cpp_regex_traits_implementation::mask_unicode, static_cast(std::ctype::upper), cpp_regex_traits_implementation::mask_vertical, static_cast(std::ctype::alnum) | cpp_regex_traits_implementation::mask_word, static_cast(std::ctype::alnum) | cpp_regex_traits_implementation::mask_word, static_cast(std::ctype::xdigit), }; if(!m_custom_class_names.empty()) { typedef typename std::map, char_class_type>::const_iterator map_iter; map_iter pos = m_custom_class_names.find(string_type(p1, p2)); if(pos != m_custom_class_names.end()) return pos->second; } std::size_t state_id = 1 + BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); BOOST_REGEX_ASSERT(state_id < sizeof(masks) / sizeof(masks[0])); return masks[state_id]; } template inline std::shared_ptr > create_cpp_regex_traits(const std::locale& l) { cpp_regex_traits_base key(l); return ::boost::object_cache, cpp_regex_traits_implementation >::get(key, 5); } } // BOOST_REGEX_DETAIL_NS template class cpp_regex_traits { private: typedef std::ctype ctype_type; public: typedef charT char_type; typedef std::size_t size_type; typedef std::basic_string string_type; typedef std::locale locale_type; typedef std::uint_least32_t char_class_type; struct boost_extensions_tag{}; cpp_regex_traits() : m_pimpl(BOOST_REGEX_DETAIL_NS::create_cpp_regex_traits(std::locale())) { } static size_type length(const char_type* p) { return std::char_traits::length(p); } regex_constants::syntax_type syntax_type(charT c)const { return m_pimpl->syntax_type(c); } regex_constants::escape_syntax_type escape_syntax_type(charT c) const { return m_pimpl->escape_syntax_type(c); } charT translate(charT c) const { return c; } charT translate_nocase(charT c) const { return m_pimpl->m_pctype->tolower(c); } charT translate(charT c, bool icase) const { return icase ? m_pimpl->m_pctype->tolower(c) : c; } charT tolower(charT c) const { return m_pimpl->m_pctype->tolower(c); } charT toupper(charT c) const { return m_pimpl->m_pctype->toupper(c); } string_type transform(const charT* p1, const charT* p2) const { return m_pimpl->transform(p1, p2); } string_type transform_primary(const charT* p1, const charT* p2) const { return m_pimpl->transform_primary(p1, p2); } char_class_type lookup_classname(const charT* p1, const charT* p2) const { return m_pimpl->lookup_classname(p1, p2); } string_type lookup_collatename(const charT* p1, const charT* p2) const { return m_pimpl->lookup_collatename(p1, p2); } bool isctype(charT c, char_class_type f) const { typedef typename std::ctype::mask ctype_mask; static const ctype_mask mask_base = static_cast( std::ctype::alnum | std::ctype::alpha | std::ctype::cntrl | std::ctype::digit | std::ctype::graph | std::ctype::lower | std::ctype::print | std::ctype::punct | std::ctype::space | std::ctype::upper | std::ctype::xdigit); if((f & mask_base) && (m_pimpl->m_pctype->is( static_cast(f & mask_base), c))) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_unicode) && BOOST_REGEX_DETAIL_NS::is_extended(c)) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_word) && (c == '_')) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_blank) && m_pimpl->m_pctype->is(std::ctype::space, c) && !BOOST_REGEX_DETAIL_NS::is_separator(c)) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\v'))) return true; else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_horizontal) && this->isctype(c, std::ctype::space) && !this->isctype(c, BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation::mask_vertical)) return true; #ifdef __CYGWIN__ // // Cygwin has a buggy ctype facet, see https://www.cygwin.com/ml/cygwin/2012-08/msg00178.html: // else if((f & std::ctype::xdigit) == std::ctype::xdigit) { if((c >= 'a') && (c <= 'f')) return true; if((c >= 'A') && (c <= 'F')) return true; } #endif return false; } std::intmax_t toi(const charT*& p1, const charT* p2, int radix)const; int value(charT c, int radix)const { const charT* pc = &c; return (int)toi(pc, pc + 1, radix); } locale_type imbue(locale_type l) { std::locale result(getloc()); m_pimpl = BOOST_REGEX_DETAIL_NS::create_cpp_regex_traits(l); return result; } locale_type getloc()const { return m_pimpl->m_locale; } std::string error_string(regex_constants::error_type n) const { return m_pimpl->error_string(n); } // // extension: // set the name of the message catalog in use (defaults to "boost_regex"). // static std::string catalog_name(const std::string& name); static std::string get_catalog_name(); private: std::shared_ptr > m_pimpl; // // catalog name handler: // static std::string& get_catalog_name_inst(); #ifdef BOOST_HAS_THREADS static std::mutex& get_mutex_inst(); #endif }; template std::intmax_t cpp_regex_traits::toi(const charT*& first, const charT* last, int radix)const { BOOST_REGEX_DETAIL_NS::parser_buf sbuf; // buffer for parsing numbers. std::basic_istream is(&sbuf); // stream for parsing numbers. // we do NOT want to parse any thousands separators inside the stream: last = std::find(first, last, std::use_facet>(is.getloc()).thousands_sep()); sbuf.pubsetbuf(const_cast(static_cast(first)), static_cast(last-first)); is.clear(); if(std::abs(radix) == 16) is >> std::hex; else if(std::abs(radix) == 8) is >> std::oct; else is >> std::dec; std::intmax_t val; if(is >> val) { first = first + ((last - first) - sbuf.in_avail()); return val; } else return -1; } template std::string cpp_regex_traits::catalog_name(const std::string& name) { #ifdef BOOST_HAS_THREADS std::lock_guard lk(get_mutex_inst()); #endif std::string result(get_catalog_name_inst()); get_catalog_name_inst() = name; return result; } template std::string& cpp_regex_traits::get_catalog_name_inst() { static std::string s_name; return s_name; } template std::string cpp_regex_traits::get_catalog_name() { #ifdef BOOST_HAS_THREADS std::lock_guard lk(get_mutex_inst()); #endif std::string result(get_catalog_name_inst()); return result; } #ifdef BOOST_HAS_THREADS template std::mutex& cpp_regex_traits::get_mutex_inst() { static std::mutex s_mutex; return s_mutex; } #endif namespace BOOST_REGEX_DETAIL_NS { inline void cpp_regex_traits_char_layer::init() { // we need to start by initialising our syntax map so we know which // character is used for which purpose: std::memset(m_char_map, 0, sizeof(m_char_map)); #ifndef __IBMCPP__ std::messages::catalog cat = static_cast::catalog>(-1); #else std::messages::catalog cat = reinterpret_cast::catalog>(-1); #endif std::string cat_name(cpp_regex_traits::get_catalog_name()); if ((!cat_name.empty()) && (m_pmessages != 0)) { cat = this->m_pmessages->open( cat_name, this->m_locale); if ((int)cat < 0) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if ((int)cat >= 0) { #ifndef BOOST_NO_EXCEPTIONS try { #endif for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { string_type mss = this->m_pmessages->get(cat, 0, i, get_default_syntax(i)); for (string_type::size_type j = 0; j < mss.size(); ++j) { m_char_map[static_cast(mss[j])] = i; } } this->m_pmessages->close(cat); #ifndef BOOST_NO_EXCEPTIONS } catch (...) { this->m_pmessages->close(cat); throw; } #endif } else { for (regex_constants::syntax_type j = 1; j < regex_constants::syntax_max; ++j) { const char* ptr = get_default_syntax(j); while (ptr && *ptr) { m_char_map[static_cast(*ptr)] = j; ++ptr; } } } // // finish off by calculating our escape types: // unsigned char i = 'A'; do { if (m_char_map[i] == 0) { if (this->m_pctype->is(std::ctype_base::lower, i)) m_char_map[i] = regex_constants::escape_type_class; else if (this->m_pctype->is(std::ctype_base::upper, i)) m_char_map[i] = regex_constants::escape_type_not_class; } } while (0xFF != i++); } } // namespace detail } // boost #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/cregex.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE cregex.cpp * VERSION see * DESCRIPTION: Declares POSIX API functions * + boost::RegEx high level wrapper. */ #ifndef BOOST_RE_CREGEX_HPP_INCLUDED #define BOOST_RE_CREGEX_HPP_INCLUDED #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #include #include #ifndef BOOST_REGEX_STANDALONE #if !defined(BOOST_REGEX_NO_LIB) && !defined(BOOST_REGEX_SOURCE) && !defined(BOOST_ALL_NO_LIB) && defined(__cplusplus) # define BOOST_LIB_NAME boost_regex # if defined(BOOST_REGEX_DYN_LINK) || defined(BOOST_ALL_DYN_LINK) # define BOOST_DYN_LINK # endif # ifdef BOOST_REGEX_DIAG # define BOOST_LIB_DIAGNOSTIC # endif # include #endif #endif #ifdef __cplusplus #include #else #include #endif /* include these defs only for POSIX compatablity */ #ifdef __cplusplus namespace boost{ extern "C" { #endif #if defined(__cplusplus) typedef std::ptrdiff_t regoff_t; typedef std::size_t regsize_t; #else typedef ptrdiff_t regoff_t; typedef size_t regsize_t; #endif typedef struct { unsigned int re_magic; #ifdef __cplusplus std::size_t re_nsub; /* number of parenthesized subexpressions */ #else size_t re_nsub; #endif const char* re_endp; /* end pointer for REG_PEND */ void* guts; /* none of your business :-) */ match_flag_type eflags; /* none of your business :-) */ } regex_tA; #ifndef BOOST_NO_WREGEX typedef struct { unsigned int re_magic; #ifdef __cplusplus std::size_t re_nsub; /* number of parenthesized subexpressions */ #else size_t re_nsub; #endif const wchar_t* re_endp; /* end pointer for REG_PEND */ void* guts; /* none of your business :-) */ match_flag_type eflags; /* none of your business :-) */ } regex_tW; #endif typedef struct { regoff_t rm_so; /* start of match */ regoff_t rm_eo; /* end of match */ } regmatch_t; /* regcomp() flags */ typedef enum{ REG_BASIC = 0000, REG_EXTENDED = 0001, REG_ICASE = 0002, REG_NOSUB = 0004, REG_NEWLINE = 0010, REG_NOSPEC = 0020, REG_PEND = 0040, REG_DUMP = 0200, REG_NOCOLLATE = 0400, REG_ESCAPE_IN_LISTS = 01000, REG_NEWLINE_ALT = 02000, REG_PERLEX = 04000, REG_PERL = REG_EXTENDED | REG_NOCOLLATE | REG_ESCAPE_IN_LISTS | REG_PERLEX, REG_AWK = REG_EXTENDED | REG_ESCAPE_IN_LISTS, REG_GREP = REG_BASIC | REG_NEWLINE_ALT, REG_EGREP = REG_EXTENDED | REG_NEWLINE_ALT, REG_ASSERT = 15, REG_INVARG = 16, REG_ATOI = 255, /* convert name to number (!) */ REG_ITOA = 0400 /* convert number to name (!) */ } reg_comp_flags; /* regexec() flags */ typedef enum{ REG_NOTBOL = 00001, REG_NOTEOL = 00002, REG_STARTEND = 00004 } reg_exec_flags; /* * POSIX error codes: */ typedef unsigned reg_error_t; typedef reg_error_t reg_errcode_t; /* backwards compatibility */ static const reg_error_t REG_NOERROR = 0; /* Success. */ static const reg_error_t REG_NOMATCH = 1; /* Didn't find a match (for regexec). */ /* POSIX regcomp return error codes. (In the order listed in the standard.) */ static const reg_error_t REG_BADPAT = 2; /* Invalid pattern. */ static const reg_error_t REG_ECOLLATE = 3; /* Undefined collating element. */ static const reg_error_t REG_ECTYPE = 4; /* Invalid character class name. */ static const reg_error_t REG_EESCAPE = 5; /* Trailing backslash. */ static const reg_error_t REG_ESUBREG = 6; /* Invalid back reference. */ static const reg_error_t REG_EBRACK = 7; /* Unmatched left bracket. */ static const reg_error_t REG_EPAREN = 8; /* Parenthesis imbalance. */ static const reg_error_t REG_EBRACE = 9; /* Unmatched \{. */ static const reg_error_t REG_BADBR = 10; /* Invalid contents of \{\}. */ static const reg_error_t REG_ERANGE = 11; /* Invalid range end. */ static const reg_error_t REG_ESPACE = 12; /* Ran out of memory. */ static const reg_error_t REG_BADRPT = 13; /* No preceding re for repetition op. */ static const reg_error_t REG_EEND = 14; /* unexpected end of expression */ static const reg_error_t REG_ESIZE = 15; /* expression too big */ static const reg_error_t REG_ERPAREN = 8; /* = REG_EPAREN : unmatched right parenthesis */ static const reg_error_t REG_EMPTY = 17; /* empty expression */ static const reg_error_t REG_E_MEMORY = 15; /* = REG_ESIZE : out of memory */ static const reg_error_t REG_ECOMPLEXITY = 18; /* complexity too high */ static const reg_error_t REG_ESTACK = 19; /* out of stack space */ static const reg_error_t REG_E_PERL = 20; /* Perl (?...) error */ static const reg_error_t REG_E_UNKNOWN = 21; /* unknown error */ static const reg_error_t REG_ENOSYS = 21; /* = REG_E_UNKNOWN : Reserved. */ BOOST_REGEX_DECL int BOOST_REGEX_CCALL regcompA(regex_tA*, const char*, int); BOOST_REGEX_DECL regsize_t BOOST_REGEX_CCALL regerrorA(int, const regex_tA*, char*, regsize_t); BOOST_REGEX_DECL int BOOST_REGEX_CCALL regexecA(const regex_tA*, const char*, regsize_t, regmatch_t*, int); BOOST_REGEX_DECL void BOOST_REGEX_CCALL regfreeA(regex_tA*); #ifndef BOOST_NO_WREGEX BOOST_REGEX_DECL int BOOST_REGEX_CCALL regcompW(regex_tW*, const wchar_t*, int); BOOST_REGEX_DECL regsize_t BOOST_REGEX_CCALL regerrorW(int, const regex_tW*, wchar_t*, regsize_t); BOOST_REGEX_DECL int BOOST_REGEX_CCALL regexecW(const regex_tW*, const wchar_t*, regsize_t, regmatch_t*, int); BOOST_REGEX_DECL void BOOST_REGEX_CCALL regfreeW(regex_tW*); #endif #ifdef UNICODE #define regcomp regcompW #define regerror regerrorW #define regexec regexecW #define regfree regfreeW #define regex_t regex_tW #else #define regcomp regcompA #define regerror regerrorA #define regexec regexecA #define regfree regfreeA #define regex_t regex_tA #endif #ifdef __cplusplus } /* extern "C" */ } /* namespace */ #endif #endif /* include guard */ ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/error_type.hpp ================================================ /* * * Copyright (c) 2003-2005 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE error_type.hpp * VERSION see * DESCRIPTION: Declares regular expression error type enumerator. */ #ifndef BOOST_REGEX_ERROR_TYPE_HPP #define BOOST_REGEX_ERROR_TYPE_HPP #ifdef __cplusplus namespace boost{ #endif #ifdef __cplusplus namespace regex_constants{ enum error_type{ error_ok = 0, /* not used */ error_no_match = 1, /* not used */ error_bad_pattern = 2, error_collate = 3, error_ctype = 4, error_escape = 5, error_backref = 6, error_brack = 7, error_paren = 8, error_brace = 9, error_badbrace = 10, error_range = 11, error_space = 12, error_badrepeat = 13, error_end = 14, /* not used */ error_size = 15, error_right_paren = 16, /* not used */ error_empty = 17, error_complexity = 18, error_stack = 19, error_perl_extension = 20, error_unknown = 21 }; } } #endif /* __cplusplus */ #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/icu.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE icu.hpp * VERSION see * DESCRIPTION: Unicode regular expressions on top of the ICU Library. */ #ifndef BOOST_REGEX_ICU_V5_HPP #define BOOST_REGEX_ICU_V5_HPP #include #include #include #include #include #include #include #ifdef BOOST_REGEX_MSVC #pragma warning (push) #pragma warning (disable: 4251) #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ // // Implementation details: // class icu_regex_traits_implementation { typedef UChar32 char_type; typedef std::size_t size_type; typedef std::vector string_type; typedef U_NAMESPACE_QUALIFIER Locale locale_type; typedef std::uint_least32_t char_class_type; public: icu_regex_traits_implementation(const U_NAMESPACE_QUALIFIER Locale& l) : m_locale(l) { UErrorCode success = U_ZERO_ERROR; m_collator.reset(U_NAMESPACE_QUALIFIER Collator::createInstance(l, success)); if(U_SUCCESS(success) == 0) init_error(); m_collator->setStrength(U_NAMESPACE_QUALIFIER Collator::IDENTICAL); success = U_ZERO_ERROR; m_primary_collator.reset(U_NAMESPACE_QUALIFIER Collator::createInstance(l, success)); if(U_SUCCESS(success) == 0) init_error(); m_primary_collator->setStrength(U_NAMESPACE_QUALIFIER Collator::PRIMARY); } U_NAMESPACE_QUALIFIER Locale getloc()const { return m_locale; } string_type do_transform(const char_type* p1, const char_type* p2, const U_NAMESPACE_QUALIFIER Collator* pcoll) const { // TODO make thread safe!!!! : typedef u32_to_u16_iterator itt; itt i(p1), j(p2); std::vector< ::UChar> t(i, j); std::uint8_t result[100]; std::int32_t len; if (!t.empty()) len = pcoll->getSortKey(&*t.begin(), static_cast(t.size()), result, sizeof(result)); else len = pcoll->getSortKey(static_cast(0), static_cast(0), result, sizeof(result)); if (std::size_t(len) > sizeof(result)) { std::unique_ptr< std::uint8_t[]> presult(new ::uint8_t[len + 1]); if (!t.empty()) len = pcoll->getSortKey(&*t.begin(), static_cast(t.size()), presult.get(), len + 1); else len = pcoll->getSortKey(static_cast(0), static_cast(0), presult.get(), len + 1); if ((0 == presult[len - 1]) && (len > 1)) --len; return string_type(presult.get(), presult.get() + len); } if ((0 == result[len - 1]) && (len > 1)) --len; return string_type(result, result + len); } string_type transform(const char_type* p1, const char_type* p2) const { return do_transform(p1, p2, m_collator.get()); } string_type transform_primary(const char_type* p1, const char_type* p2) const { return do_transform(p1, p2, m_primary_collator.get()); } private: void init_error() { std::runtime_error e("Could not initialize ICU resources"); #ifndef BOOST_REGEX_STANDALONE boost::throw_exception(e); #else throw e; #endif } U_NAMESPACE_QUALIFIER Locale m_locale; // The ICU locale that we're using std::unique_ptr< U_NAMESPACE_QUALIFIER Collator> m_collator; // The full collation object std::unique_ptr< U_NAMESPACE_QUALIFIER Collator> m_primary_collator; // The primary collation object }; inline std::shared_ptr get_icu_regex_traits_implementation(const U_NAMESPACE_QUALIFIER Locale& loc) { return std::shared_ptr(new icu_regex_traits_implementation(loc)); } } class icu_regex_traits { public: typedef UChar32 char_type; typedef std::size_t size_type; typedef std::vector string_type; typedef U_NAMESPACE_QUALIFIER Locale locale_type; typedef std::uint64_t char_class_type; struct boost_extensions_tag{}; icu_regex_traits() : m_pimpl(BOOST_REGEX_DETAIL_NS::get_icu_regex_traits_implementation(U_NAMESPACE_QUALIFIER Locale())) { } static size_type length(const char_type* p) { size_type result = 0; while (*p) { ++p; ++result; } return result; } ::boost::regex_constants::syntax_type syntax_type(char_type c)const { return ((c < 0x7f) && (c > 0)) ? BOOST_REGEX_DETAIL_NS::get_default_syntax_type(static_cast(c)) : regex_constants::syntax_char; } ::boost::regex_constants::escape_syntax_type escape_syntax_type(char_type c) const { return ((c < 0x7f) && (c > 0)) ? BOOST_REGEX_DETAIL_NS::get_default_escape_syntax_type(static_cast(c)) : regex_constants::syntax_char; } char_type translate(char_type c) const { return c; } char_type translate_nocase(char_type c) const { return ::u_foldCase(c, U_FOLD_CASE_DEFAULT); } char_type translate(char_type c, bool icase) const { return icase ? translate_nocase(c) : translate(c); } char_type tolower(char_type c) const { return ::u_tolower(c); } char_type toupper(char_type c) const { return ::u_toupper(c); } string_type transform(const char_type* p1, const char_type* p2) const { return m_pimpl->transform(p1, p2); } string_type transform_primary(const char_type* p1, const char_type* p2) const { return m_pimpl->transform_primary(p1, p2); } char_class_type lookup_classname(const char_type* p1, const char_type* p2) const { constexpr char_class_type mask_blank = char_class_type(1) << offset_blank; constexpr char_class_type mask_space = char_class_type(1) << offset_space; constexpr char_class_type mask_xdigit = char_class_type(1) << offset_xdigit; constexpr char_class_type mask_underscore = char_class_type(1) << offset_underscore; constexpr char_class_type mask_unicode = char_class_type(1) << offset_unicode; //constexpr char_class_type mask_any = char_class_type(1) << offset_any; //constexpr char_class_type mask_ascii = char_class_type(1) << offset_ascii; constexpr char_class_type mask_horizontal = char_class_type(1) << offset_horizontal; constexpr char_class_type mask_vertical = char_class_type(1) << offset_vertical; static const char_class_type masks[] = { 0, U_GC_L_MASK | U_GC_ND_MASK, U_GC_L_MASK, mask_blank, U_GC_CC_MASK | U_GC_CF_MASK | U_GC_ZL_MASK | U_GC_ZP_MASK, U_GC_ND_MASK, U_GC_ND_MASK, (0x3FFFFFFFu) & ~(U_GC_CC_MASK | U_GC_CF_MASK | U_GC_CS_MASK | U_GC_CN_MASK | U_GC_Z_MASK), mask_horizontal, U_GC_LL_MASK, U_GC_LL_MASK, ~(U_GC_C_MASK), U_GC_P_MASK, char_class_type(U_GC_Z_MASK) | mask_space, char_class_type(U_GC_Z_MASK) | mask_space, U_GC_LU_MASK, mask_unicode, U_GC_LU_MASK, mask_vertical, char_class_type(U_GC_L_MASK | U_GC_ND_MASK | U_GC_MN_MASK) | mask_underscore, char_class_type(U_GC_L_MASK | U_GC_ND_MASK | U_GC_MN_MASK) | mask_underscore, char_class_type(U_GC_ND_MASK) | mask_xdigit, }; int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); if (idx >= 0) return masks[idx + 1]; char_class_type result = lookup_icu_mask(p1, p2); if (result != 0) return result; if (idx < 0) { string_type s(p1, p2); string_type::size_type i = 0; while (i < s.size()) { s[i] = static_cast((::u_tolower)(s[i])); if (::u_isspace(s[i]) || (s[i] == '-') || (s[i] == '_')) s.erase(s.begin() + i, s.begin() + i + 1); else { s[i] = static_cast((::u_tolower)(s[i])); ++i; } } if (!s.empty()) idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size()); if (idx >= 0) return masks[idx + 1]; if (!s.empty()) result = lookup_icu_mask(&*s.begin(), &*s.begin() + s.size()); if (result != 0) return result; } BOOST_REGEX_ASSERT(std::size_t(idx + 1) < sizeof(masks) / sizeof(masks[0])); return masks[idx + 1]; } string_type lookup_collatename(const char_type* p1, const char_type* p2) const { string_type result; if (std::find_if(p1, p2, std::bind(std::greater< ::UChar32>(), std::placeholders::_1, 0x7f)) == p2) { std::string s(p1, p2); // Try Unicode name: UErrorCode err = U_ZERO_ERROR; UChar32 c = ::u_charFromName(U_UNICODE_CHAR_NAME, s.c_str(), &err); if (U_SUCCESS(err)) { result.push_back(c); return result; } // Try Unicode-extended name: err = U_ZERO_ERROR; c = ::u_charFromName(U_EXTENDED_CHAR_NAME, s.c_str(), &err); if (U_SUCCESS(err)) { result.push_back(c); return result; } // try POSIX name: s = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(s); result.assign(s.begin(), s.end()); } if (result.empty() && (p2 - p1 == 1)) result.push_back(*p1); return result; } bool isctype(char_type c, char_class_type f) const { constexpr char_class_type mask_blank = char_class_type(1) << offset_blank; constexpr char_class_type mask_space = char_class_type(1) << offset_space; constexpr char_class_type mask_xdigit = char_class_type(1) << offset_xdigit; constexpr char_class_type mask_underscore = char_class_type(1) << offset_underscore; constexpr char_class_type mask_unicode = char_class_type(1) << offset_unicode; constexpr char_class_type mask_any = char_class_type(1) << offset_any; constexpr char_class_type mask_ascii = char_class_type(1) << offset_ascii; constexpr char_class_type mask_horizontal = char_class_type(1) << offset_horizontal; constexpr char_class_type mask_vertical = char_class_type(1) << offset_vertical; // check for standard catagories first: char_class_type m = char_class_type(static_cast(1) << u_charType(c)); if ((m & f) != 0) return true; // now check for special cases: if (((f & mask_blank) != 0) && u_isblank(c)) return true; if (((f & mask_space) != 0) && u_isspace(c)) return true; if (((f & mask_xdigit) != 0) && (u_digit(c, 16) >= 0)) return true; if (((f & mask_unicode) != 0) && (c >= 0x100)) return true; if (((f & mask_underscore) != 0) && (c == '_')) return true; if (((f & mask_any) != 0) && (c <= 0x10FFFF)) return true; if (((f & mask_ascii) != 0) && (c <= 0x7F)) return true; if (((f & mask_vertical) != 0) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == static_cast('\v')) || (m == U_GC_ZL_MASK) || (m == U_GC_ZP_MASK))) return true; if (((f & mask_horizontal) != 0) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && u_isspace(c) && (c != static_cast('\v'))) return true; return false; } std::intmax_t toi(const char_type*& p1, const char_type* p2, int radix)const { return BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this); } int value(char_type c, int radix)const { return u_digit(c, static_cast< std::int8_t>(radix)); } locale_type imbue(locale_type l) { locale_type result(m_pimpl->getloc()); m_pimpl = BOOST_REGEX_DETAIL_NS::get_icu_regex_traits_implementation(l); return result; } locale_type getloc()const { return locale_type(); } std::string error_string(::boost::regex_constants::error_type n) const { return BOOST_REGEX_DETAIL_NS::get_default_error_string(n); } private: icu_regex_traits(const icu_regex_traits&); icu_regex_traits& operator=(const icu_regex_traits&); // // define the bitmasks offsets we need for additional character properties: // enum{ offset_blank = U_CHAR_CATEGORY_COUNT, offset_space = U_CHAR_CATEGORY_COUNT+1, offset_xdigit = U_CHAR_CATEGORY_COUNT+2, offset_underscore = U_CHAR_CATEGORY_COUNT+3, offset_unicode = U_CHAR_CATEGORY_COUNT+4, offset_any = U_CHAR_CATEGORY_COUNT+5, offset_ascii = U_CHAR_CATEGORY_COUNT+6, offset_horizontal = U_CHAR_CATEGORY_COUNT+7, offset_vertical = U_CHAR_CATEGORY_COUNT+8 }; static char_class_type lookup_icu_mask(const ::UChar32* p1, const ::UChar32* p2) { //constexpr char_class_type mask_blank = char_class_type(1) << offset_blank; //constexpr char_class_type mask_space = char_class_type(1) << offset_space; //constexpr char_class_type mask_xdigit = char_class_type(1) << offset_xdigit; //constexpr char_class_type mask_underscore = char_class_type(1) << offset_underscore; //constexpr char_class_type mask_unicode = char_class_type(1) << offset_unicode; constexpr char_class_type mask_any = char_class_type(1) << offset_any; constexpr char_class_type mask_ascii = char_class_type(1) << offset_ascii; //constexpr char_class_type mask_horizontal = char_class_type(1) << offset_horizontal; //constexpr char_class_type mask_vertical = char_class_type(1) << offset_vertical; static const ::UChar32 prop_name_table[] = { /* any */ 'a', 'n', 'y', /* ascii */ 'a', 's', 'c', 'i', 'i', /* assigned */ 'a', 's', 's', 'i', 'g', 'n', 'e', 'd', /* c* */ 'c', '*', /* cc */ 'c', 'c', /* cf */ 'c', 'f', /* closepunctuation */ 'c', 'l', 'o', 's', 'e', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* cn */ 'c', 'n', /* co */ 'c', 'o', /* connectorpunctuation */ 'c', 'o', 'n', 'n', 'e', 'c', 't', 'o', 'r', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* control */ 'c', 'o', 'n', 't', 'r', 'o', 'l', /* cs */ 'c', 's', /* currencysymbol */ 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 's', 'y', 'm', 'b', 'o', 'l', /* dashpunctuation */ 'd', 'a', 's', 'h', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* decimaldigitnumber */ 'd', 'e', 'c', 'i', 'm', 'a', 'l', 'd', 'i', 'g', 'i', 't', 'n', 'u', 'm', 'b', 'e', 'r', /* enclosingmark */ 'e', 'n', 'c', 'l', 'o', 's', 'i', 'n', 'g', 'm', 'a', 'r', 'k', /* finalpunctuation */ 'f', 'i', 'n', 'a', 'l', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* format */ 'f', 'o', 'r', 'm', 'a', 't', /* initialpunctuation */ 'i', 'n', 'i', 't', 'i', 'a', 'l', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* l* */ 'l', '*', /* letter */ 'l', 'e', 't', 't', 'e', 'r', /* letternumber */ 'l', 'e', 't', 't', 'e', 'r', 'n', 'u', 'm', 'b', 'e', 'r', /* lineseparator */ 'l', 'i', 'n', 'e', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', /* ll */ 'l', 'l', /* lm */ 'l', 'm', /* lo */ 'l', 'o', /* lowercaseletter */ 'l', 'o', 'w', 'e', 'r', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r', /* lt */ 'l', 't', /* lu */ 'l', 'u', /* m* */ 'm', '*', /* mark */ 'm', 'a', 'r', 'k', /* mathsymbol */ 'm', 'a', 't', 'h', 's', 'y', 'm', 'b', 'o', 'l', /* mc */ 'm', 'c', /* me */ 'm', 'e', /* mn */ 'm', 'n', /* modifierletter */ 'm', 'o', 'd', 'i', 'f', 'i', 'e', 'r', 'l', 'e', 't', 't', 'e', 'r', /* modifiersymbol */ 'm', 'o', 'd', 'i', 'f', 'i', 'e', 'r', 's', 'y', 'm', 'b', 'o', 'l', /* n* */ 'n', '*', /* nd */ 'n', 'd', /* nl */ 'n', 'l', /* no */ 'n', 'o', /* nonspacingmark */ 'n', 'o', 'n', 's', 'p', 'a', 'c', 'i', 'n', 'g', 'm', 'a', 'r', 'k', /* notassigned */ 'n', 'o', 't', 'a', 's', 's', 'i', 'g', 'n', 'e', 'd', /* number */ 'n', 'u', 'm', 'b', 'e', 'r', /* openpunctuation */ 'o', 'p', 'e', 'n', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* other */ 'o', 't', 'h', 'e', 'r', /* otherletter */ 'o', 't', 'h', 'e', 'r', 'l', 'e', 't', 't', 'e', 'r', /* othernumber */ 'o', 't', 'h', 'e', 'r', 'n', 'u', 'm', 'b', 'e', 'r', /* otherpunctuation */ 'o', 't', 'h', 'e', 'r', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* othersymbol */ 'o', 't', 'h', 'e', 'r', 's', 'y', 'm', 'b', 'o', 'l', /* p* */ 'p', '*', /* paragraphseparator */ 'p', 'a', 'r', 'a', 'g', 'r', 'a', 'p', 'h', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', /* pc */ 'p', 'c', /* pd */ 'p', 'd', /* pe */ 'p', 'e', /* pf */ 'p', 'f', /* pi */ 'p', 'i', /* po */ 'p', 'o', /* privateuse */ 'p', 'r', 'i', 'v', 'a', 't', 'e', 'u', 's', 'e', /* ps */ 'p', 's', /* punctuation */ 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n', /* s* */ 's', '*', /* sc */ 's', 'c', /* separator */ 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', /* sk */ 's', 'k', /* sm */ 's', 'm', /* so */ 's', 'o', /* spaceseparator */ 's', 'p', 'a', 'c', 'e', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r', /* spacingcombiningmark */ 's', 'p', 'a', 'c', 'i', 'n', 'g', 'c', 'o', 'm', 'b', 'i', 'n', 'i', 'n', 'g', 'm', 'a', 'r', 'k', /* surrogate */ 's', 'u', 'r', 'r', 'o', 'g', 'a', 't', 'e', /* symbol */ 's', 'y', 'm', 'b', 'o', 'l', /* titlecase */ 't', 'i', 't', 'l', 'e', 'c', 'a', 's', 'e', /* titlecaseletter */ 't', 'i', 't', 'l', 'e', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r', /* uppercaseletter */ 'u', 'p', 'p', 'e', 'r', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r', /* z* */ 'z', '*', /* zl */ 'z', 'l', /* zp */ 'z', 'p', /* zs */ 'z', 's', }; static const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32> range_data[] = { { prop_name_table + 0, prop_name_table + 3, }, // any { prop_name_table + 3, prop_name_table + 8, }, // ascii { prop_name_table + 8, prop_name_table + 16, }, // assigned { prop_name_table + 16, prop_name_table + 18, }, // c* { prop_name_table + 18, prop_name_table + 20, }, // cc { prop_name_table + 20, prop_name_table + 22, }, // cf { prop_name_table + 22, prop_name_table + 38, }, // closepunctuation { prop_name_table + 38, prop_name_table + 40, }, // cn { prop_name_table + 40, prop_name_table + 42, }, // co { prop_name_table + 42, prop_name_table + 62, }, // connectorpunctuation { prop_name_table + 62, prop_name_table + 69, }, // control { prop_name_table + 69, prop_name_table + 71, }, // cs { prop_name_table + 71, prop_name_table + 85, }, // currencysymbol { prop_name_table + 85, prop_name_table + 100, }, // dashpunctuation { prop_name_table + 100, prop_name_table + 118, }, // decimaldigitnumber { prop_name_table + 118, prop_name_table + 131, }, // enclosingmark { prop_name_table + 131, prop_name_table + 147, }, // finalpunctuation { prop_name_table + 147, prop_name_table + 153, }, // format { prop_name_table + 153, prop_name_table + 171, }, // initialpunctuation { prop_name_table + 171, prop_name_table + 173, }, // l* { prop_name_table + 173, prop_name_table + 179, }, // letter { prop_name_table + 179, prop_name_table + 191, }, // letternumber { prop_name_table + 191, prop_name_table + 204, }, // lineseparator { prop_name_table + 204, prop_name_table + 206, }, // ll { prop_name_table + 206, prop_name_table + 208, }, // lm { prop_name_table + 208, prop_name_table + 210, }, // lo { prop_name_table + 210, prop_name_table + 225, }, // lowercaseletter { prop_name_table + 225, prop_name_table + 227, }, // lt { prop_name_table + 227, prop_name_table + 229, }, // lu { prop_name_table + 229, prop_name_table + 231, }, // m* { prop_name_table + 231, prop_name_table + 235, }, // mark { prop_name_table + 235, prop_name_table + 245, }, // mathsymbol { prop_name_table + 245, prop_name_table + 247, }, // mc { prop_name_table + 247, prop_name_table + 249, }, // me { prop_name_table + 249, prop_name_table + 251, }, // mn { prop_name_table + 251, prop_name_table + 265, }, // modifierletter { prop_name_table + 265, prop_name_table + 279, }, // modifiersymbol { prop_name_table + 279, prop_name_table + 281, }, // n* { prop_name_table + 281, prop_name_table + 283, }, // nd { prop_name_table + 283, prop_name_table + 285, }, // nl { prop_name_table + 285, prop_name_table + 287, }, // no { prop_name_table + 287, prop_name_table + 301, }, // nonspacingmark { prop_name_table + 301, prop_name_table + 312, }, // notassigned { prop_name_table + 312, prop_name_table + 318, }, // number { prop_name_table + 318, prop_name_table + 333, }, // openpunctuation { prop_name_table + 333, prop_name_table + 338, }, // other { prop_name_table + 338, prop_name_table + 349, }, // otherletter { prop_name_table + 349, prop_name_table + 360, }, // othernumber { prop_name_table + 360, prop_name_table + 376, }, // otherpunctuation { prop_name_table + 376, prop_name_table + 387, }, // othersymbol { prop_name_table + 387, prop_name_table + 389, }, // p* { prop_name_table + 389, prop_name_table + 407, }, // paragraphseparator { prop_name_table + 407, prop_name_table + 409, }, // pc { prop_name_table + 409, prop_name_table + 411, }, // pd { prop_name_table + 411, prop_name_table + 413, }, // pe { prop_name_table + 413, prop_name_table + 415, }, // pf { prop_name_table + 415, prop_name_table + 417, }, // pi { prop_name_table + 417, prop_name_table + 419, }, // po { prop_name_table + 419, prop_name_table + 429, }, // privateuse { prop_name_table + 429, prop_name_table + 431, }, // ps { prop_name_table + 431, prop_name_table + 442, }, // punctuation { prop_name_table + 442, prop_name_table + 444, }, // s* { prop_name_table + 444, prop_name_table + 446, }, // sc { prop_name_table + 446, prop_name_table + 455, }, // separator { prop_name_table + 455, prop_name_table + 457, }, // sk { prop_name_table + 457, prop_name_table + 459, }, // sm { prop_name_table + 459, prop_name_table + 461, }, // so { prop_name_table + 461, prop_name_table + 475, }, // spaceseparator { prop_name_table + 475, prop_name_table + 495, }, // spacingcombiningmark { prop_name_table + 495, prop_name_table + 504, }, // surrogate { prop_name_table + 504, prop_name_table + 510, }, // symbol { prop_name_table + 510, prop_name_table + 519, }, // titlecase { prop_name_table + 519, prop_name_table + 534, }, // titlecaseletter { prop_name_table + 534, prop_name_table + 549, }, // uppercaseletter { prop_name_table + 549, prop_name_table + 551, }, // z* { prop_name_table + 551, prop_name_table + 553, }, // zl { prop_name_table + 553, prop_name_table + 555, }, // zp { prop_name_table + 555, prop_name_table + 557, }, // zs }; static const icu_regex_traits::char_class_type icu_class_map[] = { mask_any, // any mask_ascii, // ascii (0x3FFFFFFFu) & ~(U_GC_CN_MASK), // assigned U_GC_C_MASK, // c* U_GC_CC_MASK, // cc U_GC_CF_MASK, // cf U_GC_PE_MASK, // closepunctuation U_GC_CN_MASK, // cn U_GC_CO_MASK, // co U_GC_PC_MASK, // connectorpunctuation U_GC_CC_MASK, // control U_GC_CS_MASK, // cs U_GC_SC_MASK, // currencysymbol U_GC_PD_MASK, // dashpunctuation U_GC_ND_MASK, // decimaldigitnumber U_GC_ME_MASK, // enclosingmark U_GC_PF_MASK, // finalpunctuation U_GC_CF_MASK, // format U_GC_PI_MASK, // initialpunctuation U_GC_L_MASK, // l* U_GC_L_MASK, // letter U_GC_NL_MASK, // letternumber U_GC_ZL_MASK, // lineseparator U_GC_LL_MASK, // ll U_GC_LM_MASK, // lm U_GC_LO_MASK, // lo U_GC_LL_MASK, // lowercaseletter U_GC_LT_MASK, // lt U_GC_LU_MASK, // lu U_GC_M_MASK, // m* U_GC_M_MASK, // mark U_GC_SM_MASK, // mathsymbol U_GC_MC_MASK, // mc U_GC_ME_MASK, // me U_GC_MN_MASK, // mn U_GC_LM_MASK, // modifierletter U_GC_SK_MASK, // modifiersymbol U_GC_N_MASK, // n* U_GC_ND_MASK, // nd U_GC_NL_MASK, // nl U_GC_NO_MASK, // no U_GC_MN_MASK, // nonspacingmark U_GC_CN_MASK, // notassigned U_GC_N_MASK, // number U_GC_PS_MASK, // openpunctuation U_GC_C_MASK, // other U_GC_LO_MASK, // otherletter U_GC_NO_MASK, // othernumber U_GC_PO_MASK, // otherpunctuation U_GC_SO_MASK, // othersymbol U_GC_P_MASK, // p* U_GC_ZP_MASK, // paragraphseparator U_GC_PC_MASK, // pc U_GC_PD_MASK, // pd U_GC_PE_MASK, // pe U_GC_PF_MASK, // pf U_GC_PI_MASK, // pi U_GC_PO_MASK, // po U_GC_CO_MASK, // privateuse U_GC_PS_MASK, // ps U_GC_P_MASK, // punctuation U_GC_S_MASK, // s* U_GC_SC_MASK, // sc U_GC_Z_MASK, // separator U_GC_SK_MASK, // sk U_GC_SM_MASK, // sm U_GC_SO_MASK, // so U_GC_ZS_MASK, // spaceseparator U_GC_MC_MASK, // spacingcombiningmark U_GC_CS_MASK, // surrogate U_GC_S_MASK, // symbol U_GC_LT_MASK, // titlecase U_GC_LT_MASK, // titlecaseletter U_GC_LU_MASK, // uppercaseletter U_GC_Z_MASK, // z* U_GC_ZL_MASK, // zl U_GC_ZP_MASK, // zp U_GC_ZS_MASK, // zs }; const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* ranges_begin = range_data; const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* ranges_end = range_data + (sizeof(range_data) / sizeof(range_data[0])); BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32> t = { p1, p2, }; const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* p = std::lower_bound(ranges_begin, ranges_end, t); if ((p != ranges_end) && (t == *p)) return icu_class_map[p - ranges_begin]; return 0; } std::shared_ptr< ::boost::BOOST_REGEX_DETAIL_NS::icu_regex_traits_implementation> m_pimpl; }; } // namespace boost namespace boost{ // types: typedef basic_regex< ::UChar32, icu_regex_traits> u32regex; typedef match_results u32match; typedef match_results u16match; // // Construction of 32-bit regex types from UTF-8 and UTF-16 primitives: // namespace BOOST_REGEX_DETAIL_NS{ template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const std::integral_constant*) { typedef boost::u8_to_u32_iterator conv_type; return u32regex(conv_type(i, i, j), conv_type(j, i, j), opt); } template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const std::integral_constant*) { typedef boost::u16_to_u32_iterator conv_type; return u32regex(conv_type(i, i, j), conv_type(j, i, j), opt); } template inline u32regex do_make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt, const std::integral_constant*) { return u32regex(i, j, opt); } } // BOOST_REGEX_UCHAR_IS_WCHAR_T // // Source inspection of unicode/umachine.h in ICU version 59 indicates that: // // On version 59, UChar is always char16_t in C++ mode (and uint16_t in C mode) // // On earlier versions, the logic is // // #if U_SIZEOF_WCHAR_T==2 // typedef wchar_t OldUChar; // #elif defined(__CHAR16_TYPE__) // typedef __CHAR16_TYPE__ OldUChar; // #else // typedef uint16_t OldUChar; // #endif // // That is, UChar is wchar_t only on versions below 59, when U_SIZEOF_WCHAR_T==2 // // Hence, #define BOOST_REGEX_UCHAR_IS_WCHAR_T (U_ICU_VERSION_MAJOR_NUM < 59 && U_SIZEOF_WCHAR_T == 2) #if BOOST_REGEX_UCHAR_IS_WCHAR_T static_assert((std::is_same::value), "Configuration logic has failed!"); #else static_assert(!(std::is_same::value), "Configuration logic has failed!"); #endif // // Construction from an iterator pair: // template inline u32regex make_u32regex(InputIterator i, InputIterator j, boost::regex_constants::syntax_option_type opt) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(i, j, opt, static_cast const*>(0)); } // // construction from UTF-8 nul-terminated strings: // inline u32regex make_u32regex(const char* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::strlen(p), opt, static_cast const*>(0)); } inline u32regex make_u32regex(const unsigned char* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::strlen(reinterpret_cast(p)), opt, static_cast const*>(0)); } // // construction from UTF-16 nul-terminated strings: // #ifndef BOOST_NO_WREGEX inline u32regex make_u32regex(const wchar_t* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::wcslen(p), opt, static_cast const*>(0)); } #endif #if !BOOST_REGEX_UCHAR_IS_WCHAR_T inline u32regex make_u32regex(const UChar* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + u_strlen(p), opt, static_cast const*>(0)); } #endif // // construction from basic_string class-template: // template inline u32regex make_u32regex(const std::basic_string& s, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(s.begin(), s.end(), opt, static_cast const*>(0)); } // // Construction from ICU string type: // inline u32regex make_u32regex(const U_NAMESPACE_QUALIFIER UnicodeString& s, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl) { return BOOST_REGEX_DETAIL_NS::do_make_u32regex(s.getBuffer(), s.getBuffer() + s.length(), opt, static_cast const*>(0)); } // // regex_match overloads that widen the character type as appropriate: // namespace BOOST_REGEX_DETAIL_NS{ template void copy_results(MR1& out, MR2 const& in, NSubs named_subs) { // copy results from an adapted MR2 match_results: out.set_size(in.size(), in.prefix().first.base(), in.suffix().second.base()); out.set_base(in.base().base()); out.set_named_subs(named_subs); for(int i = 0; i < (int)in.size(); ++i) { if(in[i].matched || !i) { out.set_first(in[i].first.base(), i); out.set_second(in[i].second.base(), i, in[i].matched); } } #ifdef BOOST_REGEX_MATCH_EXTRA // Copy full capture info as well: for(int i = 0; i < (int)in.size(); ++i) { if(in[i].captures().size()) { out[i].get_captures().assign(in[i].captures().size(), typename MR1::value_type()); for(int j = 0; j < (int)out[i].captures().size(); ++j) { out[i].get_captures()[j].first = in[i].captures()[j].first.base(); out[i].get_captures()[j].second = in[i].captures()[j].second.base(); out[i].get_captures()[j].matched = in[i].captures()[j].matched; } } } #endif } template inline bool do_regex_match(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, std::integral_constant const*) { return ::boost::regex_match(first, last, m, e, flags); } template bool do_regex_match(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, std::integral_constant const*) { typedef u16_to_u32_iterator conv_type; typedef match_results match_type; //typedef typename match_type::allocator_type alloc_type; match_type what; bool result = ::boost::regex_match(conv_type(first, first, last), conv_type(last, first, last), what, e, flags); // copy results across to m: if(result) copy_results(m, what, e.get_named_subs()); return result; } template bool do_regex_match(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, std::integral_constant const*) { typedef u8_to_u32_iterator conv_type; typedef match_results match_type; //typedef typename match_type::allocator_type alloc_type; match_type what; bool result = ::boost::regex_match(conv_type(first, first, last), conv_type(last, first, last), what, e, flags); // copy results across to m: if(result) copy_results(m, what, e.get_named_subs()); return result; } } // namespace BOOST_REGEX_DETAIL_NS template inline bool u32regex_match(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(first, last, m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const UChar* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+u_strlen(p), m, e, flags, static_cast const*>(0)); } #if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX) inline bool u32regex_match(const wchar_t* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::wcslen(p), m, e, flags, static_cast const*>(0)); } #endif inline bool u32regex_match(const char* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::strlen(p), m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const unsigned char* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::strlen((const char*)p), m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const std::string& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast const*>(0)); } #ifndef BOOST_NO_STD_WSTRING inline bool u32regex_match(const std::wstring& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast const*>(0)); } #endif inline bool u32regex_match(const U_NAMESPACE_QUALIFIER UnicodeString& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_match(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, static_cast const*>(0)); } // // regex_match overloads that do not return what matched: // template inline bool u32regex_match(BidiIterator first, BidiIterator last, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(first, last, m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const UChar* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+u_strlen(p), m, e, flags, static_cast const*>(0)); } #if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX) inline bool u32regex_match(const wchar_t* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::wcslen(p), m, e, flags, static_cast const*>(0)); } #endif inline bool u32regex_match(const char* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::strlen(p), m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const unsigned char* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::strlen((const char*)p), m, e, flags, static_cast const*>(0)); } inline bool u32regex_match(const std::string& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast const*>(0)); } #ifndef BOOST_NO_STD_WSTRING inline bool u32regex_match(const std::wstring& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast const*>(0)); } #endif inline bool u32regex_match(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_match(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, static_cast const*>(0)); } // // regex_search overloads that widen the character type as appropriate: // namespace BOOST_REGEX_DETAIL_NS{ template inline bool do_regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, BidiIterator base, std::integral_constant const*) { return ::boost::regex_search(first, last, m, e, flags, base); } template bool do_regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, BidiIterator base, std::integral_constant const*) { typedef u16_to_u32_iterator conv_type; typedef match_results match_type; //typedef typename match_type::allocator_type alloc_type; match_type what; bool result = ::boost::regex_search(conv_type(first, first, last), conv_type(last, first, last), what, e, flags, conv_type(base)); // copy results across to m: if(result) copy_results(m, what, e.get_named_subs()); return result; } template bool do_regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, BidiIterator base, std::integral_constant const*) { typedef u8_to_u32_iterator conv_type; typedef match_results match_type; //typedef typename match_type::allocator_type alloc_type; match_type what; bool result = ::boost::regex_search(conv_type(first, first, last), conv_type(last, first, last), what, e, flags, conv_type(base)); // copy results across to m: if(result) copy_results(m, what, e.get_named_subs()); return result; } } template inline bool u32regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, first, static_cast const*>(0)); } template inline bool u32regex_search(BidiIterator first, BidiIterator last, match_results& m, const u32regex& e, match_flag_type flags, BidiIterator base) { return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, base, static_cast const*>(0)); } inline bool u32regex_search(const UChar* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+u_strlen(p), m, e, flags, p, static_cast const*>(0)); } #if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX) inline bool u32regex_search(const wchar_t* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::wcslen(p), m, e, flags, p, static_cast const*>(0)); } #endif inline bool u32regex_search(const char* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::strlen(p), m, e, flags, p, static_cast const*>(0)); } inline bool u32regex_search(const unsigned char* p, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::strlen((const char*)p), m, e, flags, p, static_cast const*>(0)); } inline bool u32regex_search(const std::string& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast const*>(0)); } #ifndef BOOST_NO_STD_WSTRING inline bool u32regex_search(const std::wstring& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast const*>(0)); } #endif inline bool u32regex_search(const U_NAMESPACE_QUALIFIER UnicodeString& s, match_results& m, const u32regex& e, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::do_regex_search(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, s.getBuffer(), static_cast const*>(0)); } template inline bool u32regex_search(BidiIterator first, BidiIterator last, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, first, static_cast const*>(0)); } inline bool u32regex_search(const UChar* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+u_strlen(p), m, e, flags, p, static_cast const*>(0)); } #if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX) inline bool u32regex_search(const wchar_t* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::wcslen(p), m, e, flags, p, static_cast const*>(0)); } #endif inline bool u32regex_search(const char* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::strlen(p), m, e, flags, p, static_cast const*>(0)); } inline bool u32regex_search(const unsigned char* p, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::strlen((const char*)p), m, e, flags, p, static_cast const*>(0)); } inline bool u32regex_search(const std::string& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast const*>(0)); } #ifndef BOOST_NO_STD_WSTRING inline bool u32regex_search(const std::wstring& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast const*>(0)); } #endif inline bool u32regex_search(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, match_flag_type flags = match_default) { match_results m; return BOOST_REGEX_DETAIL_NS::do_regex_search(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, s.getBuffer(), static_cast const*>(0)); } // // overloads for regex_replace with utf-8 and utf-16 data types: // namespace BOOST_REGEX_DETAIL_NS{ template inline std::pair< boost::u8_to_u32_iterator, boost::u8_to_u32_iterator > make_utf32_seq(I i, I j, std::integral_constant const*) { return std::pair< boost::u8_to_u32_iterator, boost::u8_to_u32_iterator >(boost::u8_to_u32_iterator(i, i, j), boost::u8_to_u32_iterator(j, i, j)); } template inline std::pair< boost::u16_to_u32_iterator, boost::u16_to_u32_iterator > make_utf32_seq(I i, I j, std::integral_constant const*) { return std::pair< boost::u16_to_u32_iterator, boost::u16_to_u32_iterator >(boost::u16_to_u32_iterator(i, i, j), boost::u16_to_u32_iterator(j, i, j)); } template inline std::pair< I, I > make_utf32_seq(I i, I j, std::integral_constant const*) { return std::pair< I, I >(i, j); } template inline std::pair< boost::u8_to_u32_iterator, boost::u8_to_u32_iterator > make_utf32_seq(const charT* p, std::integral_constant const*) { std::size_t len = std::strlen((const char*)p); return std::pair< boost::u8_to_u32_iterator, boost::u8_to_u32_iterator >(boost::u8_to_u32_iterator(p, p, p+len), boost::u8_to_u32_iterator(p+len, p, p+len)); } template inline std::pair< boost::u16_to_u32_iterator, boost::u16_to_u32_iterator > make_utf32_seq(const charT* p, std::integral_constant const*) { std::size_t len = u_strlen((const UChar*)p); return std::pair< boost::u16_to_u32_iterator, boost::u16_to_u32_iterator >(boost::u16_to_u32_iterator(p, p, p + len), boost::u16_to_u32_iterator(p+len, p, p + len)); } template inline std::pair< const charT*, const charT* > make_utf32_seq(const charT* p, std::integral_constant const*) { return std::pair< const charT*, const charT* >(p, p+icu_regex_traits::length((UChar32 const*)p)); } template inline OutputIterator make_utf32_out(OutputIterator o, std::integral_constant const*) { return o; } template inline utf16_output_iterator make_utf32_out(OutputIterator o, std::integral_constant const*) { return o; } template inline utf8_output_iterator make_utf32_out(OutputIterator o, std::integral_constant const*) { return o; } template OutputIterator do_regex_replace(OutputIterator out, std::pair const& in, const u32regex& e, const std::pair& fmt, match_flag_type flags ) { // unfortunately we have to copy the format string in order to pass in onward: std::vector f; f.assign(fmt.first, fmt.second); regex_iterator i(in.first, in.second, e, flags); regex_iterator j; if(i == j) { if(!(flags & regex_constants::format_no_copy)) out = std::copy(in.first, in.second, out); } else { I1 last_m = in.first; while(i != j) { if(!(flags & regex_constants::format_no_copy)) out = std::copy(i->prefix().first, i->prefix().second, out); if(!f.empty()) out = ::boost::BOOST_REGEX_DETAIL_NS::regex_format_imp(out, *i, &*f.begin(), &*f.begin() + f.size(), flags, e.get_traits()); else out = ::boost::BOOST_REGEX_DETAIL_NS::regex_format_imp(out, *i, static_cast(0), static_cast(0), flags, e.get_traits()); last_m = (*i)[0].second; if(flags & regex_constants::format_first_only) break; ++i; } if(!(flags & regex_constants::format_no_copy)) out = std::copy(last_m, in.second, out); } return out; } template inline const BaseIterator& extract_output_base(const BaseIterator& b) { return b; } template inline BaseIterator extract_output_base(const utf8_output_iterator& b) { return b.base(); } template inline BaseIterator extract_output_base(const utf16_output_iterator& b) { return b.base(); } } // BOOST_REGEX_DETAIL_NS template inline OutputIterator u32regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const u32regex& e, const charT* fmt, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::extract_output_base ( BOOST_REGEX_DETAIL_NS::do_regex_replace( BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast const*>(0)), BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast const*>(0)), e, BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt, static_cast const*>(0)), flags) ); } template inline OutputIterator u32regex_replace(OutputIterator out, Iterator first, Iterator last, const u32regex& e, const std::basic_string& fmt, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::extract_output_base ( BOOST_REGEX_DETAIL_NS::do_regex_replace( BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast const*>(0)), BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast const*>(0)), e, BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.begin(), fmt.end(), static_cast const*>(0)), flags) ); } template inline OutputIterator u32regex_replace(OutputIterator out, Iterator first, Iterator last, const u32regex& e, const U_NAMESPACE_QUALIFIER UnicodeString& fmt, match_flag_type flags = match_default) { return BOOST_REGEX_DETAIL_NS::extract_output_base ( BOOST_REGEX_DETAIL_NS::do_regex_replace( BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast const*>(0)), BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast const*>(0)), e, BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.getBuffer(), fmt.getBuffer() + fmt.length(), static_cast const*>(0)), flags) ); } template std::basic_string u32regex_replace(const std::basic_string& s, const u32regex& e, const charT* fmt, match_flag_type flags = match_default) { std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); u32regex_replace(i, s.begin(), s.end(), e, fmt, flags); return result; } template std::basic_string u32regex_replace(const std::basic_string& s, const u32regex& e, const std::basic_string& fmt, match_flag_type flags = match_default) { std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); u32regex_replace(i, s.begin(), s.end(), e, fmt.c_str(), flags); return result; } namespace BOOST_REGEX_DETAIL_NS{ class unicode_string_out_iterator { U_NAMESPACE_QUALIFIER UnicodeString* out; public: unicode_string_out_iterator(U_NAMESPACE_QUALIFIER UnicodeString& s) : out(&s) {} unicode_string_out_iterator& operator++() { return *this; } unicode_string_out_iterator& operator++(int) { return *this; } unicode_string_out_iterator& operator*() { return *this; } unicode_string_out_iterator& operator=(UChar v) { *out += v; return *this; } typedef std::ptrdiff_t difference_type; typedef UChar value_type; typedef value_type* pointer; typedef value_type& reference; typedef std::output_iterator_tag iterator_category; }; } inline U_NAMESPACE_QUALIFIER UnicodeString u32regex_replace(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const UChar* fmt, match_flag_type flags = match_default) { U_NAMESPACE_QUALIFIER UnicodeString result; BOOST_REGEX_DETAIL_NS::unicode_string_out_iterator i(result); u32regex_replace(i, s.getBuffer(), s.getBuffer()+s.length(), e, fmt, flags); return result; } inline U_NAMESPACE_QUALIFIER UnicodeString u32regex_replace(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const U_NAMESPACE_QUALIFIER UnicodeString& fmt, match_flag_type flags = match_default) { U_NAMESPACE_QUALIFIER UnicodeString result; BOOST_REGEX_DETAIL_NS::unicode_string_out_iterator i(result); BOOST_REGEX_DETAIL_NS::do_regex_replace( BOOST_REGEX_DETAIL_NS::make_utf32_out(i, static_cast const*>(0)), BOOST_REGEX_DETAIL_NS::make_utf32_seq(s.getBuffer(), s.getBuffer()+s.length(), static_cast const*>(0)), e, BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.getBuffer(), fmt.getBuffer() + fmt.length(), static_cast const*>(0)), flags); return result; } } // namespace boost. #ifdef BOOST_REGEX_MSVC #pragma warning (pop) #endif #include #include #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/iterator_category.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_match.hpp * VERSION see * DESCRIPTION: Iterator traits for selecting an iterator type as * an integral constant expression. */ #ifndef BOOST_REGEX_ITERATOR_CATEGORY_HPP #define BOOST_REGEX_ITERATOR_CATEGORY_HPP #include #include namespace boost{ namespace detail{ template struct is_random_imp { private: typedef typename std::iterator_traits::iterator_category cat; public: static const bool value = (std::is_convertible::value); }; template struct is_random_pointer_imp { static const bool value = true; }; template struct is_random_imp_selector { template struct rebind { typedef is_random_imp type; }; }; template <> struct is_random_imp_selector { template struct rebind { typedef is_random_pointer_imp type; }; }; } template struct is_random_access_iterator { private: typedef detail::is_random_imp_selector< std::is_pointer::value> selector; typedef typename selector::template rebind bound_type; typedef typename bound_type::type answer; public: static const bool value = answer::value; }; template const bool is_random_access_iterator::value; } #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/iterator_traits.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE iterator_traits.cpp * VERSION see * DESCRIPTION: Declares iterator traits workarounds. */ #ifndef BOOST_REGEX_V5_ITERATOR_TRAITS_HPP #define BOOST_REGEX_V5_ITERATOR_TRAITS_HPP namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ template struct regex_iterator_traits : public std::iterator_traits {}; } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/match_flags.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE match_flags.hpp * VERSION see * DESCRIPTION: Declares match_flags type. */ #ifndef BOOST_REGEX_V5_MATCH_FLAGS #define BOOST_REGEX_V5_MATCH_FLAGS #ifdef __cplusplus # include #endif #ifdef __cplusplus namespace boost{ namespace regex_constants{ #endif #ifdef BOOST_REGEX_MSVC #pragma warning(push) #if BOOST_REGEX_MSVC >= 1800 #pragma warning(disable : 26812) #endif #endif typedef enum _match_flags { match_default = 0, match_not_bol = 1, /* first is not start of line */ match_not_eol = match_not_bol << 1, /* last is not end of line */ match_not_bob = match_not_eol << 1, /* first is not start of buffer */ match_not_eob = match_not_bob << 1, /* last is not end of buffer */ match_not_bow = match_not_eob << 1, /* first is not start of word */ match_not_eow = match_not_bow << 1, /* last is not end of word */ match_not_dot_newline = match_not_eow << 1, /* \n is not matched by '.' */ match_not_dot_null = match_not_dot_newline << 1, /* '\0' is not matched by '.' */ match_prev_avail = match_not_dot_null << 1, /* *--first is a valid expression */ match_init = match_prev_avail << 1, /* internal use */ match_any = match_init << 1, /* don't care what we match */ match_not_null = match_any << 1, /* string can't be null */ match_continuous = match_not_null << 1, /* each grep match must continue from */ /* uninterrupted from the previous one */ match_partial = match_continuous << 1, /* find partial matches */ match_stop = match_partial << 1, /* stop after first match (grep) V3 only */ match_not_initial_null = match_stop, /* don't match initial null, V4 only */ match_all = match_stop << 1, /* must find the whole of input even if match_any is set */ match_perl = match_all << 1, /* Use perl matching rules */ match_posix = match_perl << 1, /* Use POSIX matching rules */ match_nosubs = match_posix << 1, /* don't trap marked subs */ match_extra = match_nosubs << 1, /* include full capture information for repeated captures */ match_single_line = match_extra << 1, /* treat text as single line and ignore any \n's when matching ^ and $. */ match_unused1 = match_single_line << 1, /* unused */ match_unused2 = match_unused1 << 1, /* unused */ match_unused3 = match_unused2 << 1, /* unused */ match_max = match_unused3, format_perl = 0, /* perl style replacement */ format_default = 0, /* ditto. */ format_sed = match_max << 1, /* sed style replacement. */ format_all = format_sed << 1, /* enable all extensions to syntax. */ format_no_copy = format_all << 1, /* don't copy non-matching segments. */ format_first_only = format_no_copy << 1, /* Only replace first occurrence. */ format_is_if = format_first_only << 1, /* internal use only. */ format_literal = format_is_if << 1, /* treat string as a literal */ match_not_any = match_not_bol | match_not_eol | match_not_bob | match_not_eob | match_not_bow | match_not_eow | match_not_dot_newline | match_not_dot_null | match_prev_avail | match_init | match_not_null | match_continuous | match_partial | match_stop | match_not_initial_null | match_stop | match_all | match_perl | match_posix | match_nosubs | match_extra | match_single_line | match_unused1 | match_unused2 | match_unused3 | match_max | format_perl | format_default | format_sed | format_all | format_no_copy | format_first_only | format_is_if | format_literal } match_flags; typedef match_flags match_flag_type; #ifdef __cplusplus inline match_flags operator&(match_flags m1, match_flags m2) { return static_cast(static_cast(m1) & static_cast(m2)); } inline match_flags operator|(match_flags m1, match_flags m2) { return static_cast(static_cast(m1) | static_cast(m2)); } inline match_flags operator^(match_flags m1, match_flags m2) { return static_cast(static_cast(m1) ^ static_cast(m2)); } inline match_flags operator~(match_flags m1) { return static_cast(~static_cast(m1)); } inline match_flags& operator&=(match_flags& m1, match_flags m2) { m1 = m1&m2; return m1; } inline match_flags& operator|=(match_flags& m1, match_flags m2) { m1 = m1|m2; return m1; } inline match_flags& operator^=(match_flags& m1, match_flags m2) { m1 = m1^m2; return m1; } #endif #ifdef __cplusplus } /* namespace regex_constants */ /* * import names into boost for backwards compatibility: */ using regex_constants::match_flag_type; using regex_constants::match_default; using regex_constants::match_not_bol; using regex_constants::match_not_eol; using regex_constants::match_not_bob; using regex_constants::match_not_eob; using regex_constants::match_not_bow; using regex_constants::match_not_eow; using regex_constants::match_not_dot_newline; using regex_constants::match_not_dot_null; using regex_constants::match_prev_avail; /* using regex_constants::match_init; */ using regex_constants::match_any; using regex_constants::match_not_null; using regex_constants::match_continuous; using regex_constants::match_partial; /*using regex_constants::match_stop; */ using regex_constants::match_all; using regex_constants::match_perl; using regex_constants::match_posix; using regex_constants::match_nosubs; using regex_constants::match_extra; using regex_constants::match_single_line; /*using regex_constants::match_max; */ using regex_constants::format_all; using regex_constants::format_sed; using regex_constants::format_perl; using regex_constants::format_default; using regex_constants::format_no_copy; using regex_constants::format_first_only; /*using regex_constants::format_is_if;*/ #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } /* namespace boost */ #endif /* __cplusplus */ #endif /* include guard */ ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/match_results.hpp ================================================ /* * * Copyright (c) 1998-2009 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE match_results.cpp * VERSION see * DESCRIPTION: Declares template class match_results. */ #ifndef BOOST_REGEX_V5_MATCH_RESULTS_HPP #define BOOST_REGEX_V5_MATCH_RESULTS_HPP namespace boost{ #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable : 4251 4459) #if BOOST_REGEX_MSVC < 1700 # pragma warning(disable : 4231) #endif # if BOOST_REGEX_MSVC < 1600 # pragma warning(disable : 4660) # endif #endif namespace BOOST_REGEX_DETAIL_NS{ class named_subexpressions; } template class match_results { private: typedef std::vector, Allocator> vector_type; public: typedef sub_match value_type; typedef typename std::allocator_traits::value_type const & const_reference; typedef const_reference reference; typedef typename vector_type::const_iterator const_iterator; typedef const_iterator iterator; typedef typename std::iterator_traits< BidiIterator>::difference_type difference_type; typedef typename std::allocator_traits::size_type size_type; typedef Allocator allocator_type; typedef typename std::iterator_traits< BidiIterator>::value_type char_type; typedef std::basic_string string_type; typedef BOOST_REGEX_DETAIL_NS::named_subexpressions named_sub_type; // construct/copy/destroy: explicit match_results(const Allocator& a = Allocator()) : m_subs(a), m_base(), m_null(), m_last_closed_paren(0), m_is_singular(true) {} // // IMPORTANT: in the code below, the crazy looking checks around m_is_singular are // all required because it is illegal to copy a singular iterator. // See https://svn.boost.org/trac/boost/ticket/3632. // match_results(const match_results& m) : m_subs(m.m_subs), m_base(), m_null(), m_named_subs(m.m_named_subs), m_last_closed_paren(m.m_last_closed_paren), m_is_singular(m.m_is_singular) { if(!m_is_singular) { m_base = m.m_base; m_null = m.m_null; } } match_results& operator=(const match_results& m) { m_subs = m.m_subs; m_named_subs = m.m_named_subs; m_last_closed_paren = m.m_last_closed_paren; m_is_singular = m.m_is_singular; if(!m_is_singular) { m_base = m.m_base; m_null = m.m_null; } return *this; } ~match_results(){} // size: size_type size() const { return empty() ? 0 : m_subs.size() - 2; } size_type max_size() const { return m_subs.max_size(); } bool empty() const { return m_subs.size() < 2; } // element access: difference_type length(int sub = 0) const { if(m_is_singular) raise_logic_error(); sub += 2; if((sub < (int)m_subs.size()) && (sub > 0)) return m_subs[sub].length(); return 0; } difference_type length(const char_type* sub) const { if(m_is_singular) raise_logic_error(); const char_type* sub_end = sub; while(*sub_end) ++sub_end; return length(named_subexpression_index(sub, sub_end)); } template difference_type length(const charT* sub) const { if(m_is_singular) raise_logic_error(); const charT* sub_end = sub; while(*sub_end) ++sub_end; return length(named_subexpression_index(sub, sub_end)); } template difference_type length(const std::basic_string& sub) const { return length(sub.c_str()); } difference_type position(size_type sub = 0) const { if(m_is_singular) raise_logic_error(); sub += 2; if(sub < m_subs.size()) { const sub_match& s = m_subs[sub]; if(s.matched || (sub == 2)) { return std::distance((BidiIterator)(m_base), (BidiIterator)(s.first)); } } return ~static_cast(0); } difference_type position(const char_type* sub) const { const char_type* sub_end = sub; while(*sub_end) ++sub_end; return position(named_subexpression_index(sub, sub_end)); } template difference_type position(const charT* sub) const { const charT* sub_end = sub; while(*sub_end) ++sub_end; return position(named_subexpression_index(sub, sub_end)); } template difference_type position(const std::basic_string& sub) const { return position(sub.c_str()); } string_type str(int sub = 0) const { if(m_is_singular) raise_logic_error(); sub += 2; string_type result; if(sub < (int)m_subs.size() && (sub > 0)) { const sub_match& s = m_subs[sub]; if(s.matched) { result = s.str(); } } return result; } string_type str(const char_type* sub) const { return (*this)[sub].str(); } template string_type str(const std::basic_string& sub) const { return (*this)[sub].str(); } template string_type str(const charT* sub) const { return (*this)[sub].str(); } template string_type str(const std::basic_string& sub) const { return (*this)[sub].str(); } const_reference operator[](int sub) const { if(m_is_singular && m_subs.empty()) raise_logic_error(); sub += 2; if(sub < (int)m_subs.size() && (sub >= 0)) { return m_subs[sub]; } return m_null; } // // Named sub-expressions: // const_reference named_subexpression(const char_type* i, const char_type* j) const { // // Scan for the leftmost *matched* subexpression with the specified named: // if(m_is_singular) raise_logic_error(); BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type r = m_named_subs->equal_range(i, j); while((r.first != r.second) && ((*this)[r.first->index].matched == false)) ++r.first; return r.first != r.second ? (*this)[r.first->index] : m_null; } template const_reference named_subexpression(const charT* i, const charT* j) const { static_assert(sizeof(charT) <= sizeof(char_type), "Failed internal logic"); if(i == j) return m_null; std::vector s; while(i != j) s.insert(s.end(), *i++); return named_subexpression(&*s.begin(), &*s.begin() + s.size()); } int named_subexpression_index(const char_type* i, const char_type* j) const { // // Scan for the leftmost *matched* subexpression with the specified named. // If none found then return the leftmost expression with that name, // otherwise an invalid index: // if(m_is_singular) raise_logic_error(); BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type s, r; s = r = m_named_subs->equal_range(i, j); while((r.first != r.second) && ((*this)[r.first->index].matched == false)) ++r.first; if(r.first == r.second) r = s; return r.first != r.second ? r.first->index : -20; } template int named_subexpression_index(const charT* i, const charT* j) const { static_assert(sizeof(charT) <= sizeof(char_type), "Failed internal logic"); if(i == j) return -20; std::vector s; while(i != j) s.insert(s.end(), *i++); return named_subexpression_index(&*s.begin(), &*s.begin() + s.size()); } template const_reference operator[](const std::basic_string& s) const { return named_subexpression(s.c_str(), s.c_str() + s.size()); } const_reference operator[](const char_type* p) const { const char_type* e = p; while(*e) ++e; return named_subexpression(p, e); } template const_reference operator[](const charT* p) const { static_assert(sizeof(charT) <= sizeof(char_type), "Failed internal logic"); if(*p == 0) return m_null; std::vector s; while(*p) s.insert(s.end(), *p++); return named_subexpression(&*s.begin(), &*s.begin() + s.size()); } template const_reference operator[](const std::basic_string& ns) const { static_assert(sizeof(charT) <= sizeof(char_type), "Failed internal logic"); if(ns.empty()) return m_null; std::vector s; for(unsigned i = 0; i < ns.size(); ++i) s.insert(s.end(), ns[i]); return named_subexpression(&*s.begin(), &*s.begin() + s.size()); } const_reference prefix() const { if(m_is_singular) raise_logic_error(); return (*this)[-1]; } const_reference suffix() const { if(m_is_singular) raise_logic_error(); return (*this)[-2]; } const_iterator begin() const { return (m_subs.size() > 2) ? (m_subs.begin() + 2) : m_subs.end(); } const_iterator end() const { return m_subs.end(); } // format: template OutputIterator format(OutputIterator out, Functor fmt, match_flag_type flags = format_default) const { if(m_is_singular) raise_logic_error(); typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type, OutputIterator>::type F; F func(fmt); return func(*this, out, flags); } template string_type format(Functor fmt, match_flag_type flags = format_default) const { if(m_is_singular) raise_logic_error(); std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type, BOOST_REGEX_DETAIL_NS::string_out_iterator > >::type F; F func(fmt); func(*this, i, flags); return result; } // format with locale: template OutputIterator format(OutputIterator out, Functor fmt, match_flag_type flags, const RegexT& re) const { if(m_is_singular) raise_logic_error(); typedef ::boost::regex_traits_wrapper traits_type; typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type, OutputIterator, traits_type>::type F; F func(fmt); return func(*this, out, flags, re.get_traits()); } template string_type format(Functor fmt, match_flag_type flags, const RegexT& re) const { if(m_is_singular) raise_logic_error(); typedef ::boost::regex_traits_wrapper traits_type; std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type, BOOST_REGEX_DETAIL_NS::string_out_iterator >, traits_type >::type F; F func(fmt); func(*this, i, flags, re.get_traits()); return result; } const_reference get_last_closed_paren()const { if(m_is_singular) raise_logic_error(); return m_last_closed_paren == 0 ? m_null : (*this)[m_last_closed_paren]; } allocator_type get_allocator() const { return m_subs.get_allocator(); } void swap(match_results& that) { std::swap(m_subs, that.m_subs); std::swap(m_named_subs, that.m_named_subs); std::swap(m_last_closed_paren, that.m_last_closed_paren); if(m_is_singular) { if(!that.m_is_singular) { m_base = that.m_base; m_null = that.m_null; } } else if(that.m_is_singular) { that.m_base = m_base; that.m_null = m_null; } else { std::swap(m_base, that.m_base); std::swap(m_null, that.m_null); } std::swap(m_is_singular, that.m_is_singular); } bool operator==(const match_results& that)const { if(m_is_singular) { return that.m_is_singular; } else if(that.m_is_singular) { return false; } return (m_subs == that.m_subs) && (m_base == that.m_base) && (m_last_closed_paren == that.m_last_closed_paren); } bool operator!=(const match_results& that)const { return !(*this == that); } #ifdef BOOST_REGEX_MATCH_EXTRA typedef typename sub_match::capture_sequence_type capture_sequence_type; const capture_sequence_type& captures(int i)const { if(m_is_singular) raise_logic_error(); return (*this)[i].captures(); } #endif // // private access functions: void set_second(BidiIterator i) { BOOST_REGEX_ASSERT(m_subs.size() > 2); m_subs[2].second = i; m_subs[2].matched = true; m_subs[0].first = i; m_subs[0].matched = (m_subs[0].first != m_subs[0].second); m_null.first = i; m_null.second = i; m_null.matched = false; m_is_singular = false; } void set_second(BidiIterator i, size_type pos, bool m = true, bool escape_k = false) { if(pos) m_last_closed_paren = static_cast(pos); pos += 2; BOOST_REGEX_ASSERT(m_subs.size() > pos); m_subs[pos].second = i; m_subs[pos].matched = m; if((pos == 2) && !escape_k) { m_subs[0].first = i; m_subs[0].matched = (m_subs[0].first != m_subs[0].second); m_null.first = i; m_null.second = i; m_null.matched = false; m_is_singular = false; } } void set_size(size_type n, BidiIterator i, BidiIterator j) { value_type v(j); size_type len = m_subs.size(); if(len > n + 2) { m_subs.erase(m_subs.begin()+n+2, m_subs.end()); std::fill(m_subs.begin(), m_subs.end(), v); } else { std::fill(m_subs.begin(), m_subs.end(), v); if(n+2 != len) m_subs.insert(m_subs.end(), n+2-len, v); } m_subs[1].first = i; m_last_closed_paren = 0; } void set_base(BidiIterator pos) { m_base = pos; } BidiIterator base()const { return m_base; } void set_first(BidiIterator i) { BOOST_REGEX_ASSERT(m_subs.size() > 2); // set up prefix: m_subs[1].second = i; m_subs[1].matched = (m_subs[1].first != i); // set up $0: m_subs[2].first = i; // zero out everything else: for(size_type n = 3; n < m_subs.size(); ++n) { m_subs[n].first = m_subs[n].second = m_subs[0].second; m_subs[n].matched = false; } } void set_first(BidiIterator i, size_type pos, bool escape_k = false) { BOOST_REGEX_ASSERT(pos+2 < m_subs.size()); if(pos || escape_k) { m_subs[pos+2].first = i; if(escape_k) { m_subs[1].second = i; m_subs[1].matched = (m_subs[1].first != m_subs[1].second); } } else set_first(i); } void maybe_assign(const match_results& m); void set_named_subs(std::shared_ptr subs) { m_named_subs = subs; } private: // // Error handler called when an uninitialized match_results is accessed: // static void raise_logic_error() { std::logic_error e("Attempt to access an uninitialized boost::match_results<> class."); #ifndef BOOST_REGEX_STANDALONE boost::throw_exception(e); #else throw e; #endif } vector_type m_subs; // subexpressions BidiIterator m_base; // where the search started from sub_match m_null; // a null match std::shared_ptr m_named_subs; // Shared copy of named subs in the regex object int m_last_closed_paren; // Last ) to be seen - used for formatting bool m_is_singular; // True if our stored iterators are singular }; template void match_results::maybe_assign(const match_results& m) { if(m_is_singular) { *this = m; return; } const_iterator p1, p2; p1 = begin(); p2 = m.begin(); // // Distances are measured from the start of *this* match, unless this isn't // a valid match in which case we use the start of the whole sequence. Note that // no subsequent match-candidate can ever be to the left of the first match found. // This ensures that when we are using bidirectional iterators, that distances // measured are as short as possible, and therefore as efficient as possible // to compute. Finally note that we don't use the "matched" data member to test // whether a sub-expression is a valid match, because partial matches set this // to false for sub-expression 0. // BidiIterator l_end = this->suffix().second; BidiIterator l_base = (p1->first == l_end) ? this->prefix().first : (*this)[0].first; difference_type len1 = 0; difference_type len2 = 0; difference_type base1 = 0; difference_type base2 = 0; std::size_t i; for(i = 0; i < size(); ++i, ++p1, ++p2) { // // Leftmost takes priority over longest; handle special cases // where distances need not be computed first (an optimisation // for bidirectional iterators: ensure that we don't accidently // compute the length of the whole sequence, as this can be really // expensive). // if(p1->first == l_end) { if(p2->first != l_end) { // p2 must be better than p1, and no need to calculate // actual distances: base1 = 1; base2 = 0; break; } else { // *p1 and *p2 are either unmatched or match end-of sequence, // either way no need to calculate distances: if((p1->matched == false) && (p2->matched == true)) break; if((p1->matched == true) && (p2->matched == false)) return; continue; } } else if(p2->first == l_end) { // p1 better than p2, and no need to calculate distances: return; } base1 = std::distance(l_base, p1->first); base2 = std::distance(l_base, p2->first); BOOST_REGEX_ASSERT(base1 >= 0); BOOST_REGEX_ASSERT(base2 >= 0); if(base1 < base2) return; if(base2 < base1) break; len1 = std::distance((BidiIterator)p1->first, (BidiIterator)p1->second); len2 = std::distance((BidiIterator)p2->first, (BidiIterator)p2->second); BOOST_REGEX_ASSERT(len1 >= 0); BOOST_REGEX_ASSERT(len2 >= 0); if((len1 != len2) || ((p1->matched == false) && (p2->matched == true))) break; if((p1->matched == true) && (p2->matched == false)) return; } if(i == size()) return; if(base2 < base1) *this = m; else if((len2 > len1) || ((p1->matched == false) && (p2->matched == true)) ) *this = m; } template void swap(match_results& a, match_results& b) { a.swap(b); } template std::basic_ostream& operator << (std::basic_ostream& os, const match_results& s) { return (os << s.str()); } #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/mem_block_cache.hpp ================================================ /* * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE mem_block_cache.hpp * VERSION see * DESCRIPTION: memory block cache used by the non-recursive matcher. */ #ifndef BOOST_REGEX_V5_MEM_BLOCK_CACHE_HPP #define BOOST_REGEX_V5_MEM_BLOCK_CACHE_HPP #include #ifdef BOOST_HAS_THREADS #include #endif #ifndef BOOST_NO_CXX11_HDR_ATOMIC #include #if ATOMIC_POINTER_LOCK_FREE == 2 #define BOOST_REGEX_MEM_BLOCK_CACHE_LOCK_FREE #define BOOST_REGEX_ATOMIC_POINTER std::atomic #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #if BOOST_REGEX_MAX_CACHE_BLOCKS != 0 #ifdef BOOST_REGEX_MEM_BLOCK_CACHE_LOCK_FREE /* lock free implementation */ struct mem_block_cache { std::atomic cache[BOOST_REGEX_MAX_CACHE_BLOCKS]; ~mem_block_cache() { for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) { if (cache[i].load()) ::operator delete(cache[i].load()); } } void* get() { for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) { void* p = cache[i].load(); if (p != NULL) { if (cache[i].compare_exchange_strong(p, NULL)) return p; } } return ::operator new(BOOST_REGEX_BLOCKSIZE); } void put(void* ptr) { for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) { void* p = cache[i].load(); if (p == NULL) { if (cache[i].compare_exchange_strong(p, ptr)) return; } } ::operator delete(ptr); } static mem_block_cache& instance() { static mem_block_cache block_cache = { { {nullptr} } }; return block_cache; } }; #else /* lock-based implementation */ struct mem_block_node { mem_block_node* next; }; struct mem_block_cache { // this member has to be statically initialsed: mem_block_node* next { nullptr }; unsigned cached_blocks { 0 }; #ifdef BOOST_HAS_THREADS std::mutex mut; #endif ~mem_block_cache() { while(next) { mem_block_node* old = next; next = next->next; ::operator delete(old); } } void* get() { #ifdef BOOST_HAS_THREADS std::lock_guard g(mut); #endif if(next) { mem_block_node* result = next; next = next->next; --cached_blocks; return result; } return ::operator new(BOOST_REGEX_BLOCKSIZE); } void put(void* p) { #ifdef BOOST_HAS_THREADS std::lock_guard g(mut); #endif if(cached_blocks >= BOOST_REGEX_MAX_CACHE_BLOCKS) { ::operator delete(p); } else { mem_block_node* old = static_cast(p); old->next = next; next = old; ++cached_blocks; } } static mem_block_cache& instance() { static mem_block_cache block_cache; return block_cache; } }; #endif #endif #if BOOST_REGEX_MAX_CACHE_BLOCKS == 0 inline void* get_mem_block() { return ::operator new(BOOST_REGEX_BLOCKSIZE); } inline void put_mem_block(void* p) { ::operator delete(p); } #else inline void* get_mem_block() { return mem_block_cache::instance().get(); } inline void put_mem_block(void* p) { mem_block_cache::instance().put(p); } #endif } } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/object_cache.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE object_cache.hpp * VERSION see * DESCRIPTION: Implements a generic object cache. */ #ifndef BOOST_REGEX_OBJECT_CACHE_HPP #define BOOST_REGEX_OBJECT_CACHE_HPP #include #include #include #include #include #include #ifdef BOOST_HAS_THREADS #include #endif namespace boost{ template class object_cache { public: typedef std::pair< ::std::shared_ptr, Key const*> value_type; typedef std::list list_type; typedef typename list_type::iterator list_iterator; typedef std::map map_type; typedef typename map_type::iterator map_iterator; typedef typename list_type::size_type size_type; static std::shared_ptr get(const Key& k, size_type l_max_cache_size); private: static std::shared_ptr do_get(const Key& k, size_type l_max_cache_size); struct data { list_type cont; map_type index; }; // Needed by compilers not implementing the resolution to DR45. For reference, // see http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#45. friend struct data; }; #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable: 4702) #endif template std::shared_ptr object_cache::get(const Key& k, size_type l_max_cache_size) { #ifdef BOOST_HAS_THREADS static std::mutex mut; std::lock_guard l(mut); return do_get(k, l_max_cache_size); #else return do_get(k, l_max_cache_size); #endif } #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif template std::shared_ptr object_cache::do_get(const Key& k, size_type l_max_cache_size) { typedef typename object_cache::data object_data; typedef typename map_type::size_type map_size_type; static object_data s_data; // // see if the object is already in the cache: // map_iterator mpos = s_data.index.find(k); if(mpos != s_data.index.end()) { // // Eureka! // We have a cached item, bump it up the list and return it: // if(--(s_data.cont.end()) != mpos->second) { // splice out the item we want to move: list_type temp; temp.splice(temp.end(), s_data.cont, mpos->second); // and now place it at the end of the list: s_data.cont.splice(s_data.cont.end(), temp, temp.begin()); BOOST_REGEX_ASSERT(*(s_data.cont.back().second) == k); // update index with new position: mpos->second = --(s_data.cont.end()); BOOST_REGEX_ASSERT(&(mpos->first) == mpos->second->second); BOOST_REGEX_ASSERT(&(mpos->first) == s_data.cont.back().second); } return s_data.cont.back().first; } // // if we get here then the item is not in the cache, // so create it: // std::shared_ptr result(new Object(k)); // // Add it to the list, and index it: // s_data.cont.push_back(value_type(result, static_cast(0))); s_data.index.insert(std::make_pair(k, --(s_data.cont.end()))); s_data.cont.back().second = &(s_data.index.find(k)->first); map_size_type s = s_data.index.size(); BOOST_REGEX_ASSERT(s_data.index[k]->first.get() == result.get()); BOOST_REGEX_ASSERT(&(s_data.index.find(k)->first) == s_data.cont.back().second); BOOST_REGEX_ASSERT(s_data.index.find(k)->first == k); if(s > l_max_cache_size) { // // We have too many items in the list, so we need to start // popping them off the back of the list, but only if they're // being held uniquely by us: // list_iterator pos = s_data.cont.begin(); list_iterator last = s_data.cont.end(); while((pos != last) && (s > l_max_cache_size)) { if(pos->first.use_count() == 1) { list_iterator condemmed(pos); ++pos; // now remove the items from our containers, // then order has to be as follows: BOOST_REGEX_ASSERT(s_data.index.find(*(condemmed->second)) != s_data.index.end()); s_data.index.erase(*(condemmed->second)); s_data.cont.erase(condemmed); --s; } else ++pos; } BOOST_REGEX_ASSERT(s_data.index[k]->first.get() == result.get()); BOOST_REGEX_ASSERT(&(s_data.index.find(k)->first) == s_data.cont.back().second); BOOST_REGEX_ASSERT(s_data.index.find(k)->first == k); } return result; } } #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/pattern_except.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE pattern_except.hpp * VERSION see * DESCRIPTION: Declares pattern-matching exception classes. */ #ifndef BOOST_RE_V5_PAT_EXCEPT_HPP #define BOOST_RE_V5_PAT_EXCEPT_HPP #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #include #include #include #include namespace boost{ #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable : 4275) #if BOOST_REGEX_MSVC >= 1800 #pragma warning(disable : 26812 4459) #endif #endif class regex_error : public std::runtime_error { public: explicit regex_error(const std::string& s, regex_constants::error_type err = regex_constants::error_unknown, std::ptrdiff_t pos = 0) : std::runtime_error(s) , m_error_code(err) , m_position(pos) { } explicit regex_error(regex_constants::error_type err) : std::runtime_error(::boost::BOOST_REGEX_DETAIL_NS::get_default_error_string(err)) , m_error_code(err) , m_position(0) { } ~regex_error() noexcept override {} regex_constants::error_type code()const { return m_error_code; } std::ptrdiff_t position()const { return m_position; } void raise()const { #ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_REGEX_STANDALONE ::boost::throw_exception(*this); #else throw* this; #endif #endif } private: regex_constants::error_type m_error_code; std::ptrdiff_t m_position; }; typedef regex_error bad_pattern; typedef regex_error bad_expression; namespace BOOST_REGEX_DETAIL_NS{ template inline void raise_runtime_error(const E& ex) { #ifndef BOOST_REGEX_STANDALONE ::boost::throw_exception(ex); #else throw ex; #endif } template void raise_error(const traits& t, regex_constants::error_type code) { (void)t; // warning suppression regex_error e(t.error_string(code), code, 0); ::boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(e); } } #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/perl_matcher.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ #ifndef BOOST_REGEX_MATCHER_HPP #define BOOST_REGEX_MATCHER_HPP #include #ifdef BOOST_REGEX_MSVC # pragma warning(push) #pragma warning(disable : 4251 4459) #if BOOST_REGEX_MSVC < 1700 # pragma warning(disable : 4231) #endif # if BOOST_REGEX_MSVC < 1600 # pragma warning(disable : 4660) # endif #if BOOST_REGEX_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ // // error checking API: // inline void verify_options(boost::regex_constants::syntax_option_type, match_flag_type mf) { // // can't mix match_extra with POSIX matching rules: // if ((mf & match_extra) && (mf & match_posix)) { std::logic_error msg("Usage Error: Can't mix regular expression captures with POSIX matching rules"); #ifndef BOOST_REGEX_STANDALONE throw_exception(msg); #else throw msg; #endif } } // // function can_start: // template inline bool can_start(charT c, const unsigned char* map, unsigned char mask) { return ((c < static_cast(0)) ? true : ((c >= static_cast(1 << CHAR_BIT)) ? true : map[c] & mask)); } inline bool can_start(char c, const unsigned char* map, unsigned char mask) { return map[(unsigned char)c] & mask; } inline bool can_start(signed char c, const unsigned char* map, unsigned char mask) { return map[(unsigned char)c] & mask; } inline bool can_start(unsigned char c, const unsigned char* map, unsigned char mask) { return map[c] & mask; } inline bool can_start(unsigned short c, const unsigned char* map, unsigned char mask) { return ((c >= (1 << CHAR_BIT)) ? true : map[c] & mask); } #if defined(WCHAR_MIN) && (WCHAR_MIN == 0) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) inline bool can_start(wchar_t c, const unsigned char* map, unsigned char mask) { return ((c >= static_cast(1u << CHAR_BIT)) ? true : map[c] & mask); } #endif #if !defined(BOOST_NO_INTRINSIC_WCHAR_T) inline bool can_start(unsigned int c, const unsigned char* map, unsigned char mask) { return (((c >= static_cast(1u << CHAR_BIT)) ? true : map[c] & mask)); } #endif template inline int string_compare(const std::basic_string& s, const C* p) { if(0 == *p) { if(s.empty() || ((s.size() == 1) && (s[0] == 0))) return 0; } return s.compare(p); } template inline int string_compare(const Seq& s, const C* p) { std::size_t i = 0; while((i < s.size()) && (p[i] == s[i])) { ++i; } return (i == s.size()) ? -(int)p[i] : (int)s[i] - (int)p[i]; } # define STR_COMP(s,p) string_compare(s,p) template inline const charT* re_skip_past_null(const charT* p) { while (*p != static_cast(0)) ++p; return ++p; } template iterator re_is_set_member(iterator next, iterator last, const re_set_long* set_, const regex_data& e, bool icase) { const charT* p = reinterpret_cast(set_+1); iterator ptr; unsigned int i; //bool icase = e.m_flags & regex_constants::icase; if(next == last) return next; typedef typename traits_type::string_type traits_string_type; const ::boost::regex_traits_wrapper& traits_inst = *(e.m_ptraits); // dwa 9/13/00 suppress incorrect MSVC warning - it claims this is never // referenced (void)traits_inst; // try and match a single character, could be a multi-character // collating element... for(i = 0; i < set_->csingles; ++i) { ptr = next; if(*p == static_cast(0)) { // treat null string as special case: if(traits_inst.translate(*ptr, icase)) { ++p; continue; } return set_->isnot ? next : (ptr == next) ? ++next : ptr; } else { while(*p && (ptr != last)) { if(traits_inst.translate(*ptr, icase) != *p) break; ++p; ++ptr; } if(*p == static_cast(0)) // if null we've matched return set_->isnot ? next : (ptr == next) ? ++next : ptr; p = re_skip_past_null(p); // skip null } } charT col = traits_inst.translate(*next, icase); if(set_->cranges || set_->cequivalents) { traits_string_type s1; // // try and match a range, NB only a single character can match if(set_->cranges) { if((e.m_flags & regex_constants::collate) == 0) s1.assign(1, col); else { charT a[2] = { col, charT(0), }; s1 = traits_inst.transform(a, a + 1); } for(i = 0; i < set_->cranges; ++i) { if(STR_COMP(s1, p) >= 0) { do{ ++p; }while(*p); ++p; if(STR_COMP(s1, p) <= 0) return set_->isnot ? next : ++next; } else { // skip first string do{ ++p; }while(*p); ++p; } // skip second string do{ ++p; }while(*p); ++p; } } // // try and match an equivalence class, NB only a single character can match if(set_->cequivalents) { charT a[2] = { col, charT(0), }; s1 = traits_inst.transform_primary(a, a +1); for(i = 0; i < set_->cequivalents; ++i) { if(STR_COMP(s1, p) == 0) return set_->isnot ? next : ++next; // skip string do{ ++p; }while(*p); ++p; } } } if(traits_inst.isctype(col, set_->cclasses) == true) return set_->isnot ? next : ++next; if((set_->cnclasses != 0) && (traits_inst.isctype(col, set_->cnclasses) == false)) return set_->isnot ? next : ++next; return set_->isnot ? ++next : next; } template class repeater_count { repeater_count** stack; repeater_count* next; int state_id; std::size_t count; // the number of iterations so far BidiIterator start_pos; // where the last repeat started repeater_count* unwind_until(int n, repeater_count* p, int current_recursion_id) { while(p && (p->state_id != n)) { if(-2 - current_recursion_id == p->state_id) return 0; p = p->next; if(p && (p->state_id < 0)) { p = unwind_until(p->state_id, p, current_recursion_id); if(!p) return p; p = p->next; } } return p; } public: repeater_count(repeater_count** s) : stack(s), next(0), state_id(-1), count(0), start_pos() {} repeater_count(int i, repeater_count** s, BidiIterator start, int current_recursion_id) : start_pos(start) { state_id = i; stack = s; next = *stack; *stack = this; if((state_id > next->state_id) && (next->state_id >= 0)) count = 0; else { repeater_count* p = next; p = unwind_until(state_id, p, current_recursion_id); if(p) { count = p->count; start_pos = p->start_pos; } else count = 0; } } ~repeater_count() { if(next) *stack = next; } std::size_t get_count() { return count; } int get_id() { return state_id; } std::size_t operator++() { return ++count; } bool check_null_repeat(const BidiIterator& pos, std::size_t max) { // this is called when we are about to start a new repeat, // if the last one was NULL move our count to max, // otherwise save the current position. bool result = (count == 0) ? false : (pos == start_pos); if(result) count = max; else start_pos = pos; return result; } }; struct saved_state; enum saved_state_type { saved_type_end = 0, saved_type_paren = 1, saved_type_recurse = 2, saved_type_assertion = 3, saved_state_alt = 4, saved_state_repeater_count = 5, saved_state_extra_block = 6, saved_state_greedy_single_repeat = 7, saved_state_rep_slow_dot = 8, saved_state_rep_fast_dot = 9, saved_state_rep_char = 10, saved_state_rep_short_set = 11, saved_state_rep_long_set = 12, saved_state_non_greedy_long_repeat = 13, saved_state_count = 14 }; #ifdef BOOST_REGEX_MSVC # pragma warning(push) #if BOOST_REGEX_MSVC >= 1800 #pragma warning(disable:26495) #endif #endif template struct recursion_info { typedef typename Results::value_type value_type; typedef typename value_type::iterator iterator; int idx; const re_syntax_base* preturn_address; Results results; repeater_count* repeater_stack; iterator location_of_start; }; #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif template class perl_matcher { public: typedef typename traits::char_type char_type; typedef perl_matcher self_type; typedef bool (self_type::*matcher_proc_type)(); typedef std::size_t traits_size_type; typedef typename is_byte::width_type width_type; typedef typename std::iterator_traits::difference_type difference_type; typedef match_results results_type; perl_matcher(BidiIterator first, BidiIterator end, match_results& what, const basic_regex& e, match_flag_type f, BidiIterator l_base) : m_result(what), base(first), last(end), position(first), backstop(l_base), re(e), traits_inst(e.get_traits()), m_independent(false), next_count(&rep_obj), rep_obj(&next_count) , m_recursions(0) { construct_init(e, f); } bool match(); bool find(); void setf(match_flag_type f) { m_match_flags |= f; } void unsetf(match_flag_type f) { m_match_flags &= ~f; } private: void construct_init(const basic_regex& e, match_flag_type f); bool find_imp(); bool match_imp(); void estimate_max_state_count(std::random_access_iterator_tag*); void estimate_max_state_count(void*); bool match_prefix(); bool match_all_states(); // match procs, stored in s_match_vtable: bool match_startmark(); bool match_endmark(); bool match_literal(); bool match_start_line(); bool match_end_line(); bool match_wild(); bool match_match(); bool match_word_boundary(); bool match_within_word(); bool match_word_start(); bool match_word_end(); bool match_buffer_start(); bool match_buffer_end(); bool match_backref(); bool match_long_set(); bool match_set(); bool match_jump(); bool match_alt(); bool match_rep(); bool match_combining(); bool match_soft_buffer_end(); bool match_restart_continue(); bool match_long_set_repeat(); bool match_set_repeat(); bool match_char_repeat(); bool match_dot_repeat_fast(); bool match_dot_repeat_slow(); bool match_dot_repeat_dispatch() { return ::boost::is_random_access_iterator::value ? match_dot_repeat_fast() : match_dot_repeat_slow(); } bool match_backstep(); bool match_assert_backref(); bool match_toggle_case(); bool match_recursion(); bool match_fail(); bool match_accept(); bool match_commit(); bool match_then(); bool skip_until_paren(int index, bool match = true); // find procs stored in s_find_vtable: bool find_restart_any(); bool find_restart_word(); bool find_restart_line(); bool find_restart_buf(); bool find_restart_lit(); private: // final result structure to be filled in: match_results& m_result; // temporary result for POSIX matches: std::unique_ptr > m_temp_match; // pointer to actual result structure to fill in: match_results* m_presult; // start of sequence being searched: BidiIterator base; // end of sequence being searched: BidiIterator last; // current character being examined: BidiIterator position; // where to restart next search after failed match attempt: BidiIterator restart; // where the current search started from, acts as base for $` during grep: BidiIterator search_base; // how far we can go back when matching lookbehind: BidiIterator backstop; // the expression being examined: const basic_regex& re; // the expression's traits class: const ::boost::regex_traits_wrapper& traits_inst; // the next state in the machine being matched: const re_syntax_base* pstate; // matching flags in use: match_flag_type m_match_flags; // how many states we have examined so far: std::ptrdiff_t state_count; // max number of states to examine before giving up: std::ptrdiff_t max_state_count; // whether we should ignore case or not: bool icase; // set to true when (position == last), indicates that we may have a partial match: bool m_has_partial_match; // set to true whenever we get a match: bool m_has_found_match; // set to true whenever we're inside an independent sub-expression: bool m_independent; // the current repeat being examined: repeater_count* next_count; // the first repeat being examined (top of linked list): repeater_count rep_obj; // the mask to pass when matching word boundaries: typename traits::char_class_type m_word_mask; // the bitmask to use when determining whether a match_any matches a newline or not: unsigned char match_any_mask; // recursion information: std::vector > recursion_stack; // // additional members for non-recursive version: // typedef bool (self_type::*unwind_proc_type)(bool); void extend_stack(); bool unwind(bool); bool unwind_end(bool); bool unwind_paren(bool); bool unwind_recursion_stopper(bool); bool unwind_assertion(bool); bool unwind_alt(bool); bool unwind_repeater_counter(bool); bool unwind_extra_block(bool); bool unwind_greedy_single_repeat(bool); bool unwind_slow_dot_repeat(bool); bool unwind_fast_dot_repeat(bool); bool unwind_char_repeat(bool); bool unwind_short_set_repeat(bool); bool unwind_long_set_repeat(bool); bool unwind_non_greedy_repeat(bool); bool unwind_recursion(bool); bool unwind_recursion_pop(bool); bool unwind_commit(bool); bool unwind_then(bool); bool unwind_case(bool); void destroy_single_repeat(); void push_matched_paren(int index, const sub_match& sub); void push_recursion_stopper(); void push_assertion(const re_syntax_base* ps, bool positive); void push_alt(const re_syntax_base* ps); void push_repeater_count(int i, repeater_count** s); void push_single_repeat(std::size_t c, const re_repeat* r, BidiIterator last_position, int state_id); void push_non_greedy_repeat(const re_syntax_base* ps); void push_recursion(int idx, const re_syntax_base* p, results_type* presults, results_type* presults2); void push_recursion_pop(); void push_case_change(bool); // pointer to base of stack: saved_state* m_stack_base; // pointer to current stack position: saved_state* m_backup_state; // how many memory blocks have we used up?: unsigned used_block_count; // determines what value to return when unwinding from recursion, // allows for mixed recursive/non-recursive algorithm: bool m_recursive_result; // We have unwound to a lookahead/lookbehind, used by COMMIT/PRUNE/SKIP: bool m_unwound_lookahead; // We have unwound to an alternative, used by THEN: bool m_unwound_alt; // We are unwinding a commit - used by independent subs to determine whether to stop there or carry on unwinding: //bool m_unwind_commit; // Recursion limit: unsigned m_recursions; #ifdef BOOST_REGEX_MSVC # pragma warning(push) #if BOOST_REGEX_MSVC >= 1800 #pragma warning(disable:26495) #endif #endif // these operations aren't allowed, so are declared private, // bodies are provided to keep explicit-instantiation requests happy: perl_matcher& operator=(const perl_matcher&) { return *this; } perl_matcher(const perl_matcher& that) : m_result(that.m_result), re(that.re), traits_inst(that.traits_inst), rep_obj(0) {} #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif }; } // namespace BOOST_REGEX_DETAIL_NS #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif } // namespace boost // // include the implementation of perl_matcher: // #include // this one has to be last: #include #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/perl_matcher_common.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE perl_matcher_common.cpp * VERSION see * DESCRIPTION: Definitions of perl_matcher member functions that are * common to both the recursive and non-recursive versions. */ #ifndef BOOST_REGEX_V5_PERL_MATCHER_COMMON_HPP #define BOOST_REGEX_V5_PERL_MATCHER_COMMON_HPP #ifdef BOOST_REGEX_MSVC # pragma warning(push) #pragma warning(disable:4459) #if BOOST_REGEX_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_REGEX_MSVC # pragma warning(push) #pragma warning(disable:26812) #endif template void perl_matcher::construct_init(const basic_regex& e, match_flag_type f) { typedef typename std::iterator_traits::iterator_category category; typedef typename basic_regex::flag_type expression_flag_type; if(e.empty()) { // precondition failure: e is not a valid regex. std::invalid_argument ex("Invalid regular expression object"); #ifndef BOOST_REGEX_STANDALONE boost::throw_exception(ex); #else throw e; #endif } pstate = 0; m_match_flags = f; estimate_max_state_count(static_cast(0)); expression_flag_type re_f = re.flags(); icase = re_f & regex_constants::icase; if(!(m_match_flags & (match_perl|match_posix))) { if((re_f & (regbase::main_option_type|regbase::no_perl_ex)) == 0) m_match_flags |= match_perl; else if((re_f & (regbase::main_option_type|regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex)) m_match_flags |= match_perl; else if((re_f & (regbase::main_option_type|regbase::literal)) == (regbase::literal)) m_match_flags |= match_perl; else m_match_flags |= match_posix; } if(m_match_flags & match_posix) { m_temp_match.reset(new match_results()); m_presult = m_temp_match.get(); } else m_presult = &m_result; m_stack_base = 0; m_backup_state = 0; // find the value to use for matching word boundaries: m_word_mask = re.get_data().m_word_mask; // find bitmask to use for matching '.': match_any_mask = static_cast((f & match_not_dot_newline) ? BOOST_REGEX_DETAIL_NS::test_not_newline : BOOST_REGEX_DETAIL_NS::test_newline); // Disable match_any if requested in the state machine: if(e.get_data().m_disable_match_any) m_match_flags &= regex_constants::match_not_any; } #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif template void perl_matcher::estimate_max_state_count(std::random_access_iterator_tag*) { // // How many states should we allow our machine to visit before giving up? // This is a heuristic: it takes the greater of O(N^2) and O(NS^2) // where N is the length of the string, and S is the number of states // in the machine. It's tempting to up this to O(N^2S) or even O(N^2S^2) // but these take unreasonably amounts of time to bale out in pathological // cases. // // Calculate NS^2 first: // static const std::ptrdiff_t k = 100000; std::ptrdiff_t dist = std::distance(base, last); if(dist == 0) dist = 1; std::ptrdiff_t states = re.size(); if(states == 0) states = 1; if ((std::numeric_limits::max)() / states < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states *= states; if((std::numeric_limits::max)() / dist < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states *= dist; if((std::numeric_limits::max)() - k < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states += k; max_state_count = states; // // Now calculate N^2: // states = dist; if((std::numeric_limits::max)() / dist < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states *= dist; if((std::numeric_limits::max)() - k < states) { max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits::max)() - 2); return; } states += k; // // N^2 can be a very large number indeed, to prevent things getting out // of control, cap the max states: // if(states > BOOST_REGEX_MAX_STATE_COUNT) states = BOOST_REGEX_MAX_STATE_COUNT; // // If (the possibly capped) N^2 is larger than our first estimate, // use this instead: // if(states > max_state_count) max_state_count = states; } template inline void perl_matcher::estimate_max_state_count(void*) { // we don't know how long the sequence is: max_state_count = BOOST_REGEX_MAX_STATE_COUNT; } template inline bool perl_matcher::match() { return match_imp(); } template bool perl_matcher::match_imp() { // initialise our stack if we are non-recursive: save_state_init init(&m_stack_base, &m_backup_state); used_block_count = BOOST_REGEX_MAX_BLOCKS; #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif // reset our state machine: position = base; search_base = base; state_count = 0; m_match_flags |= regex_constants::match_all; m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast(1u + re.mark_count()), search_base, last); m_presult->set_base(base); m_presult->set_named_subs(this->re.get_named_subs()); if(m_match_flags & match_posix) m_result = *m_presult; verify_options(re.flags(), m_match_flags); if(0 == match_prefix()) return false; return (m_result[0].second == last) && (m_result[0].first == base); #if !defined(BOOST_NO_EXCEPTIONS) } catch(...) { // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)){} throw; } #endif } template inline bool perl_matcher::find() { return find_imp(); } template bool perl_matcher::find_imp() { static matcher_proc_type const s_find_vtable[7] = { &perl_matcher::find_restart_any, &perl_matcher::find_restart_word, &perl_matcher::find_restart_line, &perl_matcher::find_restart_buf, &perl_matcher::match_prefix, &perl_matcher::find_restart_lit, &perl_matcher::find_restart_lit, }; // initialise our stack if we are non-recursive: save_state_init init(&m_stack_base, &m_backup_state); used_block_count = BOOST_REGEX_MAX_BLOCKS; #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif state_count = 0; if((m_match_flags & regex_constants::match_init) == 0) { // reset our state machine: search_base = position = base; pstate = re.get_first_state(); m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast(1u + re.mark_count()), base, last); m_presult->set_base(base); m_presult->set_named_subs(this->re.get_named_subs()); m_match_flags |= regex_constants::match_init; } else { // start again: search_base = position = m_result[0].second; // If last match was null and match_not_null was not set then increment // our start position, otherwise we go into an infinite loop: if(((m_match_flags & match_not_null) == 0) && (m_result.length() == 0)) { if(position == last) return false; else ++position; } // reset $` start: m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast(1u + re.mark_count()), search_base, last); //if((base != search_base) && (base == backstop)) // m_match_flags |= match_prev_avail; } if(m_match_flags & match_posix) { m_result.set_size(static_cast(1u + re.mark_count()), base, last); m_result.set_base(base); } verify_options(re.flags(), m_match_flags); // find out what kind of expression we have: unsigned type = (m_match_flags & match_continuous) ? static_cast(regbase::restart_continue) : static_cast(re.get_restart_type()); // call the appropriate search routine: matcher_proc_type proc = s_find_vtable[type]; return (this->*proc)(); #if !defined(BOOST_NO_EXCEPTIONS) } catch(...) { // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)){} throw; } #endif } template bool perl_matcher::match_prefix() { m_has_partial_match = false; m_has_found_match = false; pstate = re.get_first_state(); m_presult->set_first(position); restart = position; match_all_states(); if(!m_has_found_match && m_has_partial_match && (m_match_flags & match_partial)) { m_has_found_match = true; m_presult->set_second(last, 0, false); position = last; if((m_match_flags & match_posix) == match_posix) { m_result.maybe_assign(*m_presult); } } #ifdef BOOST_REGEX_MATCH_EXTRA if(m_has_found_match && (match_extra & m_match_flags)) { // // we have a match, reverse the capture information: // for(unsigned i = 0; i < m_presult->size(); ++i) { typename sub_match::capture_sequence_type & seq = ((*m_presult)[i]).get_captures(); std::reverse(seq.begin(), seq.end()); } } #endif if(!m_has_found_match) position = restart; // reset search postion return m_has_found_match; } template bool perl_matcher::match_literal() { unsigned int len = static_cast(pstate)->length; const char_type* what = reinterpret_cast(static_cast(pstate) + 1); // // compare string with what we stored in // our records: for(unsigned int i = 0; i < len; ++i, ++position) { if((position == last) || (traits_inst.translate(*position, icase) != what[i])) return false; } pstate = pstate->next.p; return true; } template bool perl_matcher::match_start_line() { if(position == backstop) { if((m_match_flags & match_prev_avail) == 0) { if((m_match_flags & match_not_bol) == 0) { pstate = pstate->next.p; return true; } return false; } } else if(m_match_flags & match_single_line) return false; // check the previous value character: BidiIterator t(position); --t; if(position != last) { if(is_separator(*t) && !((*t == static_cast('\r')) && (*position == static_cast('\n'))) ) { pstate = pstate->next.p; return true; } } else if(is_separator(*t)) { pstate = pstate->next.p; return true; } return false; } template bool perl_matcher::match_end_line() { if(position != last) { if(m_match_flags & match_single_line) return false; // we're not yet at the end so *first is always valid: if(is_separator(*position)) { if((position != backstop) || (m_match_flags & match_prev_avail)) { // check that we're not in the middle of \r\n sequence BidiIterator t(position); --t; if((*t == static_cast('\r')) && (*position == static_cast('\n'))) { return false; } } pstate = pstate->next.p; return true; } } else if((m_match_flags & match_not_eol) == 0) { pstate = pstate->next.p; return true; } return false; } template bool perl_matcher::match_wild() { if(position == last) return false; if(is_separator(*position) && ((match_any_mask & static_cast(pstate)->mask) == 0)) return false; if((*position == char_type(0)) && (m_match_flags & match_not_dot_null)) return false; pstate = pstate->next.p; ++position; return true; } template bool perl_matcher::match_word_boundary() { bool b; // indcates whether next character is a word character if(position != last) { // prev and this character must be opposites: b = traits_inst.isctype(*position, m_word_mask); } else { if (m_match_flags & match_not_eow) return false; b = false; } if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) { if(m_match_flags & match_not_bow) return false; else b ^= false; } else { --position; b ^= traits_inst.isctype(*position, m_word_mask); ++position; } if(b) { pstate = pstate->next.p; return true; } return false; // no match if we get to here... } template bool perl_matcher::match_within_word() { bool b = !match_word_boundary(); if(b) pstate = pstate->next.p; return b; /* if(position == last) return false; // both prev and this character must be m_word_mask: bool prev = traits_inst.isctype(*position, m_word_mask); { bool b; if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) return false; else { --position; b = traits_inst.isctype(*position, m_word_mask); ++position; } if(b == prev) { pstate = pstate->next.p; return true; } } return false; */ } template bool perl_matcher::match_word_start() { if(position == last) return false; // can't be starting a word if we're already at the end of input if(!traits_inst.isctype(*position, m_word_mask)) return false; // next character isn't a word character if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) { if(m_match_flags & match_not_bow) return false; // no previous input } else { // otherwise inside buffer: BidiIterator t(position); --t; if(traits_inst.isctype(*t, m_word_mask)) return false; // previous character not non-word } // OK we have a match: pstate = pstate->next.p; return true; } template bool perl_matcher::match_word_end() { if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) return false; // start of buffer can't be end of word BidiIterator t(position); --t; if(traits_inst.isctype(*t, m_word_mask) == false) return false; // previous character wasn't a word character if(position == last) { if(m_match_flags & match_not_eow) return false; // end of buffer but not end of word } else { // otherwise inside buffer: if(traits_inst.isctype(*position, m_word_mask)) return false; // next character is a word character } pstate = pstate->next.p; return true; // if we fall through to here then we've succeeded } template bool perl_matcher::match_buffer_start() { if((position != backstop) || (m_match_flags & match_not_bob)) return false; // OK match: pstate = pstate->next.p; return true; } template bool perl_matcher::match_buffer_end() { if((position != last) || (m_match_flags & match_not_eob)) return false; // OK match: pstate = pstate->next.p; return true; } template bool perl_matcher::match_backref() { // // Compare with what we previously matched. // Note that this succeeds if the backref did not partisipate // in the match, this is in line with ECMAScript, but not Perl // or PCRE. // int index = static_cast(pstate)->index; if(index >= hash_value_mask) { named_subexpressions::range_type r = re.get_data().equal_range(index); BOOST_REGEX_ASSERT(r.first != r.second); do { index = r.first->index; ++r.first; }while((r.first != r.second) && ((*m_presult)[index].matched != true)); } if((m_match_flags & match_perl) && !(*m_presult)[index].matched) return false; BidiIterator i = (*m_presult)[index].first; BidiIterator j = (*m_presult)[index].second; while(i != j) { if((position == last) || (traits_inst.translate(*position, icase) != traits_inst.translate(*i, icase))) return false; ++i; ++position; } pstate = pstate->next.p; return true; } template bool perl_matcher::match_long_set() { typedef typename traits::char_class_type char_class_type; // let the traits class do the work: if(position == last) return false; BidiIterator t = re_is_set_member(position, last, static_cast*>(pstate), re.get_data(), icase); if(t != position) { pstate = pstate->next.p; position = t; return true; } return false; } template bool perl_matcher::match_set() { if(position == last) return false; if(static_cast(pstate)->_map[static_cast(traits_inst.translate(*position, icase))]) { pstate = pstate->next.p; ++position; return true; } return false; } template bool perl_matcher::match_jump() { pstate = static_cast(pstate)->alt.p; return true; } template bool perl_matcher::match_combining() { if(position == last) return false; if(is_combining(traits_inst.translate(*position, icase))) return false; ++position; while((position != last) && is_combining(traits_inst.translate(*position, icase))) ++position; pstate = pstate->next.p; return true; } template bool perl_matcher::match_soft_buffer_end() { if(m_match_flags & match_not_eob) return false; BidiIterator p(position); while((p != last) && is_separator(traits_inst.translate(*p, icase)))++p; if(p != last) return false; pstate = pstate->next.p; return true; } template bool perl_matcher::match_restart_continue() { if(position == search_base) { pstate = pstate->next.p; return true; } return false; } template bool perl_matcher::match_backstep() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif if( ::boost::is_random_access_iterator::value) { std::ptrdiff_t maxlen = std::distance(backstop, position); if(maxlen < static_cast(pstate)->index) return false; std::advance(position, -static_cast(pstate)->index); } else { int c = static_cast(pstate)->index; while(c--) { if(position == backstop) return false; --position; } } pstate = pstate->next.p; return true; #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template inline bool perl_matcher::match_assert_backref() { // return true if marked sub-expression N has been matched: int index = static_cast(pstate)->index; bool result = false; if(index == 9999) { // Magic value for a (DEFINE) block: return false; } else if(index > 0) { // Have we matched subexpression "index"? // Check if index is a hash value: if(index >= hash_value_mask) { named_subexpressions::range_type r = re.get_data().equal_range(index); while(r.first != r.second) { if((*m_presult)[r.first->index].matched) { result = true; break; } ++r.first; } } else { result = (*m_presult)[index].matched; } pstate = pstate->next.p; } else { // Have we recursed into subexpression "index"? // If index == 0 then check for any recursion at all, otherwise for recursion to -index-1. int idx = -(index+1); if(idx >= hash_value_mask) { named_subexpressions::range_type r = re.get_data().equal_range(idx); int stack_index = recursion_stack.empty() ? -1 : recursion_stack.back().idx; while(r.first != r.second) { result |= (stack_index == r.first->index); if(result)break; ++r.first; } } else { result = !recursion_stack.empty() && ((recursion_stack.back().idx == idx) || (index == 0)); } pstate = pstate->next.p; } return result; } template bool perl_matcher::match_fail() { // Just force a backtrack: return false; } template bool perl_matcher::match_accept() { if(!recursion_stack.empty()) { return skip_until_paren(recursion_stack.back().idx); } else { return skip_until_paren(INT_MAX); } } template bool perl_matcher::find_restart_any() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif const unsigned char* _map = re.get_map(); while(true) { // skip everything we can't match: while((position != last) && !can_start(*position, _map, (unsigned char)mask_any) ) ++position; if(position == last) { // run out of characters, try a null match if possible: if(re.can_be_null()) return match_prefix(); break; } // now try and obtain a match: if(match_prefix()) return true; if(position == last) return false; ++position; } return false; #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template bool perl_matcher::find_restart_word() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif // do search optimised for word starts: const unsigned char* _map = re.get_map(); if((m_match_flags & match_prev_avail) || (position != base)) --position; else if(match_prefix()) return true; do { while((position != last) && traits_inst.isctype(*position, m_word_mask)) ++position; while((position != last) && !traits_inst.isctype(*position, m_word_mask)) ++position; if(position == last) break; if(can_start(*position, _map, (unsigned char)mask_any) ) { if(match_prefix()) return true; } if(position == last) break; } while(true); return false; #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template bool perl_matcher::find_restart_line() { // do search optimised for line starts: const unsigned char* _map = re.get_map(); if(match_prefix()) return true; while(position != last) { while((position != last) && !is_separator(*position)) ++position; if(position == last) return false; ++position; if(position == last) { if(re.can_be_null() && match_prefix()) return true; return false; } if( can_start(*position, _map, (unsigned char)mask_any) ) { if(match_prefix()) return true; } if(position == last) return false; //++position; } return false; } template bool perl_matcher::find_restart_buf() { if((position == base) && ((m_match_flags & match_not_bob) == 0)) return match_prefix(); return false; } template bool perl_matcher::find_restart_lit() { return false; } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/perl_matcher_non_recursive.hpp ================================================ /* * * Copyright (c) 2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE perl_matcher_common.cpp * VERSION see * DESCRIPTION: Definitions of perl_matcher member functions that are * specific to the non-recursive implementation. */ #ifndef BOOST_REGEX_V5_PERL_MATCHER_NON_RECURSIVE_HPP #define BOOST_REGEX_V5_PERL_MATCHER_NON_RECURSIVE_HPP #include #ifdef BOOST_REGEX_MSVC # pragma warning(push) # pragma warning(disable: 4706 4459) #if BOOST_REGEX_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ template inline void inplace_destroy(T* p) { (void)p; // warning suppression p->~T(); } struct saved_state { union{ unsigned int state_id; // this padding ensures correct alignment on 64-bit platforms: std::size_t padding1; std::ptrdiff_t padding2; void* padding3; }; saved_state(unsigned i) : state_id(i) {} }; template struct saved_matched_paren : public saved_state { int index; sub_match sub; saved_matched_paren(int i, const sub_match& s) : saved_state(1), index(i), sub(s){} }; template struct saved_position : public saved_state { const re_syntax_base* pstate; BidiIterator position; saved_position(const re_syntax_base* ps, BidiIterator pos, int i) : saved_state(i), pstate(ps), position(pos){} }; template struct saved_assertion : public saved_position { bool positive; saved_assertion(bool p, const re_syntax_base* ps, BidiIterator pos) : saved_position(ps, pos, saved_type_assertion), positive(p){} }; template struct saved_repeater : public saved_state { repeater_count count; saved_repeater(int i, repeater_count** s, BidiIterator start, int current_recursion_id) : saved_state(saved_state_repeater_count), count(i, s, start, current_recursion_id){} }; struct saved_extra_block : public saved_state { saved_state *base, *end; saved_extra_block(saved_state* b, saved_state* e) : saved_state(saved_state_extra_block), base(b), end(e) {} }; struct save_state_init { saved_state** stack; save_state_init(saved_state** base, saved_state** end) : stack(base) { *base = static_cast(get_mem_block()); *end = reinterpret_cast(reinterpret_cast(*base)+BOOST_REGEX_BLOCKSIZE); --(*end); (void) new (*end)saved_state(0); BOOST_REGEX_ASSERT(*end > *base); } ~save_state_init() { put_mem_block(*stack); *stack = 0; } }; template struct saved_single_repeat : public saved_state { std::size_t count; const re_repeat* rep; BidiIterator last_position; saved_single_repeat(std::size_t c, const re_repeat* r, BidiIterator lp, int arg_id) : saved_state(arg_id), count(c), rep(r), last_position(lp){} }; template struct saved_recursion : public saved_state { saved_recursion(int idx, const re_syntax_base* p, Results* pr, Results* pr2) : saved_state(14), recursion_id(idx), preturn_address(p), internal_results(*pr), prior_results(*pr2) {} int recursion_id; const re_syntax_base* preturn_address; Results internal_results, prior_results; }; struct saved_change_case : public saved_state { bool icase; saved_change_case(bool c) : saved_state(18), icase(c) {} }; struct incrementer { incrementer(unsigned* pu) : m_pu(pu) { ++*m_pu; } ~incrementer() { --*m_pu; } bool operator > (unsigned i) { return *m_pu > i; } private: unsigned* m_pu; }; template bool perl_matcher::match_all_states() { static matcher_proc_type const s_match_vtable[34] = { (&perl_matcher::match_startmark), &perl_matcher::match_endmark, &perl_matcher::match_literal, &perl_matcher::match_start_line, &perl_matcher::match_end_line, &perl_matcher::match_wild, &perl_matcher::match_match, &perl_matcher::match_word_boundary, &perl_matcher::match_within_word, &perl_matcher::match_word_start, &perl_matcher::match_word_end, &perl_matcher::match_buffer_start, &perl_matcher::match_buffer_end, &perl_matcher::match_backref, &perl_matcher::match_long_set, &perl_matcher::match_set, &perl_matcher::match_jump, &perl_matcher::match_alt, &perl_matcher::match_rep, &perl_matcher::match_combining, &perl_matcher::match_soft_buffer_end, &perl_matcher::match_restart_continue, // Although this next line *should* be evaluated at compile time, in practice // some compilers (VC++) emit run-time initialisation which breaks thread // safety, so use a dispatch function instead: //(::boost::is_random_access_iterator::value ? &perl_matcher::match_dot_repeat_fast : &perl_matcher::match_dot_repeat_slow), &perl_matcher::match_dot_repeat_dispatch, &perl_matcher::match_char_repeat, &perl_matcher::match_set_repeat, &perl_matcher::match_long_set_repeat, &perl_matcher::match_backstep, &perl_matcher::match_assert_backref, &perl_matcher::match_toggle_case, &perl_matcher::match_recursion, &perl_matcher::match_fail, &perl_matcher::match_accept, &perl_matcher::match_commit, &perl_matcher::match_then, }; incrementer inc(&m_recursions); if(inc > 80) raise_error(traits_inst, regex_constants::error_complexity); push_recursion_stopper(); do{ while(pstate) { matcher_proc_type proc = s_match_vtable[pstate->type]; ++state_count; if(!(this->*proc)()) { if(state_count > max_state_count) raise_error(traits_inst, regex_constants::error_complexity); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; bool successful_unwind = unwind(false); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(!successful_unwind) return m_recursive_result; } } }while(unwind(true)); return m_recursive_result; } template void perl_matcher::extend_stack() { if(used_block_count) { --used_block_count; saved_state* stack_base; saved_state* backup_state; stack_base = static_cast(get_mem_block()); backup_state = reinterpret_cast(reinterpret_cast(stack_base)+BOOST_REGEX_BLOCKSIZE); saved_extra_block* block = static_cast(backup_state); --block; (void) new (block) saved_extra_block(m_stack_base, m_backup_state); m_stack_base = stack_base; m_backup_state = block; } else raise_error(traits_inst, regex_constants::error_stack); } template inline void perl_matcher::push_matched_paren(int index, const sub_match& sub) { //BOOST_REGEX_ASSERT(index); saved_matched_paren* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_matched_paren(index, sub); m_backup_state = pmp; } template inline void perl_matcher::push_case_change(bool c) { //BOOST_REGEX_ASSERT(index); saved_change_case* pmp = static_cast(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast(m_backup_state); --pmp; } (void) new (pmp)saved_change_case(c); m_backup_state = pmp; } template inline void perl_matcher::push_recursion_stopper() { saved_state* pmp = m_backup_state; --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = m_backup_state; --pmp; } (void) new (pmp)saved_state(saved_type_recurse); m_backup_state = pmp; } template inline void perl_matcher::push_assertion(const re_syntax_base* ps, bool positive) { saved_assertion* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_assertion(positive, ps, position); m_backup_state = pmp; } template inline void perl_matcher::push_alt(const re_syntax_base* ps) { saved_position* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_position(ps, position, saved_state_alt); m_backup_state = pmp; } template inline void perl_matcher::push_non_greedy_repeat(const re_syntax_base* ps) { saved_position* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_position(ps, position, saved_state_non_greedy_long_repeat); m_backup_state = pmp; } template inline void perl_matcher::push_repeater_count(int i, repeater_count** s) { saved_repeater* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_repeater(i, s, position, this->recursion_stack.empty() ? (INT_MIN + 3) : this->recursion_stack.back().idx); m_backup_state = pmp; } template inline void perl_matcher::push_single_repeat(std::size_t c, const re_repeat* r, BidiIterator last_position, int state_id) { saved_single_repeat* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_single_repeat(c, r, last_position, state_id); m_backup_state = pmp; } template inline void perl_matcher::push_recursion(int idx, const re_syntax_base* p, results_type* presults, results_type* presults2) { saved_recursion* pmp = static_cast*>(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast*>(m_backup_state); --pmp; } (void) new (pmp)saved_recursion(idx, p, presults, presults2); m_backup_state = pmp; } template bool perl_matcher::match_toggle_case() { // change our case sensitivity: push_case_change(this->icase); this->icase = static_cast(pstate)->icase; pstate = pstate->next.p; return true; } template bool perl_matcher::match_startmark() { int index = static_cast(pstate)->index; icase = static_cast(pstate)->icase; switch(index) { case 0: pstate = pstate->next.p; break; case -1: case -2: { // forward lookahead assert: const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; push_assertion(next_pstate, index == -1); break; } case -3: { // independent sub-expression, currently this is always recursive: bool old_independent = m_independent; m_independent = true; const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; bool r = false; #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif r = match_all_states(); if(!r && !m_independent) { // Must be unwinding from a COMMIT/SKIP/PRUNE and the independent // sub failed, need to unwind everything else: while (m_backup_state->state_id) unwind(false); return false; } #if !defined(BOOST_NO_EXCEPTIONS) } catch(...) { pstate = next_pstate; // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)) {} throw; } #endif pstate = next_pstate; m_independent = old_independent; #ifdef BOOST_REGEX_MATCH_EXTRA if(r && (m_match_flags & match_extra)) { // // our captures have been stored in *m_presult // we need to unpack them, and insert them // back in the right order when we unwind the stack: // match_results temp_match(*m_presult); unsigned i; for(i = 0; i < temp_match.size(); ++i) (*m_presult)[i].get_captures().clear(); // match everything else: #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif r = match_all_states(); #if !defined(BOOST_NO_EXCEPTIONS) } catch(...) { pstate = next_pstate; // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)) {} throw; } #endif // now place the stored captures back: for(i = 0; i < temp_match.size(); ++i) { typedef typename sub_match::capture_sequence_type seq; seq& s1 = (*m_presult)[i].get_captures(); const seq& s2 = temp_match[i].captures(); s1.insert( s1.end(), s2.begin(), s2.end()); } } #endif return r; } case -4: { // conditional expression: const re_alt* alt = static_cast(pstate->next.p); BOOST_REGEX_ASSERT(alt->type == syntax_element_alt); pstate = alt->next.p; if(pstate->type == syntax_element_assert_backref) { if(!match_assert_backref()) pstate = alt->alt.p; break; } else { // zero width assertion, have to match this recursively: BOOST_REGEX_ASSERT(pstate->type == syntax_element_startmark); bool negated = static_cast(pstate)->index == -2; BidiIterator saved_position = position; const re_syntax_base* next_pstate = static_cast(pstate->next.p)->alt.p->next.p; pstate = pstate->next.p->next.p; #if !defined(BOOST_NO_EXCEPTIONS) try{ #endif bool r = match_all_states(); position = saved_position; if(negated) r = !r; if(r) pstate = next_pstate; else pstate = alt->alt.p; #if !defined(BOOST_NO_EXCEPTIONS) } catch(...) { pstate = next_pstate; // unwind all pushed states, apart from anything else this // ensures that all the states are correctly destructed // not just the memory freed. while(unwind(true)){} throw; } #endif break; } } case -5: { push_matched_paren(0, (*m_presult)[0]); m_presult->set_first(position, 0, true); pstate = pstate->next.p; break; } default: { BOOST_REGEX_ASSERT(index > 0); if((m_match_flags & match_nosubs) == 0) { push_matched_paren(index, (*m_presult)[index]); m_presult->set_first(position, index); } pstate = pstate->next.p; break; } } return true; } template bool perl_matcher::match_alt() { bool take_first, take_second; const re_alt* jmp = static_cast(pstate); // find out which of these two alternatives we need to take: if(position == last) { take_first = jmp->can_be_null & mask_take; take_second = jmp->can_be_null & mask_skip; } else { take_first = can_start(*position, jmp->_map, (unsigned char)mask_take); take_second = can_start(*position, jmp->_map, (unsigned char)mask_skip); } if(take_first) { // we can take the first alternative, // see if we need to push next alternative: if(take_second) { push_alt(jmp->alt.p); } pstate = pstate->next.p; return true; } if(take_second) { pstate = jmp->alt.p; return true; } return false; // neither option is possible } template bool perl_matcher::match_rep() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127 4244) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif const re_repeat* rep = static_cast(pstate); // find out which of these two alternatives we need to take: bool take_first, take_second; if(position == last) { take_first = rep->can_be_null & mask_take; take_second = rep->can_be_null & mask_skip; } else { take_first = can_start(*position, rep->_map, (unsigned char)mask_take); take_second = can_start(*position, rep->_map, (unsigned char)mask_skip); } if((m_backup_state->state_id != saved_state_repeater_count) || (static_cast*>(m_backup_state)->count.get_id() != rep->state_id) || (next_count->get_id() != rep->state_id)) { // we're moving to a different repeat from the last // one, so set up a counter object: push_repeater_count(rep->state_id, &next_count); } // // If we've had at least one repeat already, and the last one // matched the NULL string then set the repeat count to // maximum: // next_count->check_null_repeat(position, rep->max); if(next_count->get_count() < rep->min) { // we must take the repeat: if(take_first) { // increase the counter: ++(*next_count); pstate = rep->next.p; return true; } return false; } bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); if(greedy) { // try and take the repeat if we can: if((next_count->get_count() < rep->max) && take_first) { if(take_second) { // store position in case we fail: push_alt(rep->alt.p); } // increase the counter: ++(*next_count); pstate = rep->next.p; return true; } else if(take_second) { pstate = rep->alt.p; return true; } return false; // can't take anything, fail... } else // non-greedy { // try and skip the repeat if we can: if(take_second) { if((next_count->get_count() < rep->max) && take_first) { // store position in case we fail: push_non_greedy_repeat(rep->next.p); } pstate = rep->alt.p; return true; } if((next_count->get_count() < rep->max) && take_first) { // increase the counter: ++(*next_count); pstate = rep->next.p; return true; } } return false; #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_dot_repeat_slow() { std::size_t count = 0; const re_repeat* rep = static_cast(pstate); re_syntax_base* psingle = rep->next.p; // match compulsory repeats first: while(count < rep->min) { pstate = psingle; if(!match_wild()) return false; ++count; } bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); if(greedy) { // repeat for as long as we can: while(count < rep->max) { pstate = psingle; if(!match_wild()) break; ++count; } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_slow_dot); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } } template bool perl_matcher::match_dot_repeat_fast() { if(m_match_flags & match_not_dot_null) return match_dot_repeat_slow(); if((static_cast(pstate->next.p)->mask & match_any_mask) == 0) return match_dot_repeat_slow(); const re_repeat* rep = static_cast(pstate); bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t count = static_cast((std::min)(static_cast(std::distance(position, last)), greedy ? rep->max : rep->min)); if(rep->min > count) { position = last; return false; // not enough text left to match } std::advance(position, count); if(greedy) { if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_fast_dot); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } } template bool perl_matcher::match_char_repeat() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif const re_repeat* rep = static_cast(pstate); BOOST_REGEX_ASSERT(1 == static_cast(rep->next.p)->length); const char_type what = *reinterpret_cast(static_cast(rep->next.p) + 1); std::size_t count = 0; // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t desired = greedy ? rep->max : rep->min; if(::boost::is_random_access_iterator::value) { BidiIterator end = position; // Move end forward by "desired", preferably without using distance or advance if we can // as these can be slow for some iterator types. std::size_t len = (desired == (std::numeric_limits::max)()) ? 0u : std::distance(position, last); if(desired >= len) end = last; else std::advance(end, desired); BidiIterator origin(position); while((position != end) && (traits_inst.translate(*position, icase) == what)) { ++position; } count = (unsigned)std::distance(origin, position); } else { while((count < desired) && (position != last) && (traits_inst.translate(*position, icase) == what)) { ++position; ++count; } } if(count < rep->min) return false; if(greedy) { if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_char); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_set_repeat() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif const re_repeat* rep = static_cast(pstate); const unsigned char* map = static_cast(rep->next.p)->_map; std::size_t count = 0; // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t desired = greedy ? rep->max : rep->min; if(::boost::is_random_access_iterator::value) { BidiIterator end = position; // Move end forward by "desired", preferably without using distance or advance if we can // as these can be slow for some iterator types. std::size_t len = (desired == (std::numeric_limits::max)()) ? 0u : std::distance(position, last); if(desired >= len) end = last; else std::advance(end, desired); BidiIterator origin(position); while((position != end) && map[static_cast(traits_inst.translate(*position, icase))]) { ++position; } count = (unsigned)std::distance(origin, position); } else { while((count < desired) && (position != last) && map[static_cast(traits_inst.translate(*position, icase))]) { ++position; ++count; } } if(count < rep->min) return false; if(greedy) { if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_short_set); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_long_set_repeat() { #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif #ifdef BOOST_BORLANDC #pragma option push -w-8008 -w-8066 -w-8004 #endif typedef typename traits::char_class_type m_type; const re_repeat* rep = static_cast(pstate); const re_set_long* set = static_cast*>(pstate->next.p); std::size_t count = 0; // // start by working out how much we can skip: // bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent); std::size_t desired = greedy ? rep->max : rep->min; if(::boost::is_random_access_iterator::value) { BidiIterator end = position; // Move end forward by "desired", preferably without using distance or advance if we can // as these can be slow for some iterator types. std::size_t len = (desired == (std::numeric_limits::max)()) ? 0u : std::distance(position, last); if(desired >= len) end = last; else std::advance(end, desired); BidiIterator origin(position); while((position != end) && (position != re_is_set_member(position, last, set, re.get_data(), icase))) { ++position; } count = (unsigned)std::distance(origin, position); } else { while((count < desired) && (position != last) && (position != re_is_set_member(position, last, set, re.get_data(), icase))) { ++position; ++count; } } if(count < rep->min) return false; if(greedy) { if((rep->leading) && (count < rep->max)) restart = position; // push backtrack info if available: if(count - rep->min) push_single_repeat(count, rep, position, saved_state_greedy_single_repeat); // jump to next state: pstate = rep->alt.p; return true; } else { // non-greedy, push state and return true if we can skip: if(count < rep->max) push_single_repeat(count, rep, position, saved_state_rep_long_set); pstate = rep->alt.p; return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip); } #ifdef BOOST_BORLANDC #pragma option pop #endif #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } template bool perl_matcher::match_recursion() { BOOST_REGEX_ASSERT(pstate->type == syntax_element_recurse); // // See if we've seen this recursion before at this location, if we have then // we need to prevent infinite recursion: // for(typename std::vector >::reverse_iterator i = recursion_stack.rbegin(); i != recursion_stack.rend(); ++i) { if(i->idx == static_cast(static_cast(pstate)->alt.p)->index) { if(i->location_of_start == position) return false; break; } } // // Backup call stack: // push_recursion_pop(); // // Set new call stack: // if(recursion_stack.capacity() == 0) { recursion_stack.reserve(50); } recursion_stack.push_back(recursion_info()); recursion_stack.back().preturn_address = pstate->next.p; recursion_stack.back().results = *m_presult; pstate = static_cast(pstate)->alt.p; recursion_stack.back().idx = static_cast(pstate)->index; recursion_stack.back().location_of_start = position; //if(static_cast(pstate)->state_id > 0) { push_repeater_count(-(2 + static_cast(pstate)->index), &next_count); } return true; } template bool perl_matcher::match_endmark() { int index = static_cast(pstate)->index; icase = static_cast(pstate)->icase; if(index > 0) { if((m_match_flags & match_nosubs) == 0) { m_presult->set_second(position, index); } if(!recursion_stack.empty()) { if(index == recursion_stack.back().idx) { pstate = recursion_stack.back().preturn_address; *m_presult = recursion_stack.back().results; push_recursion(recursion_stack.back().idx, recursion_stack.back().preturn_address, m_presult, &recursion_stack.back().results); recursion_stack.pop_back(); push_repeater_count(-(2 + index), &next_count); } } } else if((index < 0) && (index != -4)) { // matched forward lookahead: pstate = 0; return true; } pstate = pstate->next.p; return true; } template bool perl_matcher::match_match() { if(!recursion_stack.empty()) { BOOST_REGEX_ASSERT(0 == recursion_stack.back().idx); pstate = recursion_stack.back().preturn_address; push_recursion(recursion_stack.back().idx, recursion_stack.back().preturn_address, m_presult, &recursion_stack.back().results); *m_presult = recursion_stack.back().results; recursion_stack.pop_back(); return true; } if((m_match_flags & match_not_null) && (position == (*m_presult)[0].first)) return false; if((m_match_flags & match_all) && (position != last)) return false; if((m_match_flags & regex_constants::match_not_initial_null) && (position == search_base)) return false; m_presult->set_second(position); pstate = 0; m_has_found_match = true; if((m_match_flags & match_posix) == match_posix) { m_result.maybe_assign(*m_presult); if((m_match_flags & match_any) == 0) return false; } #ifdef BOOST_REGEX_MATCH_EXTRA if(match_extra & m_match_flags) { for(unsigned i = 0; i < m_presult->size(); ++i) if((*m_presult)[i].matched) ((*m_presult)[i]).get_captures().push_back((*m_presult)[i]); } #endif return true; } template bool perl_matcher::match_commit() { // Ideally we would just junk all the states that are on the stack, // however we might not unwind correctly in that case, so for now, // just mark that we don't backtrack into whatever is left (or rather // we'll unwind it unconditionally without pausing to try other matches). switch(static_cast(pstate)->action) { case commit_commit: restart = last; break; case commit_skip: if(base != position) { restart = position; // Have to decrement restart since it will get incremented again later: --restart; } break; case commit_prune: break; } saved_state* pmp = m_backup_state; --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = m_backup_state; --pmp; } (void) new (pmp)saved_state(16); m_backup_state = pmp; pstate = pstate->next.p; return true; } template bool perl_matcher::match_then() { // Just leave a mark that we need to skip to next alternative: saved_state* pmp = m_backup_state; --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = m_backup_state; --pmp; } (void) new (pmp)saved_state(17); m_backup_state = pmp; pstate = pstate->next.p; return true; } template bool perl_matcher::skip_until_paren(int index, bool have_match) { while(pstate) { if(pstate->type == syntax_element_endmark) { if(static_cast(pstate)->index == index) { if(have_match) return this->match_endmark(); pstate = pstate->next.p; return true; } else { // Unenclosed closing ), occurs when (*ACCEPT) is inside some other // parenthesis which may or may not have other side effects associated with it. const re_syntax_base* sp = pstate; match_endmark(); if(!pstate) { unwind(true); // unwind may leave pstate NULL if we've unwound a forward lookahead, in which // case just move to the next state and keep looking... if (!pstate) pstate = sp->next.p; } } continue; } else if(pstate->type == syntax_element_match) return true; else if(pstate->type == syntax_element_startmark) { int idx = static_cast(pstate)->index; pstate = pstate->next.p; skip_until_paren(idx, false); continue; } pstate = pstate->next.p; } return true; } /**************************************************************************** Unwind and associated procedures follow, these perform what normal stack unwinding does in the recursive implementation. ****************************************************************************/ template bool perl_matcher::unwind(bool have_match) { static unwind_proc_type const s_unwind_table[19] = { &perl_matcher::unwind_end, &perl_matcher::unwind_paren, &perl_matcher::unwind_recursion_stopper, &perl_matcher::unwind_assertion, &perl_matcher::unwind_alt, &perl_matcher::unwind_repeater_counter, &perl_matcher::unwind_extra_block, &perl_matcher::unwind_greedy_single_repeat, &perl_matcher::unwind_slow_dot_repeat, &perl_matcher::unwind_fast_dot_repeat, &perl_matcher::unwind_char_repeat, &perl_matcher::unwind_short_set_repeat, &perl_matcher::unwind_long_set_repeat, &perl_matcher::unwind_non_greedy_repeat, &perl_matcher::unwind_recursion, &perl_matcher::unwind_recursion_pop, &perl_matcher::unwind_commit, &perl_matcher::unwind_then, &perl_matcher::unwind_case, }; m_recursive_result = have_match; m_unwound_lookahead = false; m_unwound_alt = false; unwind_proc_type unwinder; bool cont; // // keep unwinding our stack until we have something to do: // do { unwinder = s_unwind_table[m_backup_state->state_id]; cont = (this->*unwinder)(m_recursive_result); }while(cont); // // return true if we have more states to try: // return pstate ? true : false; } template bool perl_matcher::unwind_end(bool) { pstate = 0; // nothing left to search return false; // end of stack nothing more to search } template bool perl_matcher::unwind_case(bool) { saved_change_case* pmp = static_cast(m_backup_state); icase = pmp->icase; boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; } template bool perl_matcher::unwind_paren(bool have_match) { saved_matched_paren* pmp = static_cast*>(m_backup_state); // restore previous values if no match was found: if(!have_match) { m_presult->set_first(pmp->sub.first, pmp->index, pmp->index == 0); m_presult->set_second(pmp->sub.second, pmp->index, pmp->sub.matched, pmp->index == 0); } #ifdef BOOST_REGEX_MATCH_EXTRA // // we have a match, push the capture information onto the stack: // else if(pmp->sub.matched && (match_extra & m_match_flags)) ((*m_presult)[pmp->index]).get_captures().push_back(pmp->sub); #endif // unwind stack: m_backup_state = pmp+1; boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp); return true; // keep looking } template bool perl_matcher::unwind_recursion_stopper(bool) { boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++); pstate = 0; // nothing left to search return false; // end of stack nothing more to search } template bool perl_matcher::unwind_assertion(bool r) { saved_assertion* pmp = static_cast*>(m_backup_state); pstate = pmp->pstate; position = pmp->position; bool result = (r == pmp->positive); m_recursive_result = pmp->positive ? r : !r; boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; m_unwound_lookahead = true; return !result; // return false if the assertion was matched to stop search. } template bool perl_matcher::unwind_alt(bool r) { saved_position* pmp = static_cast*>(m_backup_state); if(!r) { pstate = pmp->pstate; position = pmp->position; } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; m_unwound_alt = !r; return r; } template bool perl_matcher::unwind_repeater_counter(bool) { saved_repeater* pmp = static_cast*>(m_backup_state); boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; // keep looking } template bool perl_matcher::unwind_extra_block(bool) { ++used_block_count; saved_extra_block* pmp = static_cast(m_backup_state); void* condemmed = m_stack_base; m_stack_base = pmp->base; m_backup_state = pmp->end; boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp); put_mem_block(condemmed); return true; // keep looking } template inline void perl_matcher::destroy_single_repeat() { saved_single_repeat* p = static_cast*>(m_backup_state); boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(p++); m_backup_state = p; } template bool perl_matcher::unwind_greedy_single_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); count -= rep->min; if((m_match_flags & match_partial) && (position == last)) m_has_partial_match = true; BOOST_REGEX_ASSERT(count); position = pmp->last_position; // backtrack till we can skip out: do { --position; --count; ++state_count; }while(count && !can_start(*position, rep->_map, mask_skip)); // if we've hit base, destroy this state: if(count == 0) { destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count + rep->min; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_slow_dot_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; BOOST_REGEX_ASSERT(rep->type == syntax_element_dot_rep); BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_wild); BOOST_REGEX_ASSERT(count < rep->max); pstate = rep->next.p; position = pmp->last_position; if(position != last) { // wind forward until we can skip out of the repeat: do { if(!match_wild()) { // failed repeat match, discard this state and look for another: destroy_single_repeat(); return true; } ++count; ++state_count; pstate = rep->next.p; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_fast_dot_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; BOOST_REGEX_ASSERT(count < rep->max); position = pmp->last_position; if(position != last) { // wind forward until we can skip out of the repeat: do { ++position; ++count; ++state_count; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_char_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; pstate = rep->next.p; const char_type what = *reinterpret_cast(static_cast(pstate) + 1); position = pmp->last_position; BOOST_REGEX_ASSERT(rep->type == syntax_element_char_rep); BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_literal); BOOST_REGEX_ASSERT(count < rep->max); if(position != last) { // wind forward until we can skip out of the repeat: do { if(traits_inst.translate(*position, icase) != what) { // failed repeat match, discard this state and look for another: destroy_single_repeat(); return true; } ++count; ++ position; ++state_count; pstate = rep->next.p; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_short_set_repeat(bool r) { saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; pstate = rep->next.p; const unsigned char* map = static_cast(rep->next.p)->_map; position = pmp->last_position; BOOST_REGEX_ASSERT(rep->type == syntax_element_short_set_rep); BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_set); BOOST_REGEX_ASSERT(count < rep->max); if(position != last) { // wind forward until we can skip out of the repeat: do { if(!map[static_cast(traits_inst.translate(*position, icase))]) { // failed repeat match, discard this state and look for another: destroy_single_repeat(); return true; } ++count; ++ position; ++state_count; pstate = rep->next.p; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_long_set_repeat(bool r) { typedef typename traits::char_class_type m_type; saved_single_repeat* pmp = static_cast*>(m_backup_state); // if we have a match, just discard this state: if(r) { destroy_single_repeat(); return true; } const re_repeat* rep = pmp->rep; std::size_t count = pmp->count; pstate = rep->next.p; const re_set_long* set = static_cast*>(pstate); position = pmp->last_position; BOOST_REGEX_ASSERT(rep->type == syntax_element_long_set_rep); BOOST_REGEX_ASSERT(rep->next.p != 0); BOOST_REGEX_ASSERT(rep->alt.p != 0); BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_long_set); BOOST_REGEX_ASSERT(count < rep->max); if(position != last) { // wind forward until we can skip out of the repeat: do { if(position == re_is_set_member(position, last, set, re.get_data(), icase)) { // failed repeat match, discard this state and look for another: destroy_single_repeat(); return true; } ++position; ++count; ++state_count; pstate = rep->next.p; }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip)); } // remember where we got to if this is a leading repeat: if((rep->leading) && (count < rep->max)) restart = position; if(position == last) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if((m_match_flags & match_partial) && (position == last) && (position != search_base)) m_has_partial_match = true; if(0 == (rep->can_be_null & mask_skip)) return true; } else if(count == rep->max) { // can't repeat any more, remove the pushed state: destroy_single_repeat(); if(!can_start(*position, rep->_map, mask_skip)) return true; } else { pmp->count = count; pmp->last_position = position; } pstate = rep->alt.p; return false; } template bool perl_matcher::unwind_non_greedy_repeat(bool r) { saved_position* pmp = static_cast*>(m_backup_state); if(!r) { position = pmp->position; pstate = pmp->pstate; ++(*next_count); } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return r; } template bool perl_matcher::unwind_recursion(bool r) { // We are backtracking back inside a recursion, need to push the info // back onto the recursion stack, and do so unconditionally, otherwise // we can get mismatched pushes and pops... saved_recursion* pmp = static_cast*>(m_backup_state); if (!r) { recursion_stack.push_back(recursion_info()); recursion_stack.back().idx = pmp->recursion_id; recursion_stack.back().preturn_address = pmp->preturn_address; recursion_stack.back().results = pmp->prior_results; recursion_stack.back().location_of_start = position; *m_presult = pmp->internal_results; } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; } template bool perl_matcher::unwind_recursion_pop(bool r) { // Backtracking out of a recursion, we must pop state off the recursion // stack unconditionally to ensure matched pushes and pops: saved_state* pmp = static_cast(m_backup_state); if (!r && !recursion_stack.empty()) { *m_presult = recursion_stack.back().results; position = recursion_stack.back().location_of_start; recursion_stack.pop_back(); } boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++); m_backup_state = pmp; return true; } template void perl_matcher::push_recursion_pop() { saved_state* pmp = static_cast(m_backup_state); --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = static_cast(m_backup_state); --pmp; } (void) new (pmp)saved_state(15); m_backup_state = pmp; } template bool perl_matcher::unwind_commit(bool b) { boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++); while(unwind(b) && !m_unwound_lookahead){} if(m_unwound_lookahead && pstate) { // // If we stop because we just unwound an assertion, put the // commit state back on the stack again: // m_unwound_lookahead = false; saved_state* pmp = m_backup_state; --pmp; if(pmp < m_stack_base) { extend_stack(); pmp = m_backup_state; --pmp; } (void) new (pmp)saved_state(16); m_backup_state = pmp; } // This prevents us from stopping when we exit from an independent sub-expression: m_independent = false; return false; } template bool perl_matcher::unwind_then(bool b) { // Unwind everything till we hit an alternative: boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++); bool result = false; result = unwind(b); while(result && !m_unwound_alt) { result = unwind(b); } // We're now pointing at the next alternative, need one more backtrack // since *all* the other alternatives must fail once we've reached a THEN clause: if(result && m_unwound_alt) unwind(b); return false; } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/primary_transform.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE: primary_transform.hpp * VERSION: see * DESCRIPTION: Heuristically determines the sort string format in use * by the current locale. */ #ifndef BOOST_REGEX_PRIMARY_TRANSFORM #define BOOST_REGEX_PRIMARY_TRANSFORM namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ enum{ sort_C, sort_fixed, sort_delim, sort_unknown }; template unsigned count_chars(const S& s, charT c) { // // Count how many occurrences of character c occur // in string s: if c is a delimeter between collation // fields, then this should be the same value for all // sort keys: // unsigned int count = 0; for(unsigned pos = 0; pos < s.size(); ++pos) { if(s[pos] == c) ++count; } return count; } template unsigned find_sort_syntax(const traits* pt, charT* delim) { // // compare 'a' with 'A' to see how similar they are, // should really use a-accute but we can't portably do that, // typedef typename traits::string_type string_type; typedef typename traits::char_type char_type; // Suppress incorrect warning for MSVC (void)pt; char_type a[2] = {'a', '\0', }; string_type sa(pt->transform(a, a+1)); if(sa == a) { *delim = 0; return sort_C; } char_type A[2] = { 'A', '\0', }; string_type sA(pt->transform(A, A+1)); char_type c[2] = { ';', '\0', }; string_type sc(pt->transform(c, c+1)); int pos = 0; while((pos <= static_cast(sa.size())) && (pos <= static_cast(sA.size())) && (sa[pos] == sA[pos])) ++pos; --pos; if(pos < 0) { *delim = 0; return sort_unknown; } // // at this point sa[pos] is either the end of a fixed width field // or the character that acts as a delimiter: // charT maybe_delim = sa[pos]; if((pos != 0) && (count_chars(sa, maybe_delim) == count_chars(sA, maybe_delim)) && (count_chars(sa, maybe_delim) == count_chars(sc, maybe_delim))) { *delim = maybe_delim; return sort_delim; } // // OK doen't look like a delimiter, try for fixed width field: // if((sa.size() == sA.size()) && (sa.size() == sc.size())) { // note assumes that the fixed width field is less than // (numeric_limits::max)(), should be true for all types // I can't imagine 127 character fields... *delim = static_cast(++pos); return sort_fixed; } // // don't know what it is: // *delim = 0; return sort_unknown; } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regbase.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regbase.cpp * VERSION see * DESCRIPTION: Declares class regbase. */ #ifndef BOOST_REGEX_V5_REGBASE_HPP #define BOOST_REGEX_V5_REGBASE_HPP namespace boost{ // // class regbase // handles error codes and flags // class regbase { public: enum flag_type_ { // // Divide the flags up into logical groups: // bits 0-7 indicate main synatx type. // bits 8-15 indicate syntax subtype. // bits 16-31 indicate options that are common to all // regex syntaxes. // In all cases the default is 0. // // Main synatx group: // perl_syntax_group = 0, // default basic_syntax_group = 1, // POSIX basic literal = 2, // all characters are literals main_option_type = literal | basic_syntax_group | perl_syntax_group, // everything! // // options specific to perl group: // no_bk_refs = 1 << 8, // \d not allowed no_perl_ex = 1 << 9, // disable perl extensions no_mod_m = 1 << 10, // disable Perl m modifier mod_x = 1 << 11, // Perl x modifier mod_s = 1 << 12, // force s modifier on (overrides match_not_dot_newline) no_mod_s = 1 << 13, // force s modifier off (overrides match_not_dot_newline) // // options specific to basic group: // no_char_classes = 1 << 8, // [[:CLASS:]] not allowed no_intervals = 1 << 9, // {x,y} not allowed bk_plus_qm = 1 << 10, // uses \+ and \? bk_vbar = 1 << 11, // use \| for alternatives emacs_ex = 1 << 12, // enables emacs extensions // // options common to all groups: // no_escape_in_lists = 1 << 16, // '\' not special inside [...] newline_alt = 1 << 17, // \n is the same as | no_except = 1 << 18, // no exception on error failbit = 1 << 19, // error flag icase = 1 << 20, // characters are matched regardless of case nocollate = 0, // don't use locale specific collation (deprecated) collate = 1 << 21, // use locale specific collation nosubs = 1 << 22, // don't mark sub-expressions save_subexpression_location = 1 << 23, // save subexpression locations no_empty_expressions = 1 << 24, // no empty expressions allowed optimize = 0, // not really supported basic = basic_syntax_group | collate | no_escape_in_lists, extended = no_bk_refs | collate | no_perl_ex | no_escape_in_lists, normal = 0, emacs = basic_syntax_group | collate | emacs_ex | bk_vbar, awk = no_bk_refs | collate | no_perl_ex, grep = basic | newline_alt, egrep = extended | newline_alt, sed = basic, perl = normal, ECMAScript = normal, JavaScript = normal, JScript = normal }; typedef unsigned int flag_type; enum restart_info { restart_any = 0, restart_word = 1, restart_line = 2, restart_buf = 3, restart_continue = 4, restart_lit = 5, restart_fixed_lit = 6, restart_count = 7 }; }; // // provide std lib proposal compatible constants: // namespace regex_constants{ enum flag_type_ { no_except = ::boost::regbase::no_except, failbit = ::boost::regbase::failbit, literal = ::boost::regbase::literal, icase = ::boost::regbase::icase, nocollate = ::boost::regbase::nocollate, collate = ::boost::regbase::collate, nosubs = ::boost::regbase::nosubs, optimize = ::boost::regbase::optimize, bk_plus_qm = ::boost::regbase::bk_plus_qm, bk_vbar = ::boost::regbase::bk_vbar, no_intervals = ::boost::regbase::no_intervals, no_char_classes = ::boost::regbase::no_char_classes, no_escape_in_lists = ::boost::regbase::no_escape_in_lists, no_mod_m = ::boost::regbase::no_mod_m, mod_x = ::boost::regbase::mod_x, mod_s = ::boost::regbase::mod_s, no_mod_s = ::boost::regbase::no_mod_s, save_subexpression_location = ::boost::regbase::save_subexpression_location, no_empty_expressions = ::boost::regbase::no_empty_expressions, basic = ::boost::regbase::basic, extended = ::boost::regbase::extended, normal = ::boost::regbase::normal, emacs = ::boost::regbase::emacs, awk = ::boost::regbase::awk, grep = ::boost::regbase::grep, egrep = ::boost::regbase::egrep, sed = basic, perl = normal, ECMAScript = normal, JavaScript = normal, JScript = normal }; typedef ::boost::regbase::flag_type syntax_option_type; } // namespace regex_constants } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex.cpp * VERSION see * DESCRIPTION: Declares boost::basic_regex<> and associated * functions and classes. This header is the main * entry point for the template regex code. */ #ifndef BOOST_RE_REGEX_HPP_INCLUDED #define BOOST_RE_REGEX_HPP_INCLUDED #ifdef __cplusplus // what follows is all C++ don't include in C builds!! #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost{ #ifdef BOOST_REGEX_NO_FWD typedef basic_regex > regex; #ifndef BOOST_NO_WREGEX typedef basic_regex > wregex; #endif #endif typedef match_results cmatch; typedef match_results smatch; #ifndef BOOST_NO_WREGEX typedef match_results wcmatch; typedef match_results wsmatch; #endif } // namespace boost #include #include #include #include #include #include #include #include #endif // __cplusplus #endif // include ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_format.hpp ================================================ /* * * Copyright (c) 1998-2009 John Maddock * Copyright 2008 Eric Niebler. * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_format.hpp * VERSION see * DESCRIPTION: Provides formatting output routines for search and replace * operations. Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_FORMAT_HPP #define BOOST_REGEX_FORMAT_HPP #include #include namespace boost{ // // Forward declaration: // template >::allocator_type > class match_results; namespace BOOST_REGEX_DETAIL_NS{ // // struct trivial_format_traits: // defines minimum localisation support for formatting // in the case that the actual regex traits is unavailable. // template struct trivial_format_traits { typedef charT char_type; static std::ptrdiff_t length(const charT* p) { return global_length(p); } static charT tolower(charT c) { return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c); } static charT toupper(charT c) { return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c); } static int value(const charT c, int radix) { int result = global_value(c); return result >= radix ? -1 : result; } int toi(const charT*& p1, const charT* p2, int radix)const { return (int)global_toi(p1, p2, radix, *this); } }; #ifdef BOOST_REGEX_MSVC # pragma warning(push) #pragma warning(disable:26812) #endif template class basic_regex_formatter { public: typedef typename traits::char_type char_type; basic_regex_formatter(OutputIterator o, const Results& r, const traits& t) : m_traits(t), m_results(r), m_out(o), m_position(), m_end(), m_flags(), m_state(output_copy), m_restore_state(output_copy), m_have_conditional(false) {} OutputIterator format(ForwardIter p1, ForwardIter p2, match_flag_type f); OutputIterator format(ForwardIter p1, match_flag_type f) { return format(p1, p1 + m_traits.length(p1), f); } private: typedef typename Results::value_type sub_match_type; enum output_state { output_copy, output_next_lower, output_next_upper, output_lower, output_upper, output_none }; void put(char_type c); void put(const sub_match_type& sub); void format_all(); void format_perl(); void format_escape(); void format_conditional(); void format_until_scope_end(); bool handle_perl_verb(bool have_brace); inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const std::integral_constant&) { std::vector v(i, j); return (i != j) ? this->m_results.named_subexpression(&v[0], &v[0] + v.size()) : this->m_results.named_subexpression(static_cast(0), static_cast(0)); } inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const std::integral_constant&) { return this->m_results.named_subexpression(i, j); } inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j) { typedef typename std::is_convertible::type tag_type; return get_named_sub(i, j, tag_type()); } inline int get_named_sub_index(ForwardIter i, ForwardIter j, const std::integral_constant&) { std::vector v(i, j); return (i != j) ? this->m_results.named_subexpression_index(&v[0], &v[0] + v.size()) : this->m_results.named_subexpression_index(static_cast(0), static_cast(0)); } inline int get_named_sub_index(ForwardIter i, ForwardIter j, const std::integral_constant&) { return this->m_results.named_subexpression_index(i, j); } inline int get_named_sub_index(ForwardIter i, ForwardIter j) { typedef typename std::is_convertible::type tag_type; return get_named_sub_index(i, j, tag_type()); } #ifdef BOOST_REGEX_MSVC // msvc-8.0 issues a spurious warning on the call to std::advance here: #pragma warning(push) #pragma warning(disable:4244) #endif inline int toi(ForwardIter& i, ForwardIter j, int base, const std::integral_constant&) { if(i != j) { std::vector v(i, j); const char_type* start = &v[0]; const char_type* pos = start; int r = (int)m_traits.toi(pos, &v[0] + v.size(), base); std::advance(i, pos - start); return r; } return -1; } #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif inline int toi(ForwardIter& i, ForwardIter j, int base, const std::integral_constant&) { return m_traits.toi(i, j, base); } inline int toi(ForwardIter& i, ForwardIter j, int base) { #if defined(_MSC_VER) && defined(__INTEL_COMPILER) && ((__INTEL_COMPILER == 9999) || (__INTEL_COMPILER == 1210)) // Workaround for Intel support issue #656654. // See also https://svn.boost.org/trac/boost/ticket/6359 return toi(i, j, base, std::integral_constant()); #else typedef typename std::is_convertible::type tag_type; return toi(i, j, base, tag_type()); #endif } const traits& m_traits; // the traits class for localised formatting operations const Results& m_results; // the match_results being used. OutputIterator m_out; // where to send output. ForwardIter m_position; // format string, current position ForwardIter m_end; // format string end match_flag_type m_flags; // format flags to use output_state m_state; // what to do with the next character output_state m_restore_state; // what state to restore to. bool m_have_conditional; // we are parsing a conditional private: basic_regex_formatter(const basic_regex_formatter&); basic_regex_formatter& operator=(const basic_regex_formatter&); }; #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif template OutputIterator basic_regex_formatter::format(ForwardIter p1, ForwardIter p2, match_flag_type f) { m_position = p1; m_end = p2; m_flags = f; format_all(); return m_out; } template void basic_regex_formatter::format_all() { // over and over: while(m_position != m_end) { switch(*m_position) { case '&': if(m_flags & ::boost::regex_constants::format_sed) { ++m_position; put(m_results[0]); break; } put(*m_position++); break; case '\\': format_escape(); break; case '(': if(m_flags & boost::regex_constants::format_all) { ++m_position; bool have_conditional = m_have_conditional; m_have_conditional = false; format_until_scope_end(); m_have_conditional = have_conditional; if(m_position == m_end) return; BOOST_REGEX_ASSERT(*m_position == static_cast(')')); ++m_position; // skip the closing ')' break; } put(*m_position); ++m_position; break; case ')': if(m_flags & boost::regex_constants::format_all) { return; } put(*m_position); ++m_position; break; case ':': if((m_flags & boost::regex_constants::format_all) && m_have_conditional) { return; } put(*m_position); ++m_position; break; case '?': if(m_flags & boost::regex_constants::format_all) { ++m_position; format_conditional(); break; } put(*m_position); ++m_position; break; case '$': if((m_flags & format_sed) == 0) { format_perl(); break; } // not a special character: BOOST_REGEX_FALLTHROUGH; default: put(*m_position); ++m_position; break; } } } template void basic_regex_formatter::format_perl() { // // On entry *m_position points to a '$' character // output the information that goes with it: // BOOST_REGEX_ASSERT(*m_position == '$'); // // see if this is a trailing '$': // if(++m_position == m_end) { --m_position; put(*m_position); ++m_position; return; } // // OK find out what kind it is: // bool have_brace = false; ForwardIter save_position = m_position; switch(*m_position) { case '&': ++m_position; put(this->m_results[0]); break; case '`': ++m_position; put(this->m_results.prefix()); break; case '\'': ++m_position; put(this->m_results.suffix()); break; case '$': put(*m_position++); break; case '+': if((++m_position != m_end) && (*m_position == '{')) { ForwardIter base = ++m_position; while((m_position != m_end) && (*m_position != '}')) ++m_position; if(m_position != m_end) { // Named sub-expression: put(get_named_sub(base, m_position)); ++m_position; break; } else { m_position = --base; } } put((this->m_results)[this->m_results.size() > 1 ? static_cast(this->m_results.size() - 1) : 1]); break; case '{': have_brace = true; ++m_position; BOOST_REGEX_FALLTHROUGH; default: // see if we have a number: { std::ptrdiff_t len = std::distance(m_position, m_end); //len = (std::min)(static_cast(2), len); int v = this->toi(m_position, m_position + len, 10); if((v < 0) || (have_brace && ((m_position == m_end) || (*m_position != '}')))) { // Look for a Perl-5.10 verb: if(!handle_perl_verb(have_brace)) { // leave the $ as is, and carry on: m_position = --save_position; put(*m_position); ++m_position; } break; } // otherwise output sub v: put(this->m_results[v]); if(have_brace) ++m_position; } } } template bool basic_regex_formatter::handle_perl_verb(bool have_brace) { // // We may have a capitalised string containing a Perl action: // static const char_type MATCH[] = { 'M', 'A', 'T', 'C', 'H' }; static const char_type PREMATCH[] = { 'P', 'R', 'E', 'M', 'A', 'T', 'C', 'H' }; static const char_type POSTMATCH[] = { 'P', 'O', 'S', 'T', 'M', 'A', 'T', 'C', 'H' }; static const char_type LAST_PAREN_MATCH[] = { 'L', 'A', 'S', 'T', '_', 'P', 'A', 'R', 'E', 'N', '_', 'M', 'A', 'T', 'C', 'H' }; static const char_type LAST_SUBMATCH_RESULT[] = { 'L', 'A', 'S', 'T', '_', 'S', 'U', 'B', 'M', 'A', 'T', 'C', 'H', '_', 'R', 'E', 'S', 'U', 'L', 'T' }; static const char_type LAST_SUBMATCH_RESULT_ALT[] = { '^', 'N' }; if(m_position == m_end) return false; if(have_brace && (*m_position == '^')) ++m_position; std::ptrdiff_t max_len = m_end - m_position; if((max_len >= 5) && std::equal(m_position, m_position + 5, MATCH)) { m_position += 5; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 5; return false; } } put(this->m_results[0]); return true; } if((max_len >= 8) && std::equal(m_position, m_position + 8, PREMATCH)) { m_position += 8; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 8; return false; } } put(this->m_results.prefix()); return true; } if((max_len >= 9) && std::equal(m_position, m_position + 9, POSTMATCH)) { m_position += 9; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 9; return false; } } put(this->m_results.suffix()); return true; } if((max_len >= 16) && std::equal(m_position, m_position + 16, LAST_PAREN_MATCH)) { m_position += 16; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 16; return false; } } put((this->m_results)[this->m_results.size() > 1 ? static_cast(this->m_results.size() - 1) : 1]); return true; } if((max_len >= 20) && std::equal(m_position, m_position + 20, LAST_SUBMATCH_RESULT)) { m_position += 20; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 20; return false; } } put(this->m_results.get_last_closed_paren()); return true; } if((max_len >= 2) && std::equal(m_position, m_position + 2, LAST_SUBMATCH_RESULT_ALT)) { m_position += 2; if(have_brace) { if((m_position != m_end) && (*m_position == '}')) ++m_position; else { m_position -= 2; return false; } } put(this->m_results.get_last_closed_paren()); return true; } return false; } template void basic_regex_formatter::format_escape() { // skip the escape and check for trailing escape: if(++m_position == m_end) { put(static_cast('\\')); return; } // now switch on the escape type: switch(*m_position) { case 'a': put(static_cast('\a')); ++m_position; break; case 'f': put(static_cast('\f')); ++m_position; break; case 'n': put(static_cast('\n')); ++m_position; break; case 'r': put(static_cast('\r')); ++m_position; break; case 't': put(static_cast('\t')); ++m_position; break; case 'v': put(static_cast('\v')); ++m_position; break; case 'x': if(++m_position == m_end) { put(static_cast('x')); return; } // maybe have \x{ddd} if(*m_position == static_cast('{')) { ++m_position; int val = this->toi(m_position, m_end, 16); if(val < 0) { // invalid value treat everything as literals: put(static_cast('x')); put(static_cast('{')); return; } if((m_position == m_end) || (*m_position != static_cast('}'))) { --m_position; while(*m_position != static_cast('\\')) --m_position; ++m_position; put(*m_position++); return; } ++m_position; put(static_cast(val)); return; } else { std::ptrdiff_t len = std::distance(m_position, m_end); len = (std::min)(static_cast(2), len); int val = this->toi(m_position, m_position + len, 16); if(val < 0) { --m_position; put(*m_position++); return; } put(static_cast(val)); } break; case 'c': if(++m_position == m_end) { --m_position; put(*m_position++); return; } put(static_cast(*m_position++ % 32)); break; case 'e': put(static_cast(27)); ++m_position; break; default: // see if we have a perl specific escape: if((m_flags & boost::regex_constants::format_sed) == 0) { bool breakout = false; switch(*m_position) { case 'l': ++m_position; m_restore_state = m_state; m_state = output_next_lower; breakout = true; break; case 'L': ++m_position; m_state = output_lower; breakout = true; break; case 'u': ++m_position; m_restore_state = m_state; m_state = output_next_upper; breakout = true; break; case 'U': ++m_position; m_state = output_upper; breakout = true; break; case 'E': ++m_position; m_state = output_copy; breakout = true; break; } if(breakout) break; } // see if we have a \n sed style backreference: std::ptrdiff_t len = std::distance(m_position, m_end); len = (std::min)(static_cast(1), len); int v = this->toi(m_position, m_position+len, 10); if((v > 0) || ((v == 0) && (m_flags & ::boost::regex_constants::format_sed))) { put(m_results[v]); break; } else if(v == 0) { // octal ecape sequence: --m_position; len = std::distance(m_position, m_end); len = (std::min)(static_cast(4), len); v = this->toi(m_position, m_position + len, 8); BOOST_REGEX_ASSERT(v >= 0); put(static_cast(v)); break; } // Otherwise output the character "as is": put(*m_position++); break; } } template void basic_regex_formatter::format_conditional() { if(m_position == m_end) { // oops trailing '?': put(static_cast('?')); return; } int v; if(*m_position == '{') { ForwardIter base = m_position; ++m_position; v = this->toi(m_position, m_end, 10); if(v < 0) { // Try a named subexpression: while((m_position != m_end) && (*m_position != '}')) ++m_position; v = this->get_named_sub_index(base + 1, m_position); } if((v < 0) || (*m_position != '}')) { m_position = base; // oops trailing '?': put(static_cast('?')); return; } // Skip trailing '}': ++m_position; } else { std::ptrdiff_t len = std::distance(m_position, m_end); len = (std::min)(static_cast(2), len); v = this->toi(m_position, m_position + len, 10); } if(v < 0) { // oops not a number: put(static_cast('?')); return; } // output varies depending upon whether sub-expression v matched or not: if(m_results[v].matched) { m_have_conditional = true; format_all(); m_have_conditional = false; if((m_position != m_end) && (*m_position == static_cast(':'))) { // skip the ':': ++m_position; // save output state, then turn it off: output_state saved_state = m_state; m_state = output_none; // format the rest of this scope: format_until_scope_end(); // restore output state: m_state = saved_state; } } else { // save output state, then turn it off: output_state saved_state = m_state; m_state = output_none; // format until ':' or ')': m_have_conditional = true; format_all(); m_have_conditional = false; // restore state: m_state = saved_state; if((m_position != m_end) && (*m_position == static_cast(':'))) { // skip the ':': ++m_position; // format the rest of this scope: format_until_scope_end(); } } } template void basic_regex_formatter::format_until_scope_end() { do { format_all(); if((m_position == m_end) || (*m_position == static_cast(')'))) return; put(*m_position++); }while(m_position != m_end); } template void basic_regex_formatter::put(char_type c) { // write a single character to output // according to which case translation mode we are in: switch(this->m_state) { case output_none: return; case output_next_lower: c = m_traits.tolower(c); this->m_state = m_restore_state; break; case output_next_upper: c = m_traits.toupper(c); this->m_state = m_restore_state; break; case output_lower: c = m_traits.tolower(c); break; case output_upper: c = m_traits.toupper(c); break; default: break; } *m_out = c; ++m_out; } template void basic_regex_formatter::put(const sub_match_type& sub) { typedef typename sub_match_type::iterator iterator_type; iterator_type i = sub.first; while(i != sub.second) { put(*i); ++i; } } template class string_out_iterator { S* out; public: string_out_iterator(S& s) : out(&s) {} string_out_iterator& operator++() { return *this; } string_out_iterator& operator++(int) { return *this; } string_out_iterator& operator*() { return *this; } string_out_iterator& operator=(typename S::value_type v) { out->append(1, v); return *this; } typedef std::ptrdiff_t difference_type; typedef typename S::value_type value_type; typedef value_type* pointer; typedef value_type& reference; typedef std::output_iterator_tag iterator_category; }; template OutputIterator regex_format_imp(OutputIterator out, const match_results& m, ForwardIter p1, ForwardIter p2, match_flag_type flags, const traits& t ) { if(flags & regex_constants::format_literal) { return BOOST_REGEX_DETAIL_NS::copy(p1, p2, out); } BOOST_REGEX_DETAIL_NS::basic_regex_formatter< OutputIterator, match_results, traits, ForwardIter> f(out, m, t); return f.format(p1, p2, flags); } template struct has_const_iterator { template static typename U::const_iterator tester(U*); static char tester(...); static T* get(); static const bool value = sizeof(tester(get())) != sizeof(char); }; struct any_type { template any_type(const T&); template any_type(const T&, const U&); template any_type(const T&, const U&, const V&); }; typedef char no_type; typedef char (&unary_type)[2]; typedef char (&binary_type)[3]; typedef char (&ternary_type)[4]; no_type check_is_formatter(unary_type, binary_type, ternary_type); template unary_type check_is_formatter(T const &, binary_type, ternary_type); template binary_type check_is_formatter(unary_type, T const &, ternary_type); template binary_type check_is_formatter(T const &, U const &, ternary_type); template ternary_type check_is_formatter(unary_type, binary_type, T const &); template ternary_type check_is_formatter(T const &, binary_type, U const &); template ternary_type check_is_formatter(unary_type, T const &, U const &); template ternary_type check_is_formatter(T const &, U const &, V const &); struct unary_binary_ternary { typedef unary_type (*unary_fun)(any_type); typedef binary_type (*binary_fun)(any_type, any_type); typedef ternary_type (*ternary_fun)(any_type, any_type, any_type); operator unary_fun(); operator binary_fun(); operator ternary_fun(); }; template::value> struct formatter_wrapper : Formatter , unary_binary_ternary { formatter_wrapper(){} }; template struct formatter_wrapper : unary_binary_ternary { operator Formatter *(); }; template struct formatter_wrapper : unary_binary_ternary { operator Formatter *(); }; template struct do_unwrap_reference { typedef T type; }; template struct do_unwrap_reference > { typedef T type; }; template T& do_unwrap_ref(T& r) { return r; } template T& do_unwrap_ref(std::reference_wrapper const& r) { return r.get(); } template struct format_traits_imp { private: // // F must be a pointer, a function, or a class with a function call operator: // static_assert((::std::is_pointer::value || ::std::is_function::value || ::std::is_class::value), "The functor must be a pointer or a class with a function call operator"); static formatter_wrapper::type> f; static M m; static O out; static boost::regex_constants::match_flag_type flags; public: static const int value = sizeof(check_is_formatter(f(m), f(m, out), f(m, out, flags))); }; template struct format_traits { public: // // Type is std::integral_constant where N is one of: // // 0 : F is a pointer to a presumably null-terminated string. // 1 : F is a character-container such as a std::string. // 2 : F is a Unary Functor. // 3 : F is a Binary Functor. // 4 : F is a Ternary Functor. // typedef typename std::conditional< std::is_pointer::value && !std::is_function::type>::value, std::integral_constant, typename std::conditional< has_const_iterator::value, std::integral_constant, std::integral_constant::value> >::type >::type type; // // This static assertion will fail if the functor passed does not accept // the same type of arguments passed. // static_assert( std::is_class::value && !has_const_iterator::value ? (type::value > 1) : true, "Argument mismatch in Functor type"); }; template struct format_functor3 { format_functor3(Base b) : func(b) {} template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f) { return do_unwrap_ref(func)(m, i, f); } template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&) { return (*this)(m, i, f); } private: Base func; format_functor3(const format_functor3&); format_functor3& operator=(const format_functor3&); }; template struct format_functor2 { format_functor2(Base b) : func(b) {} template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/) { return do_unwrap_ref(func)(m, i); } template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&) { return (*this)(m, i, f); } private: Base func; format_functor2(const format_functor2&); format_functor2& operator=(const format_functor2&); }; template struct format_functor1 { format_functor1(Base b) : func(b) {} template OutputIter do_format_string(const S& s, OutputIter i) { return std::copy(s.begin(), s.end(), i); } template inline OutputIter do_format_string(const S* s, OutputIter i) { while(s && *s) { *i = *s; ++i; ++s; } return i; } template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/) { return do_format_string(do_unwrap_ref(func)(m), i); } template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&) { return (*this)(m, i, f); } private: Base func; format_functor1(const format_functor1&); format_functor1& operator=(const format_functor1&); }; template struct format_functor_c_string { format_functor_c_string(const charT* ps) : func(ps) {} template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits()) { //typedef typename Match::char_type char_type; const charT* end = func; while(*end) ++end; return regex_format_imp(i, m, func, end, f, t); } private: const charT* func; format_functor_c_string(const format_functor_c_string&); format_functor_c_string& operator=(const format_functor_c_string&); }; template struct format_functor_container { format_functor_container(const Container& c) : func(c) {} template OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits()) { //typedef typename Match::char_type char_type; return BOOST_REGEX_DETAIL_NS::regex_format_imp(i, m, func.begin(), func.end(), f, t); } private: const Container& func; format_functor_container(const format_functor_container&); format_functor_container& operator=(const format_functor_container&); }; template > struct compute_functor_type { typedef typename format_traits::type tag; typedef typename std::remove_cv< typename std::remove_pointer::type>::type maybe_char_type; typedef typename std::conditional< tag::value == 0, format_functor_c_string, typename std::conditional< tag::value == 1, format_functor_container, typename std::conditional< tag::value == 2, format_functor1, typename std::conditional< tag::value == 3, format_functor2, format_functor3 >::type >::type >::type >::type type; }; } // namespace BOOST_REGEX_DETAIL_NS template inline OutputIterator regex_format(OutputIterator out, const match_results& m, Functor fmt, match_flag_type flags = format_all ) { return m.format(out, fmt, flags); } template inline std::basic_string::char_type> regex_format(const match_results& m, Functor fmt, match_flag_type flags = format_all) { return m.format(fmt, flags); } } // namespace boost #endif // BOOST_REGEX_FORMAT_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_fwd.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_fwd.cpp * VERSION see * DESCRIPTION: Forward declares boost::basic_regex<> and * associated typedefs. */ #ifndef BOOST_REGEX_FWD_HPP_INCLUDED #define BOOST_REGEX_FWD_HPP_INCLUDED #ifndef BOOST_REGEX_CONFIG_HPP #include #endif // // define BOOST_REGEX_NO_FWD if this // header doesn't work! // #ifdef BOOST_REGEX_NO_FWD # ifndef BOOST_RE_REGEX_HPP # include # endif #else namespace boost{ template class cpp_regex_traits; template struct c_regex_traits; template class w32_regex_traits; #ifdef BOOST_REGEX_USE_WIN32_LOCALE template > struct regex_traits; #elif defined(BOOST_REGEX_USE_CPP_LOCALE) template > struct regex_traits; #else template > struct regex_traits; #endif template > class basic_regex; typedef basic_regex > regex; #ifndef BOOST_NO_WREGEX typedef basic_regex > wregex; #endif } // namespace boost #endif // BOOST_REGEX_NO_FWD #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_grep.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_grep.hpp * VERSION see * DESCRIPTION: Provides regex_grep implementation. */ #ifndef BOOST_REGEX_V5_REGEX_GREP_HPP #define BOOST_REGEX_V5_REGEX_GREP_HPP namespace boost{ // // regex_grep: // find all non-overlapping matches within the sequence first last: // template inline unsigned int regex_grep(Predicate foo, BidiIterator first, BidiIterator last, const basic_regex& e, match_flag_type flags = match_default) { if(e.flags() & regex_constants::failbit) return false; typedef typename match_results::allocator_type match_allocator_type; match_results m; BOOST_REGEX_DETAIL_NS::perl_matcher matcher(first, last, m, e, flags, first); unsigned int count = 0; while(matcher.find()) { ++count; if(0 == foo(m)) return count; // caller doesn't want to go on if(m[0].second == last) return count; // we've reached the end, don't try and find an extra null match. if(m.length() == 0) { if(m[0].second == last) return count; // we found a NULL-match, now try to find // a non-NULL one at the same position: match_results m2(m); matcher.setf(match_not_null | match_continuous); if(matcher.find()) { ++count; if(0 == foo(m)) return count; } else { // reset match back to where it was: m = m2; } matcher.unsetf((match_not_null | match_continuous) & ~flags); } } return count; } // // regex_grep convenience interfaces: // template inline unsigned int regex_grep(Predicate foo, const charT* str, const basic_regex& e, match_flag_type flags = match_default) { return regex_grep(foo, str, str + traits::length(str), e, flags); } template inline unsigned int regex_grep(Predicate foo, const std::basic_string& s, const basic_regex& e, match_flag_type flags = match_default) { return regex_grep(foo, s.begin(), s.end(), e, flags); } } // namespace boost #endif // BOOST_REGEX_V5_REGEX_GREP_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_iterator.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_iterator.hpp * VERSION see * DESCRIPTION: Provides regex_iterator implementation. */ #ifndef BOOST_REGEX_V5_REGEX_ITERATOR_HPP #define BOOST_REGEX_V5_REGEX_ITERATOR_HPP #include namespace boost{ template class regex_iterator_implementation { typedef basic_regex regex_type; match_results what; // current match BidirectionalIterator base; // start of sequence BidirectionalIterator end; // end of sequence const regex_type re; // the expression match_flag_type flags; // flags for matching public: regex_iterator_implementation(const regex_type* p, BidirectionalIterator last, match_flag_type f) : base(), end(last), re(*p), flags(f){} regex_iterator_implementation(const regex_iterator_implementation& other) :what(other.what), base(other.base), end(other.end), re(other.re), flags(other.flags){} bool init(BidirectionalIterator first) { base = first; return regex_search(first, end, what, re, flags); } bool compare(const regex_iterator_implementation& that) { if(this == &that) return true; return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second); } const match_results& get() { return what; } bool next() { //if(what.prefix().first != what[0].second) // flags |= match_prev_avail; BidirectionalIterator next_start = what[0].second; match_flag_type f(flags); if(!what.length() || (f & regex_constants::match_posix)) f |= regex_constants::match_not_initial_null; //if(base != next_start) // f |= regex_constants::match_not_bob; bool result = regex_search(next_start, end, what, re, f, base); if(result) what.set_base(base); return result; } private: regex_iterator_implementation& operator=(const regex_iterator_implementation&); }; template ::value_type, class traits = regex_traits > class regex_iterator { private: typedef regex_iterator_implementation impl; typedef std::shared_ptr pimpl; public: typedef basic_regex regex_type; typedef match_results value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; regex_iterator(){} regex_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, match_flag_type m = match_default) : pdata(new impl(&re, b, m)) { if(!pdata->init(a)) { pdata.reset(); } } regex_iterator(const regex_iterator& that) : pdata(that.pdata) {} regex_iterator& operator=(const regex_iterator& that) { pdata = that.pdata; return *this; } bool operator==(const regex_iterator& that)const { if((pdata.get() == 0) || (that.pdata.get() == 0)) return pdata.get() == that.pdata.get(); return pdata->compare(*(that.pdata.get())); } bool operator!=(const regex_iterator& that)const { return !(*this == that); } const value_type& operator*()const { return pdata->get(); } const value_type* operator->()const { return &(pdata->get()); } regex_iterator& operator++() { cow(); if(0 == pdata->next()) { pdata.reset(); } return *this; } regex_iterator operator++(int) { regex_iterator result(*this); ++(*this); return result; } private: pimpl pdata; void cow() { // copy-on-write if(pdata.get() && (pdata.use_count() > 1)) { pdata.reset(new impl(*(pdata.get()))); } } }; typedef regex_iterator cregex_iterator; typedef regex_iterator sregex_iterator; #ifndef BOOST_NO_WREGEX typedef regex_iterator wcregex_iterator; typedef regex_iterator wsregex_iterator; #endif // make_regex_iterator: template inline regex_iterator make_regex_iterator(const charT* p, const basic_regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_iterator(p, p+traits::length(p), e, m); } template inline regex_iterator::const_iterator, charT, traits> make_regex_iterator(const std::basic_string& p, const basic_regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_iterator::const_iterator, charT, traits>(p.begin(), p.end(), e, m); } } // namespace boost #endif // BOOST_REGEX_V5_REGEX_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_match.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_match.hpp * VERSION see * DESCRIPTION: Regular expression matching algorithms. * Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_MATCH_HPP #define BOOST_REGEX_MATCH_HPP namespace boost{ // // proc regex_match // returns true if the specified regular expression matches // the whole of the input. Fills in what matched in m. // template bool regex_match(BidiIterator first, BidiIterator last, match_results& m, const basic_regex& e, match_flag_type flags = match_default) { BOOST_REGEX_DETAIL_NS::perl_matcher matcher(first, last, m, e, flags, first); return matcher.match(); } template bool regex_match(iterator first, iterator last, const basic_regex& e, match_flag_type flags = match_default) { match_results m; return regex_match(first, last, m, e, flags | regex_constants::match_any); } // // query_match convenience interfaces: // template inline bool regex_match(const charT* str, match_results& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_match(str, str + traits::length(str), m, e, flags); } template inline bool regex_match(const std::basic_string& s, match_results::const_iterator, Allocator>& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_match(s.begin(), s.end(), m, e, flags); } template inline bool regex_match(const charT* str, const basic_regex& e, match_flag_type flags = match_default) { match_results m; return regex_match(str, str + traits::length(str), m, e, flags | regex_constants::match_any); } template inline bool regex_match(const std::basic_string& s, const basic_regex& e, match_flag_type flags = match_default) { typedef typename std::basic_string::const_iterator iterator; match_results m; return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any); } } // namespace boost #endif // BOOST_REGEX_MATCH_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_merge.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_format.hpp * VERSION see * DESCRIPTION: Provides formatting output routines for search and replace * operations. Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_V5_REGEX_MERGE_HPP #define BOOST_REGEX_V5_REGEX_MERGE_HPP namespace boost{ template inline OutputIterator regex_merge(OutputIterator out, Iterator first, Iterator last, const basic_regex& e, const charT* fmt, match_flag_type flags = match_default) { return regex_replace(out, first, last, e, fmt, flags); } template inline OutputIterator regex_merge(OutputIterator out, Iterator first, Iterator last, const basic_regex& e, const std::basic_string& fmt, match_flag_type flags = match_default) { return regex_merge(out, first, last, e, fmt.c_str(), flags); } template inline std::basic_string regex_merge(const std::basic_string& s, const basic_regex& e, const charT* fmt, match_flag_type flags = match_default) { return regex_replace(s, e, fmt, flags); } template inline std::basic_string regex_merge(const std::basic_string& s, const basic_regex& e, const std::basic_string& fmt, match_flag_type flags = match_default) { return regex_replace(s, e, fmt, flags); } } // namespace boost #endif // BOOST_REGEX_V5_REGEX_MERGE_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_raw_buffer.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_raw_buffer.hpp * VERSION see * DESCRIPTION: Raw character buffer for regex code. * Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_RAW_BUFFER_HPP #define BOOST_REGEX_RAW_BUFFER_HPP #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #include #include namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ struct empty_padding{}; union padding { void* p; unsigned int i; }; template struct padding3 { enum{ padding_size = 8, padding_mask = 7 }; }; template<> struct padding3<2> { enum{ padding_size = 2, padding_mask = 1 }; }; template<> struct padding3<4> { enum{ padding_size = 4, padding_mask = 3 }; }; template<> struct padding3<8> { enum{ padding_size = 8, padding_mask = 7 }; }; template<> struct padding3<16> { enum{ padding_size = 16, padding_mask = 15 }; }; enum{ padding_size = padding3::padding_size, padding_mask = padding3::padding_mask }; // // class raw_storage // basically this is a simplified vector // this is used by basic_regex for expression storage // class raw_storage { public: typedef std::size_t size_type; typedef unsigned char* pointer; private: pointer last, start, end; public: raw_storage(); raw_storage(size_type n); ~raw_storage() { ::operator delete(start); } void resize(size_type n) { size_type newsize = start ? last - start : 1024; while (newsize < n) newsize *= 2; size_type datasize = end - start; // extend newsize to WORD/DWORD boundary: newsize = (newsize + padding_mask) & ~(padding_mask); // allocate and copy data: pointer ptr = static_cast(::operator new(newsize)); BOOST_REGEX_NOEH_ASSERT(ptr) if (start) std::memcpy(ptr, start, datasize); // get rid of old buffer: ::operator delete(start); // and set up pointers: start = ptr; end = ptr + datasize; last = ptr + newsize; } void* extend(size_type n) { if(size_type(last - end) < n) resize(n + (end - start)); pointer result = end; end += n; return result; } void* insert(size_type pos, size_type n) { BOOST_REGEX_ASSERT(pos <= size_type(end - start)); if (size_type(last - end) < n) resize(n + (end - start)); void* result = start + pos; std::memmove(start + pos + n, start + pos, (end - start) - pos); end += n; return result; } size_type size() { return size_type(end - start); } size_type capacity() { return size_type(last - start); } void* data()const { return start; } size_type index(void* ptr) { return size_type(static_cast(ptr) - static_cast(data())); } void clear() { end = start; } void align() { // move end up to a boundary: end = start + (((end - start) + padding_mask) & ~padding_mask); } void swap(raw_storage& that) { std::swap(start, that.start); std::swap(end, that.end); std::swap(last, that.last); } }; inline raw_storage::raw_storage() { last = start = end = 0; } inline raw_storage::raw_storage(size_type n) { start = end = static_cast(::operator new(n)); BOOST_REGEX_NOEH_ASSERT(start) last = start + n; } } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_replace.hpp ================================================ /* * * Copyright (c) 1998-2009 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_format.hpp * VERSION see * DESCRIPTION: Provides formatting output routines for search and replace * operations. Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_V5_REGEX_REPLACE_HPP #define BOOST_REGEX_V5_REGEX_REPLACE_HPP namespace boost{ template OutputIterator regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex& e, Formatter fmt, match_flag_type flags = match_default) { regex_iterator i(first, last, e, flags); regex_iterator j; if(i == j) { if(!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(first, last, out); } else { BidirectionalIterator last_m(first); while(i != j) { if(!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(i->prefix().first, i->prefix().second, out); out = i->format(out, fmt, flags, e); last_m = (*i)[0].second; if(flags & regex_constants::format_first_only) break; ++i; } if(!(flags & regex_constants::format_no_copy)) out = BOOST_REGEX_DETAIL_NS::copy(last_m, last, out); } return out; } template std::basic_string regex_replace(const std::basic_string& s, const basic_regex& e, Formatter fmt, match_flag_type flags = match_default) { std::basic_string result; BOOST_REGEX_DETAIL_NS::string_out_iterator > i(result); regex_replace(i, s.begin(), s.end(), e, fmt, flags); return result; } } // namespace boost #endif // BOOST_REGEX_V5_REGEX_REPLACE_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_search.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_search.hpp * VERSION see * DESCRIPTION: Provides regex_search implementation. */ #ifndef BOOST_REGEX_V5_REGEX_SEARCH_HPP #define BOOST_REGEX_V5_REGEX_SEARCH_HPP namespace boost{ template bool regex_search(BidiIterator first, BidiIterator last, match_results& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(first, last, m, e, flags, first); } template bool regex_search(BidiIterator first, BidiIterator last, match_results& m, const basic_regex& e, match_flag_type flags, BidiIterator base) { if(e.flags() & regex_constants::failbit) return false; BOOST_REGEX_DETAIL_NS::perl_matcher matcher(first, last, m, e, flags, base); return matcher.find(); } // // regex_search convenience interfaces: // template inline bool regex_search(const charT* str, match_results& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(str, str + traits::length(str), m, e, flags); } template inline bool regex_search(const std::basic_string& s, match_results::const_iterator, Allocator>& m, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(s.begin(), s.end(), m, e, flags); } template bool regex_search(BidiIterator first, BidiIterator last, const basic_regex& e, match_flag_type flags = match_default) { if(e.flags() & regex_constants::failbit) return false; match_results m; typedef typename match_results::allocator_type match_alloc_type; BOOST_REGEX_DETAIL_NS::perl_matcher matcher(first, last, m, e, flags | regex_constants::match_any, first); return matcher.find(); } template inline bool regex_search(const charT* str, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(str, str + traits::length(str), e, flags); } template inline bool regex_search(const std::basic_string& s, const basic_regex& e, match_flag_type flags = match_default) { return regex_search(s.begin(), s.end(), e, flags); } } // namespace boost #endif // BOOST_REGEX_V5_REGEX_SEARCH_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_split.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_split.hpp * VERSION see * DESCRIPTION: Implements regex_split and associated functions. * Note this is an internal header file included * by regex.hpp, do not include on its own. */ #ifndef BOOST_REGEX_SPLIT_HPP #define BOOST_REGEX_SPLIT_HPP namespace boost{ #ifdef BOOST_REGEX_MSVC # pragma warning(push) #if BOOST_REGEX_MSVC < 1910 #pragma warning(disable:4800) #endif #endif namespace BOOST_REGEX_DETAIL_NS{ template const basic_regex& get_default_expression(charT) { static const charT expression_text[4] = { '\\', 's', '+', '\00', }; static const basic_regex e(expression_text); return e; } template class split_pred { typedef std::basic_string string_type; typedef typename string_type::const_iterator iterator_type; iterator_type* p_last; OutputIterator* p_out; std::size_t* p_max; std::size_t initial_max; public: split_pred(iterator_type* a, OutputIterator* b, std::size_t* c) : p_last(a), p_out(b), p_max(c), initial_max(*c) {} bool operator()(const match_results& what); }; template bool split_pred::operator() (const match_results& what) { *p_last = what[0].second; if(what.size() > 1) { // output sub-expressions only: for(unsigned i = 1; i < what.size(); ++i) { *(*p_out) = what.str(i); ++(*p_out); if(0 == --*p_max) return false; } return *p_max != 0; } else { // output $` only if it's not-null or not at the start of the input: const sub_match& sub = what[-1]; if((sub.first != sub.second) || (*p_max != initial_max)) { *(*p_out) = sub.str(); ++(*p_out); return --*p_max; } } // // initial null, do nothing: return true; } } // namespace BOOST_REGEX_DETAIL_NS template std::size_t regex_split(OutputIterator out, std::basic_string& s, const basic_regex& e, match_flag_type flags, std::size_t max_split) { typedef typename std::basic_string::const_iterator ci_t; //typedef typename match_results::allocator_type match_allocator; ci_t last = s.begin(); std::size_t init_size = max_split; BOOST_REGEX_DETAIL_NS::split_pred pred(&last, &out, &max_split); ci_t i, j; i = s.begin(); j = s.end(); regex_grep(pred, i, j, e, flags); // // if there is still input left, do a final push as long as max_split // is not exhausted, and we're not splitting sub-expressions rather // than whitespace: if(max_split && (last != s.end()) && (e.mark_count() == 0)) { *out = std::basic_string((ci_t)last, (ci_t)s.end()); ++out; last = s.end(); --max_split; } // // delete from the string everything that has been processed so far: s.erase(0, last - s.begin()); // // return the number of new records pushed: return init_size - max_split; } template inline std::size_t regex_split(OutputIterator out, std::basic_string& s, const basic_regex& e, match_flag_type flags = match_default) { return regex_split(out, s, e, flags, UINT_MAX); } template inline std::size_t regex_split(OutputIterator out, std::basic_string& s) { return regex_split(out, s, BOOST_REGEX_DETAIL_NS::get_default_expression(charT(0)), match_default, UINT_MAX); } #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_token_iterator.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_token_iterator.hpp * VERSION see * DESCRIPTION: Provides regex_token_iterator implementation. */ #ifndef BOOST_REGEX_V5_REGEX_TOKEN_ITERATOR_HPP #define BOOST_REGEX_V5_REGEX_TOKEN_ITERATOR_HPP #include namespace boost{ template class regex_token_iterator_implementation { typedef basic_regex regex_type; typedef sub_match value_type; match_results what; // current match BidirectionalIterator base; // start of search area BidirectionalIterator end; // end of search area const regex_type re; // the expression match_flag_type flags; // match flags value_type result; // the current string result int N; // the current sub-expression being enumerated std::vector subs; // the sub-expressions to enumerate public: regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, int sub, match_flag_type f) : end(last), re(*p), flags(f), N(0){ subs.push_back(sub); } regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const std::vector& v, match_flag_type f) : end(last), re(*p), flags(f), N(0), subs(v){} template regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const int (&submatches)[CN], match_flag_type f) : end(last), re(*p), flags(f), N(0) { for(std::size_t i = 0; i < CN; ++i) { subs.push_back(submatches[i]); } } regex_token_iterator_implementation(const regex_token_iterator_implementation& other) = default; bool init(BidirectionalIterator first) { N = 0; base = first; if(regex_search(first, end, what, re, flags, base) == true) { N = 0; result = ((subs[N] == -1) ? what.prefix() : what[(int)subs[N]]); return true; } else if((subs[N] == -1) && (first != end)) { result.first = first; result.second = end; result.matched = (first != end); N = -1; return true; } return false; } bool compare(const regex_token_iterator_implementation& that) { if(this == &that) return true; return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (N == that.N) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second); } const value_type& get() { return result; } bool next() { if(N == -1) return false; if(N+1 < (int)subs.size()) { ++N; result =((subs[N] == -1) ? what.prefix() : what[subs[N]]); return true; } //if(what.prefix().first != what[0].second) // flags |= /*match_prev_avail |*/ regex_constants::match_not_bob; BidirectionalIterator last_end(what[0].second); if(regex_search(last_end, end, what, re, ((what[0].first == what[0].second) ? flags | regex_constants::match_not_initial_null : flags), base)) { N =0; result =((subs[N] == -1) ? what.prefix() : what[subs[N]]); return true; } else if((last_end != end) && (subs[0] == -1)) { N =-1; result.first = last_end; result.second = end; result.matched = (last_end != end); return true; } return false; } private: regex_token_iterator_implementation& operator=(const regex_token_iterator_implementation&); }; template ::value_type, class traits = regex_traits > class regex_token_iterator { private: typedef regex_token_iterator_implementation impl; typedef std::shared_ptr pimpl; public: typedef basic_regex regex_type; typedef sub_match value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; regex_token_iterator(){} regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, int submatch = 0, match_flag_type m = match_default) : pdata(new impl(&re, b, submatch, m)) { if(!pdata->init(a)) pdata.reset(); } regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const std::vector& submatches, match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } template regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const int (&submatches)[N], match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } regex_token_iterator(const regex_token_iterator& that) : pdata(that.pdata) {} regex_token_iterator& operator=(const regex_token_iterator& that) { pdata = that.pdata; return *this; } bool operator==(const regex_token_iterator& that)const { if((pdata.get() == 0) || (that.pdata.get() == 0)) return pdata.get() == that.pdata.get(); return pdata->compare(*(that.pdata.get())); } bool operator!=(const regex_token_iterator& that)const { return !(*this == that); } const value_type& operator*()const { return pdata->get(); } const value_type* operator->()const { return &(pdata->get()); } regex_token_iterator& operator++() { cow(); if(0 == pdata->next()) { pdata.reset(); } return *this; } regex_token_iterator operator++(int) { regex_token_iterator result(*this); ++(*this); return result; } private: pimpl pdata; void cow() { // copy-on-write if(pdata.get() && (pdata.use_count() > 1)) { pdata.reset(new impl(*(pdata.get()))); } } }; typedef regex_token_iterator cregex_token_iterator; typedef regex_token_iterator sregex_token_iterator; #ifndef BOOST_NO_WREGEX typedef regex_token_iterator wcregex_token_iterator; typedef regex_token_iterator wsregex_token_iterator; #endif template inline regex_token_iterator make_regex_token_iterator(const charT* p, const basic_regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator(p, p+traits::length(p), e, submatch, m); } template inline regex_token_iterator::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string& p, const basic_regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m); } template inline regex_token_iterator make_regex_token_iterator(const charT* p, const basic_regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator(p, p+traits::length(p), e, submatch, m); } template inline regex_token_iterator::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string& p, const basic_regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m); } template inline regex_token_iterator make_regex_token_iterator(const charT* p, const basic_regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator(p, p+traits::length(p), e, submatch, m); } template inline regex_token_iterator::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string& p, const basic_regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return regex_token_iterator::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m); } } // namespace boost #endif // BOOST_REGEX_V5_REGEX_TOKEN_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_traits.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits classes. */ #ifndef BOOST_REGEX_TRAITS_HPP_INCLUDED #define BOOST_REGEX_TRAITS_HPP_INCLUDED #include #include #include #include #include #include #include #if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32) # include #endif #include namespace boost{ template struct regex_traits : public implementationT { regex_traits() : implementationT() {} }; // // class regex_traits_wrapper. // this is what our implementation will actually store; // it provides default implementations of the "optional" // interfaces that we support, in addition to the // required "standard" ones: // namespace BOOST_REGEX_DETAIL_NS{ template struct has_boost_extensions_tag { template static double checker(U*, typename U::boost_extensions_tag* = nullptr); static char checker(...); static T* get(); static const bool value = sizeof(checker(get())) > 1; }; template struct default_wrapper : public BaseT { typedef typename BaseT::char_type char_type; std::string error_string(::boost::regex_constants::error_type e)const { return ::boost::BOOST_REGEX_DETAIL_NS::get_default_error_string(e); } ::boost::regex_constants::syntax_type syntax_type(char_type c)const { return (char_type(c & 0x7f) == c) ? get_default_syntax_type(static_cast(c)) : ::boost::regex_constants::syntax_char; } ::boost::regex_constants::escape_syntax_type escape_syntax_type(char_type c)const { return (char_type(c & 0x7f) == c) ? get_default_escape_syntax_type(static_cast(c)) : ::boost::regex_constants::escape_type_identity; } std::intmax_t toi(const char_type*& p1, const char_type* p2, int radix)const { return ::boost::BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this); } char_type translate(char_type c, bool icase)const { return (icase ? this->translate_nocase(c) : this->translate(c)); } char_type translate(char_type c)const { return BaseT::translate(c); } char_type tolower(char_type c)const { return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c); } char_type toupper(char_type c)const { return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c); } }; template struct compute_wrapper_base { typedef BaseT type; }; template struct compute_wrapper_base { typedef default_wrapper type; }; } // namespace BOOST_REGEX_DETAIL_NS template struct regex_traits_wrapper : public ::boost::BOOST_REGEX_DETAIL_NS::compute_wrapper_base< BaseT, ::boost::BOOST_REGEX_DETAIL_NS::has_boost_extensions_tag::value >::type { regex_traits_wrapper(){} private: regex_traits_wrapper(const regex_traits_wrapper&); regex_traits_wrapper& operator=(const regex_traits_wrapper&); }; } // namespace boost #endif // include ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_traits_defaults.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_traits_defaults.hpp * VERSION see * DESCRIPTION: Declares API's for access to regex_traits default properties. */ #ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED #define BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED #include #include #include #include #include #include #include #include #include #include namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ // // helpers to suppress warnings: // template inline bool is_extended(charT c) { typedef typename std::make_unsigned::type unsigned_type; return (sizeof(charT) > 1) && (static_cast(c) >= 256u); } inline bool is_extended(char) { return false; } inline const char* get_default_syntax(regex_constants::syntax_type n) { // if the user hasn't supplied a message catalog, then this supplies // default "messages" for us to load in the range 1-100. const char* messages[] = { "", "(", ")", "$", "^", ".", "*", "+", "?", "[", "]", "|", "\\", "#", "-", "{", "}", "0123456789", "b", "B", "<", ">", "", "", "A`", "z'", "\n", ",", "a", "f", "n", "r", "t", "v", "x", "c", ":", "=", "e", "", "", "", "", "", "", "", "", "E", "Q", "X", "C", "Z", "G", "!", "p", "P", "N", "gk", "K", "R", }; return ((n >= (sizeof(messages) / sizeof(messages[1]))) ? "" : messages[n]); } inline const char* get_default_error_string(regex_constants::error_type n) { static const char* const s_default_error_messages[] = { "Success", /* REG_NOERROR 0 error_ok */ "No match", /* REG_NOMATCH 1 error_no_match */ "Invalid regular expression.", /* REG_BADPAT 2 error_bad_pattern */ "Invalid collation character.", /* REG_ECOLLATE 3 error_collate */ "Invalid character class name, collating name, or character range.", /* REG_ECTYPE 4 error_ctype */ "Invalid or unterminated escape sequence.", /* REG_EESCAPE 5 error_escape */ "Invalid back reference: specified capturing group does not exist.", /* REG_ESUBREG 6 error_backref */ "Unmatched [ or [^ in character class declaration.", /* REG_EBRACK 7 error_brack */ "Unmatched marking parenthesis ( or \\(.", /* REG_EPAREN 8 error_paren */ "Unmatched quantified repeat operator { or \\{.", /* REG_EBRACE 9 error_brace */ "Invalid content of repeat range.", /* REG_BADBR 10 error_badbrace */ "Invalid range end in character class", /* REG_ERANGE 11 error_range */ "Out of memory.", /* REG_ESPACE 12 error_space NOT USED */ "Invalid preceding regular expression prior to repetition operator.", /* REG_BADRPT 13 error_badrepeat */ "Premature end of regular expression", /* REG_EEND 14 error_end NOT USED */ "Regular expression is too large.", /* REG_ESIZE 15 error_size NOT USED */ "Unmatched ) or \\)", /* REG_ERPAREN 16 error_right_paren NOT USED */ "Empty regular expression.", /* REG_EMPTY 17 error_empty */ "The complexity of matching the regular expression exceeded predefined bounds. " "Try refactoring the regular expression to make each choice made by the state machine unambiguous. " "This exception is thrown to prevent \"eternal\" matches that take an " "indefinite period time to locate.", /* REG_ECOMPLEXITY 18 error_complexity */ "Ran out of stack space trying to match the regular expression.", /* REG_ESTACK 19 error_stack */ "Invalid or unterminated Perl (?...) sequence.", /* REG_E_PERL 20 error_perl */ "Unknown error.", /* REG_E_UNKNOWN 21 error_unknown */ }; return (n > ::boost::regex_constants::error_unknown) ? s_default_error_messages[::boost::regex_constants::error_unknown] : s_default_error_messages[n]; } inline regex_constants::syntax_type get_default_syntax_type(char c) { // // char_syntax determines how the compiler treats a given character // in a regular expression. // static regex_constants::syntax_type char_syntax[] = { regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_newline, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /* */ // 32 regex_constants::syntax_not, /*!*/ regex_constants::syntax_char, /*"*/ regex_constants::syntax_hash, /*#*/ regex_constants::syntax_dollar, /*$*/ regex_constants::syntax_char, /*%*/ regex_constants::syntax_char, /*&*/ regex_constants::escape_type_end_buffer, /*'*/ regex_constants::syntax_open_mark, /*(*/ regex_constants::syntax_close_mark, /*)*/ regex_constants::syntax_star, /***/ regex_constants::syntax_plus, /*+*/ regex_constants::syntax_comma, /*,*/ regex_constants::syntax_dash, /*-*/ regex_constants::syntax_dot, /*.*/ regex_constants::syntax_char, /*/*/ regex_constants::syntax_digit, /*0*/ regex_constants::syntax_digit, /*1*/ regex_constants::syntax_digit, /*2*/ regex_constants::syntax_digit, /*3*/ regex_constants::syntax_digit, /*4*/ regex_constants::syntax_digit, /*5*/ regex_constants::syntax_digit, /*6*/ regex_constants::syntax_digit, /*7*/ regex_constants::syntax_digit, /*8*/ regex_constants::syntax_digit, /*9*/ regex_constants::syntax_colon, /*:*/ regex_constants::syntax_char, /*;*/ regex_constants::escape_type_left_word, /*<*/ regex_constants::syntax_equal, /*=*/ regex_constants::escape_type_right_word, /*>*/ regex_constants::syntax_question, /*?*/ regex_constants::syntax_char, /*@*/ regex_constants::syntax_char, /*A*/ regex_constants::syntax_char, /*B*/ regex_constants::syntax_char, /*C*/ regex_constants::syntax_char, /*D*/ regex_constants::syntax_char, /*E*/ regex_constants::syntax_char, /*F*/ regex_constants::syntax_char, /*G*/ regex_constants::syntax_char, /*H*/ regex_constants::syntax_char, /*I*/ regex_constants::syntax_char, /*J*/ regex_constants::syntax_char, /*K*/ regex_constants::syntax_char, /*L*/ regex_constants::syntax_char, /*M*/ regex_constants::syntax_char, /*N*/ regex_constants::syntax_char, /*O*/ regex_constants::syntax_char, /*P*/ regex_constants::syntax_char, /*Q*/ regex_constants::syntax_char, /*R*/ regex_constants::syntax_char, /*S*/ regex_constants::syntax_char, /*T*/ regex_constants::syntax_char, /*U*/ regex_constants::syntax_char, /*V*/ regex_constants::syntax_char, /*W*/ regex_constants::syntax_char, /*X*/ regex_constants::syntax_char, /*Y*/ regex_constants::syntax_char, /*Z*/ regex_constants::syntax_open_set, /*[*/ regex_constants::syntax_escape, /*\*/ regex_constants::syntax_close_set, /*]*/ regex_constants::syntax_caret, /*^*/ regex_constants::syntax_char, /*_*/ regex_constants::syntax_char, /*`*/ regex_constants::syntax_char, /*a*/ regex_constants::syntax_char, /*b*/ regex_constants::syntax_char, /*c*/ regex_constants::syntax_char, /*d*/ regex_constants::syntax_char, /*e*/ regex_constants::syntax_char, /*f*/ regex_constants::syntax_char, /*g*/ regex_constants::syntax_char, /*h*/ regex_constants::syntax_char, /*i*/ regex_constants::syntax_char, /*j*/ regex_constants::syntax_char, /*k*/ regex_constants::syntax_char, /*l*/ regex_constants::syntax_char, /*m*/ regex_constants::syntax_char, /*n*/ regex_constants::syntax_char, /*o*/ regex_constants::syntax_char, /*p*/ regex_constants::syntax_char, /*q*/ regex_constants::syntax_char, /*r*/ regex_constants::syntax_char, /*s*/ regex_constants::syntax_char, /*t*/ regex_constants::syntax_char, /*u*/ regex_constants::syntax_char, /*v*/ regex_constants::syntax_char, /*w*/ regex_constants::syntax_char, /*x*/ regex_constants::syntax_char, /*y*/ regex_constants::syntax_char, /*z*/ regex_constants::syntax_open_brace, /*{*/ regex_constants::syntax_or, /*|*/ regex_constants::syntax_close_brace, /*}*/ regex_constants::syntax_char, /*~*/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ regex_constants::syntax_char, /**/ }; return char_syntax[(unsigned char)c]; } inline regex_constants::escape_syntax_type get_default_escape_syntax_type(char c) { // // char_syntax determines how the compiler treats a given character // in a regular expression. // static regex_constants::escape_syntax_type char_syntax[] = { regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /* */ // 32 regex_constants::escape_type_identity, /*!*/ regex_constants::escape_type_identity, /*"*/ regex_constants::escape_type_identity, /*#*/ regex_constants::escape_type_identity, /*$*/ regex_constants::escape_type_identity, /*%*/ regex_constants::escape_type_identity, /*&*/ regex_constants::escape_type_end_buffer, /*'*/ regex_constants::syntax_open_mark, /*(*/ regex_constants::syntax_close_mark, /*)*/ regex_constants::escape_type_identity, /***/ regex_constants::syntax_plus, /*+*/ regex_constants::escape_type_identity, /*,*/ regex_constants::escape_type_identity, /*-*/ regex_constants::escape_type_identity, /*.*/ regex_constants::escape_type_identity, /*/*/ regex_constants::escape_type_decimal, /*0*/ regex_constants::escape_type_backref, /*1*/ regex_constants::escape_type_backref, /*2*/ regex_constants::escape_type_backref, /*3*/ regex_constants::escape_type_backref, /*4*/ regex_constants::escape_type_backref, /*5*/ regex_constants::escape_type_backref, /*6*/ regex_constants::escape_type_backref, /*7*/ regex_constants::escape_type_backref, /*8*/ regex_constants::escape_type_backref, /*9*/ regex_constants::escape_type_identity, /*:*/ regex_constants::escape_type_identity, /*;*/ regex_constants::escape_type_left_word, /*<*/ regex_constants::escape_type_identity, /*=*/ regex_constants::escape_type_right_word, /*>*/ regex_constants::syntax_question, /*?*/ regex_constants::escape_type_identity, /*@*/ regex_constants::escape_type_start_buffer, /*A*/ regex_constants::escape_type_not_word_assert, /*B*/ regex_constants::escape_type_C, /*C*/ regex_constants::escape_type_not_class, /*D*/ regex_constants::escape_type_E, /*E*/ regex_constants::escape_type_not_class, /*F*/ regex_constants::escape_type_G, /*G*/ regex_constants::escape_type_not_class, /*H*/ regex_constants::escape_type_not_class, /*I*/ regex_constants::escape_type_not_class, /*J*/ regex_constants::escape_type_reset_start_mark, /*K*/ regex_constants::escape_type_not_class, /*L*/ regex_constants::escape_type_not_class, /*M*/ regex_constants::escape_type_named_char, /*N*/ regex_constants::escape_type_not_class, /*O*/ regex_constants::escape_type_not_property, /*P*/ regex_constants::escape_type_Q, /*Q*/ regex_constants::escape_type_line_ending, /*R*/ regex_constants::escape_type_not_class, /*S*/ regex_constants::escape_type_not_class, /*T*/ regex_constants::escape_type_not_class, /*U*/ regex_constants::escape_type_not_class, /*V*/ regex_constants::escape_type_not_class, /*W*/ regex_constants::escape_type_X, /*X*/ regex_constants::escape_type_not_class, /*Y*/ regex_constants::escape_type_Z, /*Z*/ regex_constants::escape_type_identity, /*[*/ regex_constants::escape_type_identity, /*\*/ regex_constants::escape_type_identity, /*]*/ regex_constants::escape_type_identity, /*^*/ regex_constants::escape_type_identity, /*_*/ regex_constants::escape_type_start_buffer, /*`*/ regex_constants::escape_type_control_a, /*a*/ regex_constants::escape_type_word_assert, /*b*/ regex_constants::escape_type_ascii_control, /*c*/ regex_constants::escape_type_class, /*d*/ regex_constants::escape_type_e, /*e*/ regex_constants::escape_type_control_f, /*f*/ regex_constants::escape_type_extended_backref, /*g*/ regex_constants::escape_type_class, /*h*/ regex_constants::escape_type_class, /*i*/ regex_constants::escape_type_class, /*j*/ regex_constants::escape_type_extended_backref, /*k*/ regex_constants::escape_type_class, /*l*/ regex_constants::escape_type_class, /*m*/ regex_constants::escape_type_control_n, /*n*/ regex_constants::escape_type_class, /*o*/ regex_constants::escape_type_property, /*p*/ regex_constants::escape_type_class, /*q*/ regex_constants::escape_type_control_r, /*r*/ regex_constants::escape_type_class, /*s*/ regex_constants::escape_type_control_t, /*t*/ regex_constants::escape_type_class, /*u*/ regex_constants::escape_type_control_v, /*v*/ regex_constants::escape_type_class, /*w*/ regex_constants::escape_type_hex, /*x*/ regex_constants::escape_type_class, /*y*/ regex_constants::escape_type_end_buffer, /*z*/ regex_constants::syntax_open_brace, /*{*/ regex_constants::syntax_or, /*|*/ regex_constants::syntax_close_brace, /*}*/ regex_constants::escape_type_identity, /*~*/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ regex_constants::escape_type_identity, /**/ }; return char_syntax[(unsigned char)c]; } // is charT c a combining character? inline bool is_combining_implementation(std::uint_least16_t c) { const std::uint_least16_t combining_ranges[] = { 0x0300, 0x0361, 0x0483, 0x0486, 0x0903, 0x0903, 0x093E, 0x0940, 0x0949, 0x094C, 0x0982, 0x0983, 0x09BE, 0x09C0, 0x09C7, 0x09CC, 0x09D7, 0x09D7, 0x0A3E, 0x0A40, 0x0A83, 0x0A83, 0x0ABE, 0x0AC0, 0x0AC9, 0x0ACC, 0x0B02, 0x0B03, 0x0B3E, 0x0B3E, 0x0B40, 0x0B40, 0x0B47, 0x0B4C, 0x0B57, 0x0B57, 0x0B83, 0x0B83, 0x0BBE, 0x0BBF, 0x0BC1, 0x0BCC, 0x0BD7, 0x0BD7, 0x0C01, 0x0C03, 0x0C41, 0x0C44, 0x0C82, 0x0C83, 0x0CBE, 0x0CBE, 0x0CC0, 0x0CC4, 0x0CC7, 0x0CCB, 0x0CD5, 0x0CD6, 0x0D02, 0x0D03, 0x0D3E, 0x0D40, 0x0D46, 0x0D4C, 0x0D57, 0x0D57, 0x0F7F, 0x0F7F, 0x20D0, 0x20E1, 0x3099, 0x309A, 0xFE20, 0xFE23, 0xffff, 0xffff, }; const std::uint_least16_t* p = combining_ranges + 1; while (*p < c) p += 2; --p; if ((c >= *p) && (c <= *(p + 1))) return true; return false; } template inline bool is_combining(charT c) { return (c <= static_cast(0)) ? false : ((c >= static_cast((std::numeric_limits::max)())) ? false : is_combining_implementation(static_cast(c))); } template <> inline bool is_combining(char) { return false; } template <> inline bool is_combining(signed char) { return false; } template <> inline bool is_combining(unsigned char) { return false; } #ifdef _MSC_VER template<> inline bool is_combining(wchar_t c) { return is_combining_implementation(static_cast(c)); } #elif !defined(__DECCXX) && !defined(__osf__) && !defined(__OSF__) && defined(WCHAR_MIN) && (WCHAR_MIN == 0) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) #if defined(WCHAR_MAX) && (WCHAR_MAX <= USHRT_MAX) template<> inline bool is_combining(wchar_t c) { return is_combining_implementation(static_cast(c)); } #else template<> inline bool is_combining(wchar_t c) { return (c >= (std::numeric_limits::max)()) ? false : is_combining_implementation(static_cast(c)); } #endif #endif // // is a charT c a line separator? // template inline bool is_separator(charT c) { return BOOST_REGEX_MAKE_BOOL( (c == static_cast('\n')) || (c == static_cast('\r')) || (c == static_cast('\f')) || (static_cast(c) == 0x2028u) || (static_cast(c) == 0x2029u) || (static_cast(c) == 0x85u)); } template <> inline bool is_separator(char c) { return BOOST_REGEX_MAKE_BOOL((c == '\n') || (c == '\r') || (c == '\f')); } // // get a default collating element: // inline std::string lookup_default_collate_name(const std::string& name) { // // these are the POSIX collating names: // static const char* def_coll_names[] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "alert", "backspace", "tab", "newline", "vertical-tab", "form-feed", "carriage-return", "SO", "SI", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "IS4", "IS3", "IS2", "IS1", "space", "exclamation-mark", "quotation-mark", "number-sign", "dollar-sign", "percent-sign", "ampersand", "apostrophe", "left-parenthesis", "right-parenthesis", "asterisk", "plus-sign", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less-than-sign", "equals-sign", "greater-than-sign", "question-mark", "commercial-at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "left-square-bracket", "backslash", "right-square-bracket", "circumflex", "underscore", "grave-accent", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "left-curly-bracket", "vertical-line", "right-curly-bracket", "tilde", "DEL", "", }; // these multi-character collating elements // should keep most Western-European locales // happy - we should really localise these a // little more - but this will have to do for // now: static const char* def_multi_coll[] = { "ae", "Ae", "AE", "ch", "Ch", "CH", "ll", "Ll", "LL", "ss", "Ss", "SS", "nj", "Nj", "NJ", "dz", "Dz", "DZ", "lj", "Lj", "LJ", "", }; unsigned int i = 0; while (*def_coll_names[i]) { if (def_coll_names[i] == name) { return std::string(1, char(i)); } ++i; } i = 0; while (*def_multi_coll[i]) { if (def_multi_coll[i] == name) { return def_multi_coll[i]; } ++i; } return std::string(); } // // get the state_id of a character classification, the individual // traits classes then transform that state_id into a bitmask: // template struct character_pointer_range { const charT* p1; const charT* p2; bool operator < (const character_pointer_range& r)const { return std::lexicographical_compare(p1, p2, r.p1, r.p2); } bool operator == (const character_pointer_range& r)const { // Not only do we check that the ranges are of equal size before // calling std::equal, but there is no other algorithm available: // not even a non-standard MS one. So forward to unchecked_equal // in the MS case. #ifdef __cpp_lib_robust_nonmodifying_seq_ops return std::equal(p1, p2, r.p1, r.p2); #elif defined(BOOST_REGEX_MSVC) if (((p2 - p1) != (r.p2 - r.p1))) return false; const charT* with = r.p1; const charT* pos = p1; while (pos != p2) if (*pos++ != *with++) return false; return true; #else return ((p2 - p1) == (r.p2 - r.p1)) && std::equal(p1, p2, r.p1); #endif } }; template int get_default_class_id(const charT* p1, const charT* p2) { static const charT data[73] = { 'a', 'l', 'n', 'u', 'm', 'a', 'l', 'p', 'h', 'a', 'b', 'l', 'a', 'n', 'k', 'c', 'n', 't', 'r', 'l', 'd', 'i', 'g', 'i', 't', 'g', 'r', 'a', 'p', 'h', 'l', 'o', 'w', 'e', 'r', 'p', 'r', 'i', 'n', 't', 'p', 'u', 'n', 'c', 't', 's', 'p', 'a', 'c', 'e', 'u', 'n', 'i', 'c', 'o', 'd', 'e', 'u', 'p', 'p', 'e', 'r', 'v', 'w', 'o', 'r', 'd', 'x', 'd', 'i', 'g', 'i', 't', }; static const character_pointer_range ranges[21] = { {data+0, data+5,}, // alnum {data+5, data+10,}, // alpha {data+10, data+15,}, // blank {data+15, data+20,}, // cntrl {data+20, data+21,}, // d {data+20, data+25,}, // digit {data+25, data+30,}, // graph {data+29, data+30,}, // h {data+30, data+31,}, // l {data+30, data+35,}, // lower {data+35, data+40,}, // print {data+40, data+45,}, // punct {data+45, data+46,}, // s {data+45, data+50,}, // space {data+57, data+58,}, // u {data+50, data+57,}, // unicode {data+57, data+62,}, // upper {data+62, data+63,}, // v {data+63, data+64,}, // w {data+63, data+67,}, // word {data+67, data+73,}, // xdigit }; const character_pointer_range* ranges_begin = ranges; const character_pointer_range* ranges_end = ranges + (sizeof(ranges)/sizeof(ranges[0])); character_pointer_range t = { p1, p2, }; const character_pointer_range* p = std::lower_bound(ranges_begin, ranges_end, t); if((p != ranges_end) && (t == *p)) return static_cast(p - ranges); return -1; } // // helper functions: // template std::ptrdiff_t global_length(const charT* p) { std::ptrdiff_t n = 0; while(*p) { ++p; ++n; } return n; } template<> inline std::ptrdiff_t global_length(const char* p) { return (std::strlen)(p); } #ifndef BOOST_NO_WREGEX template<> inline std::ptrdiff_t global_length(const wchar_t* p) { return (std::ptrdiff_t)(std::wcslen)(p); } #endif template inline charT global_lower(charT c) { return c; } template inline charT global_upper(charT c) { return c; } inline char do_global_lower(char c) { return static_cast((std::tolower)((unsigned char)c)); } inline char do_global_upper(char c) { return static_cast((std::toupper)((unsigned char)c)); } #ifndef BOOST_NO_WREGEX inline wchar_t do_global_lower(wchar_t c) { return (std::towlower)(c); } inline wchar_t do_global_upper(wchar_t c) { return (std::towupper)(c); } #endif // // This sucks: declare template specialisations of global_lower/global_upper // that just forward to the non-template implementation functions. We do // this because there is one compiler (Compaq Tru64 C++) that doesn't seem // to differentiate between templates and non-template overloads.... // what's more, the primary template, plus all overloads have to be // defined in the same translation unit (if one is inline they all must be) // otherwise the "local template instantiation" compiler option can pick // the wrong instantiation when linking: // template<> inline char global_lower(char c) { return do_global_lower(c); } template<> inline char global_upper(char c) { return do_global_upper(c); } #ifndef BOOST_NO_WREGEX template<> inline wchar_t global_lower(wchar_t c) { return do_global_lower(c); } template<> inline wchar_t global_upper(wchar_t c) { return do_global_upper(c); } #endif template int global_value(charT c) { static const charT zero = '0'; static const charT nine = '9'; static const charT a = 'a'; static const charT f = 'f'; static const charT A = 'A'; static const charT F = 'F'; if(c > f) return -1; if(c >= a) return 10 + (c - a); if(c > F) return -1; if(c >= A) return 10 + (c - A); if(c > nine) return -1; if(c >= zero) return c - zero; return -1; } template std::intmax_t global_toi(const charT*& p1, const charT* p2, int radix, const traits& t) { (void)t; // warning suppression std::intmax_t limit = (std::numeric_limits::max)() / radix; std::intmax_t next_value = t.value(*p1, radix); if((p1 == p2) || (next_value < 0) || (next_value >= radix)) return -1; std::intmax_t result = 0; while(p1 != p2) { next_value = t.value(*p1, radix); if((next_value < 0) || (next_value >= radix)) break; result *= radix; result += next_value; ++p1; if (result > limit) return -1; } return result; } template inline typename std::enable_if<(sizeof(charT) > 1), const charT*>::type get_escape_R_string() { #ifdef BOOST_REGEX_MSVC # pragma warning(push) # pragma warning(disable:4309 4245) #endif static const charT e1[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\x0D', '\x0A', '?', '|', '[', '\x0A', '\x0B', '\x0C', static_cast(0x85), static_cast(0x2028), static_cast(0x2029), ']', ')', ')', '\0' }; static const charT e2[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\x0D', '\x0A', '?', '|', '[', '\x0A', '\x0B', '\x0C', static_cast(0x85), ']', ')', ')', '\0' }; charT c = static_cast(0x2029u); bool b = (static_cast(c) == 0x2029u); return (b ? e1 : e2); #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif } template inline typename std::enable_if<(sizeof(charT) == 1), const charT*>::type get_escape_R_string() { #ifdef BOOST_REGEX_MSVC # pragma warning(push) # pragma warning(disable:4309 4245) #endif static const charT e2[] = { static_cast('('), static_cast('?'), static_cast('-'), static_cast('x'), static_cast(':'), static_cast('('), static_cast('?'), static_cast('>'), static_cast('\x0D'), static_cast('\x0A'), static_cast('?'), static_cast('|'), static_cast('['), static_cast('\x0A'), static_cast('\x0B'), static_cast('\x0C'), static_cast('\x85'), static_cast(']'), static_cast(')'), static_cast(')'), static_cast('\0') }; return e2; #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif } } // BOOST_REGEX_DETAIL_NS } // boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/regex_workaround.hpp ================================================ /* * * Copyright (c) 1998-2005 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE regex_workarounds.cpp * VERSION see * DESCRIPTION: Declares Misc workarounds. */ #ifndef BOOST_REGEX_WORKAROUND_HPP #define BOOST_REGEX_WORKAROUND_HPP #include #include #include #include #ifndef BOOST_REGEX_STANDALONE #include #include #endif #ifdef BOOST_REGEX_NO_BOOL # define BOOST_REGEX_MAKE_BOOL(x) static_cast((x) ? true : false) #else # define BOOST_REGEX_MAKE_BOOL(x) static_cast(x) #endif /***************************************************************************** * * helper functions pointer_construct/pointer_destroy: * ****************************************************************************/ #ifdef __cplusplus namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #ifdef BOOST_REGEX_MSVC #pragma warning (push) #pragma warning (disable : 4100) #endif template inline void pointer_destroy(T* p) { p->~T(); (void)p; } #ifdef BOOST_REGEX_MSVC #pragma warning (pop) #endif template inline void pointer_construct(T* p, const T& t) { new (p) T(t); } }} // namespaces #endif /***************************************************************************** * * helper function copy: * ****************************************************************************/ #if defined(BOOST_WORKAROUND) #if BOOST_WORKAROUND(BOOST_REGEX_MSVC, >= 1400) && defined(__STDC_WANT_SECURE_LIB__) && __STDC_WANT_SECURE_LIB__ #define BOOST_REGEX_HAS_STRCPY_S #endif #endif #ifdef __cplusplus namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ #if defined(BOOST_REGEX_MSVC) && (BOOST_REGEX_MSVC < 1910) // // MSVC 10 will either emit warnings or else refuse to compile // code that makes perfectly legitimate use of std::copy, when // the OutputIterator type is a user-defined class (apparently all user // defined iterators are "unsafe"). What's more Microsoft have removed their // non-standard "unchecked" versions, even though they are still in the MS // documentation!! Work around this as best we can: // template inline OutputIterator copy( InputIterator first, InputIterator last, OutputIterator dest ) { while (first != last) *dest++ = *first++; return dest; } #else using std::copy; #endif #if defined(BOOST_REGEX_HAS_STRCPY_S) // use safe versions of strcpy etc: using ::strcpy_s; using ::strcat_s; #else inline std::size_t strcpy_s( char *strDestination, std::size_t sizeInBytes, const char *strSource ) { std::size_t lenSourceWithNull = std::strlen(strSource) + 1; if (lenSourceWithNull > sizeInBytes) return 1; std::memcpy(strDestination, strSource, lenSourceWithNull); return 0; } inline std::size_t strcat_s( char *strDestination, std::size_t sizeInBytes, const char *strSource ) { std::size_t lenSourceWithNull = std::strlen(strSource) + 1; std::size_t lenDestination = std::strlen(strDestination); if (lenSourceWithNull + lenDestination > sizeInBytes) return 1; std::memcpy(strDestination + lenDestination, strSource, lenSourceWithNull); return 0; } #endif inline void overflow_error_if_not_zero(std::size_t i) { if(i) { std::overflow_error e("String buffer too small"); #ifndef BOOST_REGEX_STANDALONE boost::throw_exception(e); #else throw e; #endif } } }} // namespaces #endif // __cplusplus #endif // include guard ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/states.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE states.cpp * VERSION see * DESCRIPTION: Declares internal state machine structures. */ #ifndef BOOST_REGEX_V5_STATES_HPP #define BOOST_REGEX_V5_STATES_HPP namespace boost{ namespace BOOST_REGEX_DETAIL_NS{ /*** mask_type ******************************************************* Whenever we have a choice of two alternatives, we use an array of bytes to indicate which of the two alternatives it is possible to take for any given input character. If mask_take is set, then we can take the next state, and if mask_skip is set then we can take the alternative. ***********************************************************************/ enum mask_type { mask_take = 1, mask_skip = 2, mask_init = 4, mask_any = mask_skip | mask_take, mask_all = mask_any }; /*** helpers ********************************************************** These helpers let us use function overload resolution to detect whether we have narrow or wide character strings: ***********************************************************************/ struct _narrow_type{}; struct _wide_type{}; template struct is_byte; template<> struct is_byte { typedef _narrow_type width_type; }; template<> struct is_byte{ typedef _narrow_type width_type; }; template<> struct is_byte { typedef _narrow_type width_type; }; template struct is_byte { typedef _wide_type width_type; }; /*** enum syntax_element_type ****************************************** Every record in the state machine falls into one of the following types: ***********************************************************************/ enum syntax_element_type { // start of a marked sub-expression, or perl-style (?...) extension syntax_element_startmark = 0, // end of a marked sub-expression, or perl-style (?...) extension syntax_element_endmark = syntax_element_startmark + 1, // any sequence of literal characters syntax_element_literal = syntax_element_endmark + 1, // start of line assertion: ^ syntax_element_start_line = syntax_element_literal + 1, // end of line assertion $ syntax_element_end_line = syntax_element_start_line + 1, // match any character: . syntax_element_wild = syntax_element_end_line + 1, // end of expression: we have a match when we get here syntax_element_match = syntax_element_wild + 1, // perl style word boundary: \b syntax_element_word_boundary = syntax_element_match + 1, // perl style within word boundary: \B syntax_element_within_word = syntax_element_word_boundary + 1, // start of word assertion: \< syntax_element_word_start = syntax_element_within_word + 1, // end of word assertion: \> syntax_element_word_end = syntax_element_word_start + 1, // start of buffer assertion: \` syntax_element_buffer_start = syntax_element_word_end + 1, // end of buffer assertion: \' syntax_element_buffer_end = syntax_element_buffer_start + 1, // backreference to previously matched sub-expression syntax_element_backref = syntax_element_buffer_end + 1, // either a wide character set [..] or one with multicharacter collating elements: syntax_element_long_set = syntax_element_backref + 1, // narrow character set: [...] syntax_element_set = syntax_element_long_set + 1, // jump to a new state in the machine: syntax_element_jump = syntax_element_set + 1, // choose between two production states: syntax_element_alt = syntax_element_jump + 1, // a repeat syntax_element_rep = syntax_element_alt + 1, // match a combining character sequence syntax_element_combining = syntax_element_rep + 1, // perl style soft buffer end: \z syntax_element_soft_buffer_end = syntax_element_combining + 1, // perl style continuation: \G syntax_element_restart_continue = syntax_element_soft_buffer_end + 1, // single character repeats: syntax_element_dot_rep = syntax_element_restart_continue + 1, syntax_element_char_rep = syntax_element_dot_rep + 1, syntax_element_short_set_rep = syntax_element_char_rep + 1, syntax_element_long_set_rep = syntax_element_short_set_rep + 1, // a backstep for lookbehind repeats: syntax_element_backstep = syntax_element_long_set_rep + 1, // an assertion that a mark was matched: syntax_element_assert_backref = syntax_element_backstep + 1, syntax_element_toggle_case = syntax_element_assert_backref + 1, // a recursive expression: syntax_element_recurse = syntax_element_toggle_case + 1, // Verbs: syntax_element_fail = syntax_element_recurse + 1, syntax_element_accept = syntax_element_fail + 1, syntax_element_commit = syntax_element_accept + 1, syntax_element_then = syntax_element_commit + 1 }; #ifdef BOOST_REGEX_DEBUG // dwa 09/26/00 - This is needed to suppress warnings about an ambiguous conversion std::ostream& operator<<(std::ostream&, syntax_element_type); #endif struct re_syntax_base; /*** union offset_type ************************************************ Points to another state in the machine. During machine construction we use integral offsets, but these are converted to pointers before execution of the machine. ***********************************************************************/ union offset_type { re_syntax_base* p; std::ptrdiff_t i; }; /*** struct re_syntax_base ******************************************** Base class for all states in the machine. ***********************************************************************/ struct re_syntax_base { syntax_element_type type; // what kind of state this is offset_type next; // next state in the machine }; /*** struct re_brace ************************************************** A marked parenthesis. ***********************************************************************/ struct re_brace : public re_syntax_base { // The index to match, can be zero (don't mark the sub-expression) // or negative (for perl style (?...) extensions): int index; bool icase; }; /*** struct re_dot ************************************************** Match anything. ***********************************************************************/ enum { dont_care = 1, force_not_newline = 0, force_newline = 2, test_not_newline = 2, test_newline = 3 }; struct re_dot : public re_syntax_base { unsigned char mask; }; /*** struct re_literal ************************************************ A string of literals, following this structure will be an array of characters: charT[length] ***********************************************************************/ struct re_literal : public re_syntax_base { unsigned int length; }; /*** struct re_case ************************************************ Indicates whether we are moving to a case insensive block or not ***********************************************************************/ struct re_case : public re_syntax_base { bool icase; }; /*** struct re_set_long *********************************************** A wide character set of characters, following this structure will be an array of type charT: First csingles null-terminated strings Then 2 * cranges NULL terminated strings Then cequivalents NULL terminated strings ***********************************************************************/ template struct re_set_long : public re_syntax_base { unsigned int csingles, cranges, cequivalents; mask_type cclasses; mask_type cnclasses; bool isnot; bool singleton; }; /*** struct re_set **************************************************** A set of narrow-characters, matches any of _map which is none-zero ***********************************************************************/ struct re_set : public re_syntax_base { unsigned char _map[1 << CHAR_BIT]; }; /*** struct re_jump *************************************************** Jump to a new location in the machine (not next). ***********************************************************************/ struct re_jump : public re_syntax_base { offset_type alt; // location to jump to }; /*** struct re_alt *************************************************** Jump to a new location in the machine (possibly next). ***********************************************************************/ struct re_alt : public re_jump { unsigned char _map[1 << CHAR_BIT]; // which characters can take the jump unsigned int can_be_null; // true if we match a NULL string }; /*** struct re_repeat ************************************************* Repeat a section of the machine ***********************************************************************/ struct re_repeat : public re_alt { std::size_t min, max; // min and max allowable repeats int state_id; // Unique identifier for this repeat bool leading; // True if this repeat is at the start of the machine (lets us optimize some searches) bool greedy; // True if this is a greedy repeat }; /*** struct re_recurse ************************************************ Recurse to a particular subexpression. **********************************************************************/ struct re_recurse : public re_jump { int state_id; // identifier of first nested repeat within the recursion. }; /*** struct re_commit ************************************************* Used for the PRUNE, SKIP and COMMIT verbs which basically differ only in what happens if no match is found and we start searching forward. **********************************************************************/ enum commit_type { commit_prune, commit_skip, commit_commit }; struct re_commit : public re_syntax_base { commit_type action; }; /*** enum re_jump_size_type ******************************************* Provides compiled size of re_jump structure (allowing for trailing alignment). We provide this so we know how manybytes to insert when constructing the machine (The value of padding_mask is defined in regex_raw_buffer.hpp). ***********************************************************************/ enum re_jump_size_type { re_jump_size = (sizeof(re_jump) + padding_mask) & ~(padding_mask), re_repeater_size = (sizeof(re_repeat) + padding_mask) & ~(padding_mask), re_alt_size = (sizeof(re_alt) + padding_mask) & ~(padding_mask) }; /*** proc re_is_set_member ********************************************* Forward declaration: we'll need this one later... ***********************************************************************/ template struct regex_data; template iterator re_is_set_member(iterator next, iterator last, const re_set_long* set_, const regex_data& e, bool icase); } // namespace BOOST_REGEX_DETAIL_NS } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/sub_match.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE sub_match.cpp * VERSION see * DESCRIPTION: Declares template class sub_match. */ #ifndef BOOST_REGEX_V5_SUB_MATCH_HPP #define BOOST_REGEX_V5_SUB_MATCH_HPP namespace boost{ template struct sub_match : public std::pair { typedef typename std::iterator_traits::value_type value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef BidiIterator iterator_type; typedef BidiIterator iterator; typedef BidiIterator const_iterator; bool matched; sub_match() : std::pair(), matched(false) {} sub_match(BidiIterator i) : std::pair(i, i), matched(false) {} template operator std::basic_string ()const { return matched ? std::basic_string(this->first, this->second) : std::basic_string(); } difference_type length()const { difference_type n = matched ? std::distance((BidiIterator)this->first, (BidiIterator)this->second) : 0; return n; } std::basic_string str()const { std::basic_string result; if(matched) { std::size_t len = std::distance((BidiIterator)this->first, (BidiIterator)this->second); result.reserve(len); BidiIterator i = this->first; while(i != this->second) { result.append(1, *i); ++i; } } return result; } int compare(const sub_match& s)const { if(matched != s.matched) return static_cast(matched) - static_cast(s.matched); return str().compare(s.str()); } int compare(const std::basic_string& s)const { return str().compare(s); } int compare(const value_type* p)const { return str().compare(p); } bool operator==(const sub_match& that)const { return compare(that) == 0; } bool operator !=(const sub_match& that)const { return compare(that) != 0; } bool operator<(const sub_match& that)const { return compare(that) < 0; } bool operator>(const sub_match& that)const { return compare(that) > 0; } bool operator<=(const sub_match& that)const { return compare(that) <= 0; } bool operator>=(const sub_match& that)const { return compare(that) >= 0; } #ifdef BOOST_REGEX_MATCH_EXTRA typedef std::vector > capture_sequence_type; const capture_sequence_type& captures()const { if(!m_captures) m_captures.reset(new capture_sequence_type()); return *m_captures; } // // Private implementation API: DO NOT USE! // capture_sequence_type& get_captures()const { if(!m_captures) m_captures.reset(new capture_sequence_type()); return *m_captures; } private: mutable std::unique_ptr m_captures; public: #endif sub_match(const sub_match& that, bool #ifdef BOOST_REGEX_MATCH_EXTRA deep_copy #endif = true ) : std::pair(that), matched(that.matched) { #ifdef BOOST_REGEX_MATCH_EXTRA if(that.m_captures) if(deep_copy) m_captures.reset(new capture_sequence_type(*(that.m_captures))); #endif } sub_match& operator=(const sub_match& that) { this->first = that.first; this->second = that.second; matched = that.matched; #ifdef BOOST_REGEX_MATCH_EXTRA if(that.m_captures) get_captures() = *(that.m_captures); #endif return *this; } // // Make this type a range, for both Boost.Range, and C++11: // BidiIterator begin()const { return this->first; } BidiIterator end()const { return this->second; } }; typedef sub_match csub_match; typedef sub_match ssub_match; #ifndef BOOST_NO_WREGEX typedef sub_match wcsub_match; typedef sub_match wssub_match; #endif // comparison to std::basic_string<> part 1: template inline bool operator == (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) == 0; } template inline bool operator != (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) != 0; } template inline bool operator < (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) < 0; } template inline bool operator <= (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) <= 0; } template inline bool operator >= (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) >= 0; } template inline bool operator > (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { return s.compare(m.str()) > 0; } // comparison to std::basic_string<> part 2: template inline bool operator == (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) == 0; } template inline bool operator != (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) != 0; } template inline bool operator < (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) < 0; } template inline bool operator > (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) > 0; } template inline bool operator <= (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) <= 0; } template inline bool operator >= (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { return m.str().compare(s) >= 0; } // comparison to const charT* part 1: template inline bool operator == (const sub_match& m, typename std::iterator_traits::value_type const* s) { return m.str().compare(s) == 0; } template inline bool operator != (const sub_match& m, typename std::iterator_traits::value_type const* s) { return m.str().compare(s) != 0; } template inline bool operator > (const sub_match& m, typename std::iterator_traits::value_type const* s) { return m.str().compare(s) > 0; } template inline bool operator < (const sub_match& m, typename std::iterator_traits::value_type const* s) { return m.str().compare(s) < 0; } template inline bool operator >= (const sub_match& m, typename std::iterator_traits::value_type const* s) { return m.str().compare(s) >= 0; } template inline bool operator <= (const sub_match& m, typename std::iterator_traits::value_type const* s) { return m.str().compare(s) <= 0; } // comparison to const charT* part 2: template inline bool operator == (typename std::iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) == 0; } template inline bool operator != (typename std::iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) != 0; } template inline bool operator < (typename std::iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) > 0; } template inline bool operator > (typename std::iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) < 0; } template inline bool operator <= (typename std::iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) >= 0; } template inline bool operator >= (typename std::iterator_traits::value_type const* s, const sub_match& m) { return m.str().compare(s) <= 0; } // comparison to const charT& part 1: template inline bool operator == (const sub_match& m, typename std::iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) == 0; } template inline bool operator != (const sub_match& m, typename std::iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) != 0; } template inline bool operator > (const sub_match& m, typename std::iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) > 0; } template inline bool operator < (const sub_match& m, typename std::iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) < 0; } template inline bool operator >= (const sub_match& m, typename std::iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) >= 0; } template inline bool operator <= (const sub_match& m, typename std::iterator_traits::value_type const& s) { return m.str().compare(0, m.length(), &s, 1) <= 0; } // comparison to const charT* part 2: template inline bool operator == (typename std::iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) == 0; } template inline bool operator != (typename std::iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) != 0; } template inline bool operator < (typename std::iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) > 0; } template inline bool operator > (typename std::iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) < 0; } template inline bool operator <= (typename std::iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) >= 0; } template inline bool operator >= (typename std::iterator_traits::value_type const& s, const sub_match& m) { return m.str().compare(0, m.length(), &s, 1) <= 0; } // addition operators: template inline std::basic_string::value_type, traits, Allocator> operator + (const std::basic_string::value_type, traits, Allocator>& s, const sub_match& m) { std::basic_string::value_type, traits, Allocator> result; result.reserve(s.size() + m.length() + 1); return result.append(s).append(m.first, m.second); } template inline std::basic_string::value_type, traits, Allocator> operator + (const sub_match& m, const std::basic_string::value_type, traits, Allocator>& s) { std::basic_string::value_type, traits, Allocator> result; result.reserve(s.size() + m.length() + 1); return result.append(m.first, m.second).append(s); } template inline std::basic_string::value_type> operator + (typename std::iterator_traits::value_type const* s, const sub_match& m) { std::basic_string::value_type> result; result.reserve(std::char_traits::value_type>::length(s) + m.length() + 1); return result.append(s).append(m.first, m.second); } template inline std::basic_string::value_type> operator + (const sub_match& m, typename std::iterator_traits::value_type const * s) { std::basic_string::value_type> result; result.reserve(std::char_traits::value_type>::length(s) + m.length() + 1); return result.append(m.first, m.second).append(s); } template inline std::basic_string::value_type> operator + (typename std::iterator_traits::value_type const& s, const sub_match& m) { std::basic_string::value_type> result; result.reserve(m.length() + 2); return result.append(1, s).append(m.first, m.second); } template inline std::basic_string::value_type> operator + (const sub_match& m, typename std::iterator_traits::value_type const& s) { std::basic_string::value_type> result; result.reserve(m.length() + 2); return result.append(m.first, m.second).append(1, s); } template inline std::basic_string::value_type> operator + (const sub_match& m1, const sub_match& m2) { std::basic_string::value_type> result; result.reserve(m1.length() + m2.length() + 1); return result.append(m1.first, m1.second).append(m2.first, m2.second); } template std::basic_ostream& operator << (std::basic_ostream& os, const sub_match& s) { return (os << s.str()); } } // namespace boost #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/syntax_type.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE syntax_type.hpp * VERSION see * DESCRIPTION: Declares regular expression synatx type enumerator. */ #ifndef BOOST_REGEX_SYNTAX_TYPE_HPP #define BOOST_REGEX_SYNTAX_TYPE_HPP namespace boost{ namespace regex_constants{ typedef unsigned char syntax_type; // // values chosen are binary compatible with previous version: // static const syntax_type syntax_char = 0; static const syntax_type syntax_open_mark = 1; static const syntax_type syntax_close_mark = 2; static const syntax_type syntax_dollar = 3; static const syntax_type syntax_caret = 4; static const syntax_type syntax_dot = 5; static const syntax_type syntax_star = 6; static const syntax_type syntax_plus = 7; static const syntax_type syntax_question = 8; static const syntax_type syntax_open_set = 9; static const syntax_type syntax_close_set = 10; static const syntax_type syntax_or = 11; static const syntax_type syntax_escape = 12; static const syntax_type syntax_dash = 14; static const syntax_type syntax_open_brace = 15; static const syntax_type syntax_close_brace = 16; static const syntax_type syntax_digit = 17; static const syntax_type syntax_comma = 27; static const syntax_type syntax_equal = 37; static const syntax_type syntax_colon = 36; static const syntax_type syntax_not = 53; // extensions: static const syntax_type syntax_hash = 13; static const syntax_type syntax_newline = 26; // escapes: typedef syntax_type escape_syntax_type; static const escape_syntax_type escape_type_word_assert = 18; static const escape_syntax_type escape_type_not_word_assert = 19; static const escape_syntax_type escape_type_control_f = 29; static const escape_syntax_type escape_type_control_n = 30; static const escape_syntax_type escape_type_control_r = 31; static const escape_syntax_type escape_type_control_t = 32; static const escape_syntax_type escape_type_control_v = 33; static const escape_syntax_type escape_type_ascii_control = 35; static const escape_syntax_type escape_type_hex = 34; static const escape_syntax_type escape_type_unicode = 0; // not used static const escape_syntax_type escape_type_identity = 0; // not used static const escape_syntax_type escape_type_backref = syntax_digit; static const escape_syntax_type escape_type_decimal = syntax_digit; // not used static const escape_syntax_type escape_type_class = 22; static const escape_syntax_type escape_type_not_class = 23; // extensions: static const escape_syntax_type escape_type_left_word = 20; static const escape_syntax_type escape_type_right_word = 21; static const escape_syntax_type escape_type_start_buffer = 24; // for \` static const escape_syntax_type escape_type_end_buffer = 25; // for \' static const escape_syntax_type escape_type_control_a = 28; // for \a static const escape_syntax_type escape_type_e = 38; // for \e static const escape_syntax_type escape_type_E = 47; // for \Q\E static const escape_syntax_type escape_type_Q = 48; // for \Q\E static const escape_syntax_type escape_type_X = 49; // for \X static const escape_syntax_type escape_type_C = 50; // for \C static const escape_syntax_type escape_type_Z = 51; // for \Z static const escape_syntax_type escape_type_G = 52; // for \G static const escape_syntax_type escape_type_property = 54; // for \p static const escape_syntax_type escape_type_not_property = 55; // for \P static const escape_syntax_type escape_type_named_char = 56; // for \N static const escape_syntax_type escape_type_extended_backref = 57; // for \g static const escape_syntax_type escape_type_reset_start_mark = 58; // for \K static const escape_syntax_type escape_type_line_ending = 59; // for \R static const escape_syntax_type syntax_max = 60; } } #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/u32regex_iterator.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE u32regex_iterator.hpp * VERSION see * DESCRIPTION: Provides u32regex_iterator implementation. */ #ifndef BOOST_REGEX_V5_U32REGEX_ITERATOR_HPP #define BOOST_REGEX_V5_U32REGEX_ITERATOR_HPP namespace boost{ template class u32regex_iterator_implementation { typedef u32regex regex_type; match_results what; // current match BidirectionalIterator base; // start of sequence BidirectionalIterator end; // end of sequence const regex_type re; // the expression match_flag_type flags; // flags for matching public: u32regex_iterator_implementation(const regex_type* p, BidirectionalIterator last, match_flag_type f) : base(), end(last), re(*p), flags(f){} bool init(BidirectionalIterator first) { base = first; return u32regex_search(first, end, what, re, flags, base); } bool compare(const u32regex_iterator_implementation& that) { if(this == &that) return true; return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second); } const match_results& get() { return what; } bool next() { //if(what.prefix().first != what[0].second) // flags |= match_prev_avail; BidirectionalIterator next_start = what[0].second; match_flag_type f(flags); if(!what.length()) f |= regex_constants::match_not_initial_null; //if(base != next_start) // f |= regex_constants::match_not_bob; bool result = u32regex_search(next_start, end, what, re, f, base); if(result) what.set_base(base); return result; } private: u32regex_iterator_implementation& operator=(const u32regex_iterator_implementation&); }; template class u32regex_iterator { private: typedef u32regex_iterator_implementation impl; typedef std::shared_ptr pimpl; public: typedef u32regex regex_type; typedef match_results value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; u32regex_iterator(){} u32regex_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, match_flag_type m = match_default) : pdata(new impl(&re, b, m)) { if(!pdata->init(a)) { pdata.reset(); } } u32regex_iterator(const u32regex_iterator& that) : pdata(that.pdata) {} u32regex_iterator& operator=(const u32regex_iterator& that) { pdata = that.pdata; return *this; } bool operator==(const u32regex_iterator& that)const { if((pdata.get() == 0) || (that.pdata.get() == 0)) return pdata.get() == that.pdata.get(); return pdata->compare(*(that.pdata.get())); } bool operator!=(const u32regex_iterator& that)const { return !(*this == that); } const value_type& operator*()const { return pdata->get(); } const value_type* operator->()const { return &(pdata->get()); } u32regex_iterator& operator++() { cow(); if(0 == pdata->next()) { pdata.reset(); } return *this; } u32regex_iterator operator++(int) { u32regex_iterator result(*this); ++(*this); return result; } private: pimpl pdata; void cow() { // copy-on-write if(pdata.get() && (pdata.use_count() > 1)) { pdata.reset(new impl(*(pdata.get()))); } } }; typedef u32regex_iterator utf8regex_iterator; typedef u32regex_iterator utf16regex_iterator; typedef u32regex_iterator utf32regex_iterator; inline u32regex_iterator make_u32regex_iterator(const char* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_iterator(p, p+std::strlen(p), e, m); } #ifndef BOOST_NO_WREGEX inline u32regex_iterator make_u32regex_iterator(const wchar_t* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_iterator(p, p+std::wcslen(p), e, m); } #endif #if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T) inline u32regex_iterator make_u32regex_iterator(const UChar* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_iterator(p, p+u_strlen(p), e, m); } #endif template inline u32regex_iterator::const_iterator> make_u32regex_iterator(const std::basic_string& p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { typedef typename std::basic_string::const_iterator iter_type; return u32regex_iterator(p.begin(), p.end(), e, m); } inline u32regex_iterator make_u32regex_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_iterator(s.getBuffer(), s.getBuffer() + s.length(), e, m); } } // namespace boost #endif // BOOST_REGEX_V5_REGEX_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/u32regex_token_iterator.hpp ================================================ /* * * Copyright (c) 2003 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE u32regex_token_iterator.hpp * VERSION see * DESCRIPTION: Provides u32regex_token_iterator implementation. */ #ifndef BOOST_REGEX_V5_U32REGEX_TOKEN_ITERATOR_HPP #define BOOST_REGEX_V5_U32REGEX_TOKEN_ITERATOR_HPP namespace boost{ #ifdef BOOST_REGEX_MSVC # pragma warning(push) # pragma warning(disable:4700) #endif template class u32regex_token_iterator_implementation { typedef u32regex regex_type; typedef sub_match value_type; match_results what; // current match BidirectionalIterator end; // end of search area BidirectionalIterator base; // start of search area const regex_type re; // the expression match_flag_type flags; // match flags value_type result; // the current string result int N; // the current sub-expression being enumerated std::vector subs; // the sub-expressions to enumerate public: u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, int sub, match_flag_type f) : end(last), re(*p), flags(f){ subs.push_back(sub); } u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const std::vector& v, match_flag_type f) : end(last), re(*p), flags(f), subs(v){} template u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const int (&submatches)[CN], match_flag_type f) : end(last), re(*p), flags(f) { for(std::size_t i = 0; i < CN; ++i) { subs.push_back(submatches[i]); } } bool init(BidirectionalIterator first) { base = first; N = 0; if(u32regex_search(first, end, what, re, flags, base) == true) { N = 0; result = ((subs[N] == -1) ? what.prefix() : what[(int)subs[N]]); return true; } else if((subs[N] == -1) && (first != end)) { result.first = first; result.second = end; result.matched = (first != end); N = -1; return true; } return false; } bool compare(const u32regex_token_iterator_implementation& that) { if(this == &that) return true; return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (N == that.N) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second); } const value_type& get() { return result; } bool next() { if(N == -1) return false; if(N+1 < (int)subs.size()) { ++N; result =((subs[N] == -1) ? what.prefix() : what[subs[N]]); return true; } //if(what.prefix().first != what[0].second) // flags |= match_prev_avail | regex_constants::match_not_bob; BidirectionalIterator last_end(what[0].second); if(u32regex_search(last_end, end, what, re, ((what[0].first == what[0].second) ? flags | regex_constants::match_not_initial_null : flags), base)) { N =0; result =((subs[N] == -1) ? what.prefix() : what[subs[N]]); return true; } else if((last_end != end) && (subs[0] == -1)) { N =-1; result.first = last_end; result.second = end; result.matched = (last_end != end); return true; } return false; } private: u32regex_token_iterator_implementation& operator=(const u32regex_token_iterator_implementation&); }; template class u32regex_token_iterator { private: typedef u32regex_token_iterator_implementation impl; typedef std::shared_ptr pimpl; public: typedef u32regex regex_type; typedef sub_match value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; u32regex_token_iterator(){} u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, int submatch = 0, match_flag_type m = match_default) : pdata(new impl(&re, b, submatch, m)) { if(!pdata->init(a)) pdata.reset(); } u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const std::vector& submatches, match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } template u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const int (&submatches)[N], match_flag_type m = match_default) : pdata(new impl(&re, b, submatches, m)) { if(!pdata->init(a)) pdata.reset(); } u32regex_token_iterator(const u32regex_token_iterator& that) : pdata(that.pdata) {} u32regex_token_iterator& operator=(const u32regex_token_iterator& that) { pdata = that.pdata; return *this; } bool operator==(const u32regex_token_iterator& that)const { if((pdata.get() == 0) || (that.pdata.get() == 0)) return pdata.get() == that.pdata.get(); return pdata->compare(*(that.pdata.get())); } bool operator!=(const u32regex_token_iterator& that)const { return !(*this == that); } const value_type& operator*()const { return pdata->get(); } const value_type* operator->()const { return &(pdata->get()); } u32regex_token_iterator& operator++() { cow(); if(0 == pdata->next()) { pdata.reset(); } return *this; } u32regex_token_iterator operator++(int) { u32regex_token_iterator result(*this); ++(*this); return result; } private: pimpl pdata; void cow() { // copy-on-write if(pdata.get() && (pdata.use_count() > 1)) { pdata.reset(new impl(*(pdata.get()))); } } }; typedef u32regex_token_iterator utf8regex_token_iterator; typedef u32regex_token_iterator utf16regex_token_iterator; typedef u32regex_token_iterator utf32regex_token_iterator; // construction from an integral sub_match state_id: inline u32regex_token_iterator make_u32regex_token_iterator(const char* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::strlen(p), e, submatch, m); } #ifndef BOOST_NO_WREGEX inline u32regex_token_iterator make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::wcslen(p), e, submatch, m); } #endif #if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T) inline u32regex_token_iterator make_u32regex_token_iterator(const UChar* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+u_strlen(p), e, submatch, m); } #endif template inline u32regex_token_iterator::const_iterator> make_u32regex_token_iterator(const std::basic_string& p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { typedef typename std::basic_string::const_iterator iter_type; return u32regex_token_iterator(p.begin(), p.end(), e, submatch, m); } inline u32regex_token_iterator make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m); } // construction from a reference to an array: template inline u32regex_token_iterator make_u32regex_token_iterator(const char* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::strlen(p), e, submatch, m); } #ifndef BOOST_NO_WREGEX template inline u32regex_token_iterator make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::wcslen(p), e, submatch, m); } #endif #if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T) template inline u32regex_token_iterator make_u32regex_token_iterator(const UChar* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+u_strlen(p), e, submatch, m); } #endif template inline u32regex_token_iterator::const_iterator> make_u32regex_token_iterator(const std::basic_string& p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { typedef typename std::basic_string::const_iterator iter_type; return u32regex_token_iterator(p.begin(), p.end(), e, submatch, m); } template inline u32regex_token_iterator make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m); } // construction from a vector of sub_match state_id's: inline u32regex_token_iterator make_u32regex_token_iterator(const char* p, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::strlen(p), e, submatch, m); } #ifndef BOOST_NO_WREGEX inline u32regex_token_iterator make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+std::wcslen(p), e, submatch, m); } #endif #if !defined(U_WCHAR_IS_UTF16) && (U_SIZEOF_WCHAR_T != 2) inline u32regex_token_iterator make_u32regex_token_iterator(const UChar* p, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(p, p+u_strlen(p), e, submatch, m); } #endif template inline u32regex_token_iterator::const_iterator> make_u32regex_token_iterator(const std::basic_string& p, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { typedef typename std::basic_string::const_iterator iter_type; return u32regex_token_iterator(p.begin(), p.end(), e, submatch, m); } inline u32regex_token_iterator make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const std::vector& submatch, regex_constants::match_flag_type m = regex_constants::match_default) { return u32regex_token_iterator(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m); } #ifdef BOOST_REGEX_MSVC # pragma warning(pop) #endif } // namespace boost #endif // BOOST_REGEX_V5_REGEX_TOKEN_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/unicode_iterator.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE unicode_iterator.hpp * VERSION see * DESCRIPTION: Iterator adapters for converting between different Unicode encodings. */ /**************************************************************************** Contents: ~~~~~~~~~ 1) Read Only, Input Adapters: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ template class u32_to_u8_iterator; Adapts sequence of UTF-32 code points to "look like" a sequence of UTF-8. template class u8_to_u32_iterator; Adapts sequence of UTF-8 code points to "look like" a sequence of UTF-32. template class u32_to_u16_iterator; Adapts sequence of UTF-32 code points to "look like" a sequence of UTF-16. template class u16_to_u32_iterator; Adapts sequence of UTF-16 code points to "look like" a sequence of UTF-32. 2) Single pass output iterator adapters: template class utf8_output_iterator; Accepts UTF-32 code points and forwards them on as UTF-8 code points. template class utf16_output_iterator; Accepts UTF-32 code points and forwards them on as UTF-16 code points. ****************************************************************************/ #ifndef BOOST_REGEX_UNICODE_ITERATOR_HPP #define BOOST_REGEX_UNICODE_ITERATOR_HPP #include #include #include #include #include #include // CHAR_BIT #ifndef BOOST_REGEX_STANDALONE #include #endif namespace boost{ namespace detail{ static const std::uint16_t high_surrogate_base = 0xD7C0u; static const std::uint16_t low_surrogate_base = 0xDC00u; static const std::uint32_t ten_bit_mask = 0x3FFu; inline bool is_high_surrogate(std::uint16_t v) { return (v & 0xFFFFFC00u) == 0xd800u; } inline bool is_low_surrogate(std::uint16_t v) { return (v & 0xFFFFFC00u) == 0xdc00u; } template inline bool is_surrogate(T v) { return (v & 0xFFFFF800u) == 0xd800; } inline unsigned utf8_byte_count(std::uint8_t c) { // if the most significant bit with a zero in it is in position // 8-N then there are N bytes in this UTF-8 sequence: std::uint8_t mask = 0x80u; unsigned result = 0; while(c & mask) { ++result; mask >>= 1; } return (result == 0) ? 1 : ((result > 4) ? 4 : result); } inline unsigned utf8_trailing_byte_count(std::uint8_t c) { return utf8_byte_count(c) - 1; } #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4100) #endif #ifndef BOOST_NO_EXCEPTIONS BOOST_REGEX_NORETURN #endif inline void invalid_utf32_code_point(std::uint32_t val) { std::stringstream ss; ss << "Invalid UTF-32 code point U+" << std::showbase << std::hex << val << " encountered while trying to encode UTF-16 sequence"; std::out_of_range e(ss.str()); #ifndef BOOST_REGEX_STANDALONE boost::throw_exception(e); #else throw e; #endif } #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif } // namespace detail template class u32_to_u16_iterator { typedef typename std::iterator_traits::value_type base_value_type; static_assert(sizeof(base_value_type)*CHAR_BIT == 32, "Incorrectly sized template argument"); static_assert(sizeof(U16Type)*CHAR_BIT == 16, "Incorrectly sized template argument"); public: typedef std::ptrdiff_t difference_type; typedef U16Type value_type; typedef value_type const* pointer; typedef value_type const reference; typedef std::bidirectional_iterator_tag iterator_category; reference operator*()const { if(m_current == 2) extract_current(); return m_values[m_current]; } bool operator==(const u32_to_u16_iterator& that)const { if(m_position == that.m_position) { // Both m_currents must be equal, or both even // this is the same as saying their sum must be even: return (m_current + that.m_current) & 1u ? false : true; } return false; } bool operator!=(const u32_to_u16_iterator& that)const { return !(*this == that); } u32_to_u16_iterator& operator++() { // if we have a pending read then read now, so that we know whether // to skip a position, or move to a low-surrogate: if(m_current == 2) { // pending read: extract_current(); } // move to the next surrogate position: ++m_current; // if we've reached the end skip a position: if(m_values[m_current] == 0) { m_current = 2; ++m_position; } return *this; } u32_to_u16_iterator operator++(int) { u32_to_u16_iterator r(*this); ++(*this); return r; } u32_to_u16_iterator& operator--() { if(m_current != 1) { // decrementing an iterator always leads to a valid position: --m_position; extract_current(); m_current = m_values[1] ? 1 : 0; } else { m_current = 0; } return *this; } u32_to_u16_iterator operator--(int) { u32_to_u16_iterator r(*this); --(*this); return r; } BaseIterator base()const { return m_position; } // construct: u32_to_u16_iterator() : m_position(), m_current(0) { m_values[0] = 0; m_values[1] = 0; m_values[2] = 0; } u32_to_u16_iterator(BaseIterator b) : m_position(b), m_current(2) { m_values[0] = 0; m_values[1] = 0; m_values[2] = 0; } private: void extract_current()const { // begin by checking for a code point out of range: std::uint32_t v = *m_position; if(v >= 0x10000u) { if(v > 0x10FFFFu) detail::invalid_utf32_code_point(*m_position); // split into two surrogates: m_values[0] = static_cast(v >> 10) + detail::high_surrogate_base; m_values[1] = static_cast(v & detail::ten_bit_mask) + detail::low_surrogate_base; m_current = 0; BOOST_REGEX_ASSERT(detail::is_high_surrogate(m_values[0])); BOOST_REGEX_ASSERT(detail::is_low_surrogate(m_values[1])); } else { // 16-bit code point: m_values[0] = static_cast(*m_position); m_values[1] = 0; m_current = 0; // value must not be a surrogate: if(detail::is_surrogate(m_values[0])) detail::invalid_utf32_code_point(*m_position); } } BaseIterator m_position; mutable U16Type m_values[3]; mutable unsigned m_current; }; template class u16_to_u32_iterator { // special values for pending iterator reads: static const U32Type pending_read = 0xffffffffu; typedef typename std::iterator_traits::value_type base_value_type; static_assert(sizeof(base_value_type)*CHAR_BIT == 16, "Incorrectly sized template argument"); static_assert(sizeof(U32Type)*CHAR_BIT == 32, "Incorrectly sized template argument"); public: typedef std::ptrdiff_t difference_type; typedef U32Type value_type; typedef value_type const* pointer; typedef value_type const reference; typedef std::bidirectional_iterator_tag iterator_category; reference operator*()const { if(m_value == pending_read) extract_current(); return m_value; } bool operator==(const u16_to_u32_iterator& that)const { return m_position == that.m_position; } bool operator!=(const u16_to_u32_iterator& that)const { return !(*this == that); } u16_to_u32_iterator& operator++() { // skip high surrogate first if there is one: if(detail::is_high_surrogate(*m_position)) ++m_position; ++m_position; m_value = pending_read; return *this; } u16_to_u32_iterator operator++(int) { u16_to_u32_iterator r(*this); ++(*this); return r; } u16_to_u32_iterator& operator--() { --m_position; // if we have a low surrogate then go back one more: if(detail::is_low_surrogate(*m_position)) --m_position; m_value = pending_read; return *this; } u16_to_u32_iterator operator--(int) { u16_to_u32_iterator r(*this); --(*this); return r; } BaseIterator base()const { return m_position; } // construct: u16_to_u32_iterator() : m_position() { m_value = pending_read; } u16_to_u32_iterator(BaseIterator b) : m_position(b) { m_value = pending_read; } // // Range checked version: // u16_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b) { m_value = pending_read; // // The range must not start with a low surrogate, or end in a high surrogate, // otherwise we run the risk of running outside the underlying input range. // Likewise b must not be located at a low surrogate. // std::uint16_t val; if(start != end) { if((b != start) && (b != end)) { val = *b; if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u)) invalid_code_point(val); } val = *start; if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u)) invalid_code_point(val); val = *--end; if(detail::is_high_surrogate(val)) invalid_code_point(val); } } private: static void invalid_code_point(std::uint16_t val) { std::stringstream ss; ss << "Misplaced UTF-16 surrogate U+" << std::showbase << std::hex << val << " encountered while trying to encode UTF-32 sequence"; std::out_of_range e(ss.str()); #ifndef BOOST_REGEX_STANDALONE boost::throw_exception(e); #else throw e; #endif } void extract_current()const { m_value = static_cast(static_cast< std::uint16_t>(*m_position)); // if the last value is a high surrogate then adjust m_position and m_value as needed: if(detail::is_high_surrogate(*m_position)) { // precondition; next value must have be a low-surrogate: BaseIterator next(m_position); std::uint16_t t = *++next; if((t & 0xFC00u) != 0xDC00u) invalid_code_point(t); m_value = (m_value - detail::high_surrogate_base) << 10; m_value |= (static_cast(static_cast< std::uint16_t>(t)) & detail::ten_bit_mask); } // postcondition; result must not be a surrogate: if(detail::is_surrogate(m_value)) invalid_code_point(static_cast< std::uint16_t>(m_value)); } BaseIterator m_position; mutable U32Type m_value; }; template class u32_to_u8_iterator { typedef typename std::iterator_traits::value_type base_value_type; static_assert(sizeof(base_value_type)*CHAR_BIT == 32, "Incorrectly sized template argument"); static_assert(sizeof(U8Type)*CHAR_BIT == 8, "Incorrectly sized template argument"); public: typedef std::ptrdiff_t difference_type; typedef U8Type value_type; typedef value_type const* pointer; typedef value_type const reference; typedef std::bidirectional_iterator_tag iterator_category; reference operator*()const { if(m_current == 4) extract_current(); return m_values[m_current]; } bool operator==(const u32_to_u8_iterator& that)const { if(m_position == that.m_position) { // either the m_current's must be equal, or one must be 0 and // the other 4: which means neither must have bits 1 or 2 set: return (m_current == that.m_current) || (((m_current | that.m_current) & 3) == 0); } return false; } bool operator!=(const u32_to_u8_iterator& that)const { return !(*this == that); } u32_to_u8_iterator& operator++() { // if we have a pending read then read now, so that we know whether // to skip a position, or move to a low-surrogate: if(m_current == 4) { // pending read: extract_current(); } // move to the next surrogate position: ++m_current; // if we've reached the end skip a position: if(m_values[m_current] == 0) { m_current = 4; ++m_position; } return *this; } u32_to_u8_iterator operator++(int) { u32_to_u8_iterator r(*this); ++(*this); return r; } u32_to_u8_iterator& operator--() { if((m_current & 3) == 0) { --m_position; extract_current(); m_current = 3; while(m_current && (m_values[m_current] == 0)) --m_current; } else --m_current; return *this; } u32_to_u8_iterator operator--(int) { u32_to_u8_iterator r(*this); --(*this); return r; } BaseIterator base()const { return m_position; } // construct: u32_to_u8_iterator() : m_position(), m_current(0) { m_values[0] = 0; m_values[1] = 0; m_values[2] = 0; m_values[3] = 0; m_values[4] = 0; } u32_to_u8_iterator(BaseIterator b) : m_position(b), m_current(4) { m_values[0] = 0; m_values[1] = 0; m_values[2] = 0; m_values[3] = 0; m_values[4] = 0; } private: void extract_current()const { std::uint32_t c = *m_position; if(c > 0x10FFFFu) detail::invalid_utf32_code_point(c); if(c < 0x80u) { m_values[0] = static_cast(c); m_values[1] = static_cast(0u); m_values[2] = static_cast(0u); m_values[3] = static_cast(0u); } else if(c < 0x800u) { m_values[0] = static_cast(0xC0u + (c >> 6)); m_values[1] = static_cast(0x80u + (c & 0x3Fu)); m_values[2] = static_cast(0u); m_values[3] = static_cast(0u); } else if(c < 0x10000u) { m_values[0] = static_cast(0xE0u + (c >> 12)); m_values[1] = static_cast(0x80u + ((c >> 6) & 0x3Fu)); m_values[2] = static_cast(0x80u + (c & 0x3Fu)); m_values[3] = static_cast(0u); } else { m_values[0] = static_cast(0xF0u + (c >> 18)); m_values[1] = static_cast(0x80u + ((c >> 12) & 0x3Fu)); m_values[2] = static_cast(0x80u + ((c >> 6) & 0x3Fu)); m_values[3] = static_cast(0x80u + (c & 0x3Fu)); } m_current= 0; } BaseIterator m_position; mutable U8Type m_values[5]; mutable unsigned m_current; }; template class u8_to_u32_iterator { // special values for pending iterator reads: static const U32Type pending_read = 0xffffffffu; typedef typename std::iterator_traits::value_type base_value_type; static_assert(sizeof(base_value_type)*CHAR_BIT == 8, "Incorrectly sized template argument"); static_assert(sizeof(U32Type)*CHAR_BIT == 32, "Incorrectly sized template argument"); public: typedef std::ptrdiff_t difference_type; typedef U32Type value_type; typedef value_type const* pointer; typedef value_type const reference; typedef std::bidirectional_iterator_tag iterator_category; reference operator*()const { if(m_value == pending_read) extract_current(); return m_value; } bool operator==(const u8_to_u32_iterator& that)const { return m_position == that.m_position; } bool operator!=(const u8_to_u32_iterator& that)const { return !(*this == that); } u8_to_u32_iterator& operator++() { // We must not start with a continuation character: if((static_cast(*m_position) & 0xC0) == 0x80) invalid_sequence(); // skip high surrogate first if there is one: unsigned c = detail::utf8_byte_count(*m_position); if(m_value == pending_read) { // Since we haven't read in a value, we need to validate the code points: for(unsigned i = 0; i < c; ++i) { ++m_position; // We must have a continuation byte: if((i != c - 1) && ((static_cast(*m_position) & 0xC0) != 0x80)) invalid_sequence(); } } else { std::advance(m_position, c); } m_value = pending_read; return *this; } u8_to_u32_iterator operator++(int) { u8_to_u32_iterator r(*this); ++(*this); return r; } u8_to_u32_iterator& operator--() { // Keep backtracking until we don't have a trailing character: unsigned count = 0; while((*--m_position & 0xC0u) == 0x80u) ++count; // now check that the sequence was valid: if(count != detail::utf8_trailing_byte_count(*m_position)) invalid_sequence(); m_value = pending_read; return *this; } u8_to_u32_iterator operator--(int) { u8_to_u32_iterator r(*this); --(*this); return r; } BaseIterator base()const { return m_position; } // construct: u8_to_u32_iterator() : m_position() { m_value = pending_read; } u8_to_u32_iterator(BaseIterator b) : m_position(b) { m_value = pending_read; } // // Checked constructor: // u8_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b) { m_value = pending_read; // // We must not start with a continuation character, or end with a // truncated UTF-8 sequence otherwise we run the risk of going past // the start/end of the underlying sequence: // if(start != end) { unsigned char v = *start; if((v & 0xC0u) == 0x80u) invalid_sequence(); if((b != start) && (b != end) && ((*b & 0xC0u) == 0x80u)) invalid_sequence(); BaseIterator pos = end; do { v = *--pos; } while((start != pos) && ((v & 0xC0u) == 0x80u)); std::ptrdiff_t extra = detail::utf8_byte_count(v); if(std::distance(pos, end) < extra) invalid_sequence(); } } private: static void invalid_sequence() { std::out_of_range e("Invalid UTF-8 sequence encountered while trying to encode UTF-32 character"); #ifndef BOOST_REGEX_STANDALONE boost::throw_exception(e); #else throw e; #endif } void extract_current()const { m_value = static_cast(static_cast< std::uint8_t>(*m_position)); // we must not have a continuation character: if((m_value & 0xC0u) == 0x80u) invalid_sequence(); // see how many extra bytes we have: unsigned extra = detail::utf8_trailing_byte_count(*m_position); // extract the extra bits, 6 from each extra byte: BaseIterator next(m_position); for(unsigned c = 0; c < extra; ++c) { ++next; m_value <<= 6; // We must have a continuation byte: if((static_cast(*next) & 0xC0) != 0x80) invalid_sequence(); m_value += static_cast(*next) & 0x3Fu; } // we now need to remove a few of the leftmost bits, but how many depends // upon how many extra bytes we've extracted: static const std::uint32_t masks[4] = { 0x7Fu, 0x7FFu, 0xFFFFu, 0x1FFFFFu, }; m_value &= masks[extra]; // check the result is in range: if(m_value > static_cast(0x10FFFFu)) invalid_sequence(); // The result must not be a surrogate: if((m_value >= static_cast(0xD800)) && (m_value <= static_cast(0xDFFF))) invalid_sequence(); // We should not have had an invalidly encoded UTF8 sequence: if((extra > 0) && (m_value <= static_cast(masks[extra - 1]))) invalid_sequence(); } BaseIterator m_position; mutable U32Type m_value; }; template class utf16_output_iterator { public: typedef void difference_type; typedef void value_type; typedef std::uint32_t* pointer; typedef std::uint32_t& reference; typedef std::output_iterator_tag iterator_category; utf16_output_iterator(const BaseIterator& b) : m_position(b){} utf16_output_iterator(const utf16_output_iterator& that) : m_position(that.m_position){} utf16_output_iterator& operator=(const utf16_output_iterator& that) { m_position = that.m_position; return *this; } const utf16_output_iterator& operator*()const { return *this; } void operator=(std::uint32_t val)const { push(val); } utf16_output_iterator& operator++() { return *this; } utf16_output_iterator& operator++(int) { return *this; } BaseIterator base()const { return m_position; } private: void push(std::uint32_t v)const { if(v >= 0x10000u) { // begin by checking for a code point out of range: if(v > 0x10FFFFu) detail::invalid_utf32_code_point(v); // split into two surrogates: *m_position++ = static_cast(v >> 10) + detail::high_surrogate_base; *m_position++ = static_cast(v & detail::ten_bit_mask) + detail::low_surrogate_base; } else { // 16-bit code point: // value must not be a surrogate: if(detail::is_surrogate(v)) detail::invalid_utf32_code_point(v); *m_position++ = static_cast(v); } } mutable BaseIterator m_position; }; template class utf8_output_iterator { public: typedef void difference_type; typedef void value_type; typedef std::uint32_t* pointer; typedef std::uint32_t& reference; typedef std::output_iterator_tag iterator_category; utf8_output_iterator(const BaseIterator& b) : m_position(b){} utf8_output_iterator(const utf8_output_iterator& that) : m_position(that.m_position){} utf8_output_iterator& operator=(const utf8_output_iterator& that) { m_position = that.m_position; return *this; } const utf8_output_iterator& operator*()const { return *this; } void operator=(std::uint32_t val)const { push(val); } utf8_output_iterator& operator++() { return *this; } utf8_output_iterator& operator++(int) { return *this; } BaseIterator base()const { return m_position; } private: void push(std::uint32_t c)const { if(c > 0x10FFFFu) detail::invalid_utf32_code_point(c); if(c < 0x80u) { *m_position++ = static_cast(c); } else if(c < 0x800u) { *m_position++ = static_cast(0xC0u + (c >> 6)); *m_position++ = static_cast(0x80u + (c & 0x3Fu)); } else if(c < 0x10000u) { *m_position++ = static_cast(0xE0u + (c >> 12)); *m_position++ = static_cast(0x80u + ((c >> 6) & 0x3Fu)); *m_position++ = static_cast(0x80u + (c & 0x3Fu)); } else { *m_position++ = static_cast(0xF0u + (c >> 18)); *m_position++ = static_cast(0x80u + ((c >> 12) & 0x3Fu)); *m_position++ = static_cast(0x80u + ((c >> 6) & 0x3Fu)); *m_position++ = static_cast(0x80u + (c & 0x3Fu)); } } mutable BaseIterator m_position; }; } // namespace boost #endif // BOOST_REGEX_UNICODE_ITERATOR_HPP ================================================ FILE: lib/third_party/boost/regex/include/boost/regex/v5/w32_regex_traits.hpp ================================================ /* * * Copyright (c) 2004 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org for most recent version. * FILE w32_regex_traits.hpp * VERSION see * DESCRIPTION: Declares regular expression traits class w32_regex_traits. */ #ifndef BOOST_W32_REGEX_TRAITS_HPP_INCLUDED #define BOOST_W32_REGEX_TRAITS_HPP_INCLUDED #ifndef BOOST_REGEX_NO_WIN32_LOCALE #include #include #ifdef BOOST_HAS_THREADS #include #endif #include #include #if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(UNDER_CE) #pragma comment(lib, "user32.lib") #endif #ifdef BOOST_REGEX_MSVC #pragma warning(push) #pragma warning(disable:4786) #if BOOST_REGEX_MSVC < 1910 #pragma warning(disable:4800) #endif #endif #ifndef BASETYPES // // windows.h not included, so lets forward declare what we need: // #ifndef NO_STRICT #ifndef STRICT #define STRICT 1 #endif #endif #if defined(STRICT) #define BOOST_RE_DETAIL_DECLARE_HANDLE(x) struct x##__; typedef struct x##__ *x #else #define BOOST_RE_DETAIL_DECLARE_HANDLE(x) typedef void* x #endif // // This must be in the global namespace: // extern "C" { BOOST_RE_DETAIL_DECLARE_HANDLE(HINSTANCE); typedef HINSTANCE HMODULE; } #endif namespace boost{ // // forward declaration is needed by some compilers: // template class w32_regex_traits; namespace BOOST_REGEX_DETAIL_NS{ // // start by typedeffing the types we'll need: // typedef unsigned long lcid_type; // placeholder for LCID. typedef std::shared_ptr cat_type; // placeholder for dll HANDLE. // // then add wrappers around the actual Win32 API's (ie implementation hiding): // lcid_type w32_get_default_locale(); bool w32_is_lower(char, lcid_type); #ifndef BOOST_NO_WREGEX bool w32_is_lower(wchar_t, lcid_type); #endif bool w32_is_upper(char, lcid_type); #ifndef BOOST_NO_WREGEX bool w32_is_upper(wchar_t, lcid_type); #endif cat_type w32_cat_open(const std::string& name); std::string w32_cat_get(const cat_type& cat, lcid_type state_id, int i, const std::string& def); #ifndef BOOST_NO_WREGEX std::wstring w32_cat_get(const cat_type& cat, lcid_type state_id, int i, const std::wstring& def); #endif std::string w32_transform(lcid_type state_id, const char* p1, const char* p2); #ifndef BOOST_NO_WREGEX std::wstring w32_transform(lcid_type state_id, const wchar_t* p1, const wchar_t* p2); #endif char w32_tolower(char c, lcid_type); #ifndef BOOST_NO_WREGEX wchar_t w32_tolower(wchar_t c, lcid_type); #endif char w32_toupper(char c, lcid_type); #ifndef BOOST_NO_WREGEX wchar_t w32_toupper(wchar_t c, lcid_type); #endif bool w32_is(lcid_type, std::uint32_t mask, char c); #ifndef BOOST_NO_WREGEX bool w32_is(lcid_type, std::uint32_t mask, wchar_t c); #endif #ifndef BASETYPES // // Forward declarations of the small number of windows types and API's we use: // #if !defined(__LP64__) using dword = unsigned long; #else using DWORD = unsigned int; #endif using word = unsigned short; using lctype = dword; static constexpr dword ct_ctype1 = 0x00000001; static constexpr dword c1_upper = 0x0001; // upper case static constexpr dword c1_lower = 0x0002; // lower case static constexpr dword c1_digit = 0x0004; // decimal digits static constexpr dword c1_space = 0x0008; // spacing characters static constexpr dword c1_punct = 0x0010; // punctuation characters static constexpr dword c1_cntrl = 0x0020; // control characters static constexpr dword c1_blank = 0x0040; // blank characters static constexpr dword c1_xdigit = 0x0080; // other digits static constexpr dword c1_alpha = 0x0100; // any linguistic character static constexpr dword c1_defined = 0x0200; // defined character static constexpr unsigned int cp_acp = 0; static constexpr dword lcmap_lowercase = 0x00000100; static constexpr dword lcmap_uppercase = 0x00000200; static constexpr dword lcmap_sortkey = 0x00000400; // WC sort key (normalize) static constexpr lctype locale_idefaultansicodepage = 0x00001004; # ifdef UNDER_CE # ifndef WINAPI # ifndef _WIN32_WCE_EMULATION # define BOOST_RE_STDCALL __cdecl // Note this doesn't match the desktop definition # else # define BOOST_RE_STDCALL __stdcall # endif # endif # else # if defined(_M_IX86) || defined(__i386__) # define BOOST_RE_STDCALL __stdcall # else // On architectures other than 32-bit x86 __stdcall is ignored. Clang also issues a warning. # define BOOST_RE_STDCALL # endif # endif #if defined (WIN32_PLATFORM_PSPC) #define BOOST_RE_IMPORT __declspec( dllimport ) #elif defined (_WIN32_WCE) #define BOOST_RE_IMPORT #else #define BOOST_RE_IMPORT __declspec( dllimport ) #endif extern "C" { BOOST_RE_IMPORT int BOOST_RE_STDCALL FreeLibrary(HMODULE hLibModule); BOOST_RE_IMPORT int BOOST_RE_STDCALL LCMapStringA(lcid_type Locale, dword dwMapFlags, const char* lpSrcStr, int cchSrc, char* lpDestStr, int cchDest); BOOST_RE_IMPORT int BOOST_RE_STDCALL LCMapStringW(lcid_type Locale, dword dwMapFlags, const wchar_t* lpSrcStr, int cchSrc, wchar_t* lpDestStr, int cchDest); BOOST_RE_IMPORT int BOOST_RE_STDCALL MultiByteToWideChar(unsigned int CodePage, dword dwFlags, const char* lpMultiByteStr, int cbMultiByte, wchar_t* lpWideCharStr, int cchWideChar); BOOST_RE_IMPORT int BOOST_RE_STDCALL LCMapStringW(lcid_type Locale, dword dwMapFlags, const wchar_t* lpSrcStr, int cchSrc, wchar_t* lpDestStr, int cchDest); BOOST_RE_IMPORT int BOOST_RE_STDCALL WideCharToMultiByte(unsigned int CodePage, dword dwFlags, const wchar_t* lpWideCharStr, int cchWideChar, char* lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, int* lpUsedDefaultChar); BOOST_RE_IMPORT int BOOST_RE_STDCALL GetStringTypeExA(lcid_type Locale, dword dwInfoType, const char* lpSrcStr, int cchSrc, word* lpCharType); BOOST_RE_IMPORT int BOOST_RE_STDCALL GetStringTypeExW(lcid_type Locale, dword dwInfoType, const wchar_t* lpSrcStr, int cchSrc, word* lpCharType); BOOST_RE_IMPORT lcid_type BOOST_RE_STDCALL GetUserDefaultLCID(); BOOST_RE_IMPORT int BOOST_RE_STDCALL GetStringTypeExA(lcid_type Locale, dword dwInfoType, const char* lpSrcStr, int cchSrc, word* lpCharType); BOOST_RE_IMPORT int BOOST_RE_STDCALL GetStringTypeExW(lcid_type Locale, dword dwInfoType, const wchar_t* lpSrcStr, int cchSrc, word* lpCharType); BOOST_RE_IMPORT HMODULE BOOST_RE_STDCALL LoadLibraryA(const char* lpLibFileName); BOOST_RE_IMPORT HMODULE BOOST_RE_STDCALL LoadLibraryW(const wchar_t* lpLibFileName); BOOST_RE_IMPORT int BOOST_RE_STDCALL LoadStringW(HINSTANCE hInstance, unsigned int uID, wchar_t* lpBuffer, int cchBufferMax); BOOST_RE_IMPORT int BOOST_RE_STDCALL LoadStringA(HINSTANCE hInstance, unsigned int uID, char* lpBuffer, int cchBufferMax); BOOST_RE_IMPORT int BOOST_RE_STDCALL GetLocaleInfoW(lcid_type Locale, lctype LCType, wchar_t* lpLCData, int cchData); } #else // // We have windows.h already included: // using dword = DWORD; using word = WORD; using lctype = LCTYPE; static constexpr dword ct_ctype1 = 0x00000001; static constexpr dword c1_upper = 0x0001; // upper case static constexpr dword c1_lower = 0x0002; // lower case static constexpr dword c1_digit = 0x0004; // decimal digits static constexpr dword c1_space = 0x0008; // spacing characters static constexpr dword c1_punct = 0x0010; // punctuation characters static constexpr dword c1_cntrl = 0x0020; // control characters static constexpr dword c1_blank = 0x0040; // blank characters static constexpr dword c1_xdigit = 0x0080; // other digits static constexpr dword c1_alpha = 0x0100; // any linguistic character static constexpr dword c1_defined = 0x0200; // defined character static constexpr unsigned int cp_acp = 0; static constexpr dword lcmap_lowercase = 0x00000100; static constexpr dword lcmap_uppercase = 0x00000200; static constexpr dword lcmap_sortkey = 0x00000400; // WC sort key (normalize) static constexpr lctype locale_idefaultansicodepage = 0x00001004; using ::FreeLibrary; using ::LCMapStringA; using ::LCMapStringW; using ::MultiByteToWideChar; using ::LCMapStringW; using ::WideCharToMultiByte; using ::GetStringTypeExA; using ::GetStringTypeExW; using ::GetUserDefaultLCID; using ::GetStringTypeExA; using ::GetStringTypeExW; using ::LoadLibraryA; using ::LoadLibraryW; using ::LoadStringW; using ::LoadStringA; using ::GetLocaleInfoW; #endif // // class w32_regex_traits_base: // acts as a container for locale and the facets we are using. // template struct w32_regex_traits_base { w32_regex_traits_base(lcid_type l) { imbue(l); } lcid_type imbue(lcid_type l); lcid_type m_locale; }; template inline lcid_type w32_regex_traits_base::imbue(lcid_type l) { lcid_type result(m_locale); m_locale = l; return result; } // // class w32_regex_traits_char_layer: // implements methods that require specialisation for narrow characters: // template class w32_regex_traits_char_layer : public w32_regex_traits_base { typedef std::basic_string string_type; typedef std::map map_type; typedef typename map_type::const_iterator map_iterator_type; public: w32_regex_traits_char_layer(const lcid_type l); regex_constants::syntax_type syntax_type(charT c)const { map_iterator_type i = m_char_map.find(c); return ((i == m_char_map.end()) ? 0 : i->second); } regex_constants::escape_syntax_type escape_syntax_type(charT c) const { map_iterator_type i = m_char_map.find(c); if(i == m_char_map.end()) { if(::boost::BOOST_REGEX_DETAIL_NS::w32_is_lower(c, this->m_locale)) return regex_constants::escape_type_class; if(::boost::BOOST_REGEX_DETAIL_NS::w32_is_upper(c, this->m_locale)) return regex_constants::escape_type_not_class; return 0; } return i->second; } charT tolower(charT c)const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_tolower(c, this->m_locale); } bool isctype(std::uint32_t mask, charT c)const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, mask, c); } private: string_type get_default_message(regex_constants::syntax_type); // TODO: use a hash table when available! map_type m_char_map; }; template w32_regex_traits_char_layer::w32_regex_traits_char_layer(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) : w32_regex_traits_base(l) { // we need to start by initialising our syntax map so we know which // character is used for which purpose: cat_type cat; std::string cat_name(w32_regex_traits::get_catalog_name()); if(cat_name.size()) { cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name); if(!cat) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if(cat) { for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { string_type mss = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i, get_default_message(i)); for(typename string_type::size_type j = 0; j < mss.size(); ++j) { this->m_char_map[mss[j]] = i; } } } else { for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { const char* ptr = get_default_syntax(i); while(ptr && *ptr) { this->m_char_map[static_cast(*ptr)] = i; ++ptr; } } } } template typename w32_regex_traits_char_layer::string_type w32_regex_traits_char_layer::get_default_message(regex_constants::syntax_type i) { const char* ptr = get_default_syntax(i); string_type result; while(ptr && *ptr) { result.append(1, static_cast(*ptr)); ++ptr; } return result; } // // specialised version for narrow characters: // template <> class w32_regex_traits_char_layer : public w32_regex_traits_base { typedef std::string string_type; public: w32_regex_traits_char_layer(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) : w32_regex_traits_base(l) { init(); } regex_constants::syntax_type syntax_type(char c)const { return m_char_map[static_cast(c)]; } regex_constants::escape_syntax_type escape_syntax_type(char c) const { return m_char_map[static_cast(c)]; } char tolower(char c)const { return m_lower_map[static_cast(c)]; } bool isctype(std::uint32_t mask, char c)const { return m_type_map[static_cast(c)] & mask; } private: regex_constants::syntax_type m_char_map[1u << CHAR_BIT]; char m_lower_map[1u << CHAR_BIT]; std::uint16_t m_type_map[1u << CHAR_BIT]; template void init(); }; // // class w32_regex_traits_implementation: // provides pimpl implementation for w32_regex_traits. // template class w32_regex_traits_implementation : public w32_regex_traits_char_layer { public: typedef typename w32_regex_traits::char_class_type char_class_type; static const char_class_type mask_word = 0x0400; // must be C1_DEFINED << 1 static const char_class_type mask_unicode = 0x0800; // must be C1_DEFINED << 2 static const char_class_type mask_horizontal = 0x1000; // must be C1_DEFINED << 3 static const char_class_type mask_vertical = 0x2000; // must be C1_DEFINED << 4 static const char_class_type mask_base = 0x3ff; // all the masks used by the CT_CTYPE1 group typedef std::basic_string string_type; typedef charT char_type; w32_regex_traits_implementation(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l); std::string error_string(regex_constants::error_type n) const { if(!m_error_strings.empty()) { std::map::const_iterator p = m_error_strings.find(n); return (p == m_error_strings.end()) ? std::string(get_default_error_string(n)) : p->second; } return get_default_error_string(n); } char_class_type lookup_classname(const charT* p1, const charT* p2) const { char_class_type result = lookup_classname_imp(p1, p2); if(result == 0) { typedef typename string_type::size_type size_type; string_type temp(p1, p2); for(size_type i = 0; i < temp.size(); ++i) temp[i] = this->tolower(temp[i]); result = lookup_classname_imp(&*temp.begin(), &*temp.begin() + temp.size()); } return result; } string_type lookup_collatename(const charT* p1, const charT* p2) const; string_type transform_primary(const charT* p1, const charT* p2) const; string_type transform(const charT* p1, const charT* p2) const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_transform(this->m_locale, p1, p2); } private: std::map m_error_strings; // error messages indexed by numberic ID std::map m_custom_class_names; // character class names std::map m_custom_collate_names; // collating element names unsigned m_collate_type; // the form of the collation string charT m_collate_delim; // the collation group delimiter // // helpers: // char_class_type lookup_classname_imp(const charT* p1, const charT* p2) const; }; template typename w32_regex_traits_implementation::string_type w32_regex_traits_implementation::transform_primary(const charT* p1, const charT* p2) const { string_type result; // // What we do here depends upon the format of the sort key returned by // sort key returned by this->transform: // switch(m_collate_type) { case sort_C: case sort_unknown: // the best we can do is translate to lower case, then get a regular sort key: { result.assign(p1, p2); typedef typename string_type::size_type size_type; for(size_type i = 0; i < result.size(); ++i) result[i] = this->tolower(result[i]); result = this->transform(&*result.begin(), &*result.begin() + result.size()); break; } case sort_fixed: { // get a regular sort key, and then truncate it: result.assign(this->transform(p1, p2)); result.erase(this->m_collate_delim); break; } case sort_delim: // get a regular sort key, and then truncate everything after the delim: result.assign(this->transform(p1, p2)); std::size_t i; for(i = 0; i < result.size(); ++i) { if(result[i] == m_collate_delim) break; } result.erase(i); break; } if(result.empty()) result = string_type(1, charT(0)); return result; } template typename w32_regex_traits_implementation::string_type w32_regex_traits_implementation::lookup_collatename(const charT* p1, const charT* p2) const { typedef typename std::map::const_iterator iter_type; if(m_custom_collate_names.size()) { iter_type pos = m_custom_collate_names.find(string_type(p1, p2)); if(pos != m_custom_collate_names.end()) return pos->second; } std::string name(p1, p2); name = lookup_default_collate_name(name); if(name.size()) return string_type(name.begin(), name.end()); if(p2 - p1 == 1) return string_type(1, *p1); return string_type(); } template w32_regex_traits_implementation::w32_regex_traits_implementation(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) : w32_regex_traits_char_layer(l) { cat_type cat; std::string cat_name(w32_regex_traits::get_catalog_name()); if(cat_name.size()) { cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name); if(!cat) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if(cat) { // // Error messages: // for(boost::regex_constants::error_type i = static_cast(0); i <= boost::regex_constants::error_unknown; i = static_cast(i + 1)) { const char* p = get_default_error_string(i); string_type default_message; while(*p) { default_message.append(1, static_cast(*p)); ++p; } string_type s = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i+200, default_message); std::string result; for(std::string::size_type j = 0; j < s.size(); ++j) { result.append(1, static_cast(s[j])); } m_error_strings[i] = result; } // // Custom class names: // static const char_class_type masks[14] = { 0x0104u, // C1_ALPHA | C1_DIGIT 0x0100u, // C1_ALPHA 0x0020u, // C1_CNTRL 0x0004u, // C1_DIGIT (~(0x0020u|0x0008u) & 0x01ffu) | 0x0400u, // not C1_CNTRL or C1_SPACE 0x0002u, // C1_LOWER (~0x0020u & 0x01ffu) | 0x0400, // not C1_CNTRL 0x0010u, // C1_PUNCT 0x0008u, // C1_SPACE 0x0001u, // C1_UPPER 0x0080u, // C1_XDIGIT 0x0040u, // C1_BLANK w32_regex_traits_implementation::mask_word, w32_regex_traits_implementation::mask_unicode, }; static const string_type null_string; for(unsigned int j = 0; j <= 13; ++j) { string_type s(::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, j+300, null_string)); if(s.size()) this->m_custom_class_names[s] = masks[j]; } } // // get the collation format used by m_pcollate: // m_collate_type = BOOST_REGEX_DETAIL_NS::find_sort_syntax(this, &m_collate_delim); } template typename w32_regex_traits_implementation::char_class_type w32_regex_traits_implementation::lookup_classname_imp(const charT* p1, const charT* p2) const { static const char_class_type masks[22] = { 0, 0x0104u, // C1_ALPHA | C1_DIGIT 0x0100u, // C1_ALPHA 0x0040u, // C1_BLANK 0x0020u, // C1_CNTRL 0x0004u, // C1_DIGIT 0x0004u, // C1_DIGIT (~(0x0020u|0x0008u|0x0040) & 0x01ffu) | 0x0400u, // not C1_CNTRL or C1_SPACE or C1_BLANK w32_regex_traits_implementation::mask_horizontal, 0x0002u, // C1_LOWER 0x0002u, // C1_LOWER (~0x0020u & 0x01ffu) | 0x0400, // not C1_CNTRL 0x0010u, // C1_PUNCT 0x0008u, // C1_SPACE 0x0008u, // C1_SPACE 0x0001u, // C1_UPPER w32_regex_traits_implementation::mask_unicode, 0x0001u, // C1_UPPER w32_regex_traits_implementation::mask_vertical, 0x0104u | w32_regex_traits_implementation::mask_word, 0x0104u | w32_regex_traits_implementation::mask_word, 0x0080u, // C1_XDIGIT }; if(m_custom_class_names.size()) { typedef typename std::map, char_class_type>::const_iterator map_iter; map_iter pos = m_custom_class_names.find(string_type(p1, p2)); if(pos != m_custom_class_names.end()) return pos->second; } std::size_t state_id = 1u + (std::size_t)BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2); if(state_id < sizeof(masks) / sizeof(masks[0])) return masks[state_id]; return masks[0]; } template std::shared_ptr > create_w32_regex_traits(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) { // TODO: create a cache for previously constructed objects. return boost::object_cache< ::boost::BOOST_REGEX_DETAIL_NS::lcid_type, w32_regex_traits_implementation >::get(l, 5); } } // BOOST_REGEX_DETAIL_NS template class w32_regex_traits { public: typedef charT char_type; typedef std::size_t size_type; typedef std::basic_string string_type; typedef ::boost::BOOST_REGEX_DETAIL_NS::lcid_type locale_type; typedef std::uint_least32_t char_class_type; struct boost_extensions_tag{}; w32_regex_traits() : m_pimpl(BOOST_REGEX_DETAIL_NS::create_w32_regex_traits(::boost::BOOST_REGEX_DETAIL_NS::w32_get_default_locale())) { } static size_type length(const char_type* p) { return std::char_traits::length(p); } regex_constants::syntax_type syntax_type(charT c)const { return m_pimpl->syntax_type(c); } regex_constants::escape_syntax_type escape_syntax_type(charT c) const { return m_pimpl->escape_syntax_type(c); } charT translate(charT c) const { return c; } charT translate_nocase(charT c) const { return this->m_pimpl->tolower(c); } charT translate(charT c, bool icase) const { return icase ? this->m_pimpl->tolower(c) : c; } charT tolower(charT c) const { return this->m_pimpl->tolower(c); } charT toupper(charT c) const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_toupper(c, this->m_pimpl->m_locale); } string_type transform(const charT* p1, const charT* p2) const { return ::boost::BOOST_REGEX_DETAIL_NS::w32_transform(this->m_pimpl->m_locale, p1, p2); } string_type transform_primary(const charT* p1, const charT* p2) const { return m_pimpl->transform_primary(p1, p2); } char_class_type lookup_classname(const charT* p1, const charT* p2) const { return m_pimpl->lookup_classname(p1, p2); } string_type lookup_collatename(const charT* p1, const charT* p2) const { return m_pimpl->lookup_collatename(p1, p2); } bool isctype(charT c, char_class_type f) const { if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_base) && (this->m_pimpl->isctype(f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_base, c))) return true; else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_unicode) && BOOST_REGEX_DETAIL_NS::is_extended(c)) return true; else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_word) && (c == '_')) return true; else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\v'))) return true; else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_horizontal) && this->isctype(c, 0x0008u) && !this->isctype(c, BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation::mask_vertical)) return true; return false; } std::intmax_t toi(const charT*& p1, const charT* p2, int radix)const { return ::boost::BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this); } int value(charT c, int radix)const { int result = (int)::boost::BOOST_REGEX_DETAIL_NS::global_value(c); return result < radix ? result : -1; } locale_type imbue(locale_type l) { ::boost::BOOST_REGEX_DETAIL_NS::lcid_type result(getloc()); m_pimpl = BOOST_REGEX_DETAIL_NS::create_w32_regex_traits(l); return result; } locale_type getloc()const { return m_pimpl->m_locale; } std::string error_string(regex_constants::error_type n) const { return m_pimpl->error_string(n); } // // extension: // set the name of the message catalog in use (defaults to "boost_regex"). // static std::string catalog_name(const std::string& name); static std::string get_catalog_name(); private: std::shared_ptr > m_pimpl; // // catalog name handler: // static std::string& get_catalog_name_inst(); #ifdef BOOST_HAS_THREADS static std::mutex& get_mutex_inst(); #endif }; template std::string w32_regex_traits::catalog_name(const std::string& name) { #ifdef BOOST_HAS_THREADS std::lock_guard lk(get_mutex_inst()); #endif std::string result(get_catalog_name_inst()); get_catalog_name_inst() = name; return result; } template std::string& w32_regex_traits::get_catalog_name_inst() { static std::string s_name; return s_name; } template std::string w32_regex_traits::get_catalog_name() { #ifdef BOOST_HAS_THREADS std::lock_guard lk(get_mutex_inst()); #endif std::string result(get_catalog_name_inst()); return result; } #ifdef BOOST_HAS_THREADS template std::mutex& w32_regex_traits::get_mutex_inst() { static std::mutex s_mutex; return s_mutex; } #endif namespace BOOST_REGEX_DETAIL_NS { #ifdef BOOST_NO_ANSI_APIS inline unsigned int get_code_page_for_locale_id(lcid_type idx) { wchar_t code_page_string[7]; if (boost::BOOST_REGEX_DETAIL_NS::GetLocaleInfoW(idx, locale_idefaultansicodepage, code_page_string, 7) == 0) return 0; return static_cast(_wtol(code_page_string)); } #endif template inline void w32_regex_traits_char_layer::init() { // we need to start by initialising our syntax map so we know which // character is used for which purpose: std::memset(m_char_map, 0, sizeof(m_char_map)); cat_type cat; std::string cat_name(w32_regex_traits::get_catalog_name()); if (cat_name.size()) { cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name); if (!cat) { std::string m("Unable to open message catalog: "); std::runtime_error err(m + cat_name); ::boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err); } } // // if we have a valid catalog then load our messages: // if (cat) { for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { string_type mss = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i, get_default_syntax(i)); for (string_type::size_type j = 0; j < mss.size(); ++j) { m_char_map[static_cast(mss[j])] = i; } } } else { for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i) { const char* ptr = get_default_syntax(i); while (ptr && *ptr) { m_char_map[static_cast(*ptr)] = i; ++ptr; } } } // // finish off by calculating our escape types: // unsigned char i = 'A'; do { if (m_char_map[i] == 0) { if (::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, 0x0002u, (char)i)) m_char_map[i] = regex_constants::escape_type_class; else if (::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, 0x0001u, (char)i)) m_char_map[i] = regex_constants::escape_type_not_class; } } while (0xFF != i++); // // fill in lower case map: // char char_map[1 << CHAR_BIT]; for (int ii = 0; ii < (1 << CHAR_BIT); ++ii) char_map[ii] = static_cast(ii); #ifndef BOOST_NO_ANSI_APIS int r = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA(this->m_locale, lcmap_lowercase, char_map, 1 << CHAR_BIT, this->m_lower_map, 1 << CHAR_BIT); BOOST_REGEX_ASSERT(r != 0); #else unsigned int code_page = get_code_page_for_locale_id(this->m_locale); BOOST_REGEX_ASSERT(code_page != 0); wchar_t wide_char_map[1 << CHAR_BIT]; int conv_r = boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, char_map, 1 << CHAR_BIT, wide_char_map, 1 << CHAR_BIT); BOOST_REGEX_ASSERT(conv_r != 0); wchar_t wide_lower_map[1 << CHAR_BIT]; int r = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(this->m_locale, lcmap_lowercase, wide_char_map, 1 << CHAR_BIT, wide_lower_map, 1 << CHAR_BIT); BOOST_REGEX_ASSERT(r != 0); conv_r = boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(code_page, 0, wide_lower_map, r, this->m_lower_map, 1 << CHAR_BIT, NULL, NULL); BOOST_REGEX_ASSERT(conv_r != 0); #endif if (r < (1 << CHAR_BIT)) { // if we have multibyte characters then not all may have been given // a lower case mapping: for (int jj = r; jj < (1 << CHAR_BIT); ++jj) this->m_lower_map[jj] = static_cast(jj); } #ifndef BOOST_NO_ANSI_APIS r = boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExA(this->m_locale, ct_ctype1, char_map, 1 << CHAR_BIT, this->m_type_map); #else r = boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(this->m_locale, ct_ctype1, wide_char_map, 1 << CHAR_BIT, this->m_type_map); #endif BOOST_REGEX_ASSERT(0 != r); } inline lcid_type w32_get_default_locale() { return boost::BOOST_REGEX_DETAIL_NS::GetUserDefaultLCID(); } inline bool w32_is_lower(char c, lcid_type idx) { #ifndef BOOST_NO_ANSI_APIS word mask; if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExA(idx, ct_ctype1, &c, 1, &mask) && (mask & c1_lower)) return true; return false; #else unsigned int code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return false; wchar_t wide_c; if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return false; word mask; if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &wide_c, 1, &mask) && (mask & c1_lower)) return true; return false; #endif } inline bool w32_is_lower(wchar_t c, lcid_type idx) { word mask; if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &c, 1, &mask) && (mask & c1_lower)) return true; return false; } inline bool w32_is_upper(char c, lcid_type idx) { #ifndef BOOST_NO_ANSI_APIS word mask; if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExA(idx, ct_ctype1, &c, 1, &mask) && (mask & c1_upper)) return true; return false; #else unsigned int code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return false; wchar_t wide_c; if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return false; word mask; if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &wide_c, 1, &mask) && (mask & c1_upper)) return true; return false; #endif } inline bool w32_is_upper(wchar_t c, lcid_type idx) { word mask; if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &c, 1, &mask) && (mask & c1_upper)) return true; return false; } inline void free_module(void* mod) { boost::BOOST_REGEX_DETAIL_NS::FreeLibrary(static_cast(mod)); } inline cat_type w32_cat_open(const std::string& name) { #ifndef BOOST_NO_ANSI_APIS cat_type result(boost::BOOST_REGEX_DETAIL_NS::LoadLibraryA(name.c_str()), &free_module); return result; #else wchar_t* wide_name = (wchar_t*)_alloca((name.size() + 1) * sizeof(wchar_t)); if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(cp_acp, 0, name.c_str(), (int)name.size(), wide_name, (int)(name.size() + 1)) == 0) return cat_type(); cat_type result(boost::BOOST_REGEX_DETAIL_NS::LoadLibraryW(wide_name), &free_module); return result; #endif } inline std::string w32_cat_get(const cat_type& cat, lcid_type, int i, const std::string& def) { #ifndef BOOST_NO_ANSI_APIS char buf[256]; if (0 == boost::BOOST_REGEX_DETAIL_NS::LoadStringA( static_cast(cat.get()), i, buf, 256 )) { return def; } #else wchar_t wbuf[256]; int r = boost::BOOST_REGEX_DETAIL_NS::LoadStringW( static_cast(cat.get()), i, wbuf, 256 ); if (r == 0) return def; int buf_size = 1 + boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(cp_acp, 0, wbuf, r, NULL, 0, NULL, NULL); char* buf = (char*)_alloca(buf_size); if (boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(cp_acp, 0, wbuf, r, buf, buf_size, NULL, NULL) == 0) return def; // failed conversion. #endif return std::string(buf); } #ifndef BOOST_NO_WREGEX inline std::wstring w32_cat_get(const cat_type& cat, lcid_type, int i, const std::wstring& def) { wchar_t buf[256]; if (0 == boost::BOOST_REGEX_DETAIL_NS::LoadStringW(static_cast(cat.get()), i, buf, 256)) { return def; } return std::wstring(buf); } #endif inline std::string w32_transform(lcid_type idx, const char* p1, const char* p2) { #ifndef BOOST_NO_ANSI_APIS int bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA( idx, // locale identifier lcmap_sortkey, // mapping transformation type p1, // source string static_cast(p2 - p1), // number of characters in source string 0, // destination buffer 0 // size of destination buffer ); if (!bytes) return std::string(p1, p2); std::string result(++bytes, '\0'); bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA( idx, // locale identifier lcmap_sortkey, // mapping transformation type p1, // source string static_cast(p2 - p1), // number of characters in source string &*result.begin(), // destination buffer bytes // size of destination buffer ); #else unsigned int code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return std::string(p1, p2); int src_len = static_cast(p2 - p1); wchar_t* wide_p1 = (wchar_t*)_alloca((src_len + 1) * 2); if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, p1, src_len, wide_p1, src_len + 1) == 0) return std::string(p1, p2); int bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW( idx, // locale identifier lcmap_sortkey, // mapping transformation type wide_p1, // source string src_len, // number of characters in source string 0, // destination buffer 0 // size of destination buffer ); if (!bytes) return std::string(p1, p2); std::string result(++bytes, '\0'); bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW( idx, // locale identifier lcmap_sortkey, // mapping transformation type wide_p1, // source string src_len, // number of characters in source string (wchar_t*) & *result.begin(), // destination buffer bytes // size of destination buffer ); #endif if (bytes > static_cast(result.size())) return std::string(p1, p2); while (result.size() && result[result.size() - 1] == '\0') { result.erase(result.size() - 1); } return result; } #ifndef BOOST_NO_WREGEX inline std::wstring w32_transform(lcid_type idx, const wchar_t* p1, const wchar_t* p2) { int bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW( idx, // locale identifier lcmap_sortkey, // mapping transformation type p1, // source string static_cast(p2 - p1), // number of characters in source string 0, // destination buffer 0 // size of destination buffer ); if (!bytes) return std::wstring(p1, p2); std::string result(++bytes, '\0'); bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW( idx, // locale identifier lcmap_sortkey, // mapping transformation type p1, // source string static_cast(p2 - p1), // number of characters in source string reinterpret_cast(&*result.begin()), // destination buffer *of bytes* bytes // size of destination buffer ); if (bytes > static_cast(result.size())) return std::wstring(p1, p2); while (result.size() && result[result.size() - 1] == L'\0') { result.erase(result.size() - 1); } std::wstring r2; for (std::string::size_type i = 0; i < result.size(); ++i) r2.append(1, static_cast(static_cast(result[i]))); return r2; } #endif inline char w32_tolower(char c, lcid_type idx) { char result[2]; #ifndef BOOST_NO_ANSI_APIS int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA( idx, // locale identifier lcmap_lowercase, // mapping transformation type &c, // source string 1, // number of characters in source string result, // destination buffer 1); // size of destination buffer if (b == 0) return c; #else unsigned int code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return c; wchar_t wide_c; if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return c; wchar_t wide_result; int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW( idx, // locale identifier lcmap_lowercase, // mapping transformation type &wide_c, // source string 1, // number of characters in source string &wide_result, // destination buffer 1); // size of destination buffer if (b == 0) return c; if (boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(code_page, 0, &wide_result, 1, result, 2, NULL, NULL) == 0) return c; // No single byte lower case equivalent available #endif return result[0]; } #ifndef BOOST_NO_WREGEX inline wchar_t w32_tolower(wchar_t c, lcid_type idx) { wchar_t result[2]; int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW( idx, // locale identifier lcmap_lowercase, // mapping transformation type &c, // source string 1, // number of characters in source string result, // destination buffer 1); // size of destination buffer if (b == 0) return c; return result[0]; } #endif inline char w32_toupper(char c, lcid_type idx) { char result[2]; #ifndef BOOST_NO_ANSI_APIS int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA( idx, // locale identifier lcmap_uppercase, // mapping transformation type &c, // source string 1, // number of characters in source string result, // destination buffer 1); // size of destination buffer if (b == 0) return c; #else unsigned int code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return c; wchar_t wide_c; if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return c; wchar_t wide_result; int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW( idx, // locale identifier lcmap_uppercase, // mapping transformation type &wide_c, // source string 1, // number of characters in source string &wide_result, // destination buffer 1); // size of destination buffer if (b == 0) return c; if (boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(code_page, 0, &wide_result, 1, result, 2, NULL, NULL) == 0) return c; // No single byte upper case equivalent available. #endif return result[0]; } #ifndef BOOST_NO_WREGEX inline wchar_t w32_toupper(wchar_t c, lcid_type idx) { wchar_t result[2]; int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW( idx, // locale identifier lcmap_uppercase, // mapping transformation type &c, // source string 1, // number of characters in source string result, // destination buffer 1); // size of destination buffer if (b == 0) return c; return result[0]; } #endif inline bool w32_is(lcid_type idx, std::uint32_t m, char c) { word mask; #ifndef BOOST_NO_ANSI_APIS if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExA(idx, ct_ctype1, &c, 1, &mask) && (mask & m & w32_regex_traits_implementation::mask_base)) return true; #else unsigned int code_page = get_code_page_for_locale_id(idx); if (code_page == 0) return false; wchar_t wide_c; if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0) return false; if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &wide_c, 1, &mask) && (mask & m & w32_regex_traits_implementation::mask_base)) return true; #endif if ((m & w32_regex_traits_implementation::mask_word) && (c == '_')) return true; return false; } #ifndef BOOST_NO_WREGEX inline bool w32_is(lcid_type idx, std::uint32_t m, wchar_t c) { word mask; if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &c, 1, &mask) && (mask & m & w32_regex_traits_implementation::mask_base)) return true; if ((m & w32_regex_traits_implementation::mask_word) && (c == '_')) return true; if ((m & w32_regex_traits_implementation::mask_unicode) && (c > 0xff)) return true; return false; } #endif } // BOOST_REGEX_DETAIL_NS } // boost #ifdef BOOST_REGEX_MSVC #pragma warning(pop) #endif #endif // BOOST_REGEX_NO_WIN32_LOCALE #endif ================================================ FILE: lib/third_party/boost/regex/include/boost/regex.h ================================================ /* * * Copyright (c) 1998-2000 * Dr John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org/libs/regex for documentation. * FILE regex.h * VERSION 3.12 * DESCRIPTION: Declares POSIX API functions */ #ifndef BOOST_RE_REGEX_H #define BOOST_RE_REGEX_H #include /* * add using declarations to bring POSIX API functions into * global scope, only if this is C++ (and not C). */ #ifdef __cplusplus using boost::regoff_t; using boost::regex_tA; using boost::regmatch_t; using boost::REG_BASIC; using boost::REG_EXTENDED; using boost::REG_ICASE; using boost::REG_NOSUB; using boost::REG_NEWLINE; using boost::REG_NOSPEC; using boost::REG_PEND; using boost::REG_DUMP; using boost::REG_NOCOLLATE; using boost::REG_ESCAPE_IN_LISTS; using boost::REG_NEWLINE_ALT; using boost::REG_PERL; using boost::REG_AWK; using boost::REG_GREP; using boost::REG_EGREP; using boost::REG_ASSERT; using boost::REG_INVARG; using boost::REG_ATOI; using boost::REG_ITOA; using boost::REG_NOTBOL; using boost::REG_NOTEOL; using boost::REG_STARTEND; using boost::reg_comp_flags; using boost::reg_exec_flags; using boost::regcompA; using boost::regerrorA; using boost::regexecA; using boost::regfreeA; #ifndef BOOST_NO_WREGEX using boost::regcompW; using boost::regerrorW; using boost::regexecW; using boost::regfreeW; using boost::regex_tW; #endif using boost::REG_NOERROR; using boost::REG_NOMATCH; using boost::REG_BADPAT; using boost::REG_ECOLLATE; using boost::REG_ECTYPE; using boost::REG_EESCAPE; using boost::REG_ESUBREG; using boost::REG_EBRACK; using boost::REG_EPAREN; using boost::REG_EBRACE; using boost::REG_BADBR; using boost::REG_ERANGE; using boost::REG_ESPACE; using boost::REG_BADRPT; using boost::REG_EEND; using boost::REG_ESIZE; using boost::REG_ERPAREN; using boost::REG_EMPTY; using boost::REG_E_MEMORY; using boost::REG_E_UNKNOWN; using boost::reg_errcode_t; #endif /* __cplusplus */ #endif /* BOOST_RE_REGEX_H */ ================================================ FILE: lib/third_party/boost/regex/include/boost/regex.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org/libs/regex for documentation. * FILE regex.cpp * VERSION see * DESCRIPTION: Declares boost::basic_regex<> and associated * functions and classes. This header is the main * entry point for the template regex code. */ /* start with C compatibility API */ #ifndef BOOST_RE_REGEX_HPP #define BOOST_RE_REGEX_HPP #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #ifdef BOOST_REGEX_CXX03 #include #else #include #endif #endif // include ================================================ FILE: lib/third_party/boost/regex/include/boost/regex_fwd.hpp ================================================ /* * * Copyright (c) 1998-2002 * John Maddock * * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ /* * LOCATION: see http://www.boost.org/libs/regex for documentation. * FILE regex_fwd.cpp * VERSION see * DESCRIPTION: Forward declares boost::basic_regex<> and * associated typedefs. */ #ifndef BOOST_REGEX_FWD_HPP #define BOOST_REGEX_FWD_HPP #ifndef BOOST_REGEX_CONFIG_HPP #include #endif #ifdef BOOST_REGEX_CXX03 #include #else #include #endif #endif ================================================ FILE: lib/third_party/imgui/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) project(imgui) set(CMAKE_CXX_STANDARD 23) add_library(imgui_all_includes INTERFACE) add_subdirectory(imgui) add_subdirectory(cimgui) add_subdirectory(implot) add_subdirectory(implot3d) add_subdirectory(imnodes) add_subdirectory(backend) add_subdirectory(imgui_test_engine) set(IMGUI_LIBRARIES imgui_imgui imgui_cimgui imgui_implot imgui_implot3d imgui_imnodes imgui_backend) set(IMGUI_LIBRARIES ${IMGUI_LIBRARIES} PARENT_SCOPE) if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD) foreach (LIBRARY IN LISTS IMGUI_LIBRARIES) target_compile_definitions(${LIBRARY} PRIVATE EXPORT_SYMBOLS=1) endforeach () endif() ================================================ FILE: lib/third_party/imgui/backend/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) # https://github.com/ocornut/imgui backends with custom modifications made to the OpenGL 3 and GLFW backends project(imgui_backend) set(CMAKE_CXX_STANDARD 23) if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD) add_library(imgui_backend OBJECT source/imgui_impl_opengl3.cpp source/imgui_impl_glfw.cpp ) target_include_directories(imgui_backend PUBLIC include ) target_link_libraries(imgui_backend PRIVATE imgui_includes) find_package(OpenGL REQUIRED) find_package(Freetype REQUIRED) if (UNIX AND NOT APPLE AND NOT EMSCRIPTEN) find_package(X11) target_include_directories(imgui_backend PUBLIC ${X11_INCLUDE_DIR}) target_link_libraries(imgui_backend PUBLIC ${X11_LIBRARIES}) endif() if (EMSCRIPTEN) target_compile_options(imgui_backend PRIVATE --use-port=contrib.glfw3) endif() find_package(GLFW QUIET) if (NOT GLFW_FOUND OR "${GLFW_LIBRARIES}" STREQUAL "") find_package(glfw3 QUIET) set(GLFW_INCLUDE_DIRS ${glfw3_INCLUDE_DIRS}) set(GLFW_LIBRARIES ${glfw3_LIBRARIES}) if (NOT glfw3_FOUND AND "${GLFW_LIBRARIES}" STREQUAL "") find_package(PkgConfig REQUIRED) pkg_search_module(GLFW REQUIRED glfw3) if ("${GLFW_LIBRARIES}" MATCHES ".+dll") set(GLFW_LIBRARIES "glfw3") endif () else() set(GLFW_LIBRARIES GLFW::GLFW) endif () endif() target_include_directories(imgui_backend PUBLIC ${FREETYPE_INCLUDE_DIRS} ${OpenGL_INCLUDE_DIRS}) target_link_directories(imgui_backend PUBLIC ${FREETYPE_LIBRARY_DIRS} ${OpenGL_LIBRARY_DIRS}) target_link_libraries(imgui_backend PUBLIC ${GLFW_LIBRARIES} ${OPENGL_LIBRARIES}) endif() target_include_directories(imgui_all_includes INTERFACE include) ================================================ FILE: lib/third_party/imgui/backend/include/emscripten_browser_clipboard.h ================================================ #ifndef EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED #define EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED #include #include #define _EM_JS_INLINE(ret, c_name, js_name, params, code) \ extern "C" { \ ret c_name params EM_IMPORT(js_name); \ EMSCRIPTEN_KEEPALIVE \ __attribute__((section("em_js"), aligned(1))) inline char __em_js__##js_name[] = \ #params "<::>" code; \ } #define EM_JS_INLINE(ret, name, params, ...) _EM_JS_INLINE(ret, name, name, params, #__VA_ARGS__) namespace emscripten_browser_clipboard { /////////////////////////////////// Interface ////////////////////////////////// using paste_handler = void(*)(std::string const&, void*); using copy_handler = char const*(*)(void*); inline void paste(paste_handler callback, void *callback_data = nullptr); inline void copy(copy_handler callback, void *callback_data = nullptr); inline void copy(std::string const &content); ///////////////////////////////// Implementation /////////////////////////////// namespace { EM_JS_INLINE(void, paste_js, (paste_handler callback, void *callback_data), { /// Register the given callback to handle paste events. Callback data pointer is passed through to the callback. /// Paste handler callback signature is: /// void my_handler(std::string const &paste_data, void *callback_data = nullptr); document.addEventListener('paste', (event) => { Module["ccall"]('paste_return', 'number', ['string', 'number', 'number'], [event.clipboardData.getData('text/plain'), callback, callback_data]); }); }); EM_JS_INLINE(void, copy_js, (copy_handler callback, void *callback_data), { /// Register the given callback to handle copy events. Callback data pointer is passed through to the callback. /// Copy handler callback signature is: /// char const *my_handler(void *callback_data = nullptr); document.addEventListener('copy', (event) => { const content_ptr = Module["ccall"]('copy_return', 'number', ['number', 'number'], [callback, callback_data]); event.clipboardData.setData('text/plain', UTF8ToString(content_ptr)); event.preventDefault(); }); }); EM_JS_INLINE(void, copy_async_js, (char const *content_ptr), { /// Attempt to copy the provided text asynchronously navigator.clipboard.writeText(UTF8ToString(content_ptr)); }); } inline void paste(paste_handler callback, void *callback_data) { /// C++ wrapper for javascript paste call paste_js(callback, callback_data); } inline void copy(copy_handler callback, void *callback_data) { /// C++ wrapper for javascript copy call copy_js(callback, callback_data); } inline void copy(std::string const &content) { /// C++ wrapper for javascript copy call copy_async_js(content.c_str()); } namespace { extern "C" { EMSCRIPTEN_KEEPALIVE inline int paste_return(char const *paste_data, paste_handler callback, void *callback_data); EMSCRIPTEN_KEEPALIVE inline int paste_return(char const *paste_data, paste_handler callback, void *callback_data) { /// Call paste callback - this function is called from javascript when the paste event occurs callback(paste_data, callback_data); return 1; } EMSCRIPTEN_KEEPALIVE inline char const *copy_return(copy_handler callback, void *callback_data); EMSCRIPTEN_KEEPALIVE inline char const *copy_return(copy_handler callback, void *callback_data) { /// Call paste callback - this function is called from javascript when the paste event occurs return callback(callback_data); } } } } #endif // EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED ================================================ FILE: lib/third_party/imgui/backend/include/imgui_impl_glfw.h ================================================ // dear imgui: Platform Backend for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (Requires: GLFW 3.0+. Prefer GLFW 3.3+/3.4+ for full feature support.) // Implemented features: // [X] Platform: Clipboard support. // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors) with GLFW 3.1+. Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // [X] Multiple Dear ImGui contexts support. // Missing features or Issues: // [ ] Platform: Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. // [ ] Platform: Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. // [ ] Platform: Multi-viewport: Missing ImGuiBackendFlags_HasParentViewport support. The viewport->ParentViewportID field is ignored, and therefore io.ConfigViewportsNoDefaultParent has no effect either. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq // - Getting Started https://dearimgui.com/getting-started // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). // - Introduction, links and more at the top of imgui.cpp #pragma once #include "imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE struct GLFWwindow; struct GLFWmonitor; // Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); // Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL) #ifdef __EMSCRIPTEN__ IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector); //static inline void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) { ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); } } // Renamed in 1.91.0 #endif // GLFW callbacks install // - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any. // - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks. IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); // GLFW callbacks options: // - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user) IMGUI_IMPL_API void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows); // GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks) IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 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); 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); IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); // GLFW helpers IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds); IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window); IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor); #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/backend/include/imgui_impl_opengl3.h ================================================ // dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline // - Desktop GL: 2.x 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 Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // About WebGL/ES: // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. // - This is done automatically on iOS, Android and Emscripten targets. // - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq // - Getting Started https://dearimgui.com/getting-started // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). // - Introduction, links and more at the top of imgui.cpp // About GLSL version: // The 'glsl_version' initialization parameter should be nullptr (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. #pragma once #include "imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE // Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr); IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); // (Optional) Called by Init/NewFrame/Shutdown IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); // (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. IMGUI_IMPL_API void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex); // Configuration flags to add in your imconfig file: //#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten) //#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android) // You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. #if !defined(IMGUI_IMPL_OPENGL_ES2) \ && !defined(IMGUI_IMPL_OPENGL_ES3) // Try to detect GLES on matching platforms #if defined(__APPLE__) #include #endif #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) #define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" #elif defined(__EMSCRIPTEN__) || defined(__amigaos4__) #define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" #else // Otherwise imgui_impl_opengl3_loader.h will be used. #endif #endif #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/backend/include/imgui_impl_opengl3_loader.h ================================================ /* * This file was generated with gl3w_gen.py, part of imgl3w * (hosted at https://github.com/dearimgui/gl3w_stripped) * * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ // We embed our own OpenGL loader to not require user to provide their own or to have to use ours, which proved to be endless problems for users. // Our loader is custom-generated, based on gl3w but automatically filtered to only include enums/functions that we use in this source file. // Regenerate with: python gl3w_gen.py --imgui-dir /path/to/imgui/ // see https://github.com/dearimgui/gl3w_stripped for more info. #ifndef __gl3w_h_ #define __gl3w_h_ #if defined(__APPLE__) #include #elif !defined(__EMSCRIPTEN__) #include #endif #if __has_include() #include #else // Adapted from KHR/khrplatform.h to avoid including entire file. typedef float khronos_float_t; typedef signed char khronos_int8_t; typedef unsigned char khronos_uint8_t; typedef signed short int khronos_int16_t; typedef unsigned short int khronos_uint16_t; #ifdef _WIN64 typedef signed long long int khronos_intptr_t; typedef signed long long int khronos_ssize_t; #else typedef signed long int khronos_intptr_t; typedef signed long int khronos_ssize_t; #endif #if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 khronos_int64_t; typedef unsigned __int64 khronos_uint64_t; #elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100) #include typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #else typedef signed long long khronos_int64_t; typedef unsigned long long khronos_uint64_t; #endif #endif #ifndef __gl_glcorearb_h_ #define __gl_glcorearb_h_ 1 #ifdef __cplusplus extern "C" { #endif /* ** Copyright 2013-2020 The Khronos Group Inc. ** SPDX-License-Identifier: MIT ** ** This header is generated from the Khronos OpenGL / OpenGL ES XML ** API Registry. The current version of the Registry, generator scripts ** used to make the header, and the header can be found at ** https://github.com/KhronosGroup/OpenGL-Registry */ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif #include #endif #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif #ifndef GLAPI #define GLAPI extern #endif /* glcorearb.h is for use with OpenGL core profile implementations. ** It should should be placed in the same directory as gl.h and ** included as . ** ** glcorearb.h includes only APIs in the latest OpenGL core profile ** implementation together with APIs in newer ARB extensions which ** can be supported by the core profile. It does not, and never will ** include functionality removed from the core profile, such as ** fixed-function vertex and fragment processing. ** ** Do not #include both and either of or ** in the same source file. */ /* Generated C header for: * API: gl * Profile: core * Versions considered: .* * Versions emitted: .* * Default extensions included: glcore * Additional extensions included: _nomatch_^ * Extensions removed: _nomatch_^ */ #ifndef GL3W_GL_VERSION_1_0 #define GL3W_GL_VERSION_1_0 1 typedef void GLvoid; typedef unsigned int GLenum; typedef khronos_float_t GLfloat; typedef int GLint; typedef int GLsizei; typedef unsigned int GLbitfield; typedef double GLdouble; typedef unsigned int GLuint; typedef unsigned char GLboolean; typedef khronos_uint8_t GLubyte; #define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_FALSE 0 #define GL_TRUE 1 #define GL_POINTS 0x0000 #define GL_LINES 0x0001 #define GL_LINE_LOOP 0x0002 #define GL_LINE_STRIP 0x0003 #define GL_TRIANGLES 0x0004 #define GL_TRIANGLE_STRIP 0x0005 #define GL_TRIANGLE_FAN 0x0006 #define GL_QUADS 0x0007 #define GL_NEVER 0x0200 #define GL_LESS 0x0201 #define GL_EQUAL 0x0202 #define GL_LEQUAL 0x0203 #define GL_GREATER 0x0204 #define GL_NOTEQUAL 0x0205 #define GL_GEQUAL 0x0206 #define GL_ALWAYS 0x0207 #define GL_ZERO 0 #define GL_ONE 1 #define GL_SRC_COLOR 0x0300 #define GL_ONE_MINUS_SRC_COLOR 0x0301 #define GL_SRC_ALPHA 0x0302 #define GL_ONE_MINUS_SRC_ALPHA 0x0303 #define GL_DST_ALPHA 0x0304 #define GL_ONE_MINUS_DST_ALPHA 0x0305 #define GL_DST_COLOR 0x0306 #define GL_ONE_MINUS_DST_COLOR 0x0307 #define GL_SRC_ALPHA_SATURATE 0x0308 #define GL_NONE 0 #define GL_FRONT_LEFT 0x0400 #define GL_FRONT_RIGHT 0x0401 #define GL_BACK_LEFT 0x0402 #define GL_BACK_RIGHT 0x0403 #define GL_FRONT 0x0404 #define GL_BACK 0x0405 #define GL_LEFT 0x0406 #define GL_RIGHT 0x0407 #define GL_FRONT_AND_BACK 0x0408 #define GL_NO_ERROR 0 #define GL_INVALID_ENUM 0x0500 #define GL_INVALID_VALUE 0x0501 #define GL_INVALID_OPERATION 0x0502 #define GL_OUT_OF_MEMORY 0x0505 #define GL_CW 0x0900 #define GL_CCW 0x0901 #define GL_POINT_SIZE 0x0B11 #define GL_POINT_SIZE_RANGE 0x0B12 #define GL_POINT_SIZE_GRANULARITY 0x0B13 #define GL_LINE_SMOOTH 0x0B20 #define GL_LINE_WIDTH 0x0B21 #define GL_LINE_WIDTH_RANGE 0x0B22 #define GL_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_POLYGON_MODE 0x0B40 #define GL_POLYGON_SMOOTH 0x0B41 #define GL_CULL_FACE 0x0B44 #define GL_CULL_FACE_MODE 0x0B45 #define GL_FRONT_FACE 0x0B46 #define GL_DEPTH_RANGE 0x0B70 #define GL_DEPTH_TEST 0x0B71 #define GL_DEPTH_WRITEMASK 0x0B72 #define GL_DEPTH_CLEAR_VALUE 0x0B73 #define GL_DEPTH_FUNC 0x0B74 #define GL_STENCIL_TEST 0x0B90 #define GL_STENCIL_CLEAR_VALUE 0x0B91 #define GL_STENCIL_FUNC 0x0B92 #define GL_STENCIL_VALUE_MASK 0x0B93 #define GL_STENCIL_FAIL 0x0B94 #define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 #define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 #define GL_STENCIL_REF 0x0B97 #define GL_STENCIL_WRITEMASK 0x0B98 #define GL_VIEWPORT 0x0BA2 #define GL_DITHER 0x0BD0 #define GL_BLEND_DST 0x0BE0 #define GL_BLEND_SRC 0x0BE1 #define GL_BLEND 0x0BE2 #define GL_LOGIC_OP_MODE 0x0BF0 #define GL_DRAW_BUFFER 0x0C01 #define GL_READ_BUFFER 0x0C02 #define GL_SCISSOR_BOX 0x0C10 #define GL_SCISSOR_TEST 0x0C11 #define GL_COLOR_CLEAR_VALUE 0x0C22 #define GL_COLOR_WRITEMASK 0x0C23 #define GL_DOUBLEBUFFER 0x0C32 #define GL_STEREO 0x0C33 #define GL_LINE_SMOOTH_HINT 0x0C52 #define GL_POLYGON_SMOOTH_HINT 0x0C53 #define GL_UNPACK_SWAP_BYTES 0x0CF0 #define GL_UNPACK_LSB_FIRST 0x0CF1 #define GL_UNPACK_ROW_LENGTH 0x0CF2 #define GL_UNPACK_SKIP_ROWS 0x0CF3 #define GL_UNPACK_SKIP_PIXELS 0x0CF4 #define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_PACK_SWAP_BYTES 0x0D00 #define GL_PACK_LSB_FIRST 0x0D01 #define GL_PACK_ROW_LENGTH 0x0D02 #define GL_PACK_SKIP_ROWS 0x0D03 #define GL_PACK_SKIP_PIXELS 0x0D04 #define GL_PACK_ALIGNMENT 0x0D05 #define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_MAX_VIEWPORT_DIMS 0x0D3A #define GL_SUBPIXEL_BITS 0x0D50 #define GL_TEXTURE_1D 0x0DE0 #define GL_TEXTURE_2D 0x0DE1 #define GL_TEXTURE_WIDTH 0x1000 #define GL_TEXTURE_HEIGHT 0x1001 #define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_DONT_CARE 0x1100 #define GL_FASTEST 0x1101 #define GL_NICEST 0x1102 #define GL_BYTE 0x1400 #define GL_UNSIGNED_BYTE 0x1401 #define GL_SHORT 0x1402 #define GL_UNSIGNED_SHORT 0x1403 #define GL_INT 0x1404 #define GL_UNSIGNED_INT 0x1405 #define GL_FLOAT 0x1406 #define GL_STACK_OVERFLOW 0x0503 #define GL_STACK_UNDERFLOW 0x0504 #define GL_CLEAR 0x1500 #define GL_AND 0x1501 #define GL_AND_REVERSE 0x1502 #define GL_COPY 0x1503 #define GL_AND_INVERTED 0x1504 #define GL_NOOP 0x1505 #define GL_XOR 0x1506 #define GL_OR 0x1507 #define GL_NOR 0x1508 #define GL_EQUIV 0x1509 #define GL_INVERT 0x150A #define GL_OR_REVERSE 0x150B #define GL_COPY_INVERTED 0x150C #define GL_OR_INVERTED 0x150D #define GL_NAND 0x150E #define GL_SET 0x150F #define GL_TEXTURE 0x1702 #define GL_COLOR 0x1800 #define GL_DEPTH 0x1801 #define GL_STENCIL 0x1802 #define GL_STENCIL_INDEX 0x1901 #define GL_DEPTH_COMPONENT 0x1902 #define GL_RED 0x1903 #define GL_GREEN 0x1904 #define GL_BLUE 0x1905 #define GL_ALPHA 0x1906 #define GL_RGB 0x1907 #define GL_RGBA 0x1908 #define GL_POINT 0x1B00 #define GL_LINE 0x1B01 #define GL_FILL 0x1B02 #define GL_KEEP 0x1E00 #define GL_REPLACE 0x1E01 #define GL_INCR 0x1E02 #define GL_DECR 0x1E03 #define GL_VENDOR 0x1F00 #define GL_RENDERER 0x1F01 #define GL_VERSION 0x1F02 #define GL_EXTENSIONS 0x1F03 #define GL_NEAREST 0x2600 #define GL_LINEAR 0x2601 #define GL_NEAREST_MIPMAP_NEAREST 0x2700 #define GL_LINEAR_MIPMAP_NEAREST 0x2701 #define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MIN_FILTER 0x2801 #define GL_TEXTURE_WRAP_S 0x2802 #define GL_TEXTURE_WRAP_T 0x2803 #define GL_REPEAT 0x2901 typedef void (APIENTRYP PFNGLCULLFACEPROC) (GLenum mode); typedef void (APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode); typedef void (APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode); typedef void (APIENTRYP PFNGLLINEWIDTHPROC) (GLfloat width); typedef void (APIENTRYP PFNGLPOINTSIZEPROC) (GLfloat size); typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode); typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLDRAWBUFFERPROC) (GLenum buf); typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask); typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); typedef void (APIENTRYP PFNGLCLEARSTENCILPROC) (GLint s); typedef void (APIENTRYP PFNGLCLEARDEPTHPROC) (GLdouble depth); typedef void (APIENTRYP PFNGLSTENCILMASKPROC) (GLuint mask); typedef void (APIENTRYP PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); typedef void (APIENTRYP PFNGLDEPTHMASKPROC) (GLboolean flag); typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); typedef void (APIENTRYP PFNGLFINISHPROC) (void); typedef void (APIENTRYP PFNGLFLUSHPROC) (void); typedef void (APIENTRYP PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor); typedef void (APIENTRYP PFNGLLOGICOPPROC) (GLenum opcode); typedef void (APIENTRYP PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask); typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass); typedef void (APIENTRYP PFNGLDEPTHFUNCPROC) (GLenum func); typedef void (APIENTRYP PFNGLPIXELSTOREFPROC) (GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); typedef void (APIENTRYP PFNGLREADBUFFERPROC) (GLenum src); typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); typedef void (APIENTRYP PFNGLGETBOOLEANVPROC) (GLenum pname, GLboolean *data); typedef void (APIENTRYP PFNGLGETDOUBLEVPROC) (GLenum pname, GLdouble *data); typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); typedef void (APIENTRYP PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *data); typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data); typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, void *pixels); typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC) (GLenum target, GLint level, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC) (GLenum target, GLint level, GLenum pname, GLint *params); typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); typedef void (APIENTRYP PFNGLDEPTHRANGEPROC) (GLdouble n, GLdouble f); typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glCullFace (GLenum mode); GLAPI void APIENTRY glFrontFace (GLenum mode); GLAPI void APIENTRY glHint (GLenum target, GLenum mode); GLAPI void APIENTRY glLineWidth (GLfloat width); GLAPI void APIENTRY glPointSize (GLfloat size); GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode); GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); GLAPI void APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); GLAPI void APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); GLAPI void APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); GLAPI void APIENTRY glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glDrawBuffer (GLenum buf); GLAPI void APIENTRY glClear (GLbitfield mask); GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); GLAPI void APIENTRY glClearStencil (GLint s); GLAPI void APIENTRY glClearDepth (GLdouble depth); GLAPI void APIENTRY glStencilMask (GLuint mask); GLAPI void APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); GLAPI void APIENTRY glDepthMask (GLboolean flag); GLAPI void APIENTRY glDisable (GLenum cap); GLAPI void APIENTRY glEnable (GLenum cap); GLAPI void APIENTRY glFinish (void); GLAPI void APIENTRY glFlush (void); GLAPI void APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); GLAPI void APIENTRY glLogicOp (GLenum opcode); GLAPI void APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); GLAPI void APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); GLAPI void APIENTRY glDepthFunc (GLenum func); GLAPI void APIENTRY glPixelStoref (GLenum pname, GLfloat param); GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param); GLAPI void APIENTRY glReadBuffer (GLenum src); GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); GLAPI void APIENTRY glGetBooleanv (GLenum pname, GLboolean *data); GLAPI void APIENTRY glGetDoublev (GLenum pname, GLdouble *data); GLAPI GLenum APIENTRY glGetError (void); GLAPI void APIENTRY glGetFloatv (GLenum pname, GLfloat *data); GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data); GLAPI const GLubyte *APIENTRY glGetString (GLenum name); GLAPI void APIENTRY glGetTexImage (GLenum target, GLint level, GLenum format, GLenum type, void *pixels); GLAPI void APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); GLAPI void APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); GLAPI void APIENTRY glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat *params); GLAPI void APIENTRY glGetTexLevelParameteriv (GLenum target, GLint level, GLenum pname, GLint *params); GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap); GLAPI void APIENTRY glDepthRange (GLdouble n, GLdouble f); GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); #endif #endif /* GL3W_GL_VERSION_1_0 */ #ifndef GL3W_GL_VERSION_1_1 #define GL3W_GL_VERSION_1_1 1 typedef khronos_float_t GLclampf; typedef double GLclampd; #define GL_COLOR_LOGIC_OP 0x0BF2 #define GL_POLYGON_OFFSET_UNITS 0x2A00 #define GL_POLYGON_OFFSET_POINT 0x2A01 #define GL_POLYGON_OFFSET_LINE 0x2A02 #define GL_POLYGON_OFFSET_FILL 0x8037 #define GL_POLYGON_OFFSET_FACTOR 0x8038 #define GL_TEXTURE_BINDING_1D 0x8068 #define GL_TEXTURE_BINDING_2D 0x8069 #define GL_TEXTURE_INTERNAL_FORMAT 0x1003 #define GL_TEXTURE_RED_SIZE 0x805C #define GL_TEXTURE_GREEN_SIZE 0x805D #define GL_TEXTURE_BLUE_SIZE 0x805E #define GL_TEXTURE_ALPHA_SIZE 0x805F #define GL_DOUBLE 0x140A #define GL_PROXY_TEXTURE_1D 0x8063 #define GL_PROXY_TEXTURE_2D 0x8064 #define GL_R3_G3_B2 0x2A10 #define GL_RGB4 0x804F #define GL_RGB5 0x8050 #define GL_RGB8 0x8051 #define GL_RGB10 0x8052 #define GL_RGB12 0x8053 #define GL_RGB16 0x8054 #define GL_RGBA2 0x8055 #define GL_RGBA4 0x8056 #define GL_RGB5_A1 0x8057 #define GL_RGBA8 0x8058 #define GL_RGB10_A2 0x8059 #define GL_RGBA12 0x805A #define GL_RGBA16 0x805B #define GL_VERTEX_ARRAY 0x8074 typedef void (APIENTRYP PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count); typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices); typedef void (APIENTRYP PFNGLGETPOINTERVPROC) (GLenum pname, void **params); typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat units); typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures); typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC) (GLuint texture); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices); GLAPI void APIENTRY glGetPointerv (GLenum pname, void **params); GLAPI void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); GLAPI void APIENTRY glCopyTexImage1D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); GLAPI void APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); GLAPI void APIENTRY glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); GLAPI void APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI void APIENTRY glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture); GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); GLAPI GLboolean APIENTRY glIsTexture (GLuint texture); #endif #endif /* GL3W_GL_VERSION_1_1 */ #ifndef GL3W_GL_VERSION_1_2 #define GL3W_GL_VERSION_1_2 1 #define GL_UNSIGNED_BYTE_3_3_2 0x8032 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_INT_8_8_8_8 0x8035 #define GL_UNSIGNED_INT_10_10_10_2 0x8036 #define GL_TEXTURE_BINDING_3D 0x806A #define GL_PACK_SKIP_IMAGES 0x806B #define GL_PACK_IMAGE_HEIGHT 0x806C #define GL_UNPACK_SKIP_IMAGES 0x806D #define GL_UNPACK_IMAGE_HEIGHT 0x806E #define GL_TEXTURE_3D 0x806F #define GL_PROXY_TEXTURE_3D 0x8070 #define GL_TEXTURE_DEPTH 0x8071 #define GL_TEXTURE_WRAP_R 0x8072 #define GL_MAX_3D_TEXTURE_SIZE 0x8073 #define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 #define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 #define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #define GL_BGR 0x80E0 #define GL_BGRA 0x80E1 #define GL_MAX_ELEMENTS_VERTICES 0x80E8 #define GL_MAX_ELEMENTS_INDICES 0x80E9 #define GL_CLAMP_TO_EDGE 0x812F #define GL_TEXTURE_MIN_LOD 0x813A #define GL_TEXTURE_MAX_LOD 0x813B #define GL_TEXTURE_BASE_LEVEL 0x813C #define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 #define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 #define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 #define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); #endif #endif /* GL3W_GL_VERSION_1_2 */ #ifndef GL3W_GL_VERSION_1_3 #define GL3W_GL_VERSION_1_3 1 #define GL_TEXTURE0 0x84C0 #define GL_TEXTURE1 0x84C1 #define GL_TEXTURE2 0x84C2 #define GL_TEXTURE3 0x84C3 #define GL_TEXTURE4 0x84C4 #define GL_TEXTURE5 0x84C5 #define GL_TEXTURE6 0x84C6 #define GL_TEXTURE7 0x84C7 #define GL_TEXTURE8 0x84C8 #define GL_TEXTURE9 0x84C9 #define GL_TEXTURE10 0x84CA #define GL_TEXTURE11 0x84CB #define GL_TEXTURE12 0x84CC #define GL_TEXTURE13 0x84CD #define GL_TEXTURE14 0x84CE #define GL_TEXTURE15 0x84CF #define GL_TEXTURE16 0x84D0 #define GL_TEXTURE17 0x84D1 #define GL_TEXTURE18 0x84D2 #define GL_TEXTURE19 0x84D3 #define GL_TEXTURE20 0x84D4 #define GL_TEXTURE21 0x84D5 #define GL_TEXTURE22 0x84D6 #define GL_TEXTURE23 0x84D7 #define GL_TEXTURE24 0x84D8 #define GL_TEXTURE25 0x84D9 #define GL_TEXTURE26 0x84DA #define GL_TEXTURE27 0x84DB #define GL_TEXTURE28 0x84DC #define GL_TEXTURE29 0x84DD #define GL_TEXTURE30 0x84DE #define GL_TEXTURE31 0x84DF #define GL_ACTIVE_TEXTURE 0x84E0 #define GL_MULTISAMPLE 0x809D #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E #define GL_SAMPLE_ALPHA_TO_ONE 0x809F #define GL_SAMPLE_COVERAGE 0x80A0 #define GL_SAMPLE_BUFFERS 0x80A8 #define GL_SAMPLES 0x80A9 #define GL_SAMPLE_COVERAGE_VALUE 0x80AA #define GL_SAMPLE_COVERAGE_INVERT 0x80AB #define GL_TEXTURE_CUBE_MAP 0x8513 #define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A #define GL_PROXY_TEXTURE_CUBE_MAP 0x851B #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C #define GL_COMPRESSED_RGB 0x84ED #define GL_COMPRESSED_RGBA 0x84EE #define GL_TEXTURE_COMPRESSION_HINT 0x84EF #define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 #define GL_TEXTURE_COMPRESSED 0x86A1 #define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 #define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 #define GL_CLAMP_TO_BORDER 0x812D typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glActiveTexture (GLenum texture); GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); #endif #endif /* GL3W_GL_VERSION_1_3 */ #ifndef GL3W_GL_VERSION_1_4 #define GL3W_GL_VERSION_1_4 1 #define GL_BLEND_DST_RGB 0x80C8 #define GL_BLEND_SRC_RGB 0x80C9 #define GL_BLEND_DST_ALPHA 0x80CA #define GL_BLEND_SRC_ALPHA 0x80CB #define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_DEPTH_COMPONENT24 0x81A6 #define GL_DEPTH_COMPONENT32 0x81A7 #define GL_MIRRORED_REPEAT 0x8370 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD #define GL_TEXTURE_LOD_BIAS 0x8501 #define GL_INCR_WRAP 0x8507 #define GL_DECR_WRAP 0x8508 #define GL_TEXTURE_DEPTH_SIZE 0x884A #define GL_TEXTURE_COMPARE_MODE 0x884C #define GL_TEXTURE_COMPARE_FUNC 0x884D #define GL_BLEND_COLOR 0x8005 #define GL_BLEND_EQUATION 0x8009 #define GL_CONSTANT_COLOR 0x8001 #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 #define GL_CONSTANT_ALPHA 0x8003 #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 #define GL_FUNC_ADD 0x8006 #define GL_FUNC_REVERSE_SUBTRACT 0x800B #define GL_FUNC_SUBTRACT 0x800A #define GL_MIN 0x8007 #define GL_MAX 0x8008 typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); GLAPI void APIENTRY glBlendEquation (GLenum mode); #endif #endif /* GL3W_GL_VERSION_1_4 */ #ifndef GL3W_GL_VERSION_1_5 #define GL3W_GL_VERSION_1_5 1 typedef khronos_ssize_t GLsizeiptr; typedef khronos_intptr_t GLintptr; #define GL_BUFFER_SIZE 0x8764 #define GL_BUFFER_USAGE 0x8765 #define GL_QUERY_COUNTER_BITS 0x8864 #define GL_CURRENT_QUERY 0x8865 #define GL_QUERY_RESULT 0x8866 #define GL_QUERY_RESULT_AVAILABLE 0x8867 #define GL_ARRAY_BUFFER 0x8892 #define GL_ELEMENT_ARRAY_BUFFER 0x8893 #define GL_ARRAY_BUFFER_BINDING 0x8894 #define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F #define GL_READ_ONLY 0x88B8 #define GL_WRITE_ONLY 0x88B9 #define GL_READ_WRITE 0x88BA #define GL_BUFFER_ACCESS 0x88BB #define GL_BUFFER_MAPPED 0x88BC #define GL_BUFFER_MAP_POINTER 0x88BD #define GL_STREAM_DRAW 0x88E0 #define GL_STREAM_READ 0x88E1 #define GL_STREAM_COPY 0x88E2 #define GL_STATIC_DRAW 0x88E4 #define GL_STATIC_READ 0x88E5 #define GL_STATIC_COPY 0x88E6 #define GL_DYNAMIC_DRAW 0x88E8 #define GL_DYNAMIC_READ 0x88E9 #define GL_DYNAMIC_COPY 0x88EA #define GL_SAMPLES_PASSED 0x8914 #define GL_SRC1_ALPHA 0x8589 typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); GLAPI GLboolean APIENTRY glIsQuery (GLuint id); GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); GLAPI void APIENTRY glEndQuery (GLenum target); GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); #endif #endif /* GL3W_GL_VERSION_1_5 */ #ifndef GL3W_GL_VERSION_2_0 #define GL3W_GL_VERSION_2_0 1 typedef char GLchar; typedef khronos_int16_t GLshort; typedef khronos_int8_t GLbyte; typedef khronos_uint16_t GLushort; #define GL_BLEND_EQUATION_RGB 0x8009 #define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 #define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 #define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 #define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 #define GL_CURRENT_VERTEX_ATTRIB 0x8626 #define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 #define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 #define GL_STENCIL_BACK_FUNC 0x8800 #define GL_STENCIL_BACK_FAIL 0x8801 #define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 #define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 #define GL_MAX_DRAW_BUFFERS 0x8824 #define GL_DRAW_BUFFER0 0x8825 #define GL_DRAW_BUFFER1 0x8826 #define GL_DRAW_BUFFER2 0x8827 #define GL_DRAW_BUFFER3 0x8828 #define GL_DRAW_BUFFER4 0x8829 #define GL_DRAW_BUFFER5 0x882A #define GL_DRAW_BUFFER6 0x882B #define GL_DRAW_BUFFER7 0x882C #define GL_DRAW_BUFFER8 0x882D #define GL_DRAW_BUFFER9 0x882E #define GL_DRAW_BUFFER10 0x882F #define GL_DRAW_BUFFER11 0x8830 #define GL_DRAW_BUFFER12 0x8831 #define GL_DRAW_BUFFER13 0x8832 #define GL_DRAW_BUFFER14 0x8833 #define GL_DRAW_BUFFER15 0x8834 #define GL_BLEND_EQUATION_ALPHA 0x883D #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 #define GL_FRAGMENT_SHADER 0x8B30 #define GL_VERTEX_SHADER 0x8B31 #define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A #define GL_MAX_VARYING_FLOATS 0x8B4B #define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D #define GL_SHADER_TYPE 0x8B4F #define GL_FLOAT_VEC2 0x8B50 #define GL_FLOAT_VEC3 0x8B51 #define GL_FLOAT_VEC4 0x8B52 #define GL_INT_VEC2 0x8B53 #define GL_INT_VEC3 0x8B54 #define GL_INT_VEC4 0x8B55 #define GL_BOOL 0x8B56 #define GL_BOOL_VEC2 0x8B57 #define GL_BOOL_VEC3 0x8B58 #define GL_BOOL_VEC4 0x8B59 #define GL_FLOAT_MAT2 0x8B5A #define GL_FLOAT_MAT3 0x8B5B #define GL_FLOAT_MAT4 0x8B5C #define GL_SAMPLER_1D 0x8B5D #define GL_SAMPLER_2D 0x8B5E #define GL_SAMPLER_3D 0x8B5F #define GL_SAMPLER_CUBE 0x8B60 #define GL_SAMPLER_1D_SHADOW 0x8B61 #define GL_SAMPLER_2D_SHADOW 0x8B62 #define GL_DELETE_STATUS 0x8B80 #define GL_COMPILE_STATUS 0x8B81 #define GL_LINK_STATUS 0x8B82 #define GL_VALIDATE_STATUS 0x8B83 #define GL_INFO_LOG_LENGTH 0x8B84 #define GL_ATTACHED_SHADERS 0x8B85 #define GL_ACTIVE_UNIFORMS 0x8B86 #define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 #define GL_SHADER_SOURCE_LENGTH 0x8B88 #define GL_ACTIVE_ATTRIBUTES 0x8B89 #define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B #define GL_SHADING_LANGUAGE_VERSION 0x8B8C #define GL_CURRENT_PROGRAM 0x8B8D #define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 #define GL_LOWER_LEFT 0x8CA1 #define GL_UPPER_LEFT 0x8CA2 #define GL_STENCIL_BACK_REF 0x8CA3 #define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 #define GL_STENCIL_BACK_WRITEMASK 0x8CA5 typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); GLAPI void APIENTRY glCompileShader (GLuint shader); GLAPI GLuint APIENTRY glCreateProgram (void); GLAPI GLuint APIENTRY glCreateShader (GLenum type); GLAPI void APIENTRY glDeleteProgram (GLuint program); GLAPI void APIENTRY glDeleteShader (GLuint shader); GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); GLAPI GLboolean APIENTRY glIsProgram (GLuint program); GLAPI GLboolean APIENTRY glIsShader (GLuint shader); GLAPI void APIENTRY glLinkProgram (GLuint program); GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); GLAPI void APIENTRY glUseProgram (GLuint program); GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glValidateProgram (GLuint program); GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); #endif #endif /* GL3W_GL_VERSION_2_0 */ #ifndef GL3W_GL_VERSION_2_1 #define GL3W_GL_VERSION_2_1 1 #define GL_PIXEL_PACK_BUFFER 0x88EB #define GL_PIXEL_UNPACK_BUFFER 0x88EC #define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED #define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF #define GL_FLOAT_MAT2x3 0x8B65 #define GL_FLOAT_MAT2x4 0x8B66 #define GL_FLOAT_MAT3x2 0x8B67 #define GL_FLOAT_MAT3x4 0x8B68 #define GL_FLOAT_MAT4x2 0x8B69 #define GL_FLOAT_MAT4x3 0x8B6A #define GL_SRGB 0x8C40 #define GL_SRGB8 0x8C41 #define GL_SRGB_ALPHA 0x8C42 #define GL_SRGB8_ALPHA8 0x8C43 #define GL_COMPRESSED_SRGB 0x8C48 #define GL_COMPRESSED_SRGB_ALPHA 0x8C49 typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); #endif #endif /* GL3W_GL_VERSION_2_1 */ #ifndef GL3W_GL_VERSION_3_0 #define GL3W_GL_VERSION_3_0 1 typedef khronos_uint16_t GLhalf; #define GL_COMPARE_REF_TO_TEXTURE 0x884E #define GL_CLIP_DISTANCE0 0x3000 #define GL_CLIP_DISTANCE1 0x3001 #define GL_CLIP_DISTANCE2 0x3002 #define GL_CLIP_DISTANCE3 0x3003 #define GL_CLIP_DISTANCE4 0x3004 #define GL_CLIP_DISTANCE5 0x3005 #define GL_CLIP_DISTANCE6 0x3006 #define GL_CLIP_DISTANCE7 0x3007 #define GL_MAX_CLIP_DISTANCES 0x0D32 #define GL_MAJOR_VERSION 0x821B #define GL_MINOR_VERSION 0x821C #define GL_NUM_EXTENSIONS 0x821D #define GL_CONTEXT_FLAGS 0x821E #define GL_COMPRESSED_RED 0x8225 #define GL_COMPRESSED_RG 0x8226 #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 #define GL_RGBA32F 0x8814 #define GL_RGB32F 0x8815 #define GL_RGBA16F 0x881A #define GL_RGB16F 0x881B #define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF #define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 #define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 #define GL_CLAMP_READ_COLOR 0x891C #define GL_FIXED_ONLY 0x891D #define GL_MAX_VARYING_COMPONENTS 0x8B4B #define GL_TEXTURE_1D_ARRAY 0x8C18 #define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 #define GL_TEXTURE_2D_ARRAY 0x8C1A #define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B #define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C #define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D #define GL_R11F_G11F_B10F 0x8C3A #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B #define GL_RGB9_E5 0x8C3D #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E #define GL_TEXTURE_SHARED_SIZE 0x8C3F #define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 #define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 #define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 #define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 #define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 #define GL_PRIMITIVES_GENERATED 0x8C87 #define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 #define GL_RASTERIZER_DISCARD 0x8C89 #define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B #define GL_INTERLEAVED_ATTRIBS 0x8C8C #define GL_SEPARATE_ATTRIBS 0x8C8D #define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E #define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F #define GL_RGBA32UI 0x8D70 #define GL_RGB32UI 0x8D71 #define GL_RGBA16UI 0x8D76 #define GL_RGB16UI 0x8D77 #define GL_RGBA8UI 0x8D7C #define GL_RGB8UI 0x8D7D #define GL_RGBA32I 0x8D82 #define GL_RGB32I 0x8D83 #define GL_RGBA16I 0x8D88 #define GL_RGB16I 0x8D89 #define GL_RGBA8I 0x8D8E #define GL_RGB8I 0x8D8F #define GL_RED_INTEGER 0x8D94 #define GL_GREEN_INTEGER 0x8D95 #define GL_BLUE_INTEGER 0x8D96 #define GL_RGB_INTEGER 0x8D98 #define GL_RGBA_INTEGER 0x8D99 #define GL_BGR_INTEGER 0x8D9A #define GL_BGRA_INTEGER 0x8D9B #define GL_SAMPLER_1D_ARRAY 0x8DC0 #define GL_SAMPLER_2D_ARRAY 0x8DC1 #define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 #define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 #define GL_SAMPLER_CUBE_SHADOW 0x8DC5 #define GL_UNSIGNED_INT_VEC2 0x8DC6 #define GL_UNSIGNED_INT_VEC3 0x8DC7 #define GL_UNSIGNED_INT_VEC4 0x8DC8 #define GL_INT_SAMPLER_1D 0x8DC9 #define GL_INT_SAMPLER_2D 0x8DCA #define GL_INT_SAMPLER_3D 0x8DCB #define GL_INT_SAMPLER_CUBE 0x8DCC #define GL_INT_SAMPLER_1D_ARRAY 0x8DCE #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF #define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 #define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 #define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 #define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 #define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 #define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 #define GL_QUERY_WAIT 0x8E13 #define GL_QUERY_NO_WAIT 0x8E14 #define GL_QUERY_BY_REGION_WAIT 0x8E15 #define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 #define GL_BUFFER_ACCESS_FLAGS 0x911F #define GL_BUFFER_MAP_LENGTH 0x9120 #define GL_BUFFER_MAP_OFFSET 0x9121 #define GL_DEPTH_COMPONENT32F 0x8CAC #define GL_DEPTH32F_STENCIL8 0x8CAD #define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 #define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 #define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 #define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 #define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 #define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 #define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 #define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 #define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 #define GL_FRAMEBUFFER_DEFAULT 0x8218 #define GL_FRAMEBUFFER_UNDEFINED 0x8219 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A #define GL_MAX_RENDERBUFFER_SIZE 0x84E8 #define GL_DEPTH_STENCIL 0x84F9 #define GL_UNSIGNED_INT_24_8 0x84FA #define GL_DEPTH24_STENCIL8 0x88F0 #define GL_TEXTURE_STENCIL_SIZE 0x88F1 #define GL_TEXTURE_RED_TYPE 0x8C10 #define GL_TEXTURE_GREEN_TYPE 0x8C11 #define GL_TEXTURE_BLUE_TYPE 0x8C12 #define GL_TEXTURE_ALPHA_TYPE 0x8C13 #define GL_TEXTURE_DEPTH_TYPE 0x8C16 #define GL_UNSIGNED_NORMALIZED 0x8C17 #define GL_FRAMEBUFFER_BINDING 0x8CA6 #ifndef __APPLE__ #define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 #endif #define GL_RENDERBUFFER_BINDING 0x8CA7 #define GL_READ_FRAMEBUFFER 0x8CA8 #define GL_DRAW_FRAMEBUFFER 0x8CA9 #define GL_READ_FRAMEBUFFER_BINDING 0x8CAA #define GL_RENDERBUFFER_SAMPLES 0x8CAB #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD #define GL_MAX_COLOR_ATTACHMENTS 0x8CDF #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_COLOR_ATTACHMENT1 0x8CE1 #define GL_COLOR_ATTACHMENT2 0x8CE2 #define GL_COLOR_ATTACHMENT3 0x8CE3 #define GL_COLOR_ATTACHMENT4 0x8CE4 #define GL_COLOR_ATTACHMENT5 0x8CE5 #define GL_COLOR_ATTACHMENT6 0x8CE6 #define GL_COLOR_ATTACHMENT7 0x8CE7 #define GL_COLOR_ATTACHMENT8 0x8CE8 #define GL_COLOR_ATTACHMENT9 0x8CE9 #define GL_COLOR_ATTACHMENT10 0x8CEA #define GL_COLOR_ATTACHMENT11 0x8CEB #define GL_COLOR_ATTACHMENT12 0x8CEC #define GL_COLOR_ATTACHMENT13 0x8CED #define GL_COLOR_ATTACHMENT14 0x8CEE #define GL_COLOR_ATTACHMENT15 0x8CEF #define GL_COLOR_ATTACHMENT16 0x8CF0 #define GL_COLOR_ATTACHMENT17 0x8CF1 #define GL_COLOR_ATTACHMENT18 0x8CF2 #define GL_COLOR_ATTACHMENT19 0x8CF3 #define GL_COLOR_ATTACHMENT20 0x8CF4 #define GL_COLOR_ATTACHMENT21 0x8CF5 #define GL_COLOR_ATTACHMENT22 0x8CF6 #define GL_COLOR_ATTACHMENT23 0x8CF7 #define GL_COLOR_ATTACHMENT24 0x8CF8 #define GL_COLOR_ATTACHMENT25 0x8CF9 #define GL_COLOR_ATTACHMENT26 0x8CFA #define GL_COLOR_ATTACHMENT27 0x8CFB #define GL_COLOR_ATTACHMENT28 0x8CFC #define GL_COLOR_ATTACHMENT29 0x8CFD #define GL_COLOR_ATTACHMENT30 0x8CFE #define GL_COLOR_ATTACHMENT31 0x8CFF #define GL_DEPTH_ATTACHMENT 0x8D00 #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_FRAMEBUFFER 0x8D40 #define GL_RENDERBUFFER 0x8D41 #define GL_RENDERBUFFER_WIDTH 0x8D42 #define GL_RENDERBUFFER_HEIGHT 0x8D43 #define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 #define GL_STENCIL_INDEX1 0x8D46 #define GL_STENCIL_INDEX4 0x8D47 #define GL_STENCIL_INDEX8 0x8D48 #define GL_STENCIL_INDEX16 0x8D49 #define GL_RENDERBUFFER_RED_SIZE 0x8D50 #define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 #define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 #define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 #define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 #define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #define GL_MAX_SAMPLES 0x8D57 #define GL_FRAMEBUFFER_SRGB 0x8DB9 #define GL_HALF_FLOAT 0x140B #define GL_MAP_READ_BIT 0x0001 #define GL_MAP_WRITE_BIT 0x0002 #define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 #define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 #define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 #define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 #define GL_COMPRESSED_RED_RGTC1 0x8DBB #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC #define GL_COMPRESSED_RG_RGTC2 0x8DBD #define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE #define GL_RG 0x8227 #define GL_RG_INTEGER 0x8228 #define GL_R8 0x8229 #define GL_R16 0x822A #define GL_RG8 0x822B #define GL_RG16 0x822C #define GL_R16F 0x822D #define GL_R32F 0x822E #define GL_RG16F 0x822F #define GL_RG32F 0x8230 #define GL_R8I 0x8231 #define GL_R8UI 0x8232 #define GL_R16I 0x8233 #define GL_R16UI 0x8234 #define GL_R32I 0x8235 #define GL_R32UI 0x8236 #define GL_RG8I 0x8237 #define GL_RG8UI 0x8238 #define GL_RG16I 0x8239 #define GL_RG16UI 0x823A #define GL_RG32I 0x823B #define GL_RG32UI 0x823C #define GL_VERTEX_ARRAY_BINDING 0x85B5 typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); GLAPI void APIENTRY glEndTransformFeedback (void); GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); GLAPI void APIENTRY glEndConditionalRender (void); GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); GLAPI void APIENTRY glGenerateMipmap (GLenum target); GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); GLAPI void APIENTRY glBindVertexArray (GLuint array); GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); #endif #endif /* GL3W_GL_VERSION_3_0 */ #ifndef GL3W_GL_VERSION_3_1 #define GL3W_GL_VERSION_3_1 1 #define GL_SAMPLER_2D_RECT 0x8B63 #define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 #define GL_SAMPLER_BUFFER 0x8DC2 #define GL_INT_SAMPLER_2D_RECT 0x8DCD #define GL_INT_SAMPLER_BUFFER 0x8DD0 #define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 #define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 #define GL_TEXTURE_BUFFER 0x8C2A #define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B #define GL_TEXTURE_BINDING_BUFFER 0x8C2C #define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D #define GL_TEXTURE_RECTANGLE 0x84F5 #define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 #define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 #define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 #define GL_R8_SNORM 0x8F94 #define GL_RG8_SNORM 0x8F95 #define GL_RGB8_SNORM 0x8F96 #define GL_RGBA8_SNORM 0x8F97 #define GL_R16_SNORM 0x8F98 #define GL_RG16_SNORM 0x8F99 #define GL_RGB16_SNORM 0x8F9A #define GL_RGBA16_SNORM 0x8F9B #define GL_SIGNED_NORMALIZED 0x8F9C #define GL_PRIMITIVE_RESTART 0x8F9D #define GL_PRIMITIVE_RESTART_INDEX 0x8F9E #define GL_COPY_READ_BUFFER 0x8F36 #define GL_COPY_WRITE_BUFFER 0x8F37 #define GL_UNIFORM_BUFFER 0x8A11 #define GL_UNIFORM_BUFFER_BINDING 0x8A28 #define GL_UNIFORM_BUFFER_START 0x8A29 #define GL_UNIFORM_BUFFER_SIZE 0x8A2A #define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B #define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C #define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D #define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E #define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F #define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 #define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 #define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 #define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 #define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 #define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 #define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 #define GL_UNIFORM_TYPE 0x8A37 #define GL_UNIFORM_SIZE 0x8A38 #define GL_UNIFORM_NAME_LENGTH 0x8A39 #define GL_UNIFORM_BLOCK_INDEX 0x8A3A #define GL_UNIFORM_OFFSET 0x8A3B #define GL_UNIFORM_ARRAY_STRIDE 0x8A3C #define GL_UNIFORM_MATRIX_STRIDE 0x8A3D #define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E #define GL_UNIFORM_BLOCK_BINDING 0x8A3F #define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 #define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 #define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 #define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 #define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 #define GL_INVALID_INDEX 0xFFFFFFFFu typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); #endif #endif /* GL3W_GL_VERSION_3_1 */ #ifndef GL3W_GL_VERSION_3_2 #define GL3W_GL_VERSION_3_2 1 typedef struct __GLsync *GLsync; typedef khronos_uint64_t GLuint64; typedef khronos_int64_t GLint64; #define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 #define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 #define GL_LINES_ADJACENCY 0x000A #define GL_LINE_STRIP_ADJACENCY 0x000B #define GL_TRIANGLES_ADJACENCY 0x000C #define GL_TRIANGLE_STRIP_ADJACENCY 0x000D #define GL_PROGRAM_POINT_SIZE 0x8642 #define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 #define GL_GEOMETRY_SHADER 0x8DD9 #define GL_GEOMETRY_VERTICES_OUT 0x8916 #define GL_GEOMETRY_INPUT_TYPE 0x8917 #define GL_GEOMETRY_OUTPUT_TYPE 0x8918 #define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF #define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 #define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 #define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 #define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 #define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 #define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 #define GL_CONTEXT_PROFILE_MASK 0x9126 #define GL_DEPTH_CLAMP 0x864F #define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C #define GL_FIRST_VERTEX_CONVENTION 0x8E4D #define GL_LAST_VERTEX_CONVENTION 0x8E4E #define GL_PROVOKING_VERTEX 0x8E4F #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F #define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 #define GL_OBJECT_TYPE 0x9112 #define GL_SYNC_CONDITION 0x9113 #define GL_SYNC_STATUS 0x9114 #define GL_SYNC_FLAGS 0x9115 #define GL_SYNC_FENCE 0x9116 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 #define GL_UNSIGNALED 0x9118 #define GL_SIGNALED 0x9119 #define GL_ALREADY_SIGNALED 0x911A #define GL_TIMEOUT_EXPIRED 0x911B #define GL_CONDITION_SATISFIED 0x911C #define GL_WAIT_FAILED 0x911D #define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull #define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 #define GL_SAMPLE_POSITION 0x8E50 #define GL_SAMPLE_MASK 0x8E51 #define GL_SAMPLE_MASK_VALUE 0x8E52 #define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 #define GL_TEXTURE_2D_MULTISAMPLE 0x9100 #define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 #define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 #define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 #define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 #define GL_TEXTURE_SAMPLES 0x9106 #define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 #define GL_SAMPLER_2D_MULTISAMPLE 0x9108 #define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 #define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A #define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B #define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C #define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D #define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E #define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F #define GL_MAX_INTEGER_SAMPLES 0x9110 typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); GLAPI void APIENTRY glProvokingVertex (GLenum mode); GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); GLAPI GLboolean APIENTRY glIsSync (GLsync sync); GLAPI void APIENTRY glDeleteSync (GLsync sync); GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); #endif #endif /* GL3W_GL_VERSION_3_2 */ #ifndef GL3W_GL_VERSION_3_3 #define GL3W_GL_VERSION_3_3 1 #define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE #define GL_SRC1_COLOR 0x88F9 #define GL_ONE_MINUS_SRC1_COLOR 0x88FA #define GL_ONE_MINUS_SRC1_ALPHA 0x88FB #define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC #define GL_ANY_SAMPLES_PASSED 0x8C2F #define GL_SAMPLER_BINDING 0x8919 #define GL_RGB10_A2UI 0x906F #define GL_TEXTURE_SWIZZLE_R 0x8E42 #define GL_TEXTURE_SWIZZLE_G 0x8E43 #define GL_TEXTURE_SWIZZLE_B 0x8E44 #define GL_TEXTURE_SWIZZLE_A 0x8E45 #define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 #define GL_TIME_ELAPSED 0x88BF #define GL_TIMESTAMP 0x8E28 #define GL_INT_2_10_10_10_REV 0x8D9F typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); #endif #endif /* GL3W_GL_VERSION_3_3 */ #ifndef GL3W_GL_VERSION_4_0 #define GL3W_GL_VERSION_4_0 1 #define GL_SAMPLE_SHADING 0x8C36 #define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 #define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E #define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F #define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 #define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A #define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B #define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C #define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D #define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E #define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F #define GL_DRAW_INDIRECT_BUFFER 0x8F3F #define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 #define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F #define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A #define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B #define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C #define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D #define GL_MAX_VERTEX_STREAMS 0x8E71 #define GL_DOUBLE_VEC2 0x8FFC #define GL_DOUBLE_VEC3 0x8FFD #define GL_DOUBLE_VEC4 0x8FFE #define GL_DOUBLE_MAT2 0x8F46 #define GL_DOUBLE_MAT3 0x8F47 #define GL_DOUBLE_MAT4 0x8F48 #define GL_DOUBLE_MAT2x3 0x8F49 #define GL_DOUBLE_MAT2x4 0x8F4A #define GL_DOUBLE_MAT3x2 0x8F4B #define GL_DOUBLE_MAT3x4 0x8F4C #define GL_DOUBLE_MAT4x2 0x8F4D #define GL_DOUBLE_MAT4x3 0x8F4E #define GL_ACTIVE_SUBROUTINES 0x8DE5 #define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 #define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 #define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 #define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 #define GL_MAX_SUBROUTINES 0x8DE7 #define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 #define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A #define GL_COMPATIBLE_SUBROUTINES 0x8E4B #define GL_PATCHES 0x000E #define GL_PATCH_VERTICES 0x8E72 #define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 #define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 #define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 #define GL_TESS_GEN_MODE 0x8E76 #define GL_TESS_GEN_SPACING 0x8E77 #define GL_TESS_GEN_VERTEX_ORDER 0x8E78 #define GL_TESS_GEN_POINT_MODE 0x8E79 #define GL_ISOLINES 0x8E7A #define GL_FRACTIONAL_ODD 0x8E7B #define GL_FRACTIONAL_EVEN 0x8E7C #define GL_MAX_PATCH_VERTICES 0x8E7D #define GL_MAX_TESS_GEN_LEVEL 0x8E7E #define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F #define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 #define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 #define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 #define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 #define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 #define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 #define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 #define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 #define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A #define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C #define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D #define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E #define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F #define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 #define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 #define GL_TESS_EVALUATION_SHADER 0x8E87 #define GL_TESS_CONTROL_SHADER 0x8E88 #define GL_TRANSFORM_FEEDBACK 0x8E22 #define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 #define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 #define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 #define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glMinSampleShading (GLfloat value); GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); GLAPI void APIENTRY glPauseTransformFeedback (void); GLAPI void APIENTRY glResumeTransformFeedback (void); GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); #endif #endif /* GL3W_GL_VERSION_4_0 */ #ifndef GL3W_GL_VERSION_4_1 #define GL3W_GL_VERSION_4_1 1 #define GL_FIXED 0x140C #define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A #define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B #define GL_LOW_FLOAT 0x8DF0 #define GL_MEDIUM_FLOAT 0x8DF1 #define GL_HIGH_FLOAT 0x8DF2 #define GL_LOW_INT 0x8DF3 #define GL_MEDIUM_INT 0x8DF4 #define GL_HIGH_INT 0x8DF5 #define GL_SHADER_COMPILER 0x8DFA #define GL_SHADER_BINARY_FORMATS 0x8DF8 #define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB #define GL_MAX_VARYING_VECTORS 0x8DFC #define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD #define GL_RGB565 0x8D62 #define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 #define GL_PROGRAM_BINARY_LENGTH 0x8741 #define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE #define GL_PROGRAM_BINARY_FORMATS 0x87FF #define GL_VERTEX_SHADER_BIT 0x00000001 #define GL_FRAGMENT_SHADER_BIT 0x00000002 #define GL_GEOMETRY_SHADER_BIT 0x00000004 #define GL_TESS_CONTROL_SHADER_BIT 0x00000008 #define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 #define GL_ALL_SHADER_BITS 0xFFFFFFFF #define GL_PROGRAM_SEPARABLE 0x8258 #define GL_ACTIVE_PROGRAM 0x8259 #define GL_PROGRAM_PIPELINE_BINDING 0x825A #define GL_MAX_VIEWPORTS 0x825B #define GL_VIEWPORT_SUBPIXEL_BITS 0x825C #define GL_VIEWPORT_BOUNDS_RANGE 0x825D #define GL_LAYER_PROVOKING_VERTEX 0x825E #define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F #define GL_UNDEFINED_VERTEX 0x8260 typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glReleaseShaderCompiler (void); GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); GLAPI void APIENTRY glClearDepthf (GLfloat d); GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); #endif #endif /* GL3W_GL_VERSION_4_1 */ #ifndef GL3W_GL_VERSION_4_2 #define GL3W_GL_VERSION_4_2 1 #define GL_COPY_READ_BUFFER_BINDING 0x8F36 #define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 #define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 #define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 #define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 #define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 #define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 #define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A #define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B #define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C #define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D #define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E #define GL_NUM_SAMPLE_COUNTS 0x9380 #define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC #define GL_ATOMIC_COUNTER_BUFFER 0x92C0 #define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 #define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 #define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 #define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 #define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 #define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB #define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC #define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD #define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE #define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF #define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 #define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 #define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 #define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 #define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 #define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 #define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 #define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 #define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 #define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC #define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 #define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA #define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB #define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 #define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 #define GL_UNIFORM_BARRIER_BIT 0x00000004 #define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 #define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 #define GL_COMMAND_BARRIER_BIT 0x00000040 #define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 #define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 #define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 #define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 #define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 #define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 #define GL_ALL_BARRIER_BITS 0xFFFFFFFF #define GL_MAX_IMAGE_UNITS 0x8F38 #define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 #define GL_IMAGE_BINDING_NAME 0x8F3A #define GL_IMAGE_BINDING_LEVEL 0x8F3B #define GL_IMAGE_BINDING_LAYERED 0x8F3C #define GL_IMAGE_BINDING_LAYER 0x8F3D #define GL_IMAGE_BINDING_ACCESS 0x8F3E #define GL_IMAGE_1D 0x904C #define GL_IMAGE_2D 0x904D #define GL_IMAGE_3D 0x904E #define GL_IMAGE_2D_RECT 0x904F #define GL_IMAGE_CUBE 0x9050 #define GL_IMAGE_BUFFER 0x9051 #define GL_IMAGE_1D_ARRAY 0x9052 #define GL_IMAGE_2D_ARRAY 0x9053 #define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 #define GL_IMAGE_2D_MULTISAMPLE 0x9055 #define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 #define GL_INT_IMAGE_1D 0x9057 #define GL_INT_IMAGE_2D 0x9058 #define GL_INT_IMAGE_3D 0x9059 #define GL_INT_IMAGE_2D_RECT 0x905A #define GL_INT_IMAGE_CUBE 0x905B #define GL_INT_IMAGE_BUFFER 0x905C #define GL_INT_IMAGE_1D_ARRAY 0x905D #define GL_INT_IMAGE_2D_ARRAY 0x905E #define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F #define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 #define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 #define GL_UNSIGNED_INT_IMAGE_1D 0x9062 #define GL_UNSIGNED_INT_IMAGE_2D 0x9063 #define GL_UNSIGNED_INT_IMAGE_3D 0x9064 #define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 #define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 #define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 #define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 #define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 #define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A #define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B #define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C #define GL_MAX_IMAGE_SAMPLES 0x906D #define GL_IMAGE_BINDING_FORMAT 0x906E #define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 #define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 #define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 #define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA #define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB #define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC #define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD #define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE #define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF #define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F #define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); #endif #endif /* GL3W_GL_VERSION_4_2 */ #ifndef GL3W_GL_VERSION_4_3 #define GL3W_GL_VERSION_4_3 1 typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); #define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 #define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E #define GL_COMPRESSED_RGB8_ETC2 0x9274 #define GL_COMPRESSED_SRGB8_ETC2 0x9275 #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 #define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 #define GL_COMPRESSED_R11_EAC 0x9270 #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 #define GL_COMPRESSED_RG11_EAC 0x9272 #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 #define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 #define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A #define GL_MAX_ELEMENT_INDEX 0x8D6B #define GL_COMPUTE_SHADER 0x91B9 #define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB #define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC #define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD #define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 #define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 #define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 #define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 #define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 #define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB #define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE #define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF #define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 #define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED #define GL_DISPATCH_INDIRECT_BUFFER 0x90EE #define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF #define GL_COMPUTE_SHADER_BIT 0x00000020 #define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 #define GL_DEBUG_CALLBACK_FUNCTION 0x8244 #define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 #define GL_DEBUG_SOURCE_API 0x8246 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 #define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 #define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 #define GL_DEBUG_SOURCE_APPLICATION 0x824A #define GL_DEBUG_SOURCE_OTHER 0x824B #define GL_DEBUG_TYPE_ERROR 0x824C #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E #define GL_DEBUG_TYPE_PORTABILITY 0x824F #define GL_DEBUG_TYPE_PERFORMANCE 0x8250 #define GL_DEBUG_TYPE_OTHER 0x8251 #define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 #define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 #define GL_DEBUG_LOGGED_MESSAGES 0x9145 #define GL_DEBUG_SEVERITY_HIGH 0x9146 #define GL_DEBUG_SEVERITY_MEDIUM 0x9147 #define GL_DEBUG_SEVERITY_LOW 0x9148 #define GL_DEBUG_TYPE_MARKER 0x8268 #define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 #define GL_DEBUG_TYPE_POP_GROUP 0x826A #define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B #define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C #define GL_DEBUG_GROUP_STACK_DEPTH 0x826D #define GL_BUFFER 0x82E0 #define GL_SHADER 0x82E1 #define GL_PROGRAM 0x82E2 #define GL_QUERY 0x82E3 #define GL_PROGRAM_PIPELINE 0x82E4 #define GL_SAMPLER 0x82E6 #define GL_MAX_LABEL_LENGTH 0x82E8 #define GL_DEBUG_OUTPUT 0x92E0 #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 #define GL_MAX_UNIFORM_LOCATIONS 0x826E #define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 #define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 #define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 #define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 #define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 #define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 #define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 #define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 #define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 #define GL_INTERNALFORMAT_SUPPORTED 0x826F #define GL_INTERNALFORMAT_PREFERRED 0x8270 #define GL_INTERNALFORMAT_RED_SIZE 0x8271 #define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 #define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 #define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 #define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 #define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 #define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 #define GL_INTERNALFORMAT_RED_TYPE 0x8278 #define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 #define GL_INTERNALFORMAT_BLUE_TYPE 0x827A #define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B #define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C #define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D #define GL_MAX_WIDTH 0x827E #define GL_MAX_HEIGHT 0x827F #define GL_MAX_DEPTH 0x8280 #define GL_MAX_LAYERS 0x8281 #define GL_MAX_COMBINED_DIMENSIONS 0x8282 #define GL_COLOR_COMPONENTS 0x8283 #define GL_DEPTH_COMPONENTS 0x8284 #define GL_STENCIL_COMPONENTS 0x8285 #define GL_COLOR_RENDERABLE 0x8286 #define GL_DEPTH_RENDERABLE 0x8287 #define GL_STENCIL_RENDERABLE 0x8288 #define GL_FRAMEBUFFER_RENDERABLE 0x8289 #define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A #define GL_FRAMEBUFFER_BLEND 0x828B #define GL_READ_PIXELS 0x828C #define GL_READ_PIXELS_FORMAT 0x828D #define GL_READ_PIXELS_TYPE 0x828E #define GL_TEXTURE_IMAGE_FORMAT 0x828F #define GL_TEXTURE_IMAGE_TYPE 0x8290 #define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 #define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 #define GL_MIPMAP 0x8293 #define GL_MANUAL_GENERATE_MIPMAP 0x8294 #define GL_AUTO_GENERATE_MIPMAP 0x8295 #define GL_COLOR_ENCODING 0x8296 #define GL_SRGB_READ 0x8297 #define GL_SRGB_WRITE 0x8298 #define GL_FILTER 0x829A #define GL_VERTEX_TEXTURE 0x829B #define GL_TESS_CONTROL_TEXTURE 0x829C #define GL_TESS_EVALUATION_TEXTURE 0x829D #define GL_GEOMETRY_TEXTURE 0x829E #define GL_FRAGMENT_TEXTURE 0x829F #define GL_COMPUTE_TEXTURE 0x82A0 #define GL_TEXTURE_SHADOW 0x82A1 #define GL_TEXTURE_GATHER 0x82A2 #define GL_TEXTURE_GATHER_SHADOW 0x82A3 #define GL_SHADER_IMAGE_LOAD 0x82A4 #define GL_SHADER_IMAGE_STORE 0x82A5 #define GL_SHADER_IMAGE_ATOMIC 0x82A6 #define GL_IMAGE_TEXEL_SIZE 0x82A7 #define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 #define GL_IMAGE_PIXEL_FORMAT 0x82A9 #define GL_IMAGE_PIXEL_TYPE 0x82AA #define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC #define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD #define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE #define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF #define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 #define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 #define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 #define GL_CLEAR_BUFFER 0x82B4 #define GL_TEXTURE_VIEW 0x82B5 #define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 #define GL_FULL_SUPPORT 0x82B7 #define GL_CAVEAT_SUPPORT 0x82B8 #define GL_IMAGE_CLASS_4_X_32 0x82B9 #define GL_IMAGE_CLASS_2_X_32 0x82BA #define GL_IMAGE_CLASS_1_X_32 0x82BB #define GL_IMAGE_CLASS_4_X_16 0x82BC #define GL_IMAGE_CLASS_2_X_16 0x82BD #define GL_IMAGE_CLASS_1_X_16 0x82BE #define GL_IMAGE_CLASS_4_X_8 0x82BF #define GL_IMAGE_CLASS_2_X_8 0x82C0 #define GL_IMAGE_CLASS_1_X_8 0x82C1 #define GL_IMAGE_CLASS_11_11_10 0x82C2 #define GL_IMAGE_CLASS_10_10_10_2 0x82C3 #define GL_VIEW_CLASS_128_BITS 0x82C4 #define GL_VIEW_CLASS_96_BITS 0x82C5 #define GL_VIEW_CLASS_64_BITS 0x82C6 #define GL_VIEW_CLASS_48_BITS 0x82C7 #define GL_VIEW_CLASS_32_BITS 0x82C8 #define GL_VIEW_CLASS_24_BITS 0x82C9 #define GL_VIEW_CLASS_16_BITS 0x82CA #define GL_VIEW_CLASS_8_BITS 0x82CB #define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC #define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD #define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE #define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF #define GL_VIEW_CLASS_RGTC1_RED 0x82D0 #define GL_VIEW_CLASS_RGTC2_RG 0x82D1 #define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 #define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 #define GL_UNIFORM 0x92E1 #define GL_UNIFORM_BLOCK 0x92E2 #define GL_PROGRAM_INPUT 0x92E3 #define GL_PROGRAM_OUTPUT 0x92E4 #define GL_BUFFER_VARIABLE 0x92E5 #define GL_SHADER_STORAGE_BLOCK 0x92E6 #define GL_VERTEX_SUBROUTINE 0x92E8 #define GL_TESS_CONTROL_SUBROUTINE 0x92E9 #define GL_TESS_EVALUATION_SUBROUTINE 0x92EA #define GL_GEOMETRY_SUBROUTINE 0x92EB #define GL_FRAGMENT_SUBROUTINE 0x92EC #define GL_COMPUTE_SUBROUTINE 0x92ED #define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE #define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF #define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 #define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 #define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 #define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 #define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 #define GL_ACTIVE_RESOURCES 0x92F5 #define GL_MAX_NAME_LENGTH 0x92F6 #define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 #define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 #define GL_NAME_LENGTH 0x92F9 #define GL_TYPE 0x92FA #define GL_ARRAY_SIZE 0x92FB #define GL_OFFSET 0x92FC #define GL_BLOCK_INDEX 0x92FD #define GL_ARRAY_STRIDE 0x92FE #define GL_MATRIX_STRIDE 0x92FF #define GL_IS_ROW_MAJOR 0x9300 #define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 #define GL_BUFFER_BINDING 0x9302 #define GL_BUFFER_DATA_SIZE 0x9303 #define GL_NUM_ACTIVE_VARIABLES 0x9304 #define GL_ACTIVE_VARIABLES 0x9305 #define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 #define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 #define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 #define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 #define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A #define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B #define GL_TOP_LEVEL_ARRAY_SIZE 0x930C #define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D #define GL_LOCATION 0x930E #define GL_LOCATION_INDEX 0x930F #define GL_IS_PER_PATCH 0x92E7 #define GL_SHADER_STORAGE_BUFFER 0x90D2 #define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 #define GL_SHADER_STORAGE_BUFFER_START 0x90D4 #define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 #define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 #define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 #define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 #define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 #define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA #define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB #define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC #define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD #define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE #define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF #define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 #define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 #define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA #define GL_TEXTURE_BUFFER_OFFSET 0x919D #define GL_TEXTURE_BUFFER_SIZE 0x919E #define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F #define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB #define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC #define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD #define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE #define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF #define GL_VERTEX_ATTRIB_BINDING 0x82D4 #define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 #define GL_VERTEX_BINDING_DIVISOR 0x82D6 #define GL_VERTEX_BINDING_OFFSET 0x82D7 #define GL_VERTEX_BINDING_STRIDE 0x82D8 #define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 #define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA #define GL_VERTEX_BINDING_BUFFER 0x8F4F typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); GLAPI void APIENTRY glPopDebugGroup (void); GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); #endif #endif /* GL3W_GL_VERSION_4_3 */ #ifndef GL3W_GL_VERSION_4_4 #define GL3W_GL_VERSION_4_4 1 #define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 #define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 #define GL_TEXTURE_BUFFER_BINDING 0x8C2A #define GL_MAP_PERSISTENT_BIT 0x0040 #define GL_MAP_COHERENT_BIT 0x0080 #define GL_DYNAMIC_STORAGE_BIT 0x0100 #define GL_CLIENT_STORAGE_BIT 0x0200 #define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 #define GL_BUFFER_IMMUTABLE_STORAGE 0x821F #define GL_BUFFER_STORAGE_FLAGS 0x8220 #define GL_CLEAR_TEXTURE 0x9365 #define GL_LOCATION_COMPONENT 0x934A #define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B #define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C #define GL_QUERY_BUFFER 0x9192 #define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 #define GL_QUERY_BUFFER_BINDING 0x9193 #define GL_QUERY_RESULT_NO_WAIT 0x9194 #define GL_MIRROR_CLAMP_TO_EDGE 0x8743 typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); #endif #endif /* GL3W_GL_VERSION_4_4 */ #ifndef GL3W_GL_VERSION_4_5 #define GL3W_GL_VERSION_4_5 1 #define GL_CONTEXT_LOST 0x0507 #define GL_NEGATIVE_ONE_TO_ONE 0x935E #define GL_ZERO_TO_ONE 0x935F #define GL_CLIP_ORIGIN 0x935C #define GL_CLIP_DEPTH_MODE 0x935D #define GL_QUERY_WAIT_INVERTED 0x8E17 #define GL_QUERY_NO_WAIT_INVERTED 0x8E18 #define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 #define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A #define GL_MAX_CULL_DISTANCES 0x82F9 #define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA #define GL_TEXTURE_TARGET 0x1006 #define GL_QUERY_TARGET 0x82EA #define GL_GUILTY_CONTEXT_RESET 0x8253 #define GL_INNOCENT_CONTEXT_RESET 0x8254 #define GL_UNKNOWN_CONTEXT_RESET 0x8255 #define GL_RESET_NOTIFICATION_STRATEGY 0x8256 #define GL_LOSE_CONTEXT_ON_RESET 0x8252 #define GL_NO_RESET_NOTIFICATION 0x8261 #define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 #define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB #define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC typedef void (APIENTRYP PFNGLCLIPCONTROLPROC) (GLenum origin, GLenum depth); typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) (GLuint xfb, GLuint index, GLuint buffer); typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC) (GLuint xfb, GLenum pname, GLint *param); typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param); typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC) (GLsizei n, GLuint *buffers); typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERPROC) (GLuint buffer, GLenum access); typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC) (GLuint buffer); typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC) (GLuint buffer, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) (GLuint buffer, GLenum pname, GLint64 *params); typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC) (GLuint buffer, GLenum pname, void **params); typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) (GLuint framebuffer, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) (GLuint framebuffer, GLenum buf); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) (GLuint framebuffer, GLenum src); typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC) (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) (GLuint framebuffer, GLenum target); typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) (GLuint framebuffer, GLenum pname, GLint *param); typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) (GLuint renderbuffer, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLCREATETEXTURESPROC) (GLenum target, GLsizei n, GLuint *textures); typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC) (GLuint texture, GLenum internalformat, GLuint buffer); typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC) (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC) (GLuint texture, GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, const GLfloat *param); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC) (GLuint texture, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, const GLuint *params); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, const GLint *param); typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC) (GLuint texture); typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC) (GLuint unit, GLuint texture); typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLsizei bufSize, void *pixels); typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC) (GLuint texture, GLint level, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC) (GLuint texture, GLint level, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, GLuint *params); typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC) (GLuint vaobj, GLuint buffer); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC) (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC) (GLuint vaobj, GLenum pname, GLint *param); typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC) (GLsizei n, GLuint *samplers); typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); typedef void (APIENTRYP PFNGLCREATEQUERIESPROC) (GLenum target, GLsizei n, GLuint *ids); typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC) (GLbitfield barriers); typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC) (void); typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint lod, GLsizei bufSize, void *pixels); typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); typedef void (APIENTRYP PFNGLREADNPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC) (void); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glClipControl (GLenum origin, GLenum depth); GLAPI void APIENTRY glCreateTransformFeedbacks (GLsizei n, GLuint *ids); GLAPI void APIENTRY glTransformFeedbackBufferBase (GLuint xfb, GLuint index, GLuint buffer); GLAPI void APIENTRY glTransformFeedbackBufferRange (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); GLAPI void APIENTRY glGetTransformFeedbackiv (GLuint xfb, GLenum pname, GLint *param); GLAPI void APIENTRY glGetTransformFeedbacki_v (GLuint xfb, GLenum pname, GLuint index, GLint *param); GLAPI void APIENTRY glGetTransformFeedbacki64_v (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); GLAPI void APIENTRY glCreateBuffers (GLsizei n, GLuint *buffers); GLAPI void APIENTRY glNamedBufferStorage (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); GLAPI void APIENTRY glNamedBufferData (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); GLAPI void APIENTRY glNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); GLAPI void APIENTRY glCopyNamedBufferSubData (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); GLAPI void APIENTRY glClearNamedBufferData (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); GLAPI void APIENTRY glClearNamedBufferSubData (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); GLAPI void *APIENTRY glMapNamedBuffer (GLuint buffer, GLenum access); GLAPI void *APIENTRY glMapNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); GLAPI GLboolean APIENTRY glUnmapNamedBuffer (GLuint buffer); GLAPI void APIENTRY glFlushMappedNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length); GLAPI void APIENTRY glGetNamedBufferParameteriv (GLuint buffer, GLenum pname, GLint *params); GLAPI void APIENTRY glGetNamedBufferParameteri64v (GLuint buffer, GLenum pname, GLint64 *params); GLAPI void APIENTRY glGetNamedBufferPointerv (GLuint buffer, GLenum pname, void **params); GLAPI void APIENTRY glGetNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); GLAPI void APIENTRY glCreateFramebuffers (GLsizei n, GLuint *framebuffers); GLAPI void APIENTRY glNamedFramebufferRenderbuffer (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); GLAPI void APIENTRY glNamedFramebufferParameteri (GLuint framebuffer, GLenum pname, GLint param); GLAPI void APIENTRY glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); GLAPI void APIENTRY glNamedFramebufferTextureLayer (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); GLAPI void APIENTRY glNamedFramebufferDrawBuffer (GLuint framebuffer, GLenum buf); GLAPI void APIENTRY glNamedFramebufferDrawBuffers (GLuint framebuffer, GLsizei n, const GLenum *bufs); GLAPI void APIENTRY glNamedFramebufferReadBuffer (GLuint framebuffer, GLenum src); GLAPI void APIENTRY glInvalidateNamedFramebufferData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); GLAPI void APIENTRY glInvalidateNamedFramebufferSubData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI void APIENTRY glClearNamedFramebufferiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); GLAPI void APIENTRY glClearNamedFramebufferuiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); GLAPI void APIENTRY glClearNamedFramebufferfv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); GLAPI void APIENTRY glClearNamedFramebufferfi (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); GLAPI void APIENTRY glBlitNamedFramebuffer (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); GLAPI GLenum APIENTRY glCheckNamedFramebufferStatus (GLuint framebuffer, GLenum target); GLAPI void APIENTRY glGetNamedFramebufferParameteriv (GLuint framebuffer, GLenum pname, GLint *param); GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameteriv (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); GLAPI void APIENTRY glCreateRenderbuffers (GLsizei n, GLuint *renderbuffers); GLAPI void APIENTRY glNamedRenderbufferStorage (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); GLAPI void APIENTRY glNamedRenderbufferStorageMultisample (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); GLAPI void APIENTRY glGetNamedRenderbufferParameteriv (GLuint renderbuffer, GLenum pname, GLint *params); GLAPI void APIENTRY glCreateTextures (GLenum target, GLsizei n, GLuint *textures); GLAPI void APIENTRY glTextureBuffer (GLuint texture, GLenum internalformat, GLuint buffer); GLAPI void APIENTRY glTextureBufferRange (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); GLAPI void APIENTRY glTextureStorage1D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); GLAPI void APIENTRY glTextureStorage2D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); GLAPI void APIENTRY glTextureStorage3D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); GLAPI void APIENTRY glTextureStorage2DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); GLAPI void APIENTRY glTextureStorage3DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); GLAPI void APIENTRY glTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); GLAPI void APIENTRY glCompressedTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); GLAPI void APIENTRY glCompressedTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); GLAPI void APIENTRY glCompressedTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); GLAPI void APIENTRY glCopyTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); GLAPI void APIENTRY glCopyTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI void APIENTRY glCopyTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI void APIENTRY glTextureParameterf (GLuint texture, GLenum pname, GLfloat param); GLAPI void APIENTRY glTextureParameterfv (GLuint texture, GLenum pname, const GLfloat *param); GLAPI void APIENTRY glTextureParameteri (GLuint texture, GLenum pname, GLint param); GLAPI void APIENTRY glTextureParameterIiv (GLuint texture, GLenum pname, const GLint *params); GLAPI void APIENTRY glTextureParameterIuiv (GLuint texture, GLenum pname, const GLuint *params); GLAPI void APIENTRY glTextureParameteriv (GLuint texture, GLenum pname, const GLint *param); GLAPI void APIENTRY glGenerateTextureMipmap (GLuint texture); GLAPI void APIENTRY glBindTextureUnit (GLuint unit, GLuint texture); GLAPI void APIENTRY glGetTextureImage (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); GLAPI void APIENTRY glGetCompressedTextureImage (GLuint texture, GLint level, GLsizei bufSize, void *pixels); GLAPI void APIENTRY glGetTextureLevelParameterfv (GLuint texture, GLint level, GLenum pname, GLfloat *params); GLAPI void APIENTRY glGetTextureLevelParameteriv (GLuint texture, GLint level, GLenum pname, GLint *params); GLAPI void APIENTRY glGetTextureParameterfv (GLuint texture, GLenum pname, GLfloat *params); GLAPI void APIENTRY glGetTextureParameterIiv (GLuint texture, GLenum pname, GLint *params); GLAPI void APIENTRY glGetTextureParameterIuiv (GLuint texture, GLenum pname, GLuint *params); GLAPI void APIENTRY glGetTextureParameteriv (GLuint texture, GLenum pname, GLint *params); GLAPI void APIENTRY glCreateVertexArrays (GLsizei n, GLuint *arrays); GLAPI void APIENTRY glDisableVertexArrayAttrib (GLuint vaobj, GLuint index); GLAPI void APIENTRY glEnableVertexArrayAttrib (GLuint vaobj, GLuint index); GLAPI void APIENTRY glVertexArrayElementBuffer (GLuint vaobj, GLuint buffer); GLAPI void APIENTRY glVertexArrayVertexBuffer (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); GLAPI void APIENTRY glVertexArrayVertexBuffers (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); GLAPI void APIENTRY glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex); GLAPI void APIENTRY glVertexArrayAttribFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); GLAPI void APIENTRY glVertexArrayAttribIFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); GLAPI void APIENTRY glVertexArrayAttribLFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); GLAPI void APIENTRY glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor); GLAPI void APIENTRY glGetVertexArrayiv (GLuint vaobj, GLenum pname, GLint *param); GLAPI void APIENTRY glGetVertexArrayIndexediv (GLuint vaobj, GLuint index, GLenum pname, GLint *param); GLAPI void APIENTRY glGetVertexArrayIndexed64iv (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); GLAPI void APIENTRY glCreateSamplers (GLsizei n, GLuint *samplers); GLAPI void APIENTRY glCreateProgramPipelines (GLsizei n, GLuint *pipelines); GLAPI void APIENTRY glCreateQueries (GLenum target, GLsizei n, GLuint *ids); GLAPI void APIENTRY glGetQueryBufferObjecti64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); GLAPI void APIENTRY glGetQueryBufferObjectiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); GLAPI void APIENTRY glGetQueryBufferObjectui64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); GLAPI void APIENTRY glGetQueryBufferObjectuiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); GLAPI void APIENTRY glMemoryBarrierByRegion (GLbitfield barriers); GLAPI void APIENTRY glGetTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); GLAPI void APIENTRY glGetCompressedTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); GLAPI GLenum APIENTRY glGetGraphicsResetStatus (void); GLAPI void APIENTRY glGetnCompressedTexImage (GLenum target, GLint lod, GLsizei bufSize, void *pixels); GLAPI void APIENTRY glGetnTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); GLAPI void APIENTRY glGetnUniformdv (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); GLAPI void APIENTRY glGetnUniformfv (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); GLAPI void APIENTRY glGetnUniformiv (GLuint program, GLint location, GLsizei bufSize, GLint *params); GLAPI void APIENTRY glGetnUniformuiv (GLuint program, GLint location, GLsizei bufSize, GLuint *params); GLAPI void APIENTRY glReadnPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); GLAPI void APIENTRY glTextureBarrier (void); #endif #endif /* GL3W_GL_VERSION_4_5 */ #ifndef GL3W_GL_VERSION_4_6 #define GL3W_GL_VERSION_4_6 1 #define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 #define GL_SPIR_V_BINARY 0x9552 #define GL_PARAMETER_BUFFER 0x80EE #define GL_PARAMETER_BUFFER_BINDING 0x80EF #define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 #define GL_VERTICES_SUBMITTED 0x82EE #define GL_PRIMITIVES_SUBMITTED 0x82EF #define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 #define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 #define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 #define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 #define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 #define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 #define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 #define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 #define GL_POLYGON_OFFSET_CLAMP 0x8E1B #define GL_SPIR_V_EXTENSIONS 0x9553 #define GL_NUM_SPIR_V_EXTENSIONS 0x9554 #define GL_TEXTURE_MAX_ANISOTROPY 0x84FE #define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF #define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC #define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC) (GLfloat factor, GLfloat units, GLfloat clamp); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glSpecializeShader (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); GLAPI void APIENTRY glMultiDrawArraysIndirectCount (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); GLAPI void APIENTRY glMultiDrawElementsIndirectCount (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); GLAPI void APIENTRY glPolygonOffsetClamp (GLfloat factor, GLfloat units, GLfloat clamp); #endif #endif /* GL3W_GL_VERSION_4_6 */ #ifndef GL_ARB_ES2_compatibility #define GL_ARB_ES2_compatibility 1 #endif /* GL_ARB_ES2_compatibility */ #ifndef GL_ARB_ES3_1_compatibility #define GL_ARB_ES3_1_compatibility 1 #endif /* GL_ARB_ES3_1_compatibility */ #ifndef GL_ARB_ES3_2_compatibility #define GL_ARB_ES3_2_compatibility 1 #define GL_PRIMITIVE_BOUNDING_BOX_ARB 0x92BE #define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381 #define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382 typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXARBPROC) (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); #endif /* GL_ARB_ES3_2_compatibility */ #ifndef GL_ARB_ES3_compatibility #define GL_ARB_ES3_compatibility 1 #endif /* GL_ARB_ES3_compatibility */ #ifndef GL_ARB_arrays_of_arrays #define GL_ARB_arrays_of_arrays 1 #endif /* GL_ARB_arrays_of_arrays */ #ifndef GL_ARB_base_instance #define GL_ARB_base_instance 1 #endif /* GL_ARB_base_instance */ #ifndef GL_ARB_bindless_texture #define GL_ARB_bindless_texture 1 typedef khronos_uint64_t GLuint64EXT; #define GL_UNSIGNED_INT64_ARB 0x140F typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); #endif /* GL_ARB_bindless_texture */ #ifndef GL_ARB_blend_func_extended #define GL_ARB_blend_func_extended 1 #endif /* GL_ARB_blend_func_extended */ #ifndef GL_ARB_buffer_storage #define GL_ARB_buffer_storage 1 #endif /* GL_ARB_buffer_storage */ #ifndef GL_ARB_cl_event #define GL_ARB_cl_event 1 struct _cl_context; struct _cl_event; #define GL_SYNC_CL_EVENT_ARB 0x8240 #define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); #endif /* GL_ARB_cl_event */ #ifndef GL_ARB_clear_buffer_object #define GL_ARB_clear_buffer_object 1 #endif /* GL_ARB_clear_buffer_object */ #ifndef GL_ARB_clear_texture #define GL_ARB_clear_texture 1 #endif /* GL_ARB_clear_texture */ #ifndef GL_ARB_clip_control #define GL_ARB_clip_control 1 #endif /* GL_ARB_clip_control */ #ifndef GL_ARB_compressed_texture_pixel_storage #define GL_ARB_compressed_texture_pixel_storage 1 #endif /* GL_ARB_compressed_texture_pixel_storage */ #ifndef GL_ARB_compute_shader #define GL_ARB_compute_shader 1 #endif /* GL_ARB_compute_shader */ #ifndef GL_ARB_compute_variable_group_size #define GL_ARB_compute_variable_group_size 1 #define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 #define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB #define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 #define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); #endif /* GL_ARB_compute_variable_group_size */ #ifndef GL_ARB_conditional_render_inverted #define GL_ARB_conditional_render_inverted 1 #endif /* GL_ARB_conditional_render_inverted */ #ifndef GL_ARB_conservative_depth #define GL_ARB_conservative_depth 1 #endif /* GL_ARB_conservative_depth */ #ifndef GL_ARB_copy_buffer #define GL_ARB_copy_buffer 1 #endif /* GL_ARB_copy_buffer */ #ifndef GL_ARB_copy_image #define GL_ARB_copy_image 1 #endif /* GL_ARB_copy_image */ #ifndef GL_ARB_cull_distance #define GL_ARB_cull_distance 1 #endif /* GL_ARB_cull_distance */ #ifndef GL_ARB_debug_output #define GL_ARB_debug_output 1 typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); #define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 #define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 #define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 #define GL_DEBUG_SOURCE_API_ARB 0x8246 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 #define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 #define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 #define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A #define GL_DEBUG_SOURCE_OTHER_ARB 0x824B #define GL_DEBUG_TYPE_ERROR_ARB 0x824C #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E #define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F #define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 #define GL_DEBUG_TYPE_OTHER_ARB 0x8251 #define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 #define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 #define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 #define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 #define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 #define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); #endif /* GL_ARB_debug_output */ #ifndef GL_ARB_depth_buffer_float #define GL_ARB_depth_buffer_float 1 #endif /* GL_ARB_depth_buffer_float */ #ifndef GL_ARB_depth_clamp #define GL_ARB_depth_clamp 1 #endif /* GL_ARB_depth_clamp */ #ifndef GL_ARB_derivative_control #define GL_ARB_derivative_control 1 #endif /* GL_ARB_derivative_control */ #ifndef GL_ARB_direct_state_access #define GL_ARB_direct_state_access 1 #endif /* GL_ARB_direct_state_access */ #ifndef GL_ARB_draw_buffers_blend #define GL_ARB_draw_buffers_blend 1 typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); #endif /* GL_ARB_draw_buffers_blend */ #ifndef GL_ARB_draw_elements_base_vertex #define GL_ARB_draw_elements_base_vertex 1 #endif /* GL_ARB_draw_elements_base_vertex */ #ifndef GL_ARB_draw_indirect #define GL_ARB_draw_indirect 1 #endif /* GL_ARB_draw_indirect */ #ifndef GL_ARB_draw_instanced #define GL_ARB_draw_instanced 1 typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); #endif /* GL_ARB_draw_instanced */ #ifndef GL_ARB_enhanced_layouts #define GL_ARB_enhanced_layouts 1 #endif /* GL_ARB_enhanced_layouts */ #ifndef GL_ARB_explicit_attrib_location #define GL_ARB_explicit_attrib_location 1 #endif /* GL_ARB_explicit_attrib_location */ #ifndef GL_ARB_explicit_uniform_location #define GL_ARB_explicit_uniform_location 1 #endif /* GL_ARB_explicit_uniform_location */ #ifndef GL_ARB_fragment_coord_conventions #define GL_ARB_fragment_coord_conventions 1 #endif /* GL_ARB_fragment_coord_conventions */ #ifndef GL_ARB_fragment_layer_viewport #define GL_ARB_fragment_layer_viewport 1 #endif /* GL_ARB_fragment_layer_viewport */ #ifndef GL_ARB_fragment_shader_interlock #define GL_ARB_fragment_shader_interlock 1 #endif /* GL_ARB_fragment_shader_interlock */ #ifndef GL_ARB_framebuffer_no_attachments #define GL_ARB_framebuffer_no_attachments 1 #endif /* GL_ARB_framebuffer_no_attachments */ #ifndef GL_ARB_framebuffer_object #define GL_ARB_framebuffer_object 1 #endif /* GL_ARB_framebuffer_object */ #ifndef GL_ARB_framebuffer_sRGB #define GL_ARB_framebuffer_sRGB 1 #endif /* GL_ARB_framebuffer_sRGB */ #ifndef GL_ARB_geometry_shader4 #define GL_ARB_geometry_shader4 1 #define GL_LINES_ADJACENCY_ARB 0x000A #define GL_LINE_STRIP_ADJACENCY_ARB 0x000B #define GL_TRIANGLES_ADJACENCY_ARB 0x000C #define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D #define GL_PROGRAM_POINT_SIZE_ARB 0x8642 #define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 #define GL_GEOMETRY_SHADER_ARB 0x8DD9 #define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA #define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB #define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC #define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD #define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE #define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF #define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 #define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); #endif /* GL_ARB_geometry_shader4 */ #ifndef GL_ARB_get_program_binary #define GL_ARB_get_program_binary 1 #endif /* GL_ARB_get_program_binary */ #ifndef GL_ARB_get_texture_sub_image #define GL_ARB_get_texture_sub_image 1 #endif /* GL_ARB_get_texture_sub_image */ #ifndef GL_ARB_gl_spirv #define GL_ARB_gl_spirv 1 #define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 #define GL_SPIR_V_BINARY_ARB 0x9552 typedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); #endif /* GL_ARB_gl_spirv */ #ifndef GL_ARB_gpu_shader5 #define GL_ARB_gpu_shader5 1 #endif /* GL_ARB_gpu_shader5 */ #ifndef GL_ARB_gpu_shader_fp64 #define GL_ARB_gpu_shader_fp64 1 #endif /* GL_ARB_gpu_shader_fp64 */ #ifndef GL_ARB_gpu_shader_int64 #define GL_ARB_gpu_shader_int64 1 #define GL_INT64_ARB 0x140E #define GL_INT64_VEC2_ARB 0x8FE9 #define GL_INT64_VEC3_ARB 0x8FEA #define GL_INT64_VEC4_ARB 0x8FEB #define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 #define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 #define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 typedef void (APIENTRYP PFNGLUNIFORM1I64ARBPROC) (GLint location, GLint64 x); typedef void (APIENTRYP PFNGLUNIFORM2I64ARBPROC) (GLint location, GLint64 x, GLint64 y); typedef void (APIENTRYP PFNGLUNIFORM3I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z); typedef void (APIENTRYP PFNGLUNIFORM4I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); typedef void (APIENTRYP PFNGLUNIFORM1I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); typedef void (APIENTRYP PFNGLUNIFORM2I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); typedef void (APIENTRYP PFNGLUNIFORM3I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); typedef void (APIENTRYP PFNGLUNIFORM4I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); typedef void (APIENTRYP PFNGLUNIFORM1UI64ARBPROC) (GLint location, GLuint64 x); typedef void (APIENTRYP PFNGLUNIFORM2UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y); typedef void (APIENTRYP PFNGLUNIFORM3UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); typedef void (APIENTRYP PFNGLUNIFORM4UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); typedef void (APIENTRYP PFNGLUNIFORM1UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLUNIFORM2UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLUNIFORM3UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLUNIFORM4UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLGETUNIFORMI64VARBPROC) (GLuint program, GLint location, GLint64 *params); typedef void (APIENTRYP PFNGLGETUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLuint64 *params); typedef void (APIENTRYP PFNGLGETNUNIFORMI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); typedef void (APIENTRYP PFNGLGETNUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64ARBPROC) (GLuint program, GLint location, GLint64 x); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64ARBPROC) (GLuint program, GLint location, GLuint64 x); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); #endif /* GL_ARB_gpu_shader_int64 */ #ifndef GL_ARB_half_float_vertex #define GL_ARB_half_float_vertex 1 #endif /* GL_ARB_half_float_vertex */ #ifndef GL_ARB_imaging #define GL_ARB_imaging 1 #endif /* GL_ARB_imaging */ #ifndef GL_ARB_indirect_parameters #define GL_ARB_indirect_parameters 1 #define GL_PARAMETER_BUFFER_ARB 0x80EE #define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); #endif /* GL_ARB_indirect_parameters */ #ifndef GL_ARB_instanced_arrays #define GL_ARB_instanced_arrays 1 #define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); #endif /* GL_ARB_instanced_arrays */ #ifndef GL_ARB_internalformat_query #define GL_ARB_internalformat_query 1 #endif /* GL_ARB_internalformat_query */ #ifndef GL_ARB_internalformat_query2 #define GL_ARB_internalformat_query2 1 #define GL_SRGB_DECODE_ARB 0x8299 #define GL_VIEW_CLASS_EAC_R11 0x9383 #define GL_VIEW_CLASS_EAC_RG11 0x9384 #define GL_VIEW_CLASS_ETC2_RGB 0x9385 #define GL_VIEW_CLASS_ETC2_RGBA 0x9386 #define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387 #define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388 #define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389 #define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A #define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B #define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C #define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D #define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E #define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F #define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390 #define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391 #define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392 #define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393 #define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394 #define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395 #endif /* GL_ARB_internalformat_query2 */ #ifndef GL_ARB_invalidate_subdata #define GL_ARB_invalidate_subdata 1 #endif /* GL_ARB_invalidate_subdata */ #ifndef GL_ARB_map_buffer_alignment #define GL_ARB_map_buffer_alignment 1 #endif /* GL_ARB_map_buffer_alignment */ #ifndef GL_ARB_map_buffer_range #define GL_ARB_map_buffer_range 1 #endif /* GL_ARB_map_buffer_range */ #ifndef GL_ARB_multi_bind #define GL_ARB_multi_bind 1 #endif /* GL_ARB_multi_bind */ #ifndef GL_ARB_multi_draw_indirect #define GL_ARB_multi_draw_indirect 1 #endif /* GL_ARB_multi_draw_indirect */ #ifndef GL_ARB_occlusion_query2 #define GL_ARB_occlusion_query2 1 #endif /* GL_ARB_occlusion_query2 */ #ifndef GL_ARB_parallel_shader_compile #define GL_ARB_parallel_shader_compile 1 #define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0 #define GL_COMPLETION_STATUS_ARB 0x91B1 typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSARBPROC) (GLuint count); #endif /* GL_ARB_parallel_shader_compile */ #ifndef GL_ARB_pipeline_statistics_query #define GL_ARB_pipeline_statistics_query 1 #define GL_VERTICES_SUBMITTED_ARB 0x82EE #define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF #define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 #define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 #define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 #define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 #define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 #define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 #define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 #define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 #endif /* GL_ARB_pipeline_statistics_query */ #ifndef GL_ARB_pixel_buffer_object #define GL_ARB_pixel_buffer_object 1 #define GL_PIXEL_PACK_BUFFER_ARB 0x88EB #define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC #define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED #define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF #endif /* GL_ARB_pixel_buffer_object */ #ifndef GL_ARB_polygon_offset_clamp #define GL_ARB_polygon_offset_clamp 1 #endif /* GL_ARB_polygon_offset_clamp */ #ifndef GL_ARB_post_depth_coverage #define GL_ARB_post_depth_coverage 1 #endif /* GL_ARB_post_depth_coverage */ #ifndef GL_ARB_program_interface_query #define GL_ARB_program_interface_query 1 #endif /* GL_ARB_program_interface_query */ #ifndef GL_ARB_provoking_vertex #define GL_ARB_provoking_vertex 1 #endif /* GL_ARB_provoking_vertex */ #ifndef GL_ARB_query_buffer_object #define GL_ARB_query_buffer_object 1 #endif /* GL_ARB_query_buffer_object */ #ifndef GL_ARB_robust_buffer_access_behavior #define GL_ARB_robust_buffer_access_behavior 1 #endif /* GL_ARB_robust_buffer_access_behavior */ #ifndef GL_ARB_robustness #define GL_ARB_robustness 1 #define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 #define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 #define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 #define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 #define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 #define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 #define GL_NO_RESET_NOTIFICATION_ARB 0x8261 typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); #endif /* GL_ARB_robustness */ #ifndef GL_ARB_robustness_isolation #define GL_ARB_robustness_isolation 1 #endif /* GL_ARB_robustness_isolation */ #ifndef GL_ARB_sample_locations #define GL_ARB_sample_locations 1 #define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D #define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E #define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F #define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340 #define GL_SAMPLE_LOCATION_ARB 0x8E50 #define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341 #define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 #define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); typedef void (APIENTRYP PFNGLEVALUATEDEPTHVALUESARBPROC) (void); #endif /* GL_ARB_sample_locations */ #ifndef GL_ARB_sample_shading #define GL_ARB_sample_shading 1 #define GL_SAMPLE_SHADING_ARB 0x8C36 #define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); #endif /* GL_ARB_sample_shading */ #ifndef GL_ARB_sampler_objects #define GL_ARB_sampler_objects 1 #endif /* GL_ARB_sampler_objects */ #ifndef GL_ARB_seamless_cube_map #define GL_ARB_seamless_cube_map 1 #endif /* GL_ARB_seamless_cube_map */ #ifndef GL_ARB_seamless_cubemap_per_texture #define GL_ARB_seamless_cubemap_per_texture 1 #endif /* GL_ARB_seamless_cubemap_per_texture */ #ifndef GL_ARB_separate_shader_objects #define GL_ARB_separate_shader_objects 1 #endif /* GL_ARB_separate_shader_objects */ #ifndef GL_ARB_shader_atomic_counter_ops #define GL_ARB_shader_atomic_counter_ops 1 #endif /* GL_ARB_shader_atomic_counter_ops */ #ifndef GL_ARB_shader_atomic_counters #define GL_ARB_shader_atomic_counters 1 #endif /* GL_ARB_shader_atomic_counters */ #ifndef GL_ARB_shader_ballot #define GL_ARB_shader_ballot 1 #endif /* GL_ARB_shader_ballot */ #ifndef GL_ARB_shader_bit_encoding #define GL_ARB_shader_bit_encoding 1 #endif /* GL_ARB_shader_bit_encoding */ #ifndef GL_ARB_shader_clock #define GL_ARB_shader_clock 1 #endif /* GL_ARB_shader_clock */ #ifndef GL_ARB_shader_draw_parameters #define GL_ARB_shader_draw_parameters 1 #endif /* GL_ARB_shader_draw_parameters */ #ifndef GL_ARB_shader_group_vote #define GL_ARB_shader_group_vote 1 #endif /* GL_ARB_shader_group_vote */ #ifndef GL_ARB_shader_image_load_store #define GL_ARB_shader_image_load_store 1 #endif /* GL_ARB_shader_image_load_store */ #ifndef GL_ARB_shader_image_size #define GL_ARB_shader_image_size 1 #endif /* GL_ARB_shader_image_size */ #ifndef GL_ARB_shader_precision #define GL_ARB_shader_precision 1 #endif /* GL_ARB_shader_precision */ #ifndef GL_ARB_shader_stencil_export #define GL_ARB_shader_stencil_export 1 #endif /* GL_ARB_shader_stencil_export */ #ifndef GL_ARB_shader_storage_buffer_object #define GL_ARB_shader_storage_buffer_object 1 #endif /* GL_ARB_shader_storage_buffer_object */ #ifndef GL_ARB_shader_subroutine #define GL_ARB_shader_subroutine 1 #endif /* GL_ARB_shader_subroutine */ #ifndef GL_ARB_shader_texture_image_samples #define GL_ARB_shader_texture_image_samples 1 #endif /* GL_ARB_shader_texture_image_samples */ #ifndef GL_ARB_shader_viewport_layer_array #define GL_ARB_shader_viewport_layer_array 1 #endif /* GL_ARB_shader_viewport_layer_array */ #ifndef GL_ARB_shading_language_420pack #define GL_ARB_shading_language_420pack 1 #endif /* GL_ARB_shading_language_420pack */ #ifndef GL_ARB_shading_language_include #define GL_ARB_shading_language_include 1 #define GL_SHADER_INCLUDE_ARB 0x8DAE #define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 #define GL_NAMED_STRING_TYPE_ARB 0x8DEA typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); #endif /* GL_ARB_shading_language_include */ #ifndef GL_ARB_shading_language_packing #define GL_ARB_shading_language_packing 1 #endif /* GL_ARB_shading_language_packing */ #ifndef GL_ARB_sparse_buffer #define GL_ARB_sparse_buffer 1 #define GL_SPARSE_STORAGE_BIT_ARB 0x0400 #define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); #endif /* GL_ARB_sparse_buffer */ #ifndef GL_ARB_sparse_texture #define GL_ARB_sparse_texture 1 #define GL_TEXTURE_SPARSE_ARB 0x91A6 #define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 #define GL_NUM_SPARSE_LEVELS_ARB 0x91AA #define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 #define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 #define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 #define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 #define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 #define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 #define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A #define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); #endif /* GL_ARB_sparse_texture */ #ifndef GL_ARB_sparse_texture2 #define GL_ARB_sparse_texture2 1 #endif /* GL_ARB_sparse_texture2 */ #ifndef GL_ARB_sparse_texture_clamp #define GL_ARB_sparse_texture_clamp 1 #endif /* GL_ARB_sparse_texture_clamp */ #ifndef GL_ARB_spirv_extensions #define GL_ARB_spirv_extensions 1 #endif /* GL_ARB_spirv_extensions */ #ifndef GL_ARB_stencil_texturing #define GL_ARB_stencil_texturing 1 #endif /* GL_ARB_stencil_texturing */ #ifndef GL_ARB_sync #define GL_ARB_sync 1 #endif /* GL_ARB_sync */ #ifndef GL_ARB_tessellation_shader #define GL_ARB_tessellation_shader 1 #endif /* GL_ARB_tessellation_shader */ #ifndef GL_ARB_texture_barrier #define GL_ARB_texture_barrier 1 #endif /* GL_ARB_texture_barrier */ #ifndef GL_ARB_texture_border_clamp #define GL_ARB_texture_border_clamp 1 #define GL_CLAMP_TO_BORDER_ARB 0x812D #endif /* GL_ARB_texture_border_clamp */ #ifndef GL_ARB_texture_buffer_object #define GL_ARB_texture_buffer_object 1 #define GL_TEXTURE_BUFFER_ARB 0x8C2A #define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B #define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C #define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D #define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); #endif /* GL_ARB_texture_buffer_object */ #ifndef GL_ARB_texture_buffer_object_rgb32 #define GL_ARB_texture_buffer_object_rgb32 1 #endif /* GL_ARB_texture_buffer_object_rgb32 */ #ifndef GL_ARB_texture_buffer_range #define GL_ARB_texture_buffer_range 1 #endif /* GL_ARB_texture_buffer_range */ #ifndef GL_ARB_texture_compression_bptc #define GL_ARB_texture_compression_bptc 1 #define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F #endif /* GL_ARB_texture_compression_bptc */ #ifndef GL_ARB_texture_compression_rgtc #define GL_ARB_texture_compression_rgtc 1 #endif /* GL_ARB_texture_compression_rgtc */ #ifndef GL_ARB_texture_cube_map_array #define GL_ARB_texture_cube_map_array 1 #define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 #define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A #define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B #define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C #define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D #define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E #define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F #endif /* GL_ARB_texture_cube_map_array */ #ifndef GL_ARB_texture_filter_anisotropic #define GL_ARB_texture_filter_anisotropic 1 #endif /* GL_ARB_texture_filter_anisotropic */ #ifndef GL_ARB_texture_filter_minmax #define GL_ARB_texture_filter_minmax 1 #define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 #define GL_WEIGHTED_AVERAGE_ARB 0x9367 #endif /* GL_ARB_texture_filter_minmax */ #ifndef GL_ARB_texture_gather #define GL_ARB_texture_gather 1 #define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E #define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F #define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F #endif /* GL_ARB_texture_gather */ #ifndef GL_ARB_texture_mirror_clamp_to_edge #define GL_ARB_texture_mirror_clamp_to_edge 1 #endif /* GL_ARB_texture_mirror_clamp_to_edge */ #ifndef GL_ARB_texture_mirrored_repeat #define GL_ARB_texture_mirrored_repeat 1 #define GL_MIRRORED_REPEAT_ARB 0x8370 #endif /* GL_ARB_texture_mirrored_repeat */ #ifndef GL_ARB_texture_multisample #define GL_ARB_texture_multisample 1 #endif /* GL_ARB_texture_multisample */ #ifndef GL_ARB_texture_non_power_of_two #define GL_ARB_texture_non_power_of_two 1 #endif /* GL_ARB_texture_non_power_of_two */ #ifndef GL_ARB_texture_query_levels #define GL_ARB_texture_query_levels 1 #endif /* GL_ARB_texture_query_levels */ #ifndef GL_ARB_texture_query_lod #define GL_ARB_texture_query_lod 1 #endif /* GL_ARB_texture_query_lod */ #ifndef GL_ARB_texture_rg #define GL_ARB_texture_rg 1 #endif /* GL_ARB_texture_rg */ #ifndef GL_ARB_texture_rgb10_a2ui #define GL_ARB_texture_rgb10_a2ui 1 #endif /* GL_ARB_texture_rgb10_a2ui */ #ifndef GL_ARB_texture_stencil8 #define GL_ARB_texture_stencil8 1 #endif /* GL_ARB_texture_stencil8 */ #ifndef GL_ARB_texture_storage #define GL_ARB_texture_storage 1 #endif /* GL_ARB_texture_storage */ #ifndef GL_ARB_texture_storage_multisample #define GL_ARB_texture_storage_multisample 1 #endif /* GL_ARB_texture_storage_multisample */ #ifndef GL_ARB_texture_swizzle #define GL_ARB_texture_swizzle 1 #endif /* GL_ARB_texture_swizzle */ #ifndef GL_ARB_texture_view #define GL_ARB_texture_view 1 #endif /* GL_ARB_texture_view */ #ifndef GL_ARB_timer_query #define GL_ARB_timer_query 1 #endif /* GL_ARB_timer_query */ #ifndef GL_ARB_transform_feedback2 #define GL_ARB_transform_feedback2 1 #endif /* GL_ARB_transform_feedback2 */ #ifndef GL_ARB_transform_feedback3 #define GL_ARB_transform_feedback3 1 #endif /* GL_ARB_transform_feedback3 */ #ifndef GL_ARB_transform_feedback_instanced #define GL_ARB_transform_feedback_instanced 1 #endif /* GL_ARB_transform_feedback_instanced */ #ifndef GL_ARB_transform_feedback_overflow_query #define GL_ARB_transform_feedback_overflow_query 1 #define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC #define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED #endif /* GL_ARB_transform_feedback_overflow_query */ #ifndef GL_ARB_uniform_buffer_object #define GL_ARB_uniform_buffer_object 1 #endif /* GL_ARB_uniform_buffer_object */ #ifndef GL_ARB_vertex_array_bgra #define GL_ARB_vertex_array_bgra 1 #endif /* GL_ARB_vertex_array_bgra */ #ifndef GL_ARB_vertex_array_object #define GL_ARB_vertex_array_object 1 #endif /* GL_ARB_vertex_array_object */ #ifndef GL_ARB_vertex_attrib_64bit #define GL_ARB_vertex_attrib_64bit 1 #endif /* GL_ARB_vertex_attrib_64bit */ #ifndef GL_ARB_vertex_attrib_binding #define GL_ARB_vertex_attrib_binding 1 #endif /* GL_ARB_vertex_attrib_binding */ #ifndef GL_ARB_vertex_type_10f_11f_11f_rev #define GL_ARB_vertex_type_10f_11f_11f_rev 1 #endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ #ifndef GL_ARB_vertex_type_2_10_10_10_rev #define GL_ARB_vertex_type_2_10_10_10_rev 1 #endif /* GL_ARB_vertex_type_2_10_10_10_rev */ #ifndef GL_ARB_viewport_array #define GL_ARB_viewport_array 1 typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYDVNVPROC) (GLuint first, GLsizei count, const GLdouble *v); typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC) (GLuint index, GLdouble n, GLdouble f); #endif /* GL_ARB_viewport_array */ #ifndef GL_KHR_blend_equation_advanced #define GL_KHR_blend_equation_advanced 1 #define GL_MULTIPLY_KHR 0x9294 #define GL_SCREEN_KHR 0x9295 #define GL_OVERLAY_KHR 0x9296 #define GL_DARKEN_KHR 0x9297 #define GL_LIGHTEN_KHR 0x9298 #define GL_COLORDODGE_KHR 0x9299 #define GL_COLORBURN_KHR 0x929A #define GL_HARDLIGHT_KHR 0x929B #define GL_SOFTLIGHT_KHR 0x929C #define GL_DIFFERENCE_KHR 0x929E #define GL_EXCLUSION_KHR 0x92A0 #define GL_HSL_HUE_KHR 0x92AD #define GL_HSL_SATURATION_KHR 0x92AE #define GL_HSL_COLOR_KHR 0x92AF #define GL_HSL_LUMINOSITY_KHR 0x92B0 typedef void (APIENTRYP PFNGLBLENDBARRIERKHRPROC) (void); #endif /* GL_KHR_blend_equation_advanced */ #ifndef GL_KHR_blend_equation_advanced_coherent #define GL_KHR_blend_equation_advanced_coherent 1 #define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 #endif /* GL_KHR_blend_equation_advanced_coherent */ #ifndef GL_KHR_context_flush_control #define GL_KHR_context_flush_control 1 #endif /* GL_KHR_context_flush_control */ #ifndef GL_KHR_debug #define GL_KHR_debug 1 #endif /* GL_KHR_debug */ #ifndef GL_KHR_no_error #define GL_KHR_no_error 1 #define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 #endif /* GL_KHR_no_error */ #ifndef GL_KHR_parallel_shader_compile #define GL_KHR_parallel_shader_compile 1 #define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 #define GL_COMPLETION_STATUS_KHR 0x91B1 typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) (GLuint count); #endif /* GL_KHR_parallel_shader_compile */ #ifndef GL_KHR_robust_buffer_access_behavior #define GL_KHR_robust_buffer_access_behavior 1 #endif /* GL_KHR_robust_buffer_access_behavior */ #ifndef GL_KHR_robustness #define GL_KHR_robustness 1 #define GL_CONTEXT_ROBUST_ACCESS 0x90F3 #endif /* GL_KHR_robustness */ #ifndef GL_KHR_shader_subgroup #define GL_KHR_shader_subgroup 1 #define GL_SUBGROUP_SIZE_KHR 0x9532 #define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 #define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 #define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 #define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 #define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 #define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 #define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 #define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 #define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 #define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 #define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 #endif /* GL_KHR_shader_subgroup */ #ifndef GL_KHR_texture_compression_astc_hdr #define GL_KHR_texture_compression_astc_hdr 1 #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 #define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 #define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 #define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 #define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 #define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 #define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 #define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 #define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 #define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA #define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB #define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC #define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD #endif /* GL_KHR_texture_compression_astc_hdr */ #ifndef GL_KHR_texture_compression_astc_ldr #define GL_KHR_texture_compression_astc_ldr 1 #endif /* GL_KHR_texture_compression_astc_ldr */ #ifndef GL_KHR_texture_compression_astc_sliced_3d #define GL_KHR_texture_compression_astc_sliced_3d 1 #endif /* GL_KHR_texture_compression_astc_sliced_3d */ #ifndef GL_AMD_framebuffer_multisample_advanced #define GL_AMD_framebuffer_multisample_advanced 1 #define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2 #define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3 #define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4 #define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5 #define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6 #define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7 typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); #endif /* GL_AMD_framebuffer_multisample_advanced */ #ifndef GL_AMD_performance_monitor #define GL_AMD_performance_monitor 1 #define GL_COUNTER_TYPE_AMD 0x8BC0 #define GL_COUNTER_RANGE_AMD 0x8BC1 #define GL_UNSIGNED_INT64_AMD 0x8BC2 #define GL_PERCENTAGE_AMD 0x8BC3 #define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 #define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 #define GL_PERFMON_RESULT_AMD 0x8BC6 typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); #endif /* GL_AMD_performance_monitor */ #ifndef GL_APPLE_rgb_422 #define GL_APPLE_rgb_422 1 #define GL_RGB_422_APPLE 0x8A1F #define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA #define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB #define GL_RGB_RAW_422_APPLE 0x8A51 #endif /* GL_APPLE_rgb_422 */ #ifndef GL_EXT_EGL_image_storage #define GL_EXT_EGL_image_storage 1 typedef void *GLeglImageOES; typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) (GLenum target, GLeglImageOES image, const GLint* attrib_list); typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) (GLuint texture, GLeglImageOES image, const GLint* attrib_list); #endif /* GL_EXT_EGL_image_storage */ #ifndef GL_EXT_EGL_sync #define GL_EXT_EGL_sync 1 #endif /* GL_EXT_EGL_sync */ #ifndef GL_EXT_debug_label #define GL_EXT_debug_label 1 #define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F #define GL_PROGRAM_OBJECT_EXT 0x8B40 #define GL_SHADER_OBJECT_EXT 0x8B48 #define GL_BUFFER_OBJECT_EXT 0x9151 #define GL_QUERY_OBJECT_EXT 0x9153 #define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); #endif /* GL_EXT_debug_label */ #ifndef GL_EXT_debug_marker #define GL_EXT_debug_marker 1 typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); #endif /* GL_EXT_debug_marker */ #ifndef GL_EXT_direct_state_access #define GL_EXT_direct_state_access 1 #define GL_PROGRAM_MATRIX_EXT 0x8E2D #define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E #define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); #endif /* GL_EXT_direct_state_access */ #ifndef GL_EXT_draw_instanced #define GL_EXT_draw_instanced 1 typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); #endif /* GL_EXT_draw_instanced */ #ifndef GL_EXT_multiview_tessellation_geometry_shader #define GL_EXT_multiview_tessellation_geometry_shader 1 #endif /* GL_EXT_multiview_tessellation_geometry_shader */ #ifndef GL_EXT_multiview_texture_multisample #define GL_EXT_multiview_texture_multisample 1 #endif /* GL_EXT_multiview_texture_multisample */ #ifndef GL_EXT_multiview_timer_query #define GL_EXT_multiview_timer_query 1 #endif /* GL_EXT_multiview_timer_query */ #ifndef GL_EXT_polygon_offset_clamp #define GL_EXT_polygon_offset_clamp 1 #define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPEXTPROC) (GLfloat factor, GLfloat units, GLfloat clamp); #endif /* GL_EXT_polygon_offset_clamp */ #ifndef GL_EXT_post_depth_coverage #define GL_EXT_post_depth_coverage 1 #endif /* GL_EXT_post_depth_coverage */ #ifndef GL_EXT_raster_multisample #define GL_EXT_raster_multisample 1 #define GL_RASTER_MULTISAMPLE_EXT 0x9327 #define GL_RASTER_SAMPLES_EXT 0x9328 #define GL_MAX_RASTER_SAMPLES_EXT 0x9329 #define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A #define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B #define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C typedef void (APIENTRYP PFNGLRASTERSAMPLESEXTPROC) (GLuint samples, GLboolean fixedsamplelocations); #endif /* GL_EXT_raster_multisample */ #ifndef GL_EXT_separate_shader_objects #define GL_EXT_separate_shader_objects 1 #define GL_ACTIVE_PROGRAM_EXT 0x8B8D typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); #endif /* GL_EXT_separate_shader_objects */ #ifndef GL_EXT_shader_framebuffer_fetch #define GL_EXT_shader_framebuffer_fetch 1 #define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 #endif /* GL_EXT_shader_framebuffer_fetch */ #ifndef GL_EXT_shader_framebuffer_fetch_non_coherent #define GL_EXT_shader_framebuffer_fetch_non_coherent 1 typedef void (APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) (void); #endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */ #ifndef GL_EXT_shader_integer_mix #define GL_EXT_shader_integer_mix 1 #endif /* GL_EXT_shader_integer_mix */ #ifndef GL_EXT_texture_compression_s3tc #define GL_EXT_texture_compression_s3tc 1 #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 #endif /* GL_EXT_texture_compression_s3tc */ #ifndef GL_EXT_texture_filter_minmax #define GL_EXT_texture_filter_minmax 1 #define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366 #define GL_WEIGHTED_AVERAGE_EXT 0x9367 #endif /* GL_EXT_texture_filter_minmax */ #ifndef GL_EXT_texture_sRGB_R8 #define GL_EXT_texture_sRGB_R8 1 #define GL_SR8_EXT 0x8FBD #endif /* GL_EXT_texture_sRGB_R8 */ #ifndef GL_EXT_texture_sRGB_RG8 #define GL_EXT_texture_sRGB_RG8 1 #define GL_SRG8_EXT 0x8FBE #endif /* GL_EXT_texture_sRGB_RG8 */ #ifndef GL_EXT_texture_sRGB_decode #define GL_EXT_texture_sRGB_decode 1 #define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 #define GL_DECODE_EXT 0x8A49 #define GL_SKIP_DECODE_EXT 0x8A4A #endif /* GL_EXT_texture_sRGB_decode */ #ifndef GL_EXT_texture_shadow_lod #define GL_EXT_texture_shadow_lod 1 #endif /* GL_EXT_texture_shadow_lod */ #ifndef GL_EXT_window_rectangles #define GL_EXT_window_rectangles 1 #define GL_INCLUSIVE_EXT 0x8F10 #define GL_EXCLUSIVE_EXT 0x8F11 #define GL_WINDOW_RECTANGLE_EXT 0x8F12 #define GL_WINDOW_RECTANGLE_MODE_EXT 0x8F13 #define GL_MAX_WINDOW_RECTANGLES_EXT 0x8F14 #define GL_NUM_WINDOW_RECTANGLES_EXT 0x8F15 typedef void (APIENTRYP PFNGLWINDOWRECTANGLESEXTPROC) (GLenum mode, GLsizei count, const GLint *box); #endif /* GL_EXT_window_rectangles */ #ifndef GL_INTEL_blackhole_render #define GL_INTEL_blackhole_render 1 #define GL_BLACKHOLE_RENDER_INTEL 0x83FC #endif /* GL_INTEL_blackhole_render */ #ifndef GL_INTEL_conservative_rasterization #define GL_INTEL_conservative_rasterization 1 #define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE #endif /* GL_INTEL_conservative_rasterization */ #ifndef GL_INTEL_framebuffer_CMAA #define GL_INTEL_framebuffer_CMAA 1 typedef void (APIENTRYP PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC) (void); #endif /* GL_INTEL_framebuffer_CMAA */ #ifndef GL_INTEL_performance_query #define GL_INTEL_performance_query 1 #define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 #define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 #define GL_PERFQUERY_WAIT_INTEL 0x83FB #define GL_PERFQUERY_FLUSH_INTEL 0x83FA #define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 #define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 #define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 #define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 #define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 #define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 #define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 #define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 #define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 #define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA #define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB #define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC #define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD #define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE #define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF #define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); #endif /* GL_INTEL_performance_query */ #ifndef GL_MESA_framebuffer_flip_x #define GL_MESA_framebuffer_flip_x 1 #define GL_FRAMEBUFFER_FLIP_X_MESA 0x8BBC #endif /* GL_MESA_framebuffer_flip_x */ #ifndef GL_MESA_framebuffer_flip_y #define GL_MESA_framebuffer_flip_y 1 #define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIMESAPROC) (GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC) (GLenum target, GLenum pname, GLint *params); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glFramebufferParameteriMESA (GLenum target, GLenum pname, GLint param); GLAPI void APIENTRY glGetFramebufferParameterivMESA (GLenum target, GLenum pname, GLint *params); #endif #endif /* GL_MESA_framebuffer_flip_y */ #ifndef GL_MESA_framebuffer_swap_xy #define GL_MESA_framebuffer_swap_xy 1 #define GL_FRAMEBUFFER_SWAP_XY_MESA 0x8BBD #endif /* GL_MESA_framebuffer_swap_xy */ #ifndef GL_NV_bindless_multi_draw_indirect #define GL_NV_bindless_multi_draw_indirect 1 typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); #endif /* GL_NV_bindless_multi_draw_indirect */ #ifndef GL_NV_bindless_multi_draw_indirect_count #define GL_NV_bindless_multi_draw_indirect_count 1 typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); #endif /* GL_NV_bindless_multi_draw_indirect_count */ #ifndef GL_NV_bindless_texture #define GL_NV_bindless_texture 1 typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); #endif /* GL_NV_bindless_texture */ #ifndef GL_NV_blend_equation_advanced #define GL_NV_blend_equation_advanced 1 #define GL_BLEND_OVERLAP_NV 0x9281 #define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 #define GL_BLUE_NV 0x1905 #define GL_COLORBURN_NV 0x929A #define GL_COLORDODGE_NV 0x9299 #define GL_CONJOINT_NV 0x9284 #define GL_CONTRAST_NV 0x92A1 #define GL_DARKEN_NV 0x9297 #define GL_DIFFERENCE_NV 0x929E #define GL_DISJOINT_NV 0x9283 #define GL_DST_ATOP_NV 0x928F #define GL_DST_IN_NV 0x928B #define GL_DST_NV 0x9287 #define GL_DST_OUT_NV 0x928D #define GL_DST_OVER_NV 0x9289 #define GL_EXCLUSION_NV 0x92A0 #define GL_GREEN_NV 0x1904 #define GL_HARDLIGHT_NV 0x929B #define GL_HARDMIX_NV 0x92A9 #define GL_HSL_COLOR_NV 0x92AF #define GL_HSL_HUE_NV 0x92AD #define GL_HSL_LUMINOSITY_NV 0x92B0 #define GL_HSL_SATURATION_NV 0x92AE #define GL_INVERT_OVG_NV 0x92B4 #define GL_INVERT_RGB_NV 0x92A3 #define GL_LIGHTEN_NV 0x9298 #define GL_LINEARBURN_NV 0x92A5 #define GL_LINEARDODGE_NV 0x92A4 #define GL_LINEARLIGHT_NV 0x92A7 #define GL_MINUS_CLAMPED_NV 0x92B3 #define GL_MINUS_NV 0x929F #define GL_MULTIPLY_NV 0x9294 #define GL_OVERLAY_NV 0x9296 #define GL_PINLIGHT_NV 0x92A8 #define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 #define GL_PLUS_CLAMPED_NV 0x92B1 #define GL_PLUS_DARKER_NV 0x9292 #define GL_PLUS_NV 0x9291 #define GL_RED_NV 0x1903 #define GL_SCREEN_NV 0x9295 #define GL_SOFTLIGHT_NV 0x929C #define GL_SRC_ATOP_NV 0x928E #define GL_SRC_IN_NV 0x928A #define GL_SRC_NV 0x9286 #define GL_SRC_OUT_NV 0x928C #define GL_SRC_OVER_NV 0x9288 #define GL_UNCORRELATED_NV 0x9282 #define GL_VIVIDLIGHT_NV 0x92A6 #define GL_XOR_NV 0x1506 typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); #endif /* GL_NV_blend_equation_advanced */ #ifndef GL_NV_blend_equation_advanced_coherent #define GL_NV_blend_equation_advanced_coherent 1 #define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 #endif /* GL_NV_blend_equation_advanced_coherent */ #ifndef GL_NV_blend_minmax_factor #define GL_NV_blend_minmax_factor 1 #define GL_FACTOR_MIN_AMD 0x901C #define GL_FACTOR_MAX_AMD 0x901D #endif /* GL_NV_blend_minmax_factor */ #ifndef GL_NV_clip_space_w_scaling #define GL_NV_clip_space_w_scaling 1 #define GL_VIEWPORT_POSITION_W_SCALE_NV 0x937C #define GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV 0x937D #define GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV 0x937E typedef void (APIENTRYP PFNGLVIEWPORTPOSITIONWSCALENVPROC) (GLuint index, GLfloat xcoeff, GLfloat ycoeff); #endif /* GL_NV_clip_space_w_scaling */ #ifndef GL_NV_command_list #define GL_NV_command_list 1 #define GL_TERMINATE_SEQUENCE_COMMAND_NV 0x0000 #define GL_NOP_COMMAND_NV 0x0001 #define GL_DRAW_ELEMENTS_COMMAND_NV 0x0002 #define GL_DRAW_ARRAYS_COMMAND_NV 0x0003 #define GL_DRAW_ELEMENTS_STRIP_COMMAND_NV 0x0004 #define GL_DRAW_ARRAYS_STRIP_COMMAND_NV 0x0005 #define GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV 0x0006 #define GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV 0x0007 #define GL_ELEMENT_ADDRESS_COMMAND_NV 0x0008 #define GL_ATTRIBUTE_ADDRESS_COMMAND_NV 0x0009 #define GL_UNIFORM_ADDRESS_COMMAND_NV 0x000A #define GL_BLEND_COLOR_COMMAND_NV 0x000B #define GL_STENCIL_REF_COMMAND_NV 0x000C #define GL_LINE_WIDTH_COMMAND_NV 0x000D #define GL_POLYGON_OFFSET_COMMAND_NV 0x000E #define GL_ALPHA_REF_COMMAND_NV 0x000F #define GL_VIEWPORT_COMMAND_NV 0x0010 #define GL_SCISSOR_COMMAND_NV 0x0011 #define GL_FRONT_FACE_COMMAND_NV 0x0012 typedef void (APIENTRYP PFNGLCREATESTATESNVPROC) (GLsizei n, GLuint *states); typedef void (APIENTRYP PFNGLDELETESTATESNVPROC) (GLsizei n, const GLuint *states); typedef GLboolean (APIENTRYP PFNGLISSTATENVPROC) (GLuint state); typedef void (APIENTRYP PFNGLSTATECAPTURENVPROC) (GLuint state, GLenum mode); typedef GLuint (APIENTRYP PFNGLGETCOMMANDHEADERNVPROC) (GLenum tokenID, GLuint size); typedef GLushort (APIENTRYP PFNGLGETSTAGEINDEXNVPROC) (GLenum shadertype); typedef void (APIENTRYP PFNGLDRAWCOMMANDSNVPROC) (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); typedef void (APIENTRYP PFNGLDRAWCOMMANDSADDRESSNVPROC) (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESNVPROC) (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC) (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); typedef void (APIENTRYP PFNGLCREATECOMMANDLISTSNVPROC) (GLsizei n, GLuint *lists); typedef void (APIENTRYP PFNGLDELETECOMMANDLISTSNVPROC) (GLsizei n, const GLuint *lists); typedef GLboolean (APIENTRYP PFNGLISCOMMANDLISTNVPROC) (GLuint list); typedef void (APIENTRYP PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC) (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); typedef void (APIENTRYP PFNGLCOMMANDLISTSEGMENTSNVPROC) (GLuint list, GLuint segments); typedef void (APIENTRYP PFNGLCOMPILECOMMANDLISTNVPROC) (GLuint list); typedef void (APIENTRYP PFNGLCALLCOMMANDLISTNVPROC) (GLuint list); #endif /* GL_NV_command_list */ #ifndef GL_NV_compute_shader_derivatives #define GL_NV_compute_shader_derivatives 1 #endif /* GL_NV_compute_shader_derivatives */ #ifndef GL_NV_conditional_render #define GL_NV_conditional_render 1 #define GL_QUERY_WAIT_NV 0x8E13 #define GL_QUERY_NO_WAIT_NV 0x8E14 #define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 #define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); #endif /* GL_NV_conditional_render */ #ifndef GL_NV_conservative_raster #define GL_NV_conservative_raster 1 #define GL_CONSERVATIVE_RASTERIZATION_NV 0x9346 #define GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV 0x9347 #define GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV 0x9348 #define GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV 0x9349 typedef void (APIENTRYP PFNGLSUBPIXELPRECISIONBIASNVPROC) (GLuint xbits, GLuint ybits); #endif /* GL_NV_conservative_raster */ #ifndef GL_NV_conservative_raster_dilate #define GL_NV_conservative_raster_dilate 1 #define GL_CONSERVATIVE_RASTER_DILATE_NV 0x9379 #define GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV 0x937A #define GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV 0x937B typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERFNVPROC) (GLenum pname, GLfloat value); #endif /* GL_NV_conservative_raster_dilate */ #ifndef GL_NV_conservative_raster_pre_snap #define GL_NV_conservative_raster_pre_snap 1 #define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550 #endif /* GL_NV_conservative_raster_pre_snap */ #ifndef GL_NV_conservative_raster_pre_snap_triangles #define GL_NV_conservative_raster_pre_snap_triangles 1 #define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D #define GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV 0x954E #define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV 0x954F typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERINVPROC) (GLenum pname, GLint param); #endif /* GL_NV_conservative_raster_pre_snap_triangles */ #ifndef GL_NV_conservative_raster_underestimation #define GL_NV_conservative_raster_underestimation 1 #endif /* GL_NV_conservative_raster_underestimation */ #ifndef GL_NV_depth_buffer_float #define GL_NV_depth_buffer_float 1 #define GL_DEPTH_COMPONENT32F_NV 0x8DAB #define GL_DEPTH32F_STENCIL8_NV 0x8DAC #define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD #define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); #endif /* GL_NV_depth_buffer_float */ #ifndef GL_NV_draw_vulkan_image #define GL_NV_draw_vulkan_image 1 typedef void (APIENTRY *GLVULKANPROCNV)(void); typedef void (APIENTRYP PFNGLDRAWVKIMAGENVPROC) (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); typedef GLVULKANPROCNV (APIENTRYP PFNGLGETVKPROCADDRNVPROC) (const GLchar *name); typedef void (APIENTRYP PFNGLWAITVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); typedef void (APIENTRYP PFNGLSIGNALVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); typedef void (APIENTRYP PFNGLSIGNALVKFENCENVPROC) (GLuint64 vkFence); #endif /* GL_NV_draw_vulkan_image */ #ifndef GL_NV_fill_rectangle #define GL_NV_fill_rectangle 1 #define GL_FILL_RECTANGLE_NV 0x933C #endif /* GL_NV_fill_rectangle */ #ifndef GL_NV_fragment_coverage_to_color #define GL_NV_fragment_coverage_to_color 1 #define GL_FRAGMENT_COVERAGE_TO_COLOR_NV 0x92DD #define GL_FRAGMENT_COVERAGE_COLOR_NV 0x92DE typedef void (APIENTRYP PFNGLFRAGMENTCOVERAGECOLORNVPROC) (GLuint color); #endif /* GL_NV_fragment_coverage_to_color */ #ifndef GL_NV_fragment_shader_barycentric #define GL_NV_fragment_shader_barycentric 1 #endif /* GL_NV_fragment_shader_barycentric */ #ifndef GL_NV_fragment_shader_interlock #define GL_NV_fragment_shader_interlock 1 #endif /* GL_NV_fragment_shader_interlock */ #ifndef GL_NV_framebuffer_mixed_samples #define GL_NV_framebuffer_mixed_samples 1 #define GL_COVERAGE_MODULATION_TABLE_NV 0x9331 #define GL_COLOR_SAMPLES_NV 0x8E20 #define GL_DEPTH_SAMPLES_NV 0x932D #define GL_STENCIL_SAMPLES_NV 0x932E #define GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV 0x932F #define GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV 0x9330 #define GL_COVERAGE_MODULATION_NV 0x9332 #define GL_COVERAGE_MODULATION_TABLE_SIZE_NV 0x9333 typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONTABLENVPROC) (GLsizei n, const GLfloat *v); typedef void (APIENTRYP PFNGLGETCOVERAGEMODULATIONTABLENVPROC) (GLsizei bufSize, GLfloat *v); typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONNVPROC) (GLenum components); #endif /* GL_NV_framebuffer_mixed_samples */ #ifndef GL_NV_framebuffer_multisample_coverage #define GL_NV_framebuffer_multisample_coverage 1 #define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB #define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 #define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 #define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); #endif /* GL_NV_framebuffer_multisample_coverage */ #ifndef GL_NV_geometry_shader_passthrough #define GL_NV_geometry_shader_passthrough 1 #endif /* GL_NV_geometry_shader_passthrough */ #ifndef GL_NV_gpu_shader5 #define GL_NV_gpu_shader5 1 typedef khronos_int64_t GLint64EXT; #define GL_INT64_NV 0x140E #define GL_UNSIGNED_INT64_NV 0x140F #define GL_INT8_NV 0x8FE0 #define GL_INT8_VEC2_NV 0x8FE1 #define GL_INT8_VEC3_NV 0x8FE2 #define GL_INT8_VEC4_NV 0x8FE3 #define GL_INT16_NV 0x8FE4 #define GL_INT16_VEC2_NV 0x8FE5 #define GL_INT16_VEC3_NV 0x8FE6 #define GL_INT16_VEC4_NV 0x8FE7 #define GL_INT64_VEC2_NV 0x8FE9 #define GL_INT64_VEC3_NV 0x8FEA #define GL_INT64_VEC4_NV 0x8FEB #define GL_UNSIGNED_INT8_NV 0x8FEC #define GL_UNSIGNED_INT8_VEC2_NV 0x8FED #define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE #define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF #define GL_UNSIGNED_INT16_NV 0x8FF0 #define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 #define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 #define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 #define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 #define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 #define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 #define GL_FLOAT16_NV 0x8FF8 #define GL_FLOAT16_VEC2_NV 0x8FF9 #define GL_FLOAT16_VEC3_NV 0x8FFA #define GL_FLOAT16_VEC4_NV 0x8FFB typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); #endif /* GL_NV_gpu_shader5 */ #ifndef GL_NV_internalformat_sample_query #define GL_NV_internalformat_sample_query 1 #define GL_MULTISAMPLES_NV 0x9371 #define GL_SUPERSAMPLE_SCALE_X_NV 0x9372 #define GL_SUPERSAMPLE_SCALE_Y_NV 0x9373 #define GL_CONFORMANT_NV 0x9374 typedef void (APIENTRYP PFNGLGETINTERNALFORMATSAMPLEIVNVPROC) (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); #endif /* GL_NV_internalformat_sample_query */ #ifndef GL_NV_memory_attachment #define GL_NV_memory_attachment 1 #define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4 #define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5 #define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6 #define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7 #define GL_MEMORY_ATTACHABLE_NV 0x95A8 #define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9 #define GL_DETACHED_TEXTURES_NV 0x95AA #define GL_DETACHED_BUFFERS_NV 0x95AB #define GL_MAX_DETACHED_TEXTURES_NV 0x95AC #define GL_MAX_DETACHED_BUFFERS_NV 0x95AD typedef void (APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC) (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); typedef void (APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC) (GLuint memory, GLenum pname); typedef void (APIENTRYP PFNGLTEXATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); typedef void (APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); typedef void (APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC) (GLuint texture, GLuint memory, GLuint64 offset); typedef void (APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC) (GLuint buffer, GLuint memory, GLuint64 offset); #endif /* GL_NV_memory_attachment */ #ifndef GL_NV_memory_object_sparse #define GL_NV_memory_object_sparse 1 typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTMEMNVPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTMEMNVPROC) (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC) (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); #endif /* GL_NV_memory_object_sparse */ #ifndef GL_NV_mesh_shader #define GL_NV_mesh_shader 1 #define GL_MESH_SHADER_NV 0x9559 #define GL_TASK_SHADER_NV 0x955A #define GL_MAX_MESH_UNIFORM_BLOCKS_NV 0x8E60 #define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61 #define GL_MAX_MESH_IMAGE_UNIFORMS_NV 0x8E62 #define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63 #define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64 #define GL_MAX_MESH_ATOMIC_COUNTERS_NV 0x8E65 #define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66 #define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67 #define GL_MAX_TASK_UNIFORM_BLOCKS_NV 0x8E68 #define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69 #define GL_MAX_TASK_IMAGE_UNIFORMS_NV 0x8E6A #define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B #define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C #define GL_MAX_TASK_ATOMIC_COUNTERS_NV 0x8E6D #define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E #define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F #define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2 #define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3 #define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV 0x9536 #define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV 0x9537 #define GL_MAX_MESH_OUTPUT_VERTICES_NV 0x9538 #define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV 0x9539 #define GL_MAX_TASK_OUTPUT_COUNT_NV 0x953A #define GL_MAX_DRAW_MESH_TASKS_COUNT_NV 0x953D #define GL_MAX_MESH_VIEWS_NV 0x9557 #define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF #define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543 #define GL_MAX_MESH_WORK_GROUP_SIZE_NV 0x953B #define GL_MAX_TASK_WORK_GROUP_SIZE_NV 0x953C #define GL_MESH_WORK_GROUP_SIZE_NV 0x953E #define GL_TASK_WORK_GROUP_SIZE_NV 0x953F #define GL_MESH_VERTICES_OUT_NV 0x9579 #define GL_MESH_PRIMITIVES_OUT_NV 0x957A #define GL_MESH_OUTPUT_TYPE_NV 0x957B #define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C #define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D #define GL_REFERENCED_BY_MESH_SHADER_NV 0x95A0 #define GL_REFERENCED_BY_TASK_SHADER_NV 0x95A1 #define GL_MESH_SHADER_BIT_NV 0x00000040 #define GL_TASK_SHADER_BIT_NV 0x00000080 #define GL_MESH_SUBROUTINE_NV 0x957C #define GL_TASK_SUBROUTINE_NV 0x957D #define GL_MESH_SUBROUTINE_UNIFORM_NV 0x957E #define GL_TASK_SUBROUTINE_UNIFORM_NV 0x957F #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F typedef void (APIENTRYP PFNGLDRAWMESHTASKSNVPROC) (GLuint first, GLuint count); typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect); typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride); typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); #endif /* GL_NV_mesh_shader */ #ifndef GL_NV_path_rendering #define GL_NV_path_rendering 1 #define GL_PATH_FORMAT_SVG_NV 0x9070 #define GL_PATH_FORMAT_PS_NV 0x9071 #define GL_STANDARD_FONT_NAME_NV 0x9072 #define GL_SYSTEM_FONT_NAME_NV 0x9073 #define GL_FILE_NAME_NV 0x9074 #define GL_PATH_STROKE_WIDTH_NV 0x9075 #define GL_PATH_END_CAPS_NV 0x9076 #define GL_PATH_INITIAL_END_CAP_NV 0x9077 #define GL_PATH_TERMINAL_END_CAP_NV 0x9078 #define GL_PATH_JOIN_STYLE_NV 0x9079 #define GL_PATH_MITER_LIMIT_NV 0x907A #define GL_PATH_DASH_CAPS_NV 0x907B #define GL_PATH_INITIAL_DASH_CAP_NV 0x907C #define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D #define GL_PATH_DASH_OFFSET_NV 0x907E #define GL_PATH_CLIENT_LENGTH_NV 0x907F #define GL_PATH_FILL_MODE_NV 0x9080 #define GL_PATH_FILL_MASK_NV 0x9081 #define GL_PATH_FILL_COVER_MODE_NV 0x9082 #define GL_PATH_STROKE_COVER_MODE_NV 0x9083 #define GL_PATH_STROKE_MASK_NV 0x9084 #define GL_COUNT_UP_NV 0x9088 #define GL_COUNT_DOWN_NV 0x9089 #define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A #define GL_CONVEX_HULL_NV 0x908B #define GL_BOUNDING_BOX_NV 0x908D #define GL_TRANSLATE_X_NV 0x908E #define GL_TRANSLATE_Y_NV 0x908F #define GL_TRANSLATE_2D_NV 0x9090 #define GL_TRANSLATE_3D_NV 0x9091 #define GL_AFFINE_2D_NV 0x9092 #define GL_AFFINE_3D_NV 0x9094 #define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 #define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 #define GL_UTF8_NV 0x909A #define GL_UTF16_NV 0x909B #define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C #define GL_PATH_COMMAND_COUNT_NV 0x909D #define GL_PATH_COORD_COUNT_NV 0x909E #define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F #define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 #define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 #define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 #define GL_SQUARE_NV 0x90A3 #define GL_ROUND_NV 0x90A4 #define GL_TRIANGULAR_NV 0x90A5 #define GL_BEVEL_NV 0x90A6 #define GL_MITER_REVERT_NV 0x90A7 #define GL_MITER_TRUNCATE_NV 0x90A8 #define GL_SKIP_MISSING_GLYPH_NV 0x90A9 #define GL_USE_MISSING_GLYPH_NV 0x90AA #define GL_PATH_ERROR_POSITION_NV 0x90AB #define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD #define GL_ADJACENT_PAIRS_NV 0x90AE #define GL_FIRST_TO_REST_NV 0x90AF #define GL_PATH_GEN_MODE_NV 0x90B0 #define GL_PATH_GEN_COEFF_NV 0x90B1 #define GL_PATH_GEN_COMPONENTS_NV 0x90B3 #define GL_PATH_STENCIL_FUNC_NV 0x90B7 #define GL_PATH_STENCIL_REF_NV 0x90B8 #define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 #define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD #define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE #define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF #define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 #define GL_MOVE_TO_RESETS_NV 0x90B5 #define GL_MOVE_TO_CONTINUES_NV 0x90B6 #define GL_CLOSE_PATH_NV 0x00 #define GL_MOVE_TO_NV 0x02 #define GL_RELATIVE_MOVE_TO_NV 0x03 #define GL_LINE_TO_NV 0x04 #define GL_RELATIVE_LINE_TO_NV 0x05 #define GL_HORIZONTAL_LINE_TO_NV 0x06 #define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 #define GL_VERTICAL_LINE_TO_NV 0x08 #define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 #define GL_QUADRATIC_CURVE_TO_NV 0x0A #define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B #define GL_CUBIC_CURVE_TO_NV 0x0C #define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D #define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E #define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F #define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 #define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 #define GL_SMALL_CCW_ARC_TO_NV 0x12 #define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 #define GL_SMALL_CW_ARC_TO_NV 0x14 #define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 #define GL_LARGE_CCW_ARC_TO_NV 0x16 #define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 #define GL_LARGE_CW_ARC_TO_NV 0x18 #define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 #define GL_RESTART_PATH_NV 0xF0 #define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 #define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 #define GL_RECT_NV 0xF6 #define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 #define GL_CIRCULAR_CW_ARC_TO_NV 0xFA #define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC #define GL_ARC_TO_NV 0xFE #define GL_RELATIVE_ARC_TO_NV 0xFF #define GL_BOLD_BIT_NV 0x01 #define GL_ITALIC_BIT_NV 0x02 #define GL_GLYPH_WIDTH_BIT_NV 0x01 #define GL_GLYPH_HEIGHT_BIT_NV 0x02 #define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 #define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 #define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 #define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 #define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 #define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 #define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 #define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 #define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 #define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 #define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 #define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 #define GL_FONT_ASCENDER_BIT_NV 0x00200000 #define GL_FONT_DESCENDER_BIT_NV 0x00400000 #define GL_FONT_HEIGHT_BIT_NV 0x00800000 #define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 #define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 #define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 #define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 #define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 #define GL_ROUNDED_RECT_NV 0xE8 #define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 #define GL_ROUNDED_RECT2_NV 0xEA #define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB #define GL_ROUNDED_RECT4_NV 0xEC #define GL_RELATIVE_ROUNDED_RECT4_NV 0xED #define GL_ROUNDED_RECT8_NV 0xEE #define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF #define GL_RELATIVE_RECT_NV 0xF7 #define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 #define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 #define GL_FONT_UNAVAILABLE_NV 0x936A #define GL_FONT_UNINTELLIGIBLE_NV 0x936B #define GL_CONIC_CURVE_TO_NV 0x1A #define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B #define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 #define GL_STANDARD_FONT_FORMAT_NV 0x936C #define GL_PATH_PROJECTION_NV 0x1701 #define GL_PATH_MODELVIEW_NV 0x1700 #define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 #define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 #define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 #define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 #define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 #define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 #define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 #define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 #define GL_FRAGMENT_INPUT_NV 0x936D typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); typedef void (APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); typedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); typedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); #endif /* GL_NV_path_rendering */ #ifndef GL_NV_path_rendering_shared_edge #define GL_NV_path_rendering_shared_edge 1 #define GL_SHARED_EDGE_NV 0xC0 #endif /* GL_NV_path_rendering_shared_edge */ #ifndef GL_NV_primitive_shading_rate #define GL_NV_primitive_shading_rate 1 #define GL_SHADING_RATE_IMAGE_PER_PRIMITIVE_NV 0x95B1 #define GL_SHADING_RATE_IMAGE_PALETTE_COUNT_NV 0x95B2 #endif /* GL_NV_primitive_shading_rate */ #ifndef GL_NV_representative_fragment_test #define GL_NV_representative_fragment_test 1 #define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F #endif /* GL_NV_representative_fragment_test */ #ifndef GL_NV_sample_locations #define GL_NV_sample_locations 1 #define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV 0x933D #define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV 0x933E #define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV 0x933F #define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV 0x9340 #define GL_SAMPLE_LOCATION_NV 0x8E50 #define GL_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9341 #define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV 0x9342 #define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV 0x9343 typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); typedef void (APIENTRYP PFNGLRESOLVEDEPTHVALUESNVPROC) (void); #endif /* GL_NV_sample_locations */ #ifndef GL_NV_sample_mask_override_coverage #define GL_NV_sample_mask_override_coverage 1 #endif /* GL_NV_sample_mask_override_coverage */ #ifndef GL_NV_scissor_exclusive #define GL_NV_scissor_exclusive 1 #define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555 #define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556 typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC) (GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC) (GLuint first, GLsizei count, const GLint *v); #endif /* GL_NV_scissor_exclusive */ #ifndef GL_NV_shader_atomic_counters #define GL_NV_shader_atomic_counters 1 #endif /* GL_NV_shader_atomic_counters */ #ifndef GL_NV_shader_atomic_float #define GL_NV_shader_atomic_float 1 #endif /* GL_NV_shader_atomic_float */ #ifndef GL_NV_shader_atomic_float64 #define GL_NV_shader_atomic_float64 1 #endif /* GL_NV_shader_atomic_float64 */ #ifndef GL_NV_shader_atomic_fp16_vector #define GL_NV_shader_atomic_fp16_vector 1 #endif /* GL_NV_shader_atomic_fp16_vector */ #ifndef GL_NV_shader_atomic_int64 #define GL_NV_shader_atomic_int64 1 #endif /* GL_NV_shader_atomic_int64 */ #ifndef GL_NV_shader_buffer_load #define GL_NV_shader_buffer_load 1 #define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D #define GL_GPU_ADDRESS_NV 0x8F34 #define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); #endif /* GL_NV_shader_buffer_load */ #ifndef GL_NV_shader_buffer_store #define GL_NV_shader_buffer_store 1 #define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 #endif /* GL_NV_shader_buffer_store */ #ifndef GL_NV_shader_subgroup_partitioned #define GL_NV_shader_subgroup_partitioned 1 #define GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV 0x00000100 #endif /* GL_NV_shader_subgroup_partitioned */ #ifndef GL_NV_shader_texture_footprint #define GL_NV_shader_texture_footprint 1 #endif /* GL_NV_shader_texture_footprint */ #ifndef GL_NV_shader_thread_group #define GL_NV_shader_thread_group 1 #define GL_WARP_SIZE_NV 0x9339 #define GL_WARPS_PER_SM_NV 0x933A #define GL_SM_COUNT_NV 0x933B #endif /* GL_NV_shader_thread_group */ #ifndef GL_NV_shader_thread_shuffle #define GL_NV_shader_thread_shuffle 1 #endif /* GL_NV_shader_thread_shuffle */ #ifndef GL_NV_shading_rate_image #define GL_NV_shading_rate_image 1 #define GL_SHADING_RATE_IMAGE_NV 0x9563 #define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564 #define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565 #define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566 #define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567 #define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568 #define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569 #define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A #define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B #define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C #define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D #define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E #define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F #define GL_SHADING_RATE_IMAGE_BINDING_NV 0x955B #define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C #define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D #define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E #define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F #define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE #define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF #define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0 typedef void (APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC) (GLuint texture); typedef void (APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint entry, GLenum *rate); typedef void (APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC) (GLenum rate, GLuint samples, GLuint index, GLint *location); typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC) (GLboolean synchronize); typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC) (GLenum order); typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC) (GLenum rate, GLuint samples, const GLint *locations); #endif /* GL_NV_shading_rate_image */ #ifndef GL_NV_stereo_view_rendering #define GL_NV_stereo_view_rendering 1 #endif /* GL_NV_stereo_view_rendering */ #ifndef GL_NV_texture_barrier #define GL_NV_texture_barrier 1 typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); #endif /* GL_NV_texture_barrier */ #ifndef GL_NV_texture_rectangle_compressed #define GL_NV_texture_rectangle_compressed 1 #endif /* GL_NV_texture_rectangle_compressed */ #ifndef GL_NV_uniform_buffer_unified_memory #define GL_NV_uniform_buffer_unified_memory 1 #define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E #define GL_UNIFORM_BUFFER_ADDRESS_NV 0x936F #define GL_UNIFORM_BUFFER_LENGTH_NV 0x9370 #endif /* GL_NV_uniform_buffer_unified_memory */ #ifndef GL_NV_vertex_attrib_integer_64bit #define GL_NV_vertex_attrib_integer_64bit 1 typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); #endif /* GL_NV_vertex_attrib_integer_64bit */ #ifndef GL_NV_vertex_buffer_unified_memory #define GL_NV_vertex_buffer_unified_memory 1 #define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E #define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F #define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 #define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 #define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 #define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 #define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 #define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 #define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 #define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 #define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 #define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 #define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A #define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B #define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C #define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D #define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E #define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F #define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 #define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 #define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 #define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 #define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 #define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 #define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); #endif /* GL_NV_vertex_buffer_unified_memory */ #ifndef GL_NV_viewport_array2 #define GL_NV_viewport_array2 1 #endif /* GL_NV_viewport_array2 */ #ifndef GL_NV_viewport_swizzle #define GL_NV_viewport_swizzle 1 #define GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350 #define GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351 #define GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352 #define GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353 #define GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354 #define GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355 #define GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356 #define GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357 #define GL_VIEWPORT_SWIZZLE_X_NV 0x9358 #define GL_VIEWPORT_SWIZZLE_Y_NV 0x9359 #define GL_VIEWPORT_SWIZZLE_Z_NV 0x935A #define GL_VIEWPORT_SWIZZLE_W_NV 0x935B typedef void (APIENTRYP PFNGLVIEWPORTSWIZZLENVPROC) (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); #endif /* GL_NV_viewport_swizzle */ #ifndef GL_OVR_multiview #define GL_OVR_multiview 1 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 #define GL_MAX_VIEWS_OVR 0x9631 #define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); #endif /* GL_OVR_multiview */ #ifndef GL_OVR_multiview2 #define GL_OVR_multiview2 1 #endif /* GL_OVR_multiview2 */ #ifdef __cplusplus } #endif #endif #ifndef GL3W_API #ifdef _MSC_VER #if EXPORT_SYMBOLS == 1 #define GL3W_API __declspec(dllexport) #else #define GL3W_API __declspec(dllimport) #endif #else #define GL3W_API #endif #endif #ifndef __gl_h_ #define __gl_h_ #endif #ifdef __cplusplus extern "C" { #endif #define GL3W_OK 0 #define GL3W_ERROR_INIT -1 #define GL3W_ERROR_LIBRARY_OPEN -2 #define GL3W_ERROR_OPENGL_VERSION -3 typedef void (*GL3WglProc)(void); typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc); /* gl3w api */ GL3W_API int imgl3wInit(void); GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc); GL3W_API void imgl3wShutdown(void); GL3W_API int imgl3wIsSupported(int major, int minor); GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); /* gl3w internal state */ union GL3WProcs { GL3WglProc ptr[659]; struct { PFNGLACTIVESHADERPROGRAMPROC ActiveShaderProgram; PFNGLACTIVETEXTUREPROC ActiveTexture; PFNGLATTACHSHADERPROC AttachShader; PFNGLBEGINCONDITIONALRENDERPROC BeginConditionalRender; PFNGLBEGINQUERYPROC BeginQuery; PFNGLBEGINQUERYINDEXEDPROC BeginQueryIndexed; PFNGLBEGINTRANSFORMFEEDBACKPROC BeginTransformFeedback; PFNGLBINDATTRIBLOCATIONPROC BindAttribLocation; PFNGLBINDBUFFERPROC BindBuffer; PFNGLBINDBUFFERBASEPROC BindBufferBase; PFNGLBINDBUFFERRANGEPROC BindBufferRange; PFNGLBINDBUFFERSBASEPROC BindBuffersBase; PFNGLBINDBUFFERSRANGEPROC BindBuffersRange; PFNGLBINDFRAGDATALOCATIONPROC BindFragDataLocation; PFNGLBINDFRAGDATALOCATIONINDEXEDPROC BindFragDataLocationIndexed; PFNGLBINDFRAMEBUFFERPROC BindFramebuffer; PFNGLBINDIMAGETEXTUREPROC BindImageTexture; PFNGLBINDIMAGETEXTURESPROC BindImageTextures; PFNGLBINDPROGRAMPIPELINEPROC BindProgramPipeline; PFNGLBINDRENDERBUFFERPROC BindRenderbuffer; PFNGLBINDSAMPLERPROC BindSampler; PFNGLBINDSAMPLERSPROC BindSamplers; PFNGLBINDTEXTUREPROC BindTexture; PFNGLBINDTEXTUREUNITPROC BindTextureUnit; PFNGLBINDTEXTURESPROC BindTextures; PFNGLBINDTRANSFORMFEEDBACKPROC BindTransformFeedback; PFNGLBINDVERTEXARRAYPROC BindVertexArray; PFNGLBINDVERTEXBUFFERPROC BindVertexBuffer; PFNGLBINDVERTEXBUFFERSPROC BindVertexBuffers; PFNGLBLENDCOLORPROC BlendColor; PFNGLBLENDEQUATIONPROC BlendEquation; PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate; PFNGLBLENDEQUATIONSEPARATEIPROC BlendEquationSeparatei; PFNGLBLENDEQUATIONIPROC BlendEquationi; PFNGLBLENDFUNCPROC BlendFunc; PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate; PFNGLBLENDFUNCSEPARATEIPROC BlendFuncSeparatei; PFNGLBLENDFUNCIPROC BlendFunci; PFNGLBLITFRAMEBUFFERPROC BlitFramebuffer; PFNGLBLITNAMEDFRAMEBUFFERPROC BlitNamedFramebuffer; PFNGLBUFFERDATAPROC BufferData; PFNGLBUFFERSTORAGEPROC BufferStorage; PFNGLBUFFERSUBDATAPROC BufferSubData; PFNGLCHECKFRAMEBUFFERSTATUSPROC CheckFramebufferStatus; PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC CheckNamedFramebufferStatus; PFNGLCLAMPCOLORPROC ClampColor; PFNGLCLEARPROC Clear; PFNGLCLEARBUFFERDATAPROC ClearBufferData; PFNGLCLEARBUFFERSUBDATAPROC ClearBufferSubData; PFNGLCLEARBUFFERFIPROC ClearBufferfi; PFNGLCLEARBUFFERFVPROC ClearBufferfv; PFNGLCLEARBUFFERIVPROC ClearBufferiv; PFNGLCLEARBUFFERUIVPROC ClearBufferuiv; PFNGLCLEARCOLORPROC ClearColor; PFNGLCLEARDEPTHPROC ClearDepth; PFNGLCLEARDEPTHFPROC ClearDepthf; PFNGLCLEARNAMEDBUFFERDATAPROC ClearNamedBufferData; PFNGLCLEARNAMEDBUFFERSUBDATAPROC ClearNamedBufferSubData; PFNGLCLEARNAMEDFRAMEBUFFERFIPROC ClearNamedFramebufferfi; PFNGLCLEARNAMEDFRAMEBUFFERFVPROC ClearNamedFramebufferfv; PFNGLCLEARNAMEDFRAMEBUFFERIVPROC ClearNamedFramebufferiv; PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC ClearNamedFramebufferuiv; PFNGLCLEARSTENCILPROC ClearStencil; PFNGLCLEARTEXIMAGEPROC ClearTexImage; PFNGLCLEARTEXSUBIMAGEPROC ClearTexSubImage; PFNGLCLIENTWAITSYNCPROC ClientWaitSync; PFNGLCLIPCONTROLPROC ClipControl; PFNGLCOLORMASKPROC ColorMask; PFNGLCOLORMASKIPROC ColorMaski; PFNGLCOMPILESHADERPROC CompileShader; PFNGLCOMPRESSEDTEXIMAGE1DPROC CompressedTexImage1D; PFNGLCOMPRESSEDTEXIMAGE2DPROC CompressedTexImage2D; PFNGLCOMPRESSEDTEXIMAGE3DPROC CompressedTexImage3D; PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC CompressedTexSubImage1D; PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC CompressedTexSubImage2D; PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC CompressedTexSubImage3D; PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC CompressedTextureSubImage1D; PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC CompressedTextureSubImage2D; PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC CompressedTextureSubImage3D; PFNGLCOPYBUFFERSUBDATAPROC CopyBufferSubData; PFNGLCOPYIMAGESUBDATAPROC CopyImageSubData; PFNGLCOPYNAMEDBUFFERSUBDATAPROC CopyNamedBufferSubData; PFNGLCOPYTEXIMAGE1DPROC CopyTexImage1D; PFNGLCOPYTEXIMAGE2DPROC CopyTexImage2D; PFNGLCOPYTEXSUBIMAGE1DPROC CopyTexSubImage1D; PFNGLCOPYTEXSUBIMAGE2DPROC CopyTexSubImage2D; PFNGLCOPYTEXSUBIMAGE3DPROC CopyTexSubImage3D; PFNGLCOPYTEXTURESUBIMAGE1DPROC CopyTextureSubImage1D; PFNGLCOPYTEXTURESUBIMAGE2DPROC CopyTextureSubImage2D; PFNGLCOPYTEXTURESUBIMAGE3DPROC CopyTextureSubImage3D; PFNGLCREATEBUFFERSPROC CreateBuffers; PFNGLCREATEFRAMEBUFFERSPROC CreateFramebuffers; PFNGLCREATEPROGRAMPROC CreateProgram; PFNGLCREATEPROGRAMPIPELINESPROC CreateProgramPipelines; PFNGLCREATEQUERIESPROC CreateQueries; PFNGLCREATERENDERBUFFERSPROC CreateRenderbuffers; PFNGLCREATESAMPLERSPROC CreateSamplers; PFNGLCREATESHADERPROC CreateShader; PFNGLCREATESHADERPROGRAMVPROC CreateShaderProgramv; PFNGLCREATETEXTURESPROC CreateTextures; PFNGLCREATETRANSFORMFEEDBACKSPROC CreateTransformFeedbacks; PFNGLCREATEVERTEXARRAYSPROC CreateVertexArrays; PFNGLCULLFACEPROC CullFace; PFNGLDEBUGMESSAGECALLBACKPROC DebugMessageCallback; PFNGLDEBUGMESSAGECONTROLPROC DebugMessageControl; PFNGLDEBUGMESSAGEINSERTPROC DebugMessageInsert; PFNGLDELETEBUFFERSPROC DeleteBuffers; PFNGLDELETEFRAMEBUFFERSPROC DeleteFramebuffers; PFNGLDELETEPROGRAMPROC DeleteProgram; PFNGLDELETEPROGRAMPIPELINESPROC DeleteProgramPipelines; PFNGLDELETEQUERIESPROC DeleteQueries; PFNGLDELETERENDERBUFFERSPROC DeleteRenderbuffers; PFNGLDELETESAMPLERSPROC DeleteSamplers; PFNGLDELETESHADERPROC DeleteShader; PFNGLDELETESYNCPROC DeleteSync; PFNGLDELETETEXTURESPROC DeleteTextures; PFNGLDELETETRANSFORMFEEDBACKSPROC DeleteTransformFeedbacks; PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays; PFNGLDEPTHFUNCPROC DepthFunc; PFNGLDEPTHMASKPROC DepthMask; PFNGLDEPTHRANGEPROC DepthRange; PFNGLDEPTHRANGEARRAYVPROC DepthRangeArrayv; PFNGLDEPTHRANGEINDEXEDPROC DepthRangeIndexed; PFNGLDEPTHRANGEFPROC DepthRangef; PFNGLDETACHSHADERPROC DetachShader; PFNGLDISABLEPROC Disable; PFNGLDISABLEVERTEXARRAYATTRIBPROC DisableVertexArrayAttrib; PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray; PFNGLDISABLEIPROC Disablei; PFNGLDISPATCHCOMPUTEPROC DispatchCompute; PFNGLDISPATCHCOMPUTEINDIRECTPROC DispatchComputeIndirect; PFNGLDRAWARRAYSPROC DrawArrays; PFNGLDRAWARRAYSINDIRECTPROC DrawArraysIndirect; PFNGLDRAWARRAYSINSTANCEDPROC DrawArraysInstanced; PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC DrawArraysInstancedBaseInstance; PFNGLDRAWBUFFERPROC DrawBuffer; PFNGLDRAWBUFFERSPROC DrawBuffers; PFNGLDRAWELEMENTSPROC DrawElements; PFNGLDRAWELEMENTSBASEVERTEXPROC DrawElementsBaseVertex; PFNGLDRAWELEMENTSINDIRECTPROC DrawElementsIndirect; PFNGLDRAWELEMENTSINSTANCEDPROC DrawElementsInstanced; PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC DrawElementsInstancedBaseInstance; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC DrawElementsInstancedBaseVertex; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC DrawElementsInstancedBaseVertexBaseInstance; PFNGLDRAWRANGEELEMENTSPROC DrawRangeElements; PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC DrawRangeElementsBaseVertex; PFNGLDRAWTRANSFORMFEEDBACKPROC DrawTransformFeedback; PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC DrawTransformFeedbackInstanced; PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC DrawTransformFeedbackStream; PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC DrawTransformFeedbackStreamInstanced; PFNGLENABLEPROC Enable; PFNGLENABLEVERTEXARRAYATTRIBPROC EnableVertexArrayAttrib; PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray; PFNGLENABLEIPROC Enablei; PFNGLENDCONDITIONALRENDERPROC EndConditionalRender; PFNGLENDQUERYPROC EndQuery; PFNGLENDQUERYINDEXEDPROC EndQueryIndexed; PFNGLENDTRANSFORMFEEDBACKPROC EndTransformFeedback; PFNGLFENCESYNCPROC FenceSync; PFNGLFINISHPROC Finish; PFNGLFLUSHPROC Flush; PFNGLFLUSHMAPPEDBUFFERRANGEPROC FlushMappedBufferRange; PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC FlushMappedNamedBufferRange; PFNGLFRAMEBUFFERPARAMETERIPROC FramebufferParameteri; PFNGLFRAMEBUFFERPARAMETERIMESAPROC FramebufferParameteriMESA; PFNGLFRAMEBUFFERRENDERBUFFERPROC FramebufferRenderbuffer; PFNGLFRAMEBUFFERTEXTUREPROC FramebufferTexture; PFNGLFRAMEBUFFERTEXTURE1DPROC FramebufferTexture1D; PFNGLFRAMEBUFFERTEXTURE2DPROC FramebufferTexture2D; PFNGLFRAMEBUFFERTEXTURE3DPROC FramebufferTexture3D; PFNGLFRAMEBUFFERTEXTURELAYERPROC FramebufferTextureLayer; PFNGLFRONTFACEPROC FrontFace; PFNGLGENBUFFERSPROC GenBuffers; PFNGLGENFRAMEBUFFERSPROC GenFramebuffers; PFNGLGENPROGRAMPIPELINESPROC GenProgramPipelines; PFNGLGENQUERIESPROC GenQueries; PFNGLGENRENDERBUFFERSPROC GenRenderbuffers; PFNGLGENSAMPLERSPROC GenSamplers; PFNGLGENTEXTURESPROC GenTextures; PFNGLGENTRANSFORMFEEDBACKSPROC GenTransformFeedbacks; PFNGLGENVERTEXARRAYSPROC GenVertexArrays; PFNGLGENERATEMIPMAPPROC GenerateMipmap; PFNGLGENERATETEXTUREMIPMAPPROC GenerateTextureMipmap; PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC GetActiveAtomicCounterBufferiv; PFNGLGETACTIVEATTRIBPROC GetActiveAttrib; PFNGLGETACTIVESUBROUTINENAMEPROC GetActiveSubroutineName; PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC GetActiveSubroutineUniformName; PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC GetActiveSubroutineUniformiv; PFNGLGETACTIVEUNIFORMPROC GetActiveUniform; PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC GetActiveUniformBlockName; PFNGLGETACTIVEUNIFORMBLOCKIVPROC GetActiveUniformBlockiv; PFNGLGETACTIVEUNIFORMNAMEPROC GetActiveUniformName; PFNGLGETACTIVEUNIFORMSIVPROC GetActiveUniformsiv; PFNGLGETATTACHEDSHADERSPROC GetAttachedShaders; PFNGLGETATTRIBLOCATIONPROC GetAttribLocation; PFNGLGETBOOLEANI_VPROC GetBooleani_v; PFNGLGETBOOLEANVPROC GetBooleanv; PFNGLGETBUFFERPARAMETERI64VPROC GetBufferParameteri64v; PFNGLGETBUFFERPARAMETERIVPROC GetBufferParameteriv; PFNGLGETBUFFERPOINTERVPROC GetBufferPointerv; PFNGLGETBUFFERSUBDATAPROC GetBufferSubData; PFNGLGETCOMPRESSEDTEXIMAGEPROC GetCompressedTexImage; PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC GetCompressedTextureImage; PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC GetCompressedTextureSubImage; PFNGLGETDEBUGMESSAGELOGPROC GetDebugMessageLog; PFNGLGETDOUBLEI_VPROC GetDoublei_v; PFNGLGETDOUBLEVPROC GetDoublev; PFNGLGETERRORPROC GetError; PFNGLGETFLOATI_VPROC GetFloati_v; PFNGLGETFLOATVPROC GetFloatv; PFNGLGETFRAGDATAINDEXPROC GetFragDataIndex; PFNGLGETFRAGDATALOCATIONPROC GetFragDataLocation; PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC GetFramebufferAttachmentParameteriv; PFNGLGETFRAMEBUFFERPARAMETERIVPROC GetFramebufferParameteriv; PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC GetFramebufferParameterivMESA; PFNGLGETGRAPHICSRESETSTATUSPROC GetGraphicsResetStatus; PFNGLGETINTEGER64I_VPROC GetInteger64i_v; PFNGLGETINTEGER64VPROC GetInteger64v; PFNGLGETINTEGERI_VPROC GetIntegeri_v; PFNGLGETINTEGERVPROC GetIntegerv; PFNGLGETINTERNALFORMATI64VPROC GetInternalformati64v; PFNGLGETINTERNALFORMATIVPROC GetInternalformativ; PFNGLGETMULTISAMPLEFVPROC GetMultisamplefv; PFNGLGETNAMEDBUFFERPARAMETERI64VPROC GetNamedBufferParameteri64v; PFNGLGETNAMEDBUFFERPARAMETERIVPROC GetNamedBufferParameteriv; PFNGLGETNAMEDBUFFERPOINTERVPROC GetNamedBufferPointerv; PFNGLGETNAMEDBUFFERSUBDATAPROC GetNamedBufferSubData; PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC GetNamedFramebufferAttachmentParameteriv; PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC GetNamedFramebufferParameteriv; PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC GetNamedRenderbufferParameteriv; PFNGLGETOBJECTLABELPROC GetObjectLabel; PFNGLGETOBJECTPTRLABELPROC GetObjectPtrLabel; PFNGLGETPOINTERVPROC GetPointerv; PFNGLGETPROGRAMBINARYPROC GetProgramBinary; PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog; PFNGLGETPROGRAMINTERFACEIVPROC GetProgramInterfaceiv; PFNGLGETPROGRAMPIPELINEINFOLOGPROC GetProgramPipelineInfoLog; PFNGLGETPROGRAMPIPELINEIVPROC GetProgramPipelineiv; PFNGLGETPROGRAMRESOURCEINDEXPROC GetProgramResourceIndex; PFNGLGETPROGRAMRESOURCELOCATIONPROC GetProgramResourceLocation; PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC GetProgramResourceLocationIndex; PFNGLGETPROGRAMRESOURCENAMEPROC GetProgramResourceName; PFNGLGETPROGRAMRESOURCEIVPROC GetProgramResourceiv; PFNGLGETPROGRAMSTAGEIVPROC GetProgramStageiv; PFNGLGETPROGRAMIVPROC GetProgramiv; PFNGLGETQUERYBUFFEROBJECTI64VPROC GetQueryBufferObjecti64v; PFNGLGETQUERYBUFFEROBJECTIVPROC GetQueryBufferObjectiv; PFNGLGETQUERYBUFFEROBJECTUI64VPROC GetQueryBufferObjectui64v; PFNGLGETQUERYBUFFEROBJECTUIVPROC GetQueryBufferObjectuiv; PFNGLGETQUERYINDEXEDIVPROC GetQueryIndexediv; PFNGLGETQUERYOBJECTI64VPROC GetQueryObjecti64v; PFNGLGETQUERYOBJECTIVPROC GetQueryObjectiv; PFNGLGETQUERYOBJECTUI64VPROC GetQueryObjectui64v; PFNGLGETQUERYOBJECTUIVPROC GetQueryObjectuiv; PFNGLGETQUERYIVPROC GetQueryiv; PFNGLGETRENDERBUFFERPARAMETERIVPROC GetRenderbufferParameteriv; PFNGLGETSAMPLERPARAMETERIIVPROC GetSamplerParameterIiv; PFNGLGETSAMPLERPARAMETERIUIVPROC GetSamplerParameterIuiv; PFNGLGETSAMPLERPARAMETERFVPROC GetSamplerParameterfv; PFNGLGETSAMPLERPARAMETERIVPROC GetSamplerParameteriv; PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog; PFNGLGETSHADERPRECISIONFORMATPROC GetShaderPrecisionFormat; PFNGLGETSHADERSOURCEPROC GetShaderSource; PFNGLGETSHADERIVPROC GetShaderiv; PFNGLGETSTRINGPROC GetString; PFNGLGETSTRINGIPROC GetStringi; PFNGLGETSUBROUTINEINDEXPROC GetSubroutineIndex; PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC GetSubroutineUniformLocation; PFNGLGETSYNCIVPROC GetSynciv; PFNGLGETTEXIMAGEPROC GetTexImage; PFNGLGETTEXLEVELPARAMETERFVPROC GetTexLevelParameterfv; PFNGLGETTEXLEVELPARAMETERIVPROC GetTexLevelParameteriv; PFNGLGETTEXPARAMETERIIVPROC GetTexParameterIiv; PFNGLGETTEXPARAMETERIUIVPROC GetTexParameterIuiv; PFNGLGETTEXPARAMETERFVPROC GetTexParameterfv; PFNGLGETTEXPARAMETERIVPROC GetTexParameteriv; PFNGLGETTEXTUREIMAGEPROC GetTextureImage; PFNGLGETTEXTURELEVELPARAMETERFVPROC GetTextureLevelParameterfv; PFNGLGETTEXTURELEVELPARAMETERIVPROC GetTextureLevelParameteriv; PFNGLGETTEXTUREPARAMETERIIVPROC GetTextureParameterIiv; PFNGLGETTEXTUREPARAMETERIUIVPROC GetTextureParameterIuiv; PFNGLGETTEXTUREPARAMETERFVPROC GetTextureParameterfv; PFNGLGETTEXTUREPARAMETERIVPROC GetTextureParameteriv; PFNGLGETTEXTURESUBIMAGEPROC GetTextureSubImage; PFNGLGETTRANSFORMFEEDBACKVARYINGPROC GetTransformFeedbackVarying; PFNGLGETTRANSFORMFEEDBACKI64_VPROC GetTransformFeedbacki64_v; PFNGLGETTRANSFORMFEEDBACKI_VPROC GetTransformFeedbacki_v; PFNGLGETTRANSFORMFEEDBACKIVPROC GetTransformFeedbackiv; PFNGLGETUNIFORMBLOCKINDEXPROC GetUniformBlockIndex; PFNGLGETUNIFORMINDICESPROC GetUniformIndices; PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation; PFNGLGETUNIFORMSUBROUTINEUIVPROC GetUniformSubroutineuiv; PFNGLGETUNIFORMDVPROC GetUniformdv; PFNGLGETUNIFORMFVPROC GetUniformfv; PFNGLGETUNIFORMIVPROC GetUniformiv; PFNGLGETUNIFORMUIVPROC GetUniformuiv; PFNGLGETVERTEXARRAYINDEXED64IVPROC GetVertexArrayIndexed64iv; PFNGLGETVERTEXARRAYINDEXEDIVPROC GetVertexArrayIndexediv; PFNGLGETVERTEXARRAYIVPROC GetVertexArrayiv; PFNGLGETVERTEXATTRIBIIVPROC GetVertexAttribIiv; PFNGLGETVERTEXATTRIBIUIVPROC GetVertexAttribIuiv; PFNGLGETVERTEXATTRIBLDVPROC GetVertexAttribLdv; PFNGLGETVERTEXATTRIBPOINTERVPROC GetVertexAttribPointerv; PFNGLGETVERTEXATTRIBDVPROC GetVertexAttribdv; PFNGLGETVERTEXATTRIBFVPROC GetVertexAttribfv; PFNGLGETVERTEXATTRIBIVPROC GetVertexAttribiv; PFNGLGETNCOMPRESSEDTEXIMAGEPROC GetnCompressedTexImage; PFNGLGETNTEXIMAGEPROC GetnTexImage; PFNGLGETNUNIFORMDVPROC GetnUniformdv; PFNGLGETNUNIFORMFVPROC GetnUniformfv; PFNGLGETNUNIFORMIVPROC GetnUniformiv; PFNGLGETNUNIFORMUIVPROC GetnUniformuiv; PFNGLHINTPROC Hint; PFNGLINVALIDATEBUFFERDATAPROC InvalidateBufferData; PFNGLINVALIDATEBUFFERSUBDATAPROC InvalidateBufferSubData; PFNGLINVALIDATEFRAMEBUFFERPROC InvalidateFramebuffer; PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC InvalidateNamedFramebufferData; PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC InvalidateNamedFramebufferSubData; PFNGLINVALIDATESUBFRAMEBUFFERPROC InvalidateSubFramebuffer; PFNGLINVALIDATETEXIMAGEPROC InvalidateTexImage; PFNGLINVALIDATETEXSUBIMAGEPROC InvalidateTexSubImage; PFNGLISBUFFERPROC IsBuffer; PFNGLISENABLEDPROC IsEnabled; PFNGLISENABLEDIPROC IsEnabledi; PFNGLISFRAMEBUFFERPROC IsFramebuffer; PFNGLISPROGRAMPROC IsProgram; PFNGLISPROGRAMPIPELINEPROC IsProgramPipeline; PFNGLISQUERYPROC IsQuery; PFNGLISRENDERBUFFERPROC IsRenderbuffer; PFNGLISSAMPLERPROC IsSampler; PFNGLISSHADERPROC IsShader; PFNGLISSYNCPROC IsSync; PFNGLISTEXTUREPROC IsTexture; PFNGLISTRANSFORMFEEDBACKPROC IsTransformFeedback; PFNGLISVERTEXARRAYPROC IsVertexArray; PFNGLLINEWIDTHPROC LineWidth; PFNGLLINKPROGRAMPROC LinkProgram; PFNGLLOGICOPPROC LogicOp; PFNGLMAPBUFFERPROC MapBuffer; PFNGLMAPBUFFERRANGEPROC MapBufferRange; PFNGLMAPNAMEDBUFFERPROC MapNamedBuffer; PFNGLMAPNAMEDBUFFERRANGEPROC MapNamedBufferRange; PFNGLMEMORYBARRIERPROC MemoryBarrier; PFNGLMEMORYBARRIERBYREGIONPROC MemoryBarrierByRegion; PFNGLMINSAMPLESHADINGPROC MinSampleShading; PFNGLMULTIDRAWARRAYSPROC MultiDrawArrays; PFNGLMULTIDRAWARRAYSINDIRECTPROC MultiDrawArraysIndirect; PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC MultiDrawArraysIndirectCount; PFNGLMULTIDRAWELEMENTSPROC MultiDrawElements; PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC MultiDrawElementsBaseVertex; PFNGLMULTIDRAWELEMENTSINDIRECTPROC MultiDrawElementsIndirect; PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC MultiDrawElementsIndirectCount; PFNGLNAMEDBUFFERDATAPROC NamedBufferData; PFNGLNAMEDBUFFERSTORAGEPROC NamedBufferStorage; PFNGLNAMEDBUFFERSUBDATAPROC NamedBufferSubData; PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC NamedFramebufferDrawBuffer; PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC NamedFramebufferDrawBuffers; PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC NamedFramebufferParameteri; PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC NamedFramebufferReadBuffer; PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC NamedFramebufferRenderbuffer; PFNGLNAMEDFRAMEBUFFERTEXTUREPROC NamedFramebufferTexture; PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC NamedFramebufferTextureLayer; PFNGLNAMEDRENDERBUFFERSTORAGEPROC NamedRenderbufferStorage; PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC NamedRenderbufferStorageMultisample; PFNGLOBJECTLABELPROC ObjectLabel; PFNGLOBJECTPTRLABELPROC ObjectPtrLabel; PFNGLPATCHPARAMETERFVPROC PatchParameterfv; PFNGLPATCHPARAMETERIPROC PatchParameteri; PFNGLPAUSETRANSFORMFEEDBACKPROC PauseTransformFeedback; PFNGLPIXELSTOREFPROC PixelStoref; PFNGLPIXELSTOREIPROC PixelStorei; PFNGLPOINTPARAMETERFPROC PointParameterf; PFNGLPOINTPARAMETERFVPROC PointParameterfv; PFNGLPOINTPARAMETERIPROC PointParameteri; PFNGLPOINTPARAMETERIVPROC PointParameteriv; PFNGLPOINTSIZEPROC PointSize; PFNGLPOLYGONMODEPROC PolygonMode; PFNGLPOLYGONOFFSETPROC PolygonOffset; PFNGLPOLYGONOFFSETCLAMPPROC PolygonOffsetClamp; PFNGLPOPDEBUGGROUPPROC PopDebugGroup; PFNGLPRIMITIVERESTARTINDEXPROC PrimitiveRestartIndex; PFNGLPROGRAMBINARYPROC ProgramBinary; PFNGLPROGRAMPARAMETERIPROC ProgramParameteri; PFNGLPROGRAMUNIFORM1DPROC ProgramUniform1d; PFNGLPROGRAMUNIFORM1DVPROC ProgramUniform1dv; PFNGLPROGRAMUNIFORM1FPROC ProgramUniform1f; PFNGLPROGRAMUNIFORM1FVPROC ProgramUniform1fv; PFNGLPROGRAMUNIFORM1IPROC ProgramUniform1i; PFNGLPROGRAMUNIFORM1IVPROC ProgramUniform1iv; PFNGLPROGRAMUNIFORM1UIPROC ProgramUniform1ui; PFNGLPROGRAMUNIFORM1UIVPROC ProgramUniform1uiv; PFNGLPROGRAMUNIFORM2DPROC ProgramUniform2d; PFNGLPROGRAMUNIFORM2DVPROC ProgramUniform2dv; PFNGLPROGRAMUNIFORM2FPROC ProgramUniform2f; PFNGLPROGRAMUNIFORM2FVPROC ProgramUniform2fv; PFNGLPROGRAMUNIFORM2IPROC ProgramUniform2i; PFNGLPROGRAMUNIFORM2IVPROC ProgramUniform2iv; PFNGLPROGRAMUNIFORM2UIPROC ProgramUniform2ui; PFNGLPROGRAMUNIFORM2UIVPROC ProgramUniform2uiv; PFNGLPROGRAMUNIFORM3DPROC ProgramUniform3d; PFNGLPROGRAMUNIFORM3DVPROC ProgramUniform3dv; PFNGLPROGRAMUNIFORM3FPROC ProgramUniform3f; PFNGLPROGRAMUNIFORM3FVPROC ProgramUniform3fv; PFNGLPROGRAMUNIFORM3IPROC ProgramUniform3i; PFNGLPROGRAMUNIFORM3IVPROC ProgramUniform3iv; PFNGLPROGRAMUNIFORM3UIPROC ProgramUniform3ui; PFNGLPROGRAMUNIFORM3UIVPROC ProgramUniform3uiv; PFNGLPROGRAMUNIFORM4DPROC ProgramUniform4d; PFNGLPROGRAMUNIFORM4DVPROC ProgramUniform4dv; PFNGLPROGRAMUNIFORM4FPROC ProgramUniform4f; PFNGLPROGRAMUNIFORM4FVPROC ProgramUniform4fv; PFNGLPROGRAMUNIFORM4IPROC ProgramUniform4i; PFNGLPROGRAMUNIFORM4IVPROC ProgramUniform4iv; PFNGLPROGRAMUNIFORM4UIPROC ProgramUniform4ui; PFNGLPROGRAMUNIFORM4UIVPROC ProgramUniform4uiv; PFNGLPROGRAMUNIFORMMATRIX2DVPROC ProgramUniformMatrix2dv; PFNGLPROGRAMUNIFORMMATRIX2FVPROC ProgramUniformMatrix2fv; PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC ProgramUniformMatrix2x3dv; PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC ProgramUniformMatrix2x3fv; PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC ProgramUniformMatrix2x4dv; PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC ProgramUniformMatrix2x4fv; PFNGLPROGRAMUNIFORMMATRIX3DVPROC ProgramUniformMatrix3dv; PFNGLPROGRAMUNIFORMMATRIX3FVPROC ProgramUniformMatrix3fv; PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC ProgramUniformMatrix3x2dv; PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC ProgramUniformMatrix3x2fv; PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC ProgramUniformMatrix3x4dv; PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC ProgramUniformMatrix3x4fv; PFNGLPROGRAMUNIFORMMATRIX4DVPROC ProgramUniformMatrix4dv; PFNGLPROGRAMUNIFORMMATRIX4FVPROC ProgramUniformMatrix4fv; PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC ProgramUniformMatrix4x2dv; PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC ProgramUniformMatrix4x2fv; PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC ProgramUniformMatrix4x3dv; PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC ProgramUniformMatrix4x3fv; PFNGLPROVOKINGVERTEXPROC ProvokingVertex; PFNGLPUSHDEBUGGROUPPROC PushDebugGroup; PFNGLQUERYCOUNTERPROC QueryCounter; PFNGLREADBUFFERPROC ReadBuffer; PFNGLREADPIXELSPROC ReadPixels; PFNGLREADNPIXELSPROC ReadnPixels; PFNGLRELEASESHADERCOMPILERPROC ReleaseShaderCompiler; PFNGLRENDERBUFFERSTORAGEPROC RenderbufferStorage; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC RenderbufferStorageMultisample; PFNGLRESUMETRANSFORMFEEDBACKPROC ResumeTransformFeedback; PFNGLSAMPLECOVERAGEPROC SampleCoverage; PFNGLSAMPLEMASKIPROC SampleMaski; PFNGLSAMPLERPARAMETERIIVPROC SamplerParameterIiv; PFNGLSAMPLERPARAMETERIUIVPROC SamplerParameterIuiv; PFNGLSAMPLERPARAMETERFPROC SamplerParameterf; PFNGLSAMPLERPARAMETERFVPROC SamplerParameterfv; PFNGLSAMPLERPARAMETERIPROC SamplerParameteri; PFNGLSAMPLERPARAMETERIVPROC SamplerParameteriv; PFNGLSCISSORPROC Scissor; PFNGLSCISSORARRAYVPROC ScissorArrayv; PFNGLSCISSORINDEXEDPROC ScissorIndexed; PFNGLSCISSORINDEXEDVPROC ScissorIndexedv; PFNGLSHADERBINARYPROC ShaderBinary; PFNGLSHADERSOURCEPROC ShaderSource; PFNGLSHADERSTORAGEBLOCKBINDINGPROC ShaderStorageBlockBinding; PFNGLSPECIALIZESHADERPROC SpecializeShader; PFNGLSTENCILFUNCPROC StencilFunc; PFNGLSTENCILFUNCSEPARATEPROC StencilFuncSeparate; PFNGLSTENCILMASKPROC StencilMask; PFNGLSTENCILMASKSEPARATEPROC StencilMaskSeparate; PFNGLSTENCILOPPROC StencilOp; PFNGLSTENCILOPSEPARATEPROC StencilOpSeparate; PFNGLTEXBUFFERPROC TexBuffer; PFNGLTEXBUFFERRANGEPROC TexBufferRange; PFNGLTEXIMAGE1DPROC TexImage1D; PFNGLTEXIMAGE2DPROC TexImage2D; PFNGLTEXIMAGE2DMULTISAMPLEPROC TexImage2DMultisample; PFNGLTEXIMAGE3DPROC TexImage3D; PFNGLTEXIMAGE3DMULTISAMPLEPROC TexImage3DMultisample; PFNGLTEXPARAMETERIIVPROC TexParameterIiv; PFNGLTEXPARAMETERIUIVPROC TexParameterIuiv; PFNGLTEXPARAMETERFPROC TexParameterf; PFNGLTEXPARAMETERFVPROC TexParameterfv; PFNGLTEXPARAMETERIPROC TexParameteri; PFNGLTEXPARAMETERIVPROC TexParameteriv; PFNGLTEXSTORAGE1DPROC TexStorage1D; PFNGLTEXSTORAGE2DPROC TexStorage2D; PFNGLTEXSTORAGE2DMULTISAMPLEPROC TexStorage2DMultisample; PFNGLTEXSTORAGE3DPROC TexStorage3D; PFNGLTEXSTORAGE3DMULTISAMPLEPROC TexStorage3DMultisample; PFNGLTEXSUBIMAGE1DPROC TexSubImage1D; PFNGLTEXSUBIMAGE2DPROC TexSubImage2D; PFNGLTEXSUBIMAGE3DPROC TexSubImage3D; PFNGLTEXTUREBARRIERPROC TextureBarrier; PFNGLTEXTUREBUFFERPROC TextureBuffer; PFNGLTEXTUREBUFFERRANGEPROC TextureBufferRange; PFNGLTEXTUREPARAMETERIIVPROC TextureParameterIiv; PFNGLTEXTUREPARAMETERIUIVPROC TextureParameterIuiv; PFNGLTEXTUREPARAMETERFPROC TextureParameterf; PFNGLTEXTUREPARAMETERFVPROC TextureParameterfv; PFNGLTEXTUREPARAMETERIPROC TextureParameteri; PFNGLTEXTUREPARAMETERIVPROC TextureParameteriv; PFNGLTEXTURESTORAGE1DPROC TextureStorage1D; PFNGLTEXTURESTORAGE2DPROC TextureStorage2D; PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC TextureStorage2DMultisample; PFNGLTEXTURESTORAGE3DPROC TextureStorage3D; PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC TextureStorage3DMultisample; PFNGLTEXTURESUBIMAGE1DPROC TextureSubImage1D; PFNGLTEXTURESUBIMAGE2DPROC TextureSubImage2D; PFNGLTEXTURESUBIMAGE3DPROC TextureSubImage3D; PFNGLTEXTUREVIEWPROC TextureView; PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC TransformFeedbackBufferBase; PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC TransformFeedbackBufferRange; PFNGLTRANSFORMFEEDBACKVARYINGSPROC TransformFeedbackVaryings; PFNGLUNIFORM1DPROC Uniform1d; PFNGLUNIFORM1DVPROC Uniform1dv; PFNGLUNIFORM1FPROC Uniform1f; PFNGLUNIFORM1FVPROC Uniform1fv; PFNGLUNIFORM1IPROC Uniform1i; PFNGLUNIFORM1IVPROC Uniform1iv; PFNGLUNIFORM1UIPROC Uniform1ui; PFNGLUNIFORM1UIVPROC Uniform1uiv; PFNGLUNIFORM2DPROC Uniform2d; PFNGLUNIFORM2DVPROC Uniform2dv; PFNGLUNIFORM2FPROC Uniform2f; PFNGLUNIFORM2FVPROC Uniform2fv; PFNGLUNIFORM2IPROC Uniform2i; PFNGLUNIFORM2IVPROC Uniform2iv; PFNGLUNIFORM2UIPROC Uniform2ui; PFNGLUNIFORM2UIVPROC Uniform2uiv; PFNGLUNIFORM3DPROC Uniform3d; PFNGLUNIFORM3DVPROC Uniform3dv; PFNGLUNIFORM3FPROC Uniform3f; PFNGLUNIFORM3FVPROC Uniform3fv; PFNGLUNIFORM3IPROC Uniform3i; PFNGLUNIFORM3IVPROC Uniform3iv; PFNGLUNIFORM3UIPROC Uniform3ui; PFNGLUNIFORM3UIVPROC Uniform3uiv; PFNGLUNIFORM4DPROC Uniform4d; PFNGLUNIFORM4DVPROC Uniform4dv; PFNGLUNIFORM4FPROC Uniform4f; PFNGLUNIFORM4FVPROC Uniform4fv; PFNGLUNIFORM4IPROC Uniform4i; PFNGLUNIFORM4IVPROC Uniform4iv; PFNGLUNIFORM4UIPROC Uniform4ui; PFNGLUNIFORM4UIVPROC Uniform4uiv; PFNGLUNIFORMBLOCKBINDINGPROC UniformBlockBinding; PFNGLUNIFORMMATRIX2DVPROC UniformMatrix2dv; PFNGLUNIFORMMATRIX2FVPROC UniformMatrix2fv; PFNGLUNIFORMMATRIX2X3DVPROC UniformMatrix2x3dv; PFNGLUNIFORMMATRIX2X3FVPROC UniformMatrix2x3fv; PFNGLUNIFORMMATRIX2X4DVPROC UniformMatrix2x4dv; PFNGLUNIFORMMATRIX2X4FVPROC UniformMatrix2x4fv; PFNGLUNIFORMMATRIX3DVPROC UniformMatrix3dv; PFNGLUNIFORMMATRIX3FVPROC UniformMatrix3fv; PFNGLUNIFORMMATRIX3X2DVPROC UniformMatrix3x2dv; PFNGLUNIFORMMATRIX3X2FVPROC UniformMatrix3x2fv; PFNGLUNIFORMMATRIX3X4DVPROC UniformMatrix3x4dv; PFNGLUNIFORMMATRIX3X4FVPROC UniformMatrix3x4fv; PFNGLUNIFORMMATRIX4DVPROC UniformMatrix4dv; PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv; PFNGLUNIFORMMATRIX4X2DVPROC UniformMatrix4x2dv; PFNGLUNIFORMMATRIX4X2FVPROC UniformMatrix4x2fv; PFNGLUNIFORMMATRIX4X3DVPROC UniformMatrix4x3dv; PFNGLUNIFORMMATRIX4X3FVPROC UniformMatrix4x3fv; PFNGLUNIFORMSUBROUTINESUIVPROC UniformSubroutinesuiv; PFNGLUNMAPBUFFERPROC UnmapBuffer; PFNGLUNMAPNAMEDBUFFERPROC UnmapNamedBuffer; PFNGLUSEPROGRAMPROC UseProgram; PFNGLUSEPROGRAMSTAGESPROC UseProgramStages; PFNGLVALIDATEPROGRAMPROC ValidateProgram; PFNGLVALIDATEPROGRAMPIPELINEPROC ValidateProgramPipeline; PFNGLVERTEXARRAYATTRIBBINDINGPROC VertexArrayAttribBinding; PFNGLVERTEXARRAYATTRIBFORMATPROC VertexArrayAttribFormat; PFNGLVERTEXARRAYATTRIBIFORMATPROC VertexArrayAttribIFormat; PFNGLVERTEXARRAYATTRIBLFORMATPROC VertexArrayAttribLFormat; PFNGLVERTEXARRAYBINDINGDIVISORPROC VertexArrayBindingDivisor; PFNGLVERTEXARRAYELEMENTBUFFERPROC VertexArrayElementBuffer; PFNGLVERTEXARRAYVERTEXBUFFERPROC VertexArrayVertexBuffer; PFNGLVERTEXARRAYVERTEXBUFFERSPROC VertexArrayVertexBuffers; PFNGLVERTEXATTRIB1DPROC VertexAttrib1d; PFNGLVERTEXATTRIB1DVPROC VertexAttrib1dv; PFNGLVERTEXATTRIB1FPROC VertexAttrib1f; PFNGLVERTEXATTRIB1FVPROC VertexAttrib1fv; PFNGLVERTEXATTRIB1SPROC VertexAttrib1s; PFNGLVERTEXATTRIB1SVPROC VertexAttrib1sv; PFNGLVERTEXATTRIB2DPROC VertexAttrib2d; PFNGLVERTEXATTRIB2DVPROC VertexAttrib2dv; PFNGLVERTEXATTRIB2FPROC VertexAttrib2f; PFNGLVERTEXATTRIB2FVPROC VertexAttrib2fv; PFNGLVERTEXATTRIB2SPROC VertexAttrib2s; PFNGLVERTEXATTRIB2SVPROC VertexAttrib2sv; PFNGLVERTEXATTRIB3DPROC VertexAttrib3d; PFNGLVERTEXATTRIB3DVPROC VertexAttrib3dv; PFNGLVERTEXATTRIB3FPROC VertexAttrib3f; PFNGLVERTEXATTRIB3FVPROC VertexAttrib3fv; PFNGLVERTEXATTRIB3SPROC VertexAttrib3s; PFNGLVERTEXATTRIB3SVPROC VertexAttrib3sv; PFNGLVERTEXATTRIB4NBVPROC VertexAttrib4Nbv; PFNGLVERTEXATTRIB4NIVPROC VertexAttrib4Niv; PFNGLVERTEXATTRIB4NSVPROC VertexAttrib4Nsv; PFNGLVERTEXATTRIB4NUBPROC VertexAttrib4Nub; PFNGLVERTEXATTRIB4NUBVPROC VertexAttrib4Nubv; PFNGLVERTEXATTRIB4NUIVPROC VertexAttrib4Nuiv; PFNGLVERTEXATTRIB4NUSVPROC VertexAttrib4Nusv; PFNGLVERTEXATTRIB4BVPROC VertexAttrib4bv; PFNGLVERTEXATTRIB4DPROC VertexAttrib4d; PFNGLVERTEXATTRIB4DVPROC VertexAttrib4dv; PFNGLVERTEXATTRIB4FPROC VertexAttrib4f; PFNGLVERTEXATTRIB4FVPROC VertexAttrib4fv; PFNGLVERTEXATTRIB4IVPROC VertexAttrib4iv; PFNGLVERTEXATTRIB4SPROC VertexAttrib4s; PFNGLVERTEXATTRIB4SVPROC VertexAttrib4sv; PFNGLVERTEXATTRIB4UBVPROC VertexAttrib4ubv; PFNGLVERTEXATTRIB4UIVPROC VertexAttrib4uiv; PFNGLVERTEXATTRIB4USVPROC VertexAttrib4usv; PFNGLVERTEXATTRIBBINDINGPROC VertexAttribBinding; PFNGLVERTEXATTRIBDIVISORPROC VertexAttribDivisor; PFNGLVERTEXATTRIBFORMATPROC VertexAttribFormat; PFNGLVERTEXATTRIBI1IPROC VertexAttribI1i; PFNGLVERTEXATTRIBI1IVPROC VertexAttribI1iv; PFNGLVERTEXATTRIBI1UIPROC VertexAttribI1ui; PFNGLVERTEXATTRIBI1UIVPROC VertexAttribI1uiv; PFNGLVERTEXATTRIBI2IPROC VertexAttribI2i; PFNGLVERTEXATTRIBI2IVPROC VertexAttribI2iv; PFNGLVERTEXATTRIBI2UIPROC VertexAttribI2ui; PFNGLVERTEXATTRIBI2UIVPROC VertexAttribI2uiv; PFNGLVERTEXATTRIBI3IPROC VertexAttribI3i; PFNGLVERTEXATTRIBI3IVPROC VertexAttribI3iv; PFNGLVERTEXATTRIBI3UIPROC VertexAttribI3ui; PFNGLVERTEXATTRIBI3UIVPROC VertexAttribI3uiv; PFNGLVERTEXATTRIBI4BVPROC VertexAttribI4bv; PFNGLVERTEXATTRIBI4IPROC VertexAttribI4i; PFNGLVERTEXATTRIBI4IVPROC VertexAttribI4iv; PFNGLVERTEXATTRIBI4SVPROC VertexAttribI4sv; PFNGLVERTEXATTRIBI4UBVPROC VertexAttribI4ubv; PFNGLVERTEXATTRIBI4UIPROC VertexAttribI4ui; PFNGLVERTEXATTRIBI4UIVPROC VertexAttribI4uiv; PFNGLVERTEXATTRIBI4USVPROC VertexAttribI4usv; PFNGLVERTEXATTRIBIFORMATPROC VertexAttribIFormat; PFNGLVERTEXATTRIBIPOINTERPROC VertexAttribIPointer; PFNGLVERTEXATTRIBL1DPROC VertexAttribL1d; PFNGLVERTEXATTRIBL1DVPROC VertexAttribL1dv; PFNGLVERTEXATTRIBL2DPROC VertexAttribL2d; PFNGLVERTEXATTRIBL2DVPROC VertexAttribL2dv; PFNGLVERTEXATTRIBL3DPROC VertexAttribL3d; PFNGLVERTEXATTRIBL3DVPROC VertexAttribL3dv; PFNGLVERTEXATTRIBL4DPROC VertexAttribL4d; PFNGLVERTEXATTRIBL4DVPROC VertexAttribL4dv; PFNGLVERTEXATTRIBLFORMATPROC VertexAttribLFormat; PFNGLVERTEXATTRIBLPOINTERPROC VertexAttribLPointer; PFNGLVERTEXATTRIBP1UIPROC VertexAttribP1ui; PFNGLVERTEXATTRIBP1UIVPROC VertexAttribP1uiv; PFNGLVERTEXATTRIBP2UIPROC VertexAttribP2ui; PFNGLVERTEXATTRIBP2UIVPROC VertexAttribP2uiv; PFNGLVERTEXATTRIBP3UIPROC VertexAttribP3ui; PFNGLVERTEXATTRIBP3UIVPROC VertexAttribP3uiv; PFNGLVERTEXATTRIBP4UIPROC VertexAttribP4ui; PFNGLVERTEXATTRIBP4UIVPROC VertexAttribP4uiv; PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer; PFNGLVERTEXBINDINGDIVISORPROC VertexBindingDivisor; PFNGLVIEWPORTPROC Viewport; PFNGLVIEWPORTARRAYVPROC ViewportArrayv; PFNGLVIEWPORTINDEXEDFPROC ViewportIndexedf; PFNGLVIEWPORTINDEXEDFVPROC ViewportIndexedfv; PFNGLWAITSYNCPROC WaitSync; } gl; }; GL3W_API extern union GL3WProcs gl3wProcs; /* OpenGL functions */ #define glActiveShaderProgram gl3wProcs.gl.ActiveShaderProgram #define glActiveTexture gl3wProcs.gl.ActiveTexture #define glAttachShader gl3wProcs.gl.AttachShader #define glBeginConditionalRender gl3wProcs.gl.BeginConditionalRender #define glBeginQuery gl3wProcs.gl.BeginQuery #define glBeginQueryIndexed gl3wProcs.gl.BeginQueryIndexed #define glBeginTransformFeedback gl3wProcs.gl.BeginTransformFeedback #define glBindAttribLocation gl3wProcs.gl.BindAttribLocation #define glBindBuffer gl3wProcs.gl.BindBuffer #define glBindBufferBase gl3wProcs.gl.BindBufferBase #define glBindBufferRange gl3wProcs.gl.BindBufferRange #define glBindBuffersBase gl3wProcs.gl.BindBuffersBase #define glBindBuffersRange gl3wProcs.gl.BindBuffersRange #define glBindFragDataLocation gl3wProcs.gl.BindFragDataLocation #define glBindFragDataLocationIndexed gl3wProcs.gl.BindFragDataLocationIndexed #define glBindFramebuffer gl3wProcs.gl.BindFramebuffer #define glBindImageTexture gl3wProcs.gl.BindImageTexture #define glBindImageTextures gl3wProcs.gl.BindImageTextures #define glBindProgramPipeline gl3wProcs.gl.BindProgramPipeline #define glBindRenderbuffer gl3wProcs.gl.BindRenderbuffer #define glBindSampler gl3wProcs.gl.BindSampler #define glBindSamplers gl3wProcs.gl.BindSamplers #define glBindTexture gl3wProcs.gl.BindTexture #define glBindTextureUnit gl3wProcs.gl.BindTextureUnit #define glBindTextures gl3wProcs.gl.BindTextures #define glBindTransformFeedback gl3wProcs.gl.BindTransformFeedback #define glBindVertexArray gl3wProcs.gl.BindVertexArray #define glBindVertexBuffer gl3wProcs.gl.BindVertexBuffer #define glBindVertexBuffers gl3wProcs.gl.BindVertexBuffers #define glBlendColor gl3wProcs.gl.BlendColor #define glBlendEquation gl3wProcs.gl.BlendEquation #define glBlendEquationSeparate gl3wProcs.gl.BlendEquationSeparate #define glBlendEquationSeparatei gl3wProcs.gl.BlendEquationSeparatei #define glBlendEquationi gl3wProcs.gl.BlendEquationi #define glBlendFunc gl3wProcs.gl.BlendFunc #define glBlendFuncSeparate gl3wProcs.gl.BlendFuncSeparate #define glBlendFuncSeparatei gl3wProcs.gl.BlendFuncSeparatei #define glBlendFunci gl3wProcs.gl.BlendFunci #define glBlitFramebuffer gl3wProcs.gl.BlitFramebuffer #define glBlitNamedFramebuffer gl3wProcs.gl.BlitNamedFramebuffer #define glBufferData gl3wProcs.gl.BufferData #define glBufferStorage gl3wProcs.gl.BufferStorage #define glBufferSubData gl3wProcs.gl.BufferSubData #define glCheckFramebufferStatus gl3wProcs.gl.CheckFramebufferStatus #define glCheckNamedFramebufferStatus gl3wProcs.gl.CheckNamedFramebufferStatus #define glClampColor gl3wProcs.gl.ClampColor #define glClear gl3wProcs.gl.Clear #define glClearBufferData gl3wProcs.gl.ClearBufferData #define glClearBufferSubData gl3wProcs.gl.ClearBufferSubData #define glClearBufferfi gl3wProcs.gl.ClearBufferfi #define glClearBufferfv gl3wProcs.gl.ClearBufferfv #define glClearBufferiv gl3wProcs.gl.ClearBufferiv #define glClearBufferuiv gl3wProcs.gl.ClearBufferuiv #define glClearColor gl3wProcs.gl.ClearColor #define glClearDepth gl3wProcs.gl.ClearDepth #define glClearDepthf gl3wProcs.gl.ClearDepthf #define glClearNamedBufferData gl3wProcs.gl.ClearNamedBufferData #define glClearNamedBufferSubData gl3wProcs.gl.ClearNamedBufferSubData #define glClearNamedFramebufferfi gl3wProcs.gl.ClearNamedFramebufferfi #define glClearNamedFramebufferfv gl3wProcs.gl.ClearNamedFramebufferfv #define glClearNamedFramebufferiv gl3wProcs.gl.ClearNamedFramebufferiv #define glClearNamedFramebufferuiv gl3wProcs.gl.ClearNamedFramebufferuiv #define glClearStencil gl3wProcs.gl.ClearStencil #define glClearTexImage gl3wProcs.gl.ClearTexImage #define glClearTexSubImage gl3wProcs.gl.ClearTexSubImage #define glClientWaitSync gl3wProcs.gl.ClientWaitSync #define glClipControl gl3wProcs.gl.ClipControl #define glColorMask gl3wProcs.gl.ColorMask #define glColorMaski gl3wProcs.gl.ColorMaski #define glCompileShader gl3wProcs.gl.CompileShader #define glCompressedTexImage1D gl3wProcs.gl.CompressedTexImage1D #define glCompressedTexImage2D gl3wProcs.gl.CompressedTexImage2D #define glCompressedTexImage3D gl3wProcs.gl.CompressedTexImage3D #define glCompressedTexSubImage1D gl3wProcs.gl.CompressedTexSubImage1D #define glCompressedTexSubImage2D gl3wProcs.gl.CompressedTexSubImage2D #define glCompressedTexSubImage3D gl3wProcs.gl.CompressedTexSubImage3D #define glCompressedTextureSubImage1D gl3wProcs.gl.CompressedTextureSubImage1D #define glCompressedTextureSubImage2D gl3wProcs.gl.CompressedTextureSubImage2D #define glCompressedTextureSubImage3D gl3wProcs.gl.CompressedTextureSubImage3D #define glCopyBufferSubData gl3wProcs.gl.CopyBufferSubData #define glCopyImageSubData gl3wProcs.gl.CopyImageSubData #define glCopyNamedBufferSubData gl3wProcs.gl.CopyNamedBufferSubData #define glCopyTexImage1D gl3wProcs.gl.CopyTexImage1D #define glCopyTexImage2D gl3wProcs.gl.CopyTexImage2D #define glCopyTexSubImage1D gl3wProcs.gl.CopyTexSubImage1D #define glCopyTexSubImage2D gl3wProcs.gl.CopyTexSubImage2D #define glCopyTexSubImage3D gl3wProcs.gl.CopyTexSubImage3D #define glCopyTextureSubImage1D gl3wProcs.gl.CopyTextureSubImage1D #define glCopyTextureSubImage2D gl3wProcs.gl.CopyTextureSubImage2D #define glCopyTextureSubImage3D gl3wProcs.gl.CopyTextureSubImage3D #define glCreateBuffers gl3wProcs.gl.CreateBuffers #define glCreateFramebuffers gl3wProcs.gl.CreateFramebuffers #define glCreateProgram gl3wProcs.gl.CreateProgram #define glCreateProgramPipelines gl3wProcs.gl.CreateProgramPipelines #define glCreateQueries gl3wProcs.gl.CreateQueries #define glCreateRenderbuffers gl3wProcs.gl.CreateRenderbuffers #define glCreateSamplers gl3wProcs.gl.CreateSamplers #define glCreateShader gl3wProcs.gl.CreateShader #define glCreateShaderProgramv gl3wProcs.gl.CreateShaderProgramv #define glCreateTextures gl3wProcs.gl.CreateTextures #define glCreateTransformFeedbacks gl3wProcs.gl.CreateTransformFeedbacks #define glCreateVertexArrays gl3wProcs.gl.CreateVertexArrays #define glCullFace gl3wProcs.gl.CullFace #define glDebugMessageCallback gl3wProcs.gl.DebugMessageCallback #define glDebugMessageControl gl3wProcs.gl.DebugMessageControl #define glDebugMessageInsert gl3wProcs.gl.DebugMessageInsert #define glDeleteBuffers gl3wProcs.gl.DeleteBuffers #define glDeleteFramebuffers gl3wProcs.gl.DeleteFramebuffers #define glDeleteProgram gl3wProcs.gl.DeleteProgram #define glDeleteProgramPipelines gl3wProcs.gl.DeleteProgramPipelines #define glDeleteQueries gl3wProcs.gl.DeleteQueries #define glDeleteRenderbuffers gl3wProcs.gl.DeleteRenderbuffers #define glDeleteSamplers gl3wProcs.gl.DeleteSamplers #define glDeleteShader gl3wProcs.gl.DeleteShader #define glDeleteSync gl3wProcs.gl.DeleteSync #define glDeleteTextures gl3wProcs.gl.DeleteTextures #define glDeleteTransformFeedbacks gl3wProcs.gl.DeleteTransformFeedbacks #define glDeleteVertexArrays gl3wProcs.gl.DeleteVertexArrays #define glDepthFunc gl3wProcs.gl.DepthFunc #define glDepthMask gl3wProcs.gl.DepthMask #define glDepthRange gl3wProcs.gl.DepthRange #define glDepthRangeArrayv gl3wProcs.gl.DepthRangeArrayv #define glDepthRangeIndexed gl3wProcs.gl.DepthRangeIndexed #define glDepthRangef gl3wProcs.gl.DepthRangef #define glDetachShader gl3wProcs.gl.DetachShader #define glDisable gl3wProcs.gl.Disable #define glDisableVertexArrayAttrib gl3wProcs.gl.DisableVertexArrayAttrib #define glDisableVertexAttribArray gl3wProcs.gl.DisableVertexAttribArray #define glDisablei gl3wProcs.gl.Disablei #define glDispatchCompute gl3wProcs.gl.DispatchCompute #define glDispatchComputeIndirect gl3wProcs.gl.DispatchComputeIndirect #define glDrawArrays gl3wProcs.gl.DrawArrays #define glDrawArraysIndirect gl3wProcs.gl.DrawArraysIndirect #define glDrawArraysInstanced gl3wProcs.gl.DrawArraysInstanced #define glDrawArraysInstancedBaseInstance gl3wProcs.gl.DrawArraysInstancedBaseInstance #define glDrawBuffer gl3wProcs.gl.DrawBuffer #define glDrawBuffers gl3wProcs.gl.DrawBuffers #define glDrawElements gl3wProcs.gl.DrawElements #define glDrawElementsBaseVertex gl3wProcs.gl.DrawElementsBaseVertex #define glDrawElementsIndirect gl3wProcs.gl.DrawElementsIndirect #define glDrawElementsInstanced gl3wProcs.gl.DrawElementsInstanced #define glDrawElementsInstancedBaseInstance gl3wProcs.gl.DrawElementsInstancedBaseInstance #define glDrawElementsInstancedBaseVertex gl3wProcs.gl.DrawElementsInstancedBaseVertex #define glDrawElementsInstancedBaseVertexBaseInstance gl3wProcs.gl.DrawElementsInstancedBaseVertexBaseInstance #define glDrawRangeElements gl3wProcs.gl.DrawRangeElements #define glDrawRangeElementsBaseVertex gl3wProcs.gl.DrawRangeElementsBaseVertex #define glDrawTransformFeedback gl3wProcs.gl.DrawTransformFeedback #define glDrawTransformFeedbackInstanced gl3wProcs.gl.DrawTransformFeedbackInstanced #define glDrawTransformFeedbackStream gl3wProcs.gl.DrawTransformFeedbackStream #define glDrawTransformFeedbackStreamInstanced gl3wProcs.gl.DrawTransformFeedbackStreamInstanced #define glEnable gl3wProcs.gl.Enable #define glEnableVertexArrayAttrib gl3wProcs.gl.EnableVertexArrayAttrib #define glEnableVertexAttribArray gl3wProcs.gl.EnableVertexAttribArray #define glEnablei gl3wProcs.gl.Enablei #define glEndConditionalRender gl3wProcs.gl.EndConditionalRender #define glEndQuery gl3wProcs.gl.EndQuery #define glEndQueryIndexed gl3wProcs.gl.EndQueryIndexed #define glEndTransformFeedback gl3wProcs.gl.EndTransformFeedback #define glFenceSync gl3wProcs.gl.FenceSync #define glFinish gl3wProcs.gl.Finish #define glFlush gl3wProcs.gl.Flush #define glFlushMappedBufferRange gl3wProcs.gl.FlushMappedBufferRange #define glFlushMappedNamedBufferRange gl3wProcs.gl.FlushMappedNamedBufferRange #define glFramebufferParameteri gl3wProcs.gl.FramebufferParameteri #define glFramebufferParameteriMESA gl3wProcs.gl.FramebufferParameteriMESA #define glFramebufferRenderbuffer gl3wProcs.gl.FramebufferRenderbuffer #define glFramebufferTexture gl3wProcs.gl.FramebufferTexture #define glFramebufferTexture1D gl3wProcs.gl.FramebufferTexture1D #define glFramebufferTexture2D gl3wProcs.gl.FramebufferTexture2D #define glFramebufferTexture3D gl3wProcs.gl.FramebufferTexture3D #define glFramebufferTextureLayer gl3wProcs.gl.FramebufferTextureLayer #define glFrontFace gl3wProcs.gl.FrontFace #define glGenBuffers gl3wProcs.gl.GenBuffers #define glGenFramebuffers gl3wProcs.gl.GenFramebuffers #define glGenProgramPipelines gl3wProcs.gl.GenProgramPipelines #define glGenQueries gl3wProcs.gl.GenQueries #define glGenRenderbuffers gl3wProcs.gl.GenRenderbuffers #define glGenSamplers gl3wProcs.gl.GenSamplers #define glGenTextures gl3wProcs.gl.GenTextures #define glGenTransformFeedbacks gl3wProcs.gl.GenTransformFeedbacks #define glGenVertexArrays gl3wProcs.gl.GenVertexArrays #define glGenerateMipmap gl3wProcs.gl.GenerateMipmap #define glGenerateTextureMipmap gl3wProcs.gl.GenerateTextureMipmap #define glGetActiveAtomicCounterBufferiv gl3wProcs.gl.GetActiveAtomicCounterBufferiv #define glGetActiveAttrib gl3wProcs.gl.GetActiveAttrib #define glGetActiveSubroutineName gl3wProcs.gl.GetActiveSubroutineName #define glGetActiveSubroutineUniformName gl3wProcs.gl.GetActiveSubroutineUniformName #define glGetActiveSubroutineUniformiv gl3wProcs.gl.GetActiveSubroutineUniformiv #define glGetActiveUniform gl3wProcs.gl.GetActiveUniform #define glGetActiveUniformBlockName gl3wProcs.gl.GetActiveUniformBlockName #define glGetActiveUniformBlockiv gl3wProcs.gl.GetActiveUniformBlockiv #define glGetActiveUniformName gl3wProcs.gl.GetActiveUniformName #define glGetActiveUniformsiv gl3wProcs.gl.GetActiveUniformsiv #define glGetAttachedShaders gl3wProcs.gl.GetAttachedShaders #define glGetAttribLocation gl3wProcs.gl.GetAttribLocation #define glGetBooleani_v gl3wProcs.gl.GetBooleani_v #define glGetBooleanv gl3wProcs.gl.GetBooleanv #define glGetBufferParameteri64v gl3wProcs.gl.GetBufferParameteri64v #define glGetBufferParameteriv gl3wProcs.gl.GetBufferParameteriv #define glGetBufferPointerv gl3wProcs.gl.GetBufferPointerv #define glGetBufferSubData gl3wProcs.gl.GetBufferSubData #define glGetCompressedTexImage gl3wProcs.gl.GetCompressedTexImage #define glGetCompressedTextureImage gl3wProcs.gl.GetCompressedTextureImage #define glGetCompressedTextureSubImage gl3wProcs.gl.GetCompressedTextureSubImage #define glGetDebugMessageLog gl3wProcs.gl.GetDebugMessageLog #define glGetDoublei_v gl3wProcs.gl.GetDoublei_v #define glGetDoublev gl3wProcs.gl.GetDoublev #define glGetError gl3wProcs.gl.GetError #define glGetFloati_v gl3wProcs.gl.GetFloati_v #define glGetFloatv gl3wProcs.gl.GetFloatv #define glGetFragDataIndex gl3wProcs.gl.GetFragDataIndex #define glGetFragDataLocation gl3wProcs.gl.GetFragDataLocation #define glGetFramebufferAttachmentParameteriv gl3wProcs.gl.GetFramebufferAttachmentParameteriv #define glGetFramebufferParameteriv gl3wProcs.gl.GetFramebufferParameteriv #define glGetFramebufferParameterivMESA gl3wProcs.gl.GetFramebufferParameterivMESA #define glGetGraphicsResetStatus gl3wProcs.gl.GetGraphicsResetStatus #define glGetInteger64i_v gl3wProcs.gl.GetInteger64i_v #define glGetInteger64v gl3wProcs.gl.GetInteger64v #define glGetIntegeri_v gl3wProcs.gl.GetIntegeri_v #define glGetIntegerv gl3wProcs.gl.GetIntegerv #define glGetInternalformati64v gl3wProcs.gl.GetInternalformati64v #define glGetInternalformativ gl3wProcs.gl.GetInternalformativ #define glGetMultisamplefv gl3wProcs.gl.GetMultisamplefv #define glGetNamedBufferParameteri64v gl3wProcs.gl.GetNamedBufferParameteri64v #define glGetNamedBufferParameteriv gl3wProcs.gl.GetNamedBufferParameteriv #define glGetNamedBufferPointerv gl3wProcs.gl.GetNamedBufferPointerv #define glGetNamedBufferSubData gl3wProcs.gl.GetNamedBufferSubData #define glGetNamedFramebufferAttachmentParameteriv gl3wProcs.gl.GetNamedFramebufferAttachmentParameteriv #define glGetNamedFramebufferParameteriv gl3wProcs.gl.GetNamedFramebufferParameteriv #define glGetNamedRenderbufferParameteriv gl3wProcs.gl.GetNamedRenderbufferParameteriv #define glGetObjectLabel gl3wProcs.gl.GetObjectLabel #define glGetObjectPtrLabel gl3wProcs.gl.GetObjectPtrLabel #define glGetPointerv gl3wProcs.gl.GetPointerv #define glGetProgramBinary gl3wProcs.gl.GetProgramBinary #define glGetProgramInfoLog gl3wProcs.gl.GetProgramInfoLog #define glGetProgramInterfaceiv gl3wProcs.gl.GetProgramInterfaceiv #define glGetProgramPipelineInfoLog gl3wProcs.gl.GetProgramPipelineInfoLog #define glGetProgramPipelineiv gl3wProcs.gl.GetProgramPipelineiv #define glGetProgramResourceIndex gl3wProcs.gl.GetProgramResourceIndex #define glGetProgramResourceLocation gl3wProcs.gl.GetProgramResourceLocation #define glGetProgramResourceLocationIndex gl3wProcs.gl.GetProgramResourceLocationIndex #define glGetProgramResourceName gl3wProcs.gl.GetProgramResourceName #define glGetProgramResourceiv gl3wProcs.gl.GetProgramResourceiv #define glGetProgramStageiv gl3wProcs.gl.GetProgramStageiv #define glGetProgramiv gl3wProcs.gl.GetProgramiv #define glGetQueryBufferObjecti64v gl3wProcs.gl.GetQueryBufferObjecti64v #define glGetQueryBufferObjectiv gl3wProcs.gl.GetQueryBufferObjectiv #define glGetQueryBufferObjectui64v gl3wProcs.gl.GetQueryBufferObjectui64v #define glGetQueryBufferObjectuiv gl3wProcs.gl.GetQueryBufferObjectuiv #define glGetQueryIndexediv gl3wProcs.gl.GetQueryIndexediv #define glGetQueryObjecti64v gl3wProcs.gl.GetQueryObjecti64v #define glGetQueryObjectiv gl3wProcs.gl.GetQueryObjectiv #define glGetQueryObjectui64v gl3wProcs.gl.GetQueryObjectui64v #define glGetQueryObjectuiv gl3wProcs.gl.GetQueryObjectuiv #define glGetQueryiv gl3wProcs.gl.GetQueryiv #define glGetRenderbufferParameteriv gl3wProcs.gl.GetRenderbufferParameteriv #define glGetSamplerParameterIiv gl3wProcs.gl.GetSamplerParameterIiv #define glGetSamplerParameterIuiv gl3wProcs.gl.GetSamplerParameterIuiv #define glGetSamplerParameterfv gl3wProcs.gl.GetSamplerParameterfv #define glGetSamplerParameteriv gl3wProcs.gl.GetSamplerParameteriv #define glGetShaderInfoLog gl3wProcs.gl.GetShaderInfoLog #define glGetShaderPrecisionFormat gl3wProcs.gl.GetShaderPrecisionFormat #define glGetShaderSource gl3wProcs.gl.GetShaderSource #define glGetShaderiv gl3wProcs.gl.GetShaderiv #define glGetString gl3wProcs.gl.GetString #define glGetStringi gl3wProcs.gl.GetStringi #define glGetSubroutineIndex gl3wProcs.gl.GetSubroutineIndex #define glGetSubroutineUniformLocation gl3wProcs.gl.GetSubroutineUniformLocation #define glGetSynciv gl3wProcs.gl.GetSynciv #define glGetTexImage gl3wProcs.gl.GetTexImage #define glGetTexLevelParameterfv gl3wProcs.gl.GetTexLevelParameterfv #define glGetTexLevelParameteriv gl3wProcs.gl.GetTexLevelParameteriv #define glGetTexParameterIiv gl3wProcs.gl.GetTexParameterIiv #define glGetTexParameterIuiv gl3wProcs.gl.GetTexParameterIuiv #define glGetTexParameterfv gl3wProcs.gl.GetTexParameterfv #define glGetTexParameteriv gl3wProcs.gl.GetTexParameteriv #define glGetTextureImage gl3wProcs.gl.GetTextureImage #define glGetTextureLevelParameterfv gl3wProcs.gl.GetTextureLevelParameterfv #define glGetTextureLevelParameteriv gl3wProcs.gl.GetTextureLevelParameteriv #define glGetTextureParameterIiv gl3wProcs.gl.GetTextureParameterIiv #define glGetTextureParameterIuiv gl3wProcs.gl.GetTextureParameterIuiv #define glGetTextureParameterfv gl3wProcs.gl.GetTextureParameterfv #define glGetTextureParameteriv gl3wProcs.gl.GetTextureParameteriv #define glGetTextureSubImage gl3wProcs.gl.GetTextureSubImage #define glGetTransformFeedbackVarying gl3wProcs.gl.GetTransformFeedbackVarying #define glGetTransformFeedbacki64_v gl3wProcs.gl.GetTransformFeedbacki64_v #define glGetTransformFeedbacki_v gl3wProcs.gl.GetTransformFeedbacki_v #define glGetTransformFeedbackiv gl3wProcs.gl.GetTransformFeedbackiv #define glGetUniformBlockIndex gl3wProcs.gl.GetUniformBlockIndex #define glGetUniformIndices gl3wProcs.gl.GetUniformIndices #define glGetUniformLocation gl3wProcs.gl.GetUniformLocation #define glGetUniformSubroutineuiv gl3wProcs.gl.GetUniformSubroutineuiv #define glGetUniformdv gl3wProcs.gl.GetUniformdv #define glGetUniformfv gl3wProcs.gl.GetUniformfv #define glGetUniformiv gl3wProcs.gl.GetUniformiv #define glGetUniformuiv gl3wProcs.gl.GetUniformuiv #define glGetVertexArrayIndexed64iv gl3wProcs.gl.GetVertexArrayIndexed64iv #define glGetVertexArrayIndexediv gl3wProcs.gl.GetVertexArrayIndexediv #define glGetVertexArrayiv gl3wProcs.gl.GetVertexArrayiv #define glGetVertexAttribIiv gl3wProcs.gl.GetVertexAttribIiv #define glGetVertexAttribIuiv gl3wProcs.gl.GetVertexAttribIuiv #define glGetVertexAttribLdv gl3wProcs.gl.GetVertexAttribLdv #define glGetVertexAttribPointerv gl3wProcs.gl.GetVertexAttribPointerv #define glGetVertexAttribdv gl3wProcs.gl.GetVertexAttribdv #define glGetVertexAttribfv gl3wProcs.gl.GetVertexAttribfv #define glGetVertexAttribiv gl3wProcs.gl.GetVertexAttribiv #define glGetnCompressedTexImage gl3wProcs.gl.GetnCompressedTexImage #define glGetnTexImage gl3wProcs.gl.GetnTexImage #define glGetnUniformdv gl3wProcs.gl.GetnUniformdv #define glGetnUniformfv gl3wProcs.gl.GetnUniformfv #define glGetnUniformiv gl3wProcs.gl.GetnUniformiv #define glGetnUniformuiv gl3wProcs.gl.GetnUniformuiv #define glHint gl3wProcs.gl.Hint #define glInvalidateBufferData gl3wProcs.gl.InvalidateBufferData #define glInvalidateBufferSubData gl3wProcs.gl.InvalidateBufferSubData #define glInvalidateFramebuffer gl3wProcs.gl.InvalidateFramebuffer #define glInvalidateNamedFramebufferData gl3wProcs.gl.InvalidateNamedFramebufferData #define glInvalidateNamedFramebufferSubData gl3wProcs.gl.InvalidateNamedFramebufferSubData #define glInvalidateSubFramebuffer gl3wProcs.gl.InvalidateSubFramebuffer #define glInvalidateTexImage gl3wProcs.gl.InvalidateTexImage #define glInvalidateTexSubImage gl3wProcs.gl.InvalidateTexSubImage #define glIsBuffer gl3wProcs.gl.IsBuffer #define glIsEnabled gl3wProcs.gl.IsEnabled #define glIsEnabledi gl3wProcs.gl.IsEnabledi #define glIsFramebuffer gl3wProcs.gl.IsFramebuffer #define glIsProgram gl3wProcs.gl.IsProgram #define glIsProgramPipeline gl3wProcs.gl.IsProgramPipeline #define glIsQuery gl3wProcs.gl.IsQuery #define glIsRenderbuffer gl3wProcs.gl.IsRenderbuffer #define glIsSampler gl3wProcs.gl.IsSampler #define glIsShader gl3wProcs.gl.IsShader #define glIsSync gl3wProcs.gl.IsSync #define glIsTexture gl3wProcs.gl.IsTexture #define glIsTransformFeedback gl3wProcs.gl.IsTransformFeedback #define glIsVertexArray gl3wProcs.gl.IsVertexArray #define glLineWidth gl3wProcs.gl.LineWidth #define glLinkProgram gl3wProcs.gl.LinkProgram #define glLogicOp gl3wProcs.gl.LogicOp #define glMapBuffer gl3wProcs.gl.MapBuffer #define glMapBufferRange gl3wProcs.gl.MapBufferRange #define glMapNamedBuffer gl3wProcs.gl.MapNamedBuffer #define glMapNamedBufferRange gl3wProcs.gl.MapNamedBufferRange #define glMemoryBarrier gl3wProcs.gl.MemoryBarrier #define glMemoryBarrierByRegion gl3wProcs.gl.MemoryBarrierByRegion #define glMinSampleShading gl3wProcs.gl.MinSampleShading #define glMultiDrawArrays gl3wProcs.gl.MultiDrawArrays #define glMultiDrawArraysIndirect gl3wProcs.gl.MultiDrawArraysIndirect #define glMultiDrawArraysIndirectCount gl3wProcs.gl.MultiDrawArraysIndirectCount #define glMultiDrawElements gl3wProcs.gl.MultiDrawElements #define glMultiDrawElementsBaseVertex gl3wProcs.gl.MultiDrawElementsBaseVertex #define glMultiDrawElementsIndirect gl3wProcs.gl.MultiDrawElementsIndirect #define glMultiDrawElementsIndirectCount gl3wProcs.gl.MultiDrawElementsIndirectCount #define glNamedBufferData gl3wProcs.gl.NamedBufferData #define glNamedBufferStorage gl3wProcs.gl.NamedBufferStorage #define glNamedBufferSubData gl3wProcs.gl.NamedBufferSubData #define glNamedFramebufferDrawBuffer gl3wProcs.gl.NamedFramebufferDrawBuffer #define glNamedFramebufferDrawBuffers gl3wProcs.gl.NamedFramebufferDrawBuffers #define glNamedFramebufferParameteri gl3wProcs.gl.NamedFramebufferParameteri #define glNamedFramebufferReadBuffer gl3wProcs.gl.NamedFramebufferReadBuffer #define glNamedFramebufferRenderbuffer gl3wProcs.gl.NamedFramebufferRenderbuffer #define glNamedFramebufferTexture gl3wProcs.gl.NamedFramebufferTexture #define glNamedFramebufferTextureLayer gl3wProcs.gl.NamedFramebufferTextureLayer #define glNamedRenderbufferStorage gl3wProcs.gl.NamedRenderbufferStorage #define glNamedRenderbufferStorageMultisample gl3wProcs.gl.NamedRenderbufferStorageMultisample #define glObjectLabel gl3wProcs.gl.ObjectLabel #define glObjectPtrLabel gl3wProcs.gl.ObjectPtrLabel #define glPatchParameterfv gl3wProcs.gl.PatchParameterfv #define glPatchParameteri gl3wProcs.gl.PatchParameteri #define glPauseTransformFeedback gl3wProcs.gl.PauseTransformFeedback #define glPixelStoref gl3wProcs.gl.PixelStoref #define glPixelStorei gl3wProcs.gl.PixelStorei #define glPointParameterf gl3wProcs.gl.PointParameterf #define glPointParameterfv gl3wProcs.gl.PointParameterfv #define glPointParameteri gl3wProcs.gl.PointParameteri #define glPointParameteriv gl3wProcs.gl.PointParameteriv #define glPointSize gl3wProcs.gl.PointSize #define glPolygonMode gl3wProcs.gl.PolygonMode #define glPolygonOffset gl3wProcs.gl.PolygonOffset #define glPolygonOffsetClamp gl3wProcs.gl.PolygonOffsetClamp #define glPopDebugGroup gl3wProcs.gl.PopDebugGroup #define glPrimitiveRestartIndex gl3wProcs.gl.PrimitiveRestartIndex #define glProgramBinary gl3wProcs.gl.ProgramBinary #define glProgramParameteri gl3wProcs.gl.ProgramParameteri #define glProgramUniform1d gl3wProcs.gl.ProgramUniform1d #define glProgramUniform1dv gl3wProcs.gl.ProgramUniform1dv #define glProgramUniform1f gl3wProcs.gl.ProgramUniform1f #define glProgramUniform1fv gl3wProcs.gl.ProgramUniform1fv #define glProgramUniform1i gl3wProcs.gl.ProgramUniform1i #define glProgramUniform1iv gl3wProcs.gl.ProgramUniform1iv #define glProgramUniform1ui gl3wProcs.gl.ProgramUniform1ui #define glProgramUniform1uiv gl3wProcs.gl.ProgramUniform1uiv #define glProgramUniform2d gl3wProcs.gl.ProgramUniform2d #define glProgramUniform2dv gl3wProcs.gl.ProgramUniform2dv #define glProgramUniform2f gl3wProcs.gl.ProgramUniform2f #define glProgramUniform2fv gl3wProcs.gl.ProgramUniform2fv #define glProgramUniform2i gl3wProcs.gl.ProgramUniform2i #define glProgramUniform2iv gl3wProcs.gl.ProgramUniform2iv #define glProgramUniform2ui gl3wProcs.gl.ProgramUniform2ui #define glProgramUniform2uiv gl3wProcs.gl.ProgramUniform2uiv #define glProgramUniform3d gl3wProcs.gl.ProgramUniform3d #define glProgramUniform3dv gl3wProcs.gl.ProgramUniform3dv #define glProgramUniform3f gl3wProcs.gl.ProgramUniform3f #define glProgramUniform3fv gl3wProcs.gl.ProgramUniform3fv #define glProgramUniform3i gl3wProcs.gl.ProgramUniform3i #define glProgramUniform3iv gl3wProcs.gl.ProgramUniform3iv #define glProgramUniform3ui gl3wProcs.gl.ProgramUniform3ui #define glProgramUniform3uiv gl3wProcs.gl.ProgramUniform3uiv #define glProgramUniform4d gl3wProcs.gl.ProgramUniform4d #define glProgramUniform4dv gl3wProcs.gl.ProgramUniform4dv #define glProgramUniform4f gl3wProcs.gl.ProgramUniform4f #define glProgramUniform4fv gl3wProcs.gl.ProgramUniform4fv #define glProgramUniform4i gl3wProcs.gl.ProgramUniform4i #define glProgramUniform4iv gl3wProcs.gl.ProgramUniform4iv #define glProgramUniform4ui gl3wProcs.gl.ProgramUniform4ui #define glProgramUniform4uiv gl3wProcs.gl.ProgramUniform4uiv #define glProgramUniformMatrix2dv gl3wProcs.gl.ProgramUniformMatrix2dv #define glProgramUniformMatrix2fv gl3wProcs.gl.ProgramUniformMatrix2fv #define glProgramUniformMatrix2x3dv gl3wProcs.gl.ProgramUniformMatrix2x3dv #define glProgramUniformMatrix2x3fv gl3wProcs.gl.ProgramUniformMatrix2x3fv #define glProgramUniformMatrix2x4dv gl3wProcs.gl.ProgramUniformMatrix2x4dv #define glProgramUniformMatrix2x4fv gl3wProcs.gl.ProgramUniformMatrix2x4fv #define glProgramUniformMatrix3dv gl3wProcs.gl.ProgramUniformMatrix3dv #define glProgramUniformMatrix3fv gl3wProcs.gl.ProgramUniformMatrix3fv #define glProgramUniformMatrix3x2dv gl3wProcs.gl.ProgramUniformMatrix3x2dv #define glProgramUniformMatrix3x2fv gl3wProcs.gl.ProgramUniformMatrix3x2fv #define glProgramUniformMatrix3x4dv gl3wProcs.gl.ProgramUniformMatrix3x4dv #define glProgramUniformMatrix3x4fv gl3wProcs.gl.ProgramUniformMatrix3x4fv #define glProgramUniformMatrix4dv gl3wProcs.gl.ProgramUniformMatrix4dv #define glProgramUniformMatrix4fv gl3wProcs.gl.ProgramUniformMatrix4fv #define glProgramUniformMatrix4x2dv gl3wProcs.gl.ProgramUniformMatrix4x2dv #define glProgramUniformMatrix4x2fv gl3wProcs.gl.ProgramUniformMatrix4x2fv #define glProgramUniformMatrix4x3dv gl3wProcs.gl.ProgramUniformMatrix4x3dv #define glProgramUniformMatrix4x3fv gl3wProcs.gl.ProgramUniformMatrix4x3fv #define glProvokingVertex gl3wProcs.gl.ProvokingVertex #define glPushDebugGroup gl3wProcs.gl.PushDebugGroup #define glQueryCounter gl3wProcs.gl.QueryCounter #define glReadBuffer gl3wProcs.gl.ReadBuffer #define glReadPixels gl3wProcs.gl.ReadPixels #define glReadnPixels gl3wProcs.gl.ReadnPixels #define glReleaseShaderCompiler gl3wProcs.gl.ReleaseShaderCompiler #define glRenderbufferStorage gl3wProcs.gl.RenderbufferStorage #define glRenderbufferStorageMultisample gl3wProcs.gl.RenderbufferStorageMultisample #define glResumeTransformFeedback gl3wProcs.gl.ResumeTransformFeedback #define glSampleCoverage gl3wProcs.gl.SampleCoverage #define glSampleMaski gl3wProcs.gl.SampleMaski #define glSamplerParameterIiv gl3wProcs.gl.SamplerParameterIiv #define glSamplerParameterIuiv gl3wProcs.gl.SamplerParameterIuiv #define glSamplerParameterf gl3wProcs.gl.SamplerParameterf #define glSamplerParameterfv gl3wProcs.gl.SamplerParameterfv #define glSamplerParameteri gl3wProcs.gl.SamplerParameteri #define glSamplerParameteriv gl3wProcs.gl.SamplerParameteriv #define glScissor gl3wProcs.gl.Scissor #define glScissorArrayv gl3wProcs.gl.ScissorArrayv #define glScissorIndexed gl3wProcs.gl.ScissorIndexed #define glScissorIndexedv gl3wProcs.gl.ScissorIndexedv #define glShaderBinary gl3wProcs.gl.ShaderBinary #define glShaderSource gl3wProcs.gl.ShaderSource #define glShaderStorageBlockBinding gl3wProcs.gl.ShaderStorageBlockBinding #define glSpecializeShader gl3wProcs.gl.SpecializeShader #define glStencilFunc gl3wProcs.gl.StencilFunc #define glStencilFuncSeparate gl3wProcs.gl.StencilFuncSeparate #define glStencilMask gl3wProcs.gl.StencilMask #define glStencilMaskSeparate gl3wProcs.gl.StencilMaskSeparate #define glStencilOp gl3wProcs.gl.StencilOp #define glStencilOpSeparate gl3wProcs.gl.StencilOpSeparate #define glTexBuffer gl3wProcs.gl.TexBuffer #define glTexBufferRange gl3wProcs.gl.TexBufferRange #define glTexImage1D gl3wProcs.gl.TexImage1D #define glTexImage2D gl3wProcs.gl.TexImage2D #define glTexImage2DMultisample gl3wProcs.gl.TexImage2DMultisample #define glTexImage3D gl3wProcs.gl.TexImage3D #define glTexImage3DMultisample gl3wProcs.gl.TexImage3DMultisample #define glTexParameterIiv gl3wProcs.gl.TexParameterIiv #define glTexParameterIuiv gl3wProcs.gl.TexParameterIuiv #define glTexParameterf gl3wProcs.gl.TexParameterf #define glTexParameterfv gl3wProcs.gl.TexParameterfv #define glTexParameteri gl3wProcs.gl.TexParameteri #define glTexParameteriv gl3wProcs.gl.TexParameteriv #define glTexStorage1D gl3wProcs.gl.TexStorage1D #define glTexStorage2D gl3wProcs.gl.TexStorage2D #define glTexStorage2DMultisample gl3wProcs.gl.TexStorage2DMultisample #define glTexStorage3D gl3wProcs.gl.TexStorage3D #define glTexStorage3DMultisample gl3wProcs.gl.TexStorage3DMultisample #define glTexSubImage1D gl3wProcs.gl.TexSubImage1D #define glTexSubImage2D gl3wProcs.gl.TexSubImage2D #define glTexSubImage3D gl3wProcs.gl.TexSubImage3D #define glTextureBarrier gl3wProcs.gl.TextureBarrier #define glTextureBuffer gl3wProcs.gl.TextureBuffer #define glTextureBufferRange gl3wProcs.gl.TextureBufferRange #define glTextureParameterIiv gl3wProcs.gl.TextureParameterIiv #define glTextureParameterIuiv gl3wProcs.gl.TextureParameterIuiv #define glTextureParameterf gl3wProcs.gl.TextureParameterf #define glTextureParameterfv gl3wProcs.gl.TextureParameterfv #define glTextureParameteri gl3wProcs.gl.TextureParameteri #define glTextureParameteriv gl3wProcs.gl.TextureParameteriv #define glTextureStorage1D gl3wProcs.gl.TextureStorage1D #define glTextureStorage2D gl3wProcs.gl.TextureStorage2D #define glTextureStorage2DMultisample gl3wProcs.gl.TextureStorage2DMultisample #define glTextureStorage3D gl3wProcs.gl.TextureStorage3D #define glTextureStorage3DMultisample gl3wProcs.gl.TextureStorage3DMultisample #define glTextureSubImage1D gl3wProcs.gl.TextureSubImage1D #define glTextureSubImage2D gl3wProcs.gl.TextureSubImage2D #define glTextureSubImage3D gl3wProcs.gl.TextureSubImage3D #define glTextureView gl3wProcs.gl.TextureView #define glTransformFeedbackBufferBase gl3wProcs.gl.TransformFeedbackBufferBase #define glTransformFeedbackBufferRange gl3wProcs.gl.TransformFeedbackBufferRange #define glTransformFeedbackVaryings gl3wProcs.gl.TransformFeedbackVaryings #define glUniform1d gl3wProcs.gl.Uniform1d #define glUniform1dv gl3wProcs.gl.Uniform1dv #define glUniform1f gl3wProcs.gl.Uniform1f #define glUniform1fv gl3wProcs.gl.Uniform1fv #define glUniform1i gl3wProcs.gl.Uniform1i #define glUniform1iv gl3wProcs.gl.Uniform1iv #define glUniform1ui gl3wProcs.gl.Uniform1ui #define glUniform1uiv gl3wProcs.gl.Uniform1uiv #define glUniform2d gl3wProcs.gl.Uniform2d #define glUniform2dv gl3wProcs.gl.Uniform2dv #define glUniform2f gl3wProcs.gl.Uniform2f #define glUniform2fv gl3wProcs.gl.Uniform2fv #define glUniform2i gl3wProcs.gl.Uniform2i #define glUniform2iv gl3wProcs.gl.Uniform2iv #define glUniform2ui gl3wProcs.gl.Uniform2ui #define glUniform2uiv gl3wProcs.gl.Uniform2uiv #define glUniform3d gl3wProcs.gl.Uniform3d #define glUniform3dv gl3wProcs.gl.Uniform3dv #define glUniform3f gl3wProcs.gl.Uniform3f #define glUniform3fv gl3wProcs.gl.Uniform3fv #define glUniform3i gl3wProcs.gl.Uniform3i #define glUniform3iv gl3wProcs.gl.Uniform3iv #define glUniform3ui gl3wProcs.gl.Uniform3ui #define glUniform3uiv gl3wProcs.gl.Uniform3uiv #define glUniform4d gl3wProcs.gl.Uniform4d #define glUniform4dv gl3wProcs.gl.Uniform4dv #define glUniform4f gl3wProcs.gl.Uniform4f #define glUniform4fv gl3wProcs.gl.Uniform4fv #define glUniform4i gl3wProcs.gl.Uniform4i #define glUniform4iv gl3wProcs.gl.Uniform4iv #define glUniform4ui gl3wProcs.gl.Uniform4ui #define glUniform4uiv gl3wProcs.gl.Uniform4uiv #define glUniformBlockBinding gl3wProcs.gl.UniformBlockBinding #define glUniformMatrix2dv gl3wProcs.gl.UniformMatrix2dv #define glUniformMatrix2fv gl3wProcs.gl.UniformMatrix2fv #define glUniformMatrix2x3dv gl3wProcs.gl.UniformMatrix2x3dv #define glUniformMatrix2x3fv gl3wProcs.gl.UniformMatrix2x3fv #define glUniformMatrix2x4dv gl3wProcs.gl.UniformMatrix2x4dv #define glUniformMatrix2x4fv gl3wProcs.gl.UniformMatrix2x4fv #define glUniformMatrix3dv gl3wProcs.gl.UniformMatrix3dv #define glUniformMatrix3fv gl3wProcs.gl.UniformMatrix3fv #define glUniformMatrix3x2dv gl3wProcs.gl.UniformMatrix3x2dv #define glUniformMatrix3x2fv gl3wProcs.gl.UniformMatrix3x2fv #define glUniformMatrix3x4dv gl3wProcs.gl.UniformMatrix3x4dv #define glUniformMatrix3x4fv gl3wProcs.gl.UniformMatrix3x4fv #define glUniformMatrix4dv gl3wProcs.gl.UniformMatrix4dv #define glUniformMatrix4fv gl3wProcs.gl.UniformMatrix4fv #define glUniformMatrix4x2dv gl3wProcs.gl.UniformMatrix4x2dv #define glUniformMatrix4x2fv gl3wProcs.gl.UniformMatrix4x2fv #define glUniformMatrix4x3dv gl3wProcs.gl.UniformMatrix4x3dv #define glUniformMatrix4x3fv gl3wProcs.gl.UniformMatrix4x3fv #define glUniformSubroutinesuiv gl3wProcs.gl.UniformSubroutinesuiv #define glUnmapBuffer gl3wProcs.gl.UnmapBuffer #define glUnmapNamedBuffer gl3wProcs.gl.UnmapNamedBuffer #define glUseProgram gl3wProcs.gl.UseProgram #define glUseProgramStages gl3wProcs.gl.UseProgramStages #define glValidateProgram gl3wProcs.gl.ValidateProgram #define glValidateProgramPipeline gl3wProcs.gl.ValidateProgramPipeline #define glVertexArrayAttribBinding gl3wProcs.gl.VertexArrayAttribBinding #define glVertexArrayAttribFormat gl3wProcs.gl.VertexArrayAttribFormat #define glVertexArrayAttribIFormat gl3wProcs.gl.VertexArrayAttribIFormat #define glVertexArrayAttribLFormat gl3wProcs.gl.VertexArrayAttribLFormat #define glVertexArrayBindingDivisor gl3wProcs.gl.VertexArrayBindingDivisor #define glVertexArrayElementBuffer gl3wProcs.gl.VertexArrayElementBuffer #define glVertexArrayVertexBuffer gl3wProcs.gl.VertexArrayVertexBuffer #define glVertexArrayVertexBuffers gl3wProcs.gl.VertexArrayVertexBuffers #define glVertexAttrib1d gl3wProcs.gl.VertexAttrib1d #define glVertexAttrib1dv gl3wProcs.gl.VertexAttrib1dv #define glVertexAttrib1f gl3wProcs.gl.VertexAttrib1f #define glVertexAttrib1fv gl3wProcs.gl.VertexAttrib1fv #define glVertexAttrib1s gl3wProcs.gl.VertexAttrib1s #define glVertexAttrib1sv gl3wProcs.gl.VertexAttrib1sv #define glVertexAttrib2d gl3wProcs.gl.VertexAttrib2d #define glVertexAttrib2dv gl3wProcs.gl.VertexAttrib2dv #define glVertexAttrib2f gl3wProcs.gl.VertexAttrib2f #define glVertexAttrib2fv gl3wProcs.gl.VertexAttrib2fv #define glVertexAttrib2s gl3wProcs.gl.VertexAttrib2s #define glVertexAttrib2sv gl3wProcs.gl.VertexAttrib2sv #define glVertexAttrib3d gl3wProcs.gl.VertexAttrib3d #define glVertexAttrib3dv gl3wProcs.gl.VertexAttrib3dv #define glVertexAttrib3f gl3wProcs.gl.VertexAttrib3f #define glVertexAttrib3fv gl3wProcs.gl.VertexAttrib3fv #define glVertexAttrib3s gl3wProcs.gl.VertexAttrib3s #define glVertexAttrib3sv gl3wProcs.gl.VertexAttrib3sv #define glVertexAttrib4Nbv gl3wProcs.gl.VertexAttrib4Nbv #define glVertexAttrib4Niv gl3wProcs.gl.VertexAttrib4Niv #define glVertexAttrib4Nsv gl3wProcs.gl.VertexAttrib4Nsv #define glVertexAttrib4Nub gl3wProcs.gl.VertexAttrib4Nub #define glVertexAttrib4Nubv gl3wProcs.gl.VertexAttrib4Nubv #define glVertexAttrib4Nuiv gl3wProcs.gl.VertexAttrib4Nuiv #define glVertexAttrib4Nusv gl3wProcs.gl.VertexAttrib4Nusv #define glVertexAttrib4bv gl3wProcs.gl.VertexAttrib4bv #define glVertexAttrib4d gl3wProcs.gl.VertexAttrib4d #define glVertexAttrib4dv gl3wProcs.gl.VertexAttrib4dv #define glVertexAttrib4f gl3wProcs.gl.VertexAttrib4f #define glVertexAttrib4fv gl3wProcs.gl.VertexAttrib4fv #define glVertexAttrib4iv gl3wProcs.gl.VertexAttrib4iv #define glVertexAttrib4s gl3wProcs.gl.VertexAttrib4s #define glVertexAttrib4sv gl3wProcs.gl.VertexAttrib4sv #define glVertexAttrib4ubv gl3wProcs.gl.VertexAttrib4ubv #define glVertexAttrib4uiv gl3wProcs.gl.VertexAttrib4uiv #define glVertexAttrib4usv gl3wProcs.gl.VertexAttrib4usv #define glVertexAttribBinding gl3wProcs.gl.VertexAttribBinding #define glVertexAttribDivisor gl3wProcs.gl.VertexAttribDivisor #define glVertexAttribFormat gl3wProcs.gl.VertexAttribFormat #define glVertexAttribI1i gl3wProcs.gl.VertexAttribI1i #define glVertexAttribI1iv gl3wProcs.gl.VertexAttribI1iv #define glVertexAttribI1ui gl3wProcs.gl.VertexAttribI1ui #define glVertexAttribI1uiv gl3wProcs.gl.VertexAttribI1uiv #define glVertexAttribI2i gl3wProcs.gl.VertexAttribI2i #define glVertexAttribI2iv gl3wProcs.gl.VertexAttribI2iv #define glVertexAttribI2ui gl3wProcs.gl.VertexAttribI2ui #define glVertexAttribI2uiv gl3wProcs.gl.VertexAttribI2uiv #define glVertexAttribI3i gl3wProcs.gl.VertexAttribI3i #define glVertexAttribI3iv gl3wProcs.gl.VertexAttribI3iv #define glVertexAttribI3ui gl3wProcs.gl.VertexAttribI3ui #define glVertexAttribI3uiv gl3wProcs.gl.VertexAttribI3uiv #define glVertexAttribI4bv gl3wProcs.gl.VertexAttribI4bv #define glVertexAttribI4i gl3wProcs.gl.VertexAttribI4i #define glVertexAttribI4iv gl3wProcs.gl.VertexAttribI4iv #define glVertexAttribI4sv gl3wProcs.gl.VertexAttribI4sv #define glVertexAttribI4ubv gl3wProcs.gl.VertexAttribI4ubv #define glVertexAttribI4ui gl3wProcs.gl.VertexAttribI4ui #define glVertexAttribI4uiv gl3wProcs.gl.VertexAttribI4uiv #define glVertexAttribI4usv gl3wProcs.gl.VertexAttribI4usv #define glVertexAttribIFormat gl3wProcs.gl.VertexAttribIFormat #define glVertexAttribIPointer gl3wProcs.gl.VertexAttribIPointer #define glVertexAttribL1d gl3wProcs.gl.VertexAttribL1d #define glVertexAttribL1dv gl3wProcs.gl.VertexAttribL1dv #define glVertexAttribL2d gl3wProcs.gl.VertexAttribL2d #define glVertexAttribL2dv gl3wProcs.gl.VertexAttribL2dv #define glVertexAttribL3d gl3wProcs.gl.VertexAttribL3d #define glVertexAttribL3dv gl3wProcs.gl.VertexAttribL3dv #define glVertexAttribL4d gl3wProcs.gl.VertexAttribL4d #define glVertexAttribL4dv gl3wProcs.gl.VertexAttribL4dv #define glVertexAttribLFormat gl3wProcs.gl.VertexAttribLFormat #define glVertexAttribLPointer gl3wProcs.gl.VertexAttribLPointer #define glVertexAttribP1ui gl3wProcs.gl.VertexAttribP1ui #define glVertexAttribP1uiv gl3wProcs.gl.VertexAttribP1uiv #define glVertexAttribP2ui gl3wProcs.gl.VertexAttribP2ui #define glVertexAttribP2uiv gl3wProcs.gl.VertexAttribP2uiv #define glVertexAttribP3ui gl3wProcs.gl.VertexAttribP3ui #define glVertexAttribP3uiv gl3wProcs.gl.VertexAttribP3uiv #define glVertexAttribP4ui gl3wProcs.gl.VertexAttribP4ui #define glVertexAttribP4uiv gl3wProcs.gl.VertexAttribP4uiv #define glVertexAttribPointer gl3wProcs.gl.VertexAttribPointer #define glVertexBindingDivisor gl3wProcs.gl.VertexBindingDivisor #define glViewport gl3wProcs.gl.Viewport #define glViewportArrayv gl3wProcs.gl.ViewportArrayv #define glViewportIndexedf gl3wProcs.gl.ViewportIndexedf #define glViewportIndexedfv gl3wProcs.gl.ViewportIndexedfv #define glWaitSync gl3wProcs.gl.WaitSync #ifdef __cplusplus } #endif #endif #ifdef IMGL3W_IMPL #ifdef __cplusplus extern "C" { #endif #include #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #if defined(_WIN32) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif #include static HMODULE libgl; typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR); static GL3WglGetProcAddr wgl_get_proc_address; static int open_libgl(void) { libgl = LoadLibraryA("opengl32.dll"); if (!libgl) return GL3W_ERROR_LIBRARY_OPEN; wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress"); return GL3W_OK; } static void close_libgl(void) { FreeLibrary(libgl); } static GL3WglProc get_proc(const char *proc) { GL3WglProc res; res = (GL3WglProc)wgl_get_proc_address(proc); if (!res) res = (GL3WglProc)GetProcAddress(libgl, proc); return res; } #elif defined(__APPLE__) #include static void *libgl; static int open_libgl(void) { libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL); if (!libgl) return GL3W_ERROR_LIBRARY_OPEN; return GL3W_OK; } static void close_libgl(void) { dlclose(libgl); } static GL3WglProc get_proc(const char *proc) { GL3WglProc res; *(void **)(&res) = dlsym(libgl, proc); return res; } #else #include static void *libgl; static GL3WglProc (*glx_get_proc_address)(const GLubyte *); static int open_libgl(void) { libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL); if (!libgl) return GL3W_ERROR_LIBRARY_OPEN; *(void **)(&glx_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB"); return GL3W_OK; } static void close_libgl(void) { if (!libgl) return; dlclose(libgl); libgl = NULL; } static GL3WglProc get_proc(const char *proc) { GL3WglProc res; res = glx_get_proc_address((const GLubyte *)proc); if (!res) *(void **)(&res) = dlsym(libgl, proc); return res; } #endif static struct { int major, minor; } version; static int parse_version(void) { if (!glGetIntegerv) return GL3W_ERROR_INIT; glGetIntegerv(GL_MAJOR_VERSION, &version.major); glGetIntegerv(GL_MINOR_VERSION, &version.minor); if (version.major < 3) return GL3W_ERROR_OPENGL_VERSION; return GL3W_OK; } static void load_procs(GL3WGetProcAddressProc proc); int imgl3wInit(void) { int res = open_libgl(); if (res) return res; atexit(close_libgl); return imgl3wInit2(get_proc); } int imgl3wInit2(GL3WGetProcAddressProc proc) { load_procs(proc); return parse_version(); } void imgl3wShutdown(void) { close_libgl(); gl3wProcs = {}; } int imgl3wIsSupported(int major, int minor) { if (major < 3) return 0; if (version.major == major) return version.minor >= minor; return version.major >= major; } GL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); } static const char *proc_names[] = { "glActiveShaderProgram", "glActiveTexture", "glAttachShader", "glBeginConditionalRender", "glBeginQuery", "glBeginQueryIndexed", "glBeginTransformFeedback", "glBindAttribLocation", "glBindBuffer", "glBindBufferBase", "glBindBufferRange", "glBindBuffersBase", "glBindBuffersRange", "glBindFragDataLocation", "glBindFragDataLocationIndexed", "glBindFramebuffer", "glBindImageTexture", "glBindImageTextures", "glBindProgramPipeline", "glBindRenderbuffer", "glBindSampler", "glBindSamplers", "glBindTexture", "glBindTextureUnit", "glBindTextures", "glBindTransformFeedback", "glBindVertexArray", "glBindVertexBuffer", "glBindVertexBuffers", "glBlendColor", "glBlendEquation", "glBlendEquationSeparate", "glBlendEquationSeparatei", "glBlendEquationi", "glBlendFunc", "glBlendFuncSeparate", "glBlendFuncSeparatei", "glBlendFunci", "glBlitFramebuffer", "glBlitNamedFramebuffer", "glBufferData", "glBufferStorage", "glBufferSubData", "glCheckFramebufferStatus", "glCheckNamedFramebufferStatus", "glClampColor", "glClear", "glClearBufferData", "glClearBufferSubData", "glClearBufferfi", "glClearBufferfv", "glClearBufferiv", "glClearBufferuiv", "glClearColor", "glClearDepth", "glClearDepthf", "glClearNamedBufferData", "glClearNamedBufferSubData", "glClearNamedFramebufferfi", "glClearNamedFramebufferfv", "glClearNamedFramebufferiv", "glClearNamedFramebufferuiv", "glClearStencil", "glClearTexImage", "glClearTexSubImage", "glClientWaitSync", "glClipControl", "glColorMask", "glColorMaski", "glCompileShader", "glCompressedTexImage1D", "glCompressedTexImage2D", "glCompressedTexImage3D", "glCompressedTexSubImage1D", "glCompressedTexSubImage2D", "glCompressedTexSubImage3D", "glCompressedTextureSubImage1D", "glCompressedTextureSubImage2D", "glCompressedTextureSubImage3D", "glCopyBufferSubData", "glCopyImageSubData", "glCopyNamedBufferSubData", "glCopyTexImage1D", "glCopyTexImage2D", "glCopyTexSubImage1D", "glCopyTexSubImage2D", "glCopyTexSubImage3D", "glCopyTextureSubImage1D", "glCopyTextureSubImage2D", "glCopyTextureSubImage3D", "glCreateBuffers", "glCreateFramebuffers", "glCreateProgram", "glCreateProgramPipelines", "glCreateQueries", "glCreateRenderbuffers", "glCreateSamplers", "glCreateShader", "glCreateShaderProgramv", "glCreateTextures", "glCreateTransformFeedbacks", "glCreateVertexArrays", "glCullFace", "glDebugMessageCallback", "glDebugMessageControl", "glDebugMessageInsert", "glDeleteBuffers", "glDeleteFramebuffers", "glDeleteProgram", "glDeleteProgramPipelines", "glDeleteQueries", "glDeleteRenderbuffers", "glDeleteSamplers", "glDeleteShader", "glDeleteSync", "glDeleteTextures", "glDeleteTransformFeedbacks", "glDeleteVertexArrays", "glDepthFunc", "glDepthMask", "glDepthRange", "glDepthRangeArrayv", "glDepthRangeIndexed", "glDepthRangef", "glDetachShader", "glDisable", "glDisableVertexArrayAttrib", "glDisableVertexAttribArray", "glDisablei", "glDispatchCompute", "glDispatchComputeIndirect", "glDrawArrays", "glDrawArraysIndirect", "glDrawArraysInstanced", "glDrawArraysInstancedBaseInstance", "glDrawBuffer", "glDrawBuffers", "glDrawElements", "glDrawElementsBaseVertex", "glDrawElementsIndirect", "glDrawElementsInstanced", "glDrawElementsInstancedBaseInstance", "glDrawElementsInstancedBaseVertex", "glDrawElementsInstancedBaseVertexBaseInstance", "glDrawRangeElements", "glDrawRangeElementsBaseVertex", "glDrawTransformFeedback", "glDrawTransformFeedbackInstanced", "glDrawTransformFeedbackStream", "glDrawTransformFeedbackStreamInstanced", "glEnable", "glEnableVertexArrayAttrib", "glEnableVertexAttribArray", "glEnablei", "glEndConditionalRender", "glEndQuery", "glEndQueryIndexed", "glEndTransformFeedback", "glFenceSync", "glFinish", "glFlush", "glFlushMappedBufferRange", "glFlushMappedNamedBufferRange", "glFramebufferParameteri", "glFramebufferParameteriMESA", "glFramebufferRenderbuffer", "glFramebufferTexture", "glFramebufferTexture1D", "glFramebufferTexture2D", "glFramebufferTexture3D", "glFramebufferTextureLayer", "glFrontFace", "glGenBuffers", "glGenFramebuffers", "glGenProgramPipelines", "glGenQueries", "glGenRenderbuffers", "glGenSamplers", "glGenTextures", "glGenTransformFeedbacks", "glGenVertexArrays", "glGenerateMipmap", "glGenerateTextureMipmap", "glGetActiveAtomicCounterBufferiv", "glGetActiveAttrib", "glGetActiveSubroutineName", "glGetActiveSubroutineUniformName", "glGetActiveSubroutineUniformiv", "glGetActiveUniform", "glGetActiveUniformBlockName", "glGetActiveUniformBlockiv", "glGetActiveUniformName", "glGetActiveUniformsiv", "glGetAttachedShaders", "glGetAttribLocation", "glGetBooleani_v", "glGetBooleanv", "glGetBufferParameteri64v", "glGetBufferParameteriv", "glGetBufferPointerv", "glGetBufferSubData", "glGetCompressedTexImage", "glGetCompressedTextureImage", "glGetCompressedTextureSubImage", "glGetDebugMessageLog", "glGetDoublei_v", "glGetDoublev", "glGetError", "glGetFloati_v", "glGetFloatv", "glGetFragDataIndex", "glGetFragDataLocation", "glGetFramebufferAttachmentParameteriv", "glGetFramebufferParameteriv", "glGetFramebufferParameterivMESA", "glGetGraphicsResetStatus", "glGetInteger64i_v", "glGetInteger64v", "glGetIntegeri_v", "glGetIntegerv", "glGetInternalformati64v", "glGetInternalformativ", "glGetMultisamplefv", "glGetNamedBufferParameteri64v", "glGetNamedBufferParameteriv", "glGetNamedBufferPointerv", "glGetNamedBufferSubData", "glGetNamedFramebufferAttachmentParameteriv", "glGetNamedFramebufferParameteriv", "glGetNamedRenderbufferParameteriv", "glGetObjectLabel", "glGetObjectPtrLabel", "glGetPointerv", "glGetProgramBinary", "glGetProgramInfoLog", "glGetProgramInterfaceiv", "glGetProgramPipelineInfoLog", "glGetProgramPipelineiv", "glGetProgramResourceIndex", "glGetProgramResourceLocation", "glGetProgramResourceLocationIndex", "glGetProgramResourceName", "glGetProgramResourceiv", "glGetProgramStageiv", "glGetProgramiv", "glGetQueryBufferObjecti64v", "glGetQueryBufferObjectiv", "glGetQueryBufferObjectui64v", "glGetQueryBufferObjectuiv", "glGetQueryIndexediv", "glGetQueryObjecti64v", "glGetQueryObjectiv", "glGetQueryObjectui64v", "glGetQueryObjectuiv", "glGetQueryiv", "glGetRenderbufferParameteriv", "glGetSamplerParameterIiv", "glGetSamplerParameterIuiv", "glGetSamplerParameterfv", "glGetSamplerParameteriv", "glGetShaderInfoLog", "glGetShaderPrecisionFormat", "glGetShaderSource", "glGetShaderiv", "glGetString", "glGetStringi", "glGetSubroutineIndex", "glGetSubroutineUniformLocation", "glGetSynciv", "glGetTexImage", "glGetTexLevelParameterfv", "glGetTexLevelParameteriv", "glGetTexParameterIiv", "glGetTexParameterIuiv", "glGetTexParameterfv", "glGetTexParameteriv", "glGetTextureImage", "glGetTextureLevelParameterfv", "glGetTextureLevelParameteriv", "glGetTextureParameterIiv", "glGetTextureParameterIuiv", "glGetTextureParameterfv", "glGetTextureParameteriv", "glGetTextureSubImage", "glGetTransformFeedbackVarying", "glGetTransformFeedbacki64_v", "glGetTransformFeedbacki_v", "glGetTransformFeedbackiv", "glGetUniformBlockIndex", "glGetUniformIndices", "glGetUniformLocation", "glGetUniformSubroutineuiv", "glGetUniformdv", "glGetUniformfv", "glGetUniformiv", "glGetUniformuiv", "glGetVertexArrayIndexed64iv", "glGetVertexArrayIndexediv", "glGetVertexArrayiv", "glGetVertexAttribIiv", "glGetVertexAttribIuiv", "glGetVertexAttribLdv", "glGetVertexAttribPointerv", "glGetVertexAttribdv", "glGetVertexAttribfv", "glGetVertexAttribiv", "glGetnCompressedTexImage", "glGetnTexImage", "glGetnUniformdv", "glGetnUniformfv", "glGetnUniformiv", "glGetnUniformuiv", "glHint", "glInvalidateBufferData", "glInvalidateBufferSubData", "glInvalidateFramebuffer", "glInvalidateNamedFramebufferData", "glInvalidateNamedFramebufferSubData", "glInvalidateSubFramebuffer", "glInvalidateTexImage", "glInvalidateTexSubImage", "glIsBuffer", "glIsEnabled", "glIsEnabledi", "glIsFramebuffer", "glIsProgram", "glIsProgramPipeline", "glIsQuery", "glIsRenderbuffer", "glIsSampler", "glIsShader", "glIsSync", "glIsTexture", "glIsTransformFeedback", "glIsVertexArray", "glLineWidth", "glLinkProgram", "glLogicOp", "glMapBuffer", "glMapBufferRange", "glMapNamedBuffer", "glMapNamedBufferRange", "glMemoryBarrier", "glMemoryBarrierByRegion", "glMinSampleShading", "glMultiDrawArrays", "glMultiDrawArraysIndirect", "glMultiDrawArraysIndirectCount", "glMultiDrawElements", "glMultiDrawElementsBaseVertex", "glMultiDrawElementsIndirect", "glMultiDrawElementsIndirectCount", "glNamedBufferData", "glNamedBufferStorage", "glNamedBufferSubData", "glNamedFramebufferDrawBuffer", "glNamedFramebufferDrawBuffers", "glNamedFramebufferParameteri", "glNamedFramebufferReadBuffer", "glNamedFramebufferRenderbuffer", "glNamedFramebufferTexture", "glNamedFramebufferTextureLayer", "glNamedRenderbufferStorage", "glNamedRenderbufferStorageMultisample", "glObjectLabel", "glObjectPtrLabel", "glPatchParameterfv", "glPatchParameteri", "glPauseTransformFeedback", "glPixelStoref", "glPixelStorei", "glPointParameterf", "glPointParameterfv", "glPointParameteri", "glPointParameteriv", "glPointSize", "glPolygonMode", "glPolygonOffset", "glPolygonOffsetClamp", "glPopDebugGroup", "glPrimitiveRestartIndex", "glProgramBinary", "glProgramParameteri", "glProgramUniform1d", "glProgramUniform1dv", "glProgramUniform1f", "glProgramUniform1fv", "glProgramUniform1i", "glProgramUniform1iv", "glProgramUniform1ui", "glProgramUniform1uiv", "glProgramUniform2d", "glProgramUniform2dv", "glProgramUniform2f", "glProgramUniform2fv", "glProgramUniform2i", "glProgramUniform2iv", "glProgramUniform2ui", "glProgramUniform2uiv", "glProgramUniform3d", "glProgramUniform3dv", "glProgramUniform3f", "glProgramUniform3fv", "glProgramUniform3i", "glProgramUniform3iv", "glProgramUniform3ui", "glProgramUniform3uiv", "glProgramUniform4d", "glProgramUniform4dv", "glProgramUniform4f", "glProgramUniform4fv", "glProgramUniform4i", "glProgramUniform4iv", "glProgramUniform4ui", "glProgramUniform4uiv", "glProgramUniformMatrix2dv", "glProgramUniformMatrix2fv", "glProgramUniformMatrix2x3dv", "glProgramUniformMatrix2x3fv", "glProgramUniformMatrix2x4dv", "glProgramUniformMatrix2x4fv", "glProgramUniformMatrix3dv", "glProgramUniformMatrix3fv", "glProgramUniformMatrix3x2dv", "glProgramUniformMatrix3x2fv", "glProgramUniformMatrix3x4dv", "glProgramUniformMatrix3x4fv", "glProgramUniformMatrix4dv", "glProgramUniformMatrix4fv", "glProgramUniformMatrix4x2dv", "glProgramUniformMatrix4x2fv", "glProgramUniformMatrix4x3dv", "glProgramUniformMatrix4x3fv", "glProvokingVertex", "glPushDebugGroup", "glQueryCounter", "glReadBuffer", "glReadPixels", "glReadnPixels", "glReleaseShaderCompiler", "glRenderbufferStorage", "glRenderbufferStorageMultisample", "glResumeTransformFeedback", "glSampleCoverage", "glSampleMaski", "glSamplerParameterIiv", "glSamplerParameterIuiv", "glSamplerParameterf", "glSamplerParameterfv", "glSamplerParameteri", "glSamplerParameteriv", "glScissor", "glScissorArrayv", "glScissorIndexed", "glScissorIndexedv", "glShaderBinary", "glShaderSource", "glShaderStorageBlockBinding", "glSpecializeShader", "glStencilFunc", "glStencilFuncSeparate", "glStencilMask", "glStencilMaskSeparate", "glStencilOp", "glStencilOpSeparate", "glTexBuffer", "glTexBufferRange", "glTexImage1D", "glTexImage2D", "glTexImage2DMultisample", "glTexImage3D", "glTexImage3DMultisample", "glTexParameterIiv", "glTexParameterIuiv", "glTexParameterf", "glTexParameterfv", "glTexParameteri", "glTexParameteriv", "glTexStorage1D", "glTexStorage2D", "glTexStorage2DMultisample", "glTexStorage3D", "glTexStorage3DMultisample", "glTexSubImage1D", "glTexSubImage2D", "glTexSubImage3D", "glTextureBarrier", "glTextureBuffer", "glTextureBufferRange", "glTextureParameterIiv", "glTextureParameterIuiv", "glTextureParameterf", "glTextureParameterfv", "glTextureParameteri", "glTextureParameteriv", "glTextureStorage1D", "glTextureStorage2D", "glTextureStorage2DMultisample", "glTextureStorage3D", "glTextureStorage3DMultisample", "glTextureSubImage1D", "glTextureSubImage2D", "glTextureSubImage3D", "glTextureView", "glTransformFeedbackBufferBase", "glTransformFeedbackBufferRange", "glTransformFeedbackVaryings", "glUniform1d", "glUniform1dv", "glUniform1f", "glUniform1fv", "glUniform1i", "glUniform1iv", "glUniform1ui", "glUniform1uiv", "glUniform2d", "glUniform2dv", "glUniform2f", "glUniform2fv", "glUniform2i", "glUniform2iv", "glUniform2ui", "glUniform2uiv", "glUniform3d", "glUniform3dv", "glUniform3f", "glUniform3fv", "glUniform3i", "glUniform3iv", "glUniform3ui", "glUniform3uiv", "glUniform4d", "glUniform4dv", "glUniform4f", "glUniform4fv", "glUniform4i", "glUniform4iv", "glUniform4ui", "glUniform4uiv", "glUniformBlockBinding", "glUniformMatrix2dv", "glUniformMatrix2fv", "glUniformMatrix2x3dv", "glUniformMatrix2x3fv", "glUniformMatrix2x4dv", "glUniformMatrix2x4fv", "glUniformMatrix3dv", "glUniformMatrix3fv", "glUniformMatrix3x2dv", "glUniformMatrix3x2fv", "glUniformMatrix3x4dv", "glUniformMatrix3x4fv", "glUniformMatrix4dv", "glUniformMatrix4fv", "glUniformMatrix4x2dv", "glUniformMatrix4x2fv", "glUniformMatrix4x3dv", "glUniformMatrix4x3fv", "glUniformSubroutinesuiv", "glUnmapBuffer", "glUnmapNamedBuffer", "glUseProgram", "glUseProgramStages", "glValidateProgram", "glValidateProgramPipeline", "glVertexArrayAttribBinding", "glVertexArrayAttribFormat", "glVertexArrayAttribIFormat", "glVertexArrayAttribLFormat", "glVertexArrayBindingDivisor", "glVertexArrayElementBuffer", "glVertexArrayVertexBuffer", "glVertexArrayVertexBuffers", "glVertexAttrib1d", "glVertexAttrib1dv", "glVertexAttrib1f", "glVertexAttrib1fv", "glVertexAttrib1s", "glVertexAttrib1sv", "glVertexAttrib2d", "glVertexAttrib2dv", "glVertexAttrib2f", "glVertexAttrib2fv", "glVertexAttrib2s", "glVertexAttrib2sv", "glVertexAttrib3d", "glVertexAttrib3dv", "glVertexAttrib3f", "glVertexAttrib3fv", "glVertexAttrib3s", "glVertexAttrib3sv", "glVertexAttrib4Nbv", "glVertexAttrib4Niv", "glVertexAttrib4Nsv", "glVertexAttrib4Nub", "glVertexAttrib4Nubv", "glVertexAttrib4Nuiv", "glVertexAttrib4Nusv", "glVertexAttrib4bv", "glVertexAttrib4d", "glVertexAttrib4dv", "glVertexAttrib4f", "glVertexAttrib4fv", "glVertexAttrib4iv", "glVertexAttrib4s", "glVertexAttrib4sv", "glVertexAttrib4ubv", "glVertexAttrib4uiv", "glVertexAttrib4usv", "glVertexAttribBinding", "glVertexAttribDivisor", "glVertexAttribFormat", "glVertexAttribI1i", "glVertexAttribI1iv", "glVertexAttribI1ui", "glVertexAttribI1uiv", "glVertexAttribI2i", "glVertexAttribI2iv", "glVertexAttribI2ui", "glVertexAttribI2uiv", "glVertexAttribI3i", "glVertexAttribI3iv", "glVertexAttribI3ui", "glVertexAttribI3uiv", "glVertexAttribI4bv", "glVertexAttribI4i", "glVertexAttribI4iv", "glVertexAttribI4sv", "glVertexAttribI4ubv", "glVertexAttribI4ui", "glVertexAttribI4uiv", "glVertexAttribI4usv", "glVertexAttribIFormat", "glVertexAttribIPointer", "glVertexAttribL1d", "glVertexAttribL1dv", "glVertexAttribL2d", "glVertexAttribL2dv", "glVertexAttribL3d", "glVertexAttribL3dv", "glVertexAttribL4d", "glVertexAttribL4dv", "glVertexAttribLFormat", "glVertexAttribLPointer", "glVertexAttribP1ui", "glVertexAttribP1uiv", "glVertexAttribP2ui", "glVertexAttribP2uiv", "glVertexAttribP3ui", "glVertexAttribP3uiv", "glVertexAttribP4ui", "glVertexAttribP4uiv", "glVertexAttribPointer", "glVertexBindingDivisor", "glViewport", "glViewportArrayv", "glViewportIndexedf", "glViewportIndexedfv", "glWaitSync", }; GL3W_API union GL3WProcs gl3wProcs; static void load_procs(GL3WGetProcAddressProc proc) { size_t i; for (i = 0; i < ARRAY_SIZE(proc_names); i++) gl3wProcs.ptr[i] = proc(proc_names[i]); } #ifdef __cplusplus } #endif #endif ================================================ FILE: lib/third_party/imgui/backend/include/opengl_support.h ================================================ #pragma once #if defined(OS_WINDOWS) #include #include #endif #if defined(OS_WEB) #define GLFW_INCLUDE_ES3 #include #else #include #endif ================================================ FILE: lib/third_party/imgui/backend/include/stb_image.h ================================================ /* stb_image - v2.28 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION before you include this file in *one* C or C++ file to create the implementation. // i.e. it should look like this: #include ... #include ... #include ... #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free QUICK NOTES: Primarily of interest to game developers and other people who can avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) PNG 1/2/4/8/16-bit-per-channel TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE PSD (composited view only, no extra channels, 8/16 bit-per-channel) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) Animated GIF still needs a proper API, but here's one way to do it: http://gist.github.com/urraka/685d9a6340b26b830d49 - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) Full documentation under "DOCUMENTATION" below. LICENSE See end of file for license information. RECENT REVISION HISTORY: 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically 2.23 (2019-08-11) fix clang static analysis warning 2.22 (2019-03-04) gif fixes, fix warnings 2.21 (2019-02-25) fix typo in comment 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 RGB-format JPEG; remove white matting in PSD; allocate large structures on the stack; correct channel count for PNG & BMP 2.10 (2016-01-22) avoid warning introduced in 2.09 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED See end of file for full revision history. ============================ Contributors ========================= Image formats Extensions, features Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) github:urraka (animated gif) Junggon Kim (PNM comments) Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) socks-the-fox (16-bit PNG) Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine Simon Breuss (16-bit PNM) John-Mark Allen Carmelo J Fdez-Aguera Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski Phil Jordan Dave Moore Roy Eltham Hayaki Saito Nathan Reed Won Chun Luke Graham Johan Duparc Nick Verigakis the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh Janez Zemva John Bartholomew Michal Cichon github:romigrou Jonathan Blow Ken Hamada Tero Hanninen github:svdijk Eugene Golushkov Laurent Gomila Cort Stratton github:snagar Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 Brad Weinberger Matvey Cherevko github:mosra Luca Sas Alexander Veselov Zack Middleton [reserved] Ryan C. Gordon [reserved] [reserved] DO NOT ADD YOUR NAME HERE Jacko Dirks To add your name to the credits, pick a random blank space in the middle and fill it. 80% of merge conflicts on stb PRs are due to people adding their name at the end of the credits. */ #ifndef STBI_INCLUDE_STB_IMAGE_H #define STBI_INCLUDE_STB_IMAGE_H // DOCUMENTATION // // Limitations: // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): // int x,y,n; // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); // // ... process data if not NULL ... // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 // stbi_image_free(data); // // Standard parameters: // int *x -- outputs image width in pixels // int *y -- outputs image height in pixels // int *channels_in_file -- outputs # of image components in image file // int desired_channels -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is // corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of // components N is 'desired_channels' if desired_channels is non-zero, or // *channels_in_file otherwise. If desired_channels is non-zero, // *channels_in_file has the number of components that _would_ have been // output otherwise. E.g. if you set desired_channels to 4, you will always // get RGBA output, but you can check *channels_in_file to see if it's trivially // opaque because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: // // N=#comp components // 1 grey // 2 grey, alpha // 3 red, green, blue // 4 red, green, blue, alpha // // If image loading fails for any reason, the return value will be NULL, // and *x, *y, *channels_in_file will be unchanged. The function // stbi_failure_reason() can be queried for an extremely brief, end-user // unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS // to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly // more user-friendly ones. // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // // To query the width, height and component count of an image without having to // decode the full file, you can use the stbi_info family of functions: // // int x,y,n,ok; // ok = stbi_info(filename, &x, &y, &n); // // returns ok=1 and sets x, y, n if image is a supported format, // // 0 otherwise. // // Note that stb_image pervasively uses ints in its public API for sizes, // including sizes of memory buffers. This is now part of the API and thus // hard to change without causing breakage. As a result, the various image // loaders all have certain limits on image size; these differ somewhat // by format but generally boil down to either just under 2GB or just under // 1GB. When the decoded image would be larger than this, stb_image decoding // will fail. // // Additionally, stb_image will reject image files that have any of their // dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, // which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, // the only way to have an image with such dimensions load correctly // is for it to have a rather extreme aspect ratio. Either way, the // assumption here is that such larger images are likely to be malformed // or malicious. If you do need to load an image with individual dimensions // larger than that, and it still fits in the overall size limit, you can // #define STBI_MAX_DIMENSIONS on your own to be something larger. // // =========================================================================== // // UNICODE: // // If compiling for Windows and you wish to use Unicode filenames, compile // with // #define STBI_WINDOWS_UTF8 // and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert // Windows wchar_t filenames to utf8. // // =========================================================================== // // Philosophy // // stb libraries are designed with the following priorities: // // 1. easy to use // 2. easy to maintain // 3. good performance // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher // performance, in addition to the easy-to-use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which // provide more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small source code footprint ("easy to maintain") // - No dependencies ("ease of use") // // =========================================================================== // // I/O callbacks // // I/O callbacks allow you to read from arbitrary sources, like packaged // files or some other source. Data read from callbacks are processed // through a small internal buffer (currently 128 bytes) to try to reduce // overhead. // // The three functions you must define are "read" (reads some bytes of data), // "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). // // =========================================================================== // // SIMD support // // The JPEG decoder will try to automatically use SIMD kernels on x86 when // supported by the compiler. For ARM Neon support, you must explicitly // request it. // // (The old do-it-yourself SIMD API is no longer supported in the current // code.) // // On x86, SSE2 will automatically be used when available based on a run-time // test; if not, the generic C versions are used as a fall-back. On ARM targets, // the typical path is to have separate builds for NEON and non-NEON devices // (at least this is true for iOS and Android). Therefore, the NEON support is // toggled by a build flag: define STBI_NEON to get NEON loops. // // If for some reason you do not want to use any of SIMD code, or if // you have issues compiling it, you can disable it entirely by // defining STBI_NO_SIMD. // // =========================================================================== // // HDR image support (disable by defining STBI_NO_HDR) // // stb_image supports loading HDR images in general, and currently the Radiance // .HDR file format specifically. You can still load any file through the existing // interface; if you attempt to load an HDR file, it will be automatically remapped // to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); // stbi_hdr_to_ldr_scale(1.0f); // // (note, do not use _inverse_ constants; stbi_image will invert them // appropriately). // // Additionally, there is a new, parallel interface for loading files as // (linear) floats to preserve the full dynamic range: // // float *data = stbi_loadf(filename, &x, &y, &n, 0); // // If you load LDR images through this interface, those images will // be promoted to floating point values, run through the inverse of // constants corresponding to the above: // // stbi_ldr_to_hdr_scale(1.0f); // stbi_ldr_to_hdr_gamma(2.2f); // // Finally, given a filename (or an open file or memory block--see header // file for details) containing image data, you can query for the "most // appropriate" interface to use (that is, whether the image is HDR or // not), using: // // stbi_is_hdr(char *filename); // // =========================================================================== // // iPhone PNG support: // // We optionally support converting iPhone-formatted PNGs (which store // premultiplied BGRA) back to RGB, even though they're internally encoded // differently. To enable this conversion, call // stbi_convert_iphone_png_to_rgb(1). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly // says there's premultiplied data (currently only happens in iPhone images, // and only if iPhone convert-to-rgb processing is on). // // =========================================================================== // // ADDITIONAL CONFIGURATION // // - You can suppress implementation of any of the decoders to reduce // your code footprint by #defining one or more of the following // symbols before creating the implementation. // // STBI_NO_JPEG // STBI_NO_PNG // STBI_NO_BMP // STBI_NO_PSD // STBI_NO_TGA // STBI_NO_GIF // STBI_NO_HDR // STBI_NO_PIC // STBI_NO_PNM (.ppm and .pgm) // // - You can request *only* certain decoders and suppress all other ones // (this will be more forward-compatible, as addition of new decoders // doesn't require you to disable them explicitly): // // STBI_ONLY_JPEG // STBI_ONLY_PNG // STBI_ONLY_BMP // STBI_ONLY_PSD // STBI_ONLY_TGA // STBI_ONLY_GIF // STBI_ONLY_HDR // STBI_ONLY_PIC // STBI_ONLY_PNM (.ppm and .pgm) // // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // // - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater // than that size (in either width or height) without further processing. // This is to let programs in the wild set an upper bound to prevent // denial-of-service attacks on untrusted data, as one could generate a // valid image of gigantic dimensions and force stb_image to allocate a // huge block of memory and spend disproportionate time decoding it. By // default this is set to (1 << 24), which is 16777216, but that's still // very big. #ifndef STBI_NO_STDIO #include #endif // STBI_NO_STDIO #define STBI_VERSION 1 enum { STBI_default = 0, // only used for desired_channels STBI_grey = 1, STBI_grey_alpha = 2, STBI_rgb = 3, STBI_rgb_alpha = 4 }; #include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { #endif #ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif #endif ////////////////////////////////////////////////////////////////////////////// // // PRIMARY API - works on images of any type // // // load image by filename, open file, or memory buffer // typedef struct { int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; //////////////////////////////////// // // 8-bits-per-channel interface // STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif #ifndef STBI_NO_GIF STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); #endif #ifdef STBI_WINDOWS_UTF8 STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif //////////////////////////////////// // // 16-bits-per-channel interface // STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif //////////////////////////////////// // // float-per-channel interface // #ifndef STBI_NO_LINEAR STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif #ifndef STBI_NO_HDR STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); STBIDEF void stbi_hdr_to_ldr_scale(float scale); #endif // STBI_NO_HDR #ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); STBIDEF void stbi_ldr_to_hdr_scale(float scale); #endif // STBI_NO_LINEAR // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); #ifndef STBI_NO_STDIO STBIDEF int stbi_is_hdr (char const *filename); STBIDEF int stbi_is_hdr_from_file(FILE *f); #endif // STBI_NO_STDIO // get a VERY brief reason for failure // on most compilers (and ALL modern mainstream compilers) this is threadsafe STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); STBIDEF int stbi_is_16_bit (char const *filename); STBIDEF int stbi_is_16_bit_from_file(FILE *f); #endif // for image formats that explicitly notate that they have premultiplied alpha, // we just return the colors as stored in the file. set this flag to force // unpremultiplication. results are undefined if the unpremultiply overflow. STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); // indicate whether we should process iphone images back to canonical format, // or just pass them through "as-is" STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // as above, but only applies to images loaded on the thread that calls the function // this function is only available if your compiler supports thread-local variables; // calling it will fail to link if your compiler doesn't STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); #ifdef __cplusplus } #endif // // //// end header file ///////////////////////////////////////////////////// #endif // STBI_INCLUDE_STB_IMAGE_H #ifdef STB_IMAGE_IMPLEMENTATION #if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ || defined(STBI_ONLY_ZLIB) #ifndef STBI_ONLY_JPEG #define STBI_NO_JPEG #endif #ifndef STBI_ONLY_PNG #define STBI_NO_PNG #endif #ifndef STBI_ONLY_BMP #define STBI_NO_BMP #endif #ifndef STBI_ONLY_PSD #define STBI_NO_PSD #endif #ifndef STBI_ONLY_TGA #define STBI_NO_TGA #endif #ifndef STBI_ONLY_GIF #define STBI_NO_GIF #endif #ifndef STBI_ONLY_HDR #define STBI_NO_HDR #endif #ifndef STBI_ONLY_PIC #define STBI_NO_PIC #endif #ifndef STBI_ONLY_PNM #define STBI_NO_PNM #endif #endif #if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) #define STBI_NO_ZLIB #endif #include #include // ptrdiff_t on osx #include #include #include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) #include // ldexp, pow #endif #ifndef STBI_NO_STDIO #include #endif #ifndef STBI_ASSERT #include #define STBI_ASSERT(x) assert(x) #endif #ifdef __cplusplus #define STBI_EXTERN extern "C" #else #define STBI_EXTERN extern #endif #ifndef _MSC_VER #ifdef __cplusplus #define stbi_inline inline #else #define stbi_inline #endif #else #define stbi_inline __forceinline #endif #ifndef STBI_NO_THREAD_LOCALS #if defined(__cplusplus) && __cplusplus >= 201103L #define STBI_THREAD_LOCAL thread_local #elif defined(__GNUC__) && __GNUC__ < 5 #define STBI_THREAD_LOCAL __thread #elif defined(_MSC_VER) #define STBI_THREAD_LOCAL __declspec(thread) #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) #define STBI_THREAD_LOCAL _Thread_local #endif #ifndef STBI_THREAD_LOCAL #if defined(__GNUC__) #define STBI_THREAD_LOCAL __thread #endif #endif #endif #if defined(_MSC_VER) || defined(__SYMBIAN32__) typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; typedef signed int stbi__int32; #else #include typedef uint16_t stbi__uint16; typedef int16_t stbi__int16; typedef uint32_t stbi__uint32; typedef int32_t stbi__int32; #endif // should produce compiler error if size is wrong typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef _MSC_VER #define STBI_NOTUSED(v) (void)(v) #else #define STBI_NOTUSED(v) (void)sizeof(v) #endif #ifdef _MSC_VER #define STBI_HAS_LROTL #endif #ifdef STBI_HAS_LROTL #define stbi_lrot(x,y) _lrotl(x,y) #else #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) // ok #elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) // ok #else #error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." #endif #ifndef STBI_MALLOC #define STBI_MALLOC(sz) malloc(sz) #define STBI_REALLOC(p,newsz) realloc(p,newsz) #define STBI_FREE(p) free(p) #endif #ifndef STBI_REALLOC_SIZED #define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) #endif // x86/x64 detection #if defined(__x86_64__) || defined(_M_X64) #define STBI__X64_TARGET #elif defined(__i386) || defined(_M_IX86) #define STBI__X86_TARGET #endif #if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) // gcc doesn't support sse2 intrinsics unless you compile with -msse2, // which in turn means it gets to use SSE2 everywhere. This is unfortunate, // but previous attempts to provide the SSE2 functions with runtime // detection caused numerous issues. The way architecture extensions are // exposed in GCC/Clang is, sadly, not really suited for one-file libs. // New behavior: if compiled with -msse2, we use SSE2 without any // detection; if not, we don't use it at all. #define STBI_NO_SIMD #endif #if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) // Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET // // 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the // Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. // As a result, enabling SSE2 on 32-bit MinGW is dangerous when not // simultaneously enabling "-mstackrealign". // // See https://github.com/nothings/stb/issues/81 for more information. // // So default to no SSE2 on 32-bit MinGW. If you've read this far and added // -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. #define STBI_NO_SIMD #endif #if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) #define STBI_SSE2 #include #ifdef _MSC_VER #if _MSC_VER >= 1400 // not VC6 #include // __cpuid static int stbi__cpuid3(void) { int info[4]; __cpuid(info,1); return info[3]; } #else static int stbi__cpuid3(void) { int res; __asm { mov eax,1 cpuid mov res,edx } return res; } #endif #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } #endif #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { // If we're even attempting to compile this on GCC/Clang, that means // -msse2 is on, which means the compiler is allowed to use SSE2 // instructions at will, and so are we. return 1; } #endif #endif #endif // ARM NEON #if defined(STBI_NO_SIMD) && defined(STBI_NEON) #undef STBI_NEON #endif #ifdef STBI_NEON #include #ifdef _MSC_VER #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name #else #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif #endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name #endif #ifndef STBI_MAX_DIMENSIONS #define STBI_MAX_DIMENSIONS (1 << 24) #endif /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions // stbi__context structure is our basic context used by all images, so it // contains all the IO context, plus some basic image information typedef struct { stbi__uint32 img_x, img_y; int img_n, img_out_n; stbi_io_callbacks io; void *io_user_data; int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; int callback_already_read; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original, *img_buffer_original_end; } stbi__context; static void stbi__refill_buffer(stbi__context *s); // initialize a memory-decode context static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } // initialize a callback-based context static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) { s->io = *c; s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } #ifndef STBI_NO_STDIO static int stbi__stdio_read(void *user, char *data, int size) { return (int) fread(data,1,size,(FILE*) user); } static void stbi__stdio_skip(void *user, int n) { int ch; fseek((FILE*) user, n, SEEK_CUR); ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ if (ch != EOF) { ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ } } static int stbi__stdio_eof(void *user) { return feof((FILE*) user) || ferror((FILE *) user); } static stbi_io_callbacks stbi__stdio_callbacks = { stbi__stdio_read, stbi__stdio_skip, stbi__stdio_eof, }; static void stbi__start_file(stbi__context *s, FILE *f) { stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); } //static void stop_file(stbi__context *s) { } #endif // !STBI_NO_STDIO static void stbi__rewind(stbi__context *s) { // conceptually rewind SHOULD rewind to the beginning of the stream, // but we just rewind to the beginning of the initial buffer, because // we only use it after doing 'test', which only ever looks at at most 92 bytes s->img_buffer = s->img_buffer_original; s->img_buffer_end = s->img_buffer_original_end; } enum { STBI_ORDER_RGB, STBI_ORDER_BGR }; typedef struct { int bits_per_channel; int num_channels; int channel_order; } stbi__result_info; #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__png_is16(stbi__context *s); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__psd_is16(stbi__context *s); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__pnm_is16(stbi__context *s); #endif static #ifdef STBI_THREAD_LOCAL STBI_THREAD_LOCAL #endif const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } #ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } #endif static void *stbi__malloc(size_t size) { return STBI_MALLOC(size); } // stb_image uses ints pervasively, including for offset calculations. // therefore the largest decoded image size we can support with the // current code, even on 64-bit targets, is INT_MAX. this is not a // significant limitation for the intended use case. // // we do, however, need to make sure our size calculations don't // overflow. hence a few helper functions for size calculations that // multiply integers together, making sure that they're non-negative // and no overflow occurs. // return 1 if the sum is valid, 0 on overflow. // negative terms are considered invalid. static int stbi__addsizes_valid(int a, int b) { if (b < 0) return 0; // now 0 <= b <= INT_MAX, hence also // 0 <= INT_MAX - b <= INTMAX. // And "a + b <= INT_MAX" (which might overflow) is the // same as a <= INT_MAX - b (no overflow) return a <= INT_MAX - b; } // returns 1 if the product is valid, 0 on overflow. // negative factors are considered invalid. static int stbi__mul2sizes_valid(int a, int b) { if (a < 0 || b < 0) return 0; if (b == 0) return 1; // mul-by-0 is always safe // portable way to check for no overflows in a*b return a <= INT_MAX/b; } #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } #endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__addsizes_valid(a*b*c, add); } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); } #endif #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } #endif static void *stbi__malloc_mad3(int a, int b, int c, int add) { if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; return stbi__malloc(a*b*c + add); } #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; return stbi__malloc(a*b*c*d + add); } #endif // returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. static int stbi__addints_valid(int a, int b) { if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. return a <= INT_MAX - b; } // returns 1 if the product of two signed shorts is valid, 0 on overflow. static int stbi__mul2shorts_valid(short a, short b) { if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN return a >= SHRT_MIN / b; } // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char #ifdef STBI_NO_FAILURE_STRINGS #define stbi__err(x,y) 0 #elif defined(STBI_FAILURE_USERMSG) #define stbi__err(x,y) stbi__err(y) #else #define stbi__err(x,y) stbi__err(x) #endif #define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) #define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) STBIDEF void stbi_image_free(void *retval_from_stbi_load) { STBI_FREE(retval_from_stbi_load); } #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); #endif #ifndef STBI_NO_HDR static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif static int stbi__vertically_flip_on_load_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } #ifndef STBI_THREAD_LOCAL #define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global #else static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) { stbi__vertically_flip_on_load_local = flag_true_if_should_flip; stbi__vertically_flip_on_load_set = 1; } #define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ ? stbi__vertically_flip_on_load_local \ : stbi__vertically_flip_on_load_global) #endif // STBI_THREAD_LOCAL static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order ri->num_channels = 0; // test the formats with a very explicit header first (at least a FOURCC // or distinctive magic number first) #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_BMP if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_GIF if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); #else STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif // then the formats that can end up attempting to load with just 1 or 2 // bytes matching expectations; these are prone to false positives, so // try them later #ifndef STBI_NO_JPEG if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) return stbi__tga_load(s,x,y,comp,req_comp, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { int i; int img_len = w * h * channels; stbi_uc *reduced; reduced = (stbi_uc *) stbi__malloc(img_len); if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling STBI_FREE(orig); return reduced; } static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) { int i; int img_len = w * h * channels; stbi__uint16 *enlarged; enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff STBI_FREE(orig); return enlarged; } static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) { int row; size_t bytes_per_row = (size_t)w * bytes_per_pixel; stbi_uc temp[2048]; stbi_uc *bytes = (stbi_uc *)image; for (row = 0; row < (h>>1); row++) { stbi_uc *row0 = bytes + row*bytes_per_row; stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; // swap row0 with row1 size_t bytes_left = bytes_per_row; while (bytes_left) { size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); memcpy(temp, row0, bytes_copy); memcpy(row0, row1, bytes_copy); memcpy(row1, temp, bytes_copy); row0 += bytes_copy; row1 += bytes_copy; bytes_left -= bytes_copy; } } } #ifndef STBI_NO_GIF static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { int slice; int slice_size = w * h * bytes_per_pixel; stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { stbi__vertical_flip(bytes, w, h, bytes_per_pixel); bytes += slice_size; } } #endif static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); if (result == NULL) return NULL; // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 8) { result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } // @TODO: move stbi__convert_format to here if (stbi__vertically_flip_on_load) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); } return (unsigned char *) result; } static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); if (result == NULL) return NULL; // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 16) { result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } // @TODO: move stbi__convert_format16 to here // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision if (stbi__vertically_flip_on_load) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); } return (stbi__uint16 *) result; } #if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); } } #endif #ifndef STBI_NO_STDIO #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); #endif #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; #if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else f = fopen(filename, mode); #endif return f; } STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) { FILE *f = stbi__fopen(filename, "rb"); unsigned char *result; if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); result = stbi_load_from_file(f,x,y,comp,req_comp); fclose(f); return result; } STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { unsigned char *result; stbi__context s; stbi__start_file(&s,f); result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) { stbi__uint16 *result; stbi__context s; stbi__start_file(&s,f); result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) { FILE *f = stbi__fopen(filename, "rb"); stbi__uint16 *result; if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); result = stbi_load_from_file_16(f,x,y,comp,req_comp); fclose(f); return result; } #endif //!STBI_NO_STDIO STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); } STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); } STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } #ifndef STBI_NO_GIF STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; stbi__context s; stbi__start_mem(&s,buffer,len); result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } return result; } #endif #ifndef STBI_NO_LINEAR static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { stbi__result_info ri; float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); } STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__loadf_main(&s,x,y,comp,req_comp); } STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__loadf_main(&s,x,y,comp,req_comp); } #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) { float *result; FILE *f = stbi__fopen(filename, "rb"); if (!f) return stbi__errpf("can't fopen", "Unable to open file"); result = stbi_loadf_from_file(f,x,y,comp,req_comp); fclose(f); return result; } STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_file(&s,f); return stbi__loadf_main(&s,x,y,comp,req_comp); } #endif // !STBI_NO_STDIO #endif // !STBI_NO_LINEAR // these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is // defined, for API simplicity; if STBI_NO_LINEAR is defined, it always // reports false! STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__hdr_test(&s); #else STBI_NOTUSED(buffer); STBI_NOTUSED(len); return 0; #endif } #ifndef STBI_NO_STDIO STBIDEF int stbi_is_hdr (char const *filename) { FILE *f = stbi__fopen(filename, "rb"); int result=0; if (f) { result = stbi_is_hdr_from_file(f); fclose(f); } return result; } STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR long pos = ftell(f); int res; stbi__context s; stbi__start_file(&s,f); res = stbi__hdr_test(&s); fseek(f, pos, SEEK_SET); return res; #else STBI_NOTUSED(f); return 0; #endif } #endif // !STBI_NO_STDIO STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__hdr_test(&s); #else STBI_NOTUSED(clbk); STBI_NOTUSED(user); return 0; #endif } #ifndef STBI_NO_LINEAR static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } #endif static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } ////////////////////////////////////////////////////////////////////////////// // // Common code used by all image loaders // enum { STBI__SCAN_load=0, STBI__SCAN_type, STBI__SCAN_header }; static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file s->read_from_callbacks = 0; s->img_buffer = s->buffer_start; s->img_buffer_end = s->buffer_start+1; *s->img_buffer = 0; } else { s->img_buffer = s->buffer_start; s->img_buffer_end = s->buffer_start + n; } } stbi_inline static stbi_uc stbi__get8(stbi__context *s) { if (s->img_buffer < s->img_buffer_end) return *s->img_buffer++; if (s->read_from_callbacks) { stbi__refill_buffer(s); return *s->img_buffer++; } return 0; } #if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { if (!(s->io.eof)(s->io_user_data)) return 0; // if feof() is true, check if buffer = end // special case: we've only got the special 0 character at the end if (s->read_from_callbacks == 0) return 1; } return s->img_buffer >= s->img_buffer_end; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) // nothing #else static void stbi__skip(stbi__context *s, int n) { if (n == 0) return; // already there! if (n < 0) { s->img_buffer = s->img_buffer_end; return; } if (s->io.read) { int blen = (int) (s->img_buffer_end - s->img_buffer); if (blen < n) { s->img_buffer = s->img_buffer_end; (s->io.skip)(s->io_user_data, n - blen); return; } } s->img_buffer += n; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) // nothing #else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { int blen = (int) (s->img_buffer_end - s->img_buffer); if (blen < n) { int res, count; memcpy(buffer, s->img_buffer, blen); count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); res = (count == (n-blen)); s->img_buffer = s->img_buffer_end; return res; } } if (s->img_buffer+n <= s->img_buffer_end) { memcpy(buffer, s->img_buffer, n); s->img_buffer += n; return 1; } else return 0; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } #endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing #else static int stbi__get16le(stbi__context *s) { int z = stbi__get8(s); return z + (stbi__get8(s) << 8); } #endif #ifndef STBI_NO_BMP static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); z += (stbi__uint32)stbi__get16le(s) << 16; return z; } #endif #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp // individual types do this automatically as much as possible (e.g. jpeg // does all cases internally since it needs to colorspace convert anyway, // and it never has alpha, so very few cases ). png can automatically // interleave an alpha=255 channel, but falls back to this for other cases // // assume data buffer is malloced, so malloc a new one and free that one // only failure mode is malloc failing static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; unsigned char *good; if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; #define STBI__COMBO(a,b) ((a)*8+(b)) #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } STBI_FREE(data); return good; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; stbi__uint16 *good; if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); if (good == NULL) { STBI_FREE(data); return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { stbi__uint16 *src = data + j * x * img_n ; stbi__uint16 *dest = good + j * x * req_comp; #define STBI__COMBO(a,b) ((a)*8+(b)) #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } STBI_FREE(data); return good; } #endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; float *output; if (!data) return NULL; output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; for (i=0; i < x*y; ++i) { for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } } if (n < comp) { for (i=0; i < x*y; ++i) { output[i*comp + n] = data[i*comp + n]/255.0f; } } STBI_FREE(data); return output; } #endif #ifndef STBI_NO_HDR #define stbi__float2int(x) ((int) (x)) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; stbi_uc *output; if (!data) return NULL; output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; for (i=0; i < x*y; ++i) { for (k=0; k < n; ++k) { float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; if (z < 0) z = 0; if (z > 255) z = 255; output[i*comp + k] = (stbi_uc) stbi__float2int(z); } if (k < comp) { float z = data[i*comp+k] * 255 + 0.5f; if (z < 0) z = 0; if (z > 255) z = 255; output[i*comp + k] = (stbi_uc) stbi__float2int(z); } } STBI_FREE(data); return output; } #endif ////////////////////////////////////////////////////////////////////////////// // // "baseline" JPEG/JFIF decoder // // simple implementation // - doesn't support delayed output of y-dimension // - simple interface (only one output format: 8-bit interleaved RGB) // - doesn't try to recover corrupt jpegs // - doesn't allow partial loading, loading multiple at once // - still fast on x86 (copying globals into locals doesn't help x86) // - allocates lots of intermediate memory (full size of all components) // - non-interleaved case requires this anyway // - allows good upsampling (see next) // high-quality // - upsampled channels are bilinearly interpolated, even across blocks // - quality integer IDCT derived from IJG's 'slow' // performance // - fast huffman; reasonable integer IDCT // - some SIMD kernels for common paths on targets with SSE2/NEON // - uses a lot of intermediate memory, could cache poorly #ifndef STBI_NO_JPEG // huffman decoding acceleration #define FAST_BITS 9 // larger handles more cases; smaller stomps less cache typedef struct { stbi_uc fast[1 << FAST_BITS]; // weirdly, repacking this into AoS is a 10% speed loss, instead of a win stbi__uint16 code[256]; stbi_uc values[256]; stbi_uc size[257]; unsigned int maxcode[18]; int delta[17]; // old 'firstsymbol' - old 'firstcode' } stbi__huffman; typedef struct { stbi__context *s; stbi__huffman huff_dc[4]; stbi__huffman huff_ac[4]; stbi__uint16 dequant[4][64]; stbi__int16 fast_ac[4][1 << FAST_BITS]; // sizes for components, interleaved MCUs int img_h_max, img_v_max; int img_mcu_x, img_mcu_y; int img_mcu_w, img_mcu_h; // definition of jpeg image component struct { int id; int h,v; int tq; int hd,ha; int dc_pred; int x,y,w2,h2; stbi_uc *data; void *raw_data, *raw_coeff; stbi_uc *linebuf; short *coeff; // progressive only int coeff_w, coeff_h; // number of 8x8 coefficient blocks } img_comp[4]; stbi__uint32 code_buffer; // jpeg entropy-coded buffer int code_bits; // number of valid bits unsigned char marker; // marker seen while filling entropy buffer int nomore; // flag if we saw a marker so must stop int progressive; int spec_start; int spec_end; int succ_high; int succ_low; int eob_run; int jfif; int app14_color_transform; // Adobe APP14 tag int rgb; int scan_n, order[4]; int restart_interval, todo; // kernels void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); } stbi__jpeg; static int stbi__build_huffman(stbi__huffman *h, int *count) { int i,j,k=0; unsigned int code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) { for (j=0; j < count[i]; ++j) { h->size[k++] = (stbi_uc) (i+1); if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); } } h->size[k] = 0; // compute actual symbols (from jpeg spec) code = 0; k = 0; for(j=1; j <= 16; ++j) { // compute delta to add to code to compute symbol id h->delta[j] = k - code; if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); code <<= 1; } h->maxcode[j] = 0xffffffff; // build non-spec acceleration table; 255 is flag for not-accelerated memset(h->fast, 255, 1 << FAST_BITS); for (i=0; i < k; ++i) { int s = h->size[i]; if (s <= FAST_BITS) { int c = h->code[i] << (FAST_BITS-s); int m = 1 << (FAST_BITS-s); for (j=0; j < m; ++j) { h->fast[c+j] = (stbi_uc) i; } } } return 1; } // build a table that decodes both magnitude and value of small ACs in // one go. static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) { int i; for (i=0; i < (1 << FAST_BITS); ++i) { stbi_uc fast = h->fast[i]; fast_ac[i] = 0; if (fast < 255) { int rs = h->values[fast]; int run = (rs >> 4) & 15; int magbits = rs & 15; int len = h->size[fast]; if (magbits && len + magbits <= FAST_BITS) { // magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); int m = 1 << (magbits - 1); if (k < m) k += (~0U << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); } } } } static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { unsigned int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes if (c != 0) { j->marker = (unsigned char) c; j->nomore = 1; return; } } j->code_buffer |= b << (24 - j->code_bits); j->code_bits += 8; } while (j->code_bits <= 24); } // (1 << n) - 1 static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { unsigned int temp; int c,k; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); // look at the top FAST_BITS and determine what symbol ID it is, // if the code is <= FAST_BITS c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); k = h->fast[c]; if (k < 255) { int s = h->size[k]; if (s > j->code_bits) return -1; j->code_buffer <<= s; j->code_bits -= s; return h->values[k]; } // naive test is to shift the code_buffer down so k bits are // valid, then test against maxcode. To speed this up, we've // preshifted maxcode left so that it has (16-k) 0s at the // end; in other words, regardless of the number of bits, it // wants to be compared against something shifted to have 16; // that way we don't need to shift inside the loop. temp = j->code_buffer >> 16; for (k=FAST_BITS+1 ; ; ++k) if (temp < h->maxcode[k]) break; if (k == 17) { // error! code not found j->code_bits -= 16; return -1; } if (k > j->code_bits) return -1; // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; if(c < 0 || c >= 256) // symbol id out of bounds! return -1; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol j->code_bits -= k; j->code_buffer <<= k; return h->values[c]; } // bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k; } stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; return k & 0x80000000; } // given a value that's at position X in the zigzag stream, // where does it appear in the 8x8 matrix coded as row-major? static const stbi_uc stbi__jpeg_dezigzag[64+15] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, // let corrupt input sample past end 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }; // decode one 64-entry block-- static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) { int diff,dc,k; int t; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec k = 1; do { unsigned int zig; int c,r,s; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); r = fac[c]; if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) * dequant[zig]); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (rs != 0xf0) break; // end block k += 16; } else { k += r; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); } } } while (k < 64); return 1; } static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) { int diff,dc; int t; if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); if (j->succ_high == 0) { // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * (1 << j->succ_low)); } else { // refinement scan for DC coefficient if (stbi__jpeg_get_bit(j)) data[0] += (short) (1 << j->succ_low); } return 1; } // @OPTIMIZE: store non-zigzagged during the decode passes, // and only de-zigzag when dequantizing static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) { int k; if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); if (j->succ_high == 0) { int shift = j->succ_low; if (j->eob_run) { --j->eob_run; return 1; } k = j->spec_start; do { unsigned int zig; int c,r,s; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); r = fac[c]; if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) * (1 << shift)); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (r < 15) { j->eob_run = (1 << r); if (r) j->eob_run += stbi__jpeg_get_bits(j, r); --j->eob_run; break; } k += 16; } else { k += r; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); } } } while (k <= j->spec_end); } else { // refinement scan for these AC coefficients short bit = (short) (1 << j->succ_low); if (j->eob_run) { --j->eob_run; for (k = j->spec_start; k <= j->spec_end; ++k) { short *p = &data[stbi__jpeg_dezigzag[k]]; if (*p != 0) if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } } } else { k = j->spec_start; do { int r,s; int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (r < 15) { j->eob_run = (1 << r) - 1; if (r) j->eob_run += stbi__jpeg_get_bits(j, r); r = 64; // force end of block } else { // r=15 s=0 should write 16 0s, so we just do // a run of 15 0s and then write s (which is 0), // so we don't have to do anything special here } } else { if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); // sign bit if (stbi__jpeg_get_bit(j)) s = bit; else s = -bit; } // advance by r while (k <= j->spec_end) { short *p = &data[stbi__jpeg_dezigzag[k++]]; if (*p != 0) { if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } } else { if (r == 0) { *p = (short) s; break; } --r; } } } while (k <= j->spec_end); } } return 1; } // take a -128..127 value and stbi__clamp it and convert to 0..255 stbi_inline static stbi_uc stbi__clamp(int x) { // trick to use a single test to catch both cases if ((unsigned int) x > 255) { if (x < 0) return 0; if (x > 255) return 255; } return (stbi_uc) x; } #define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) #define stbi__fsh(x) ((x) * 4096) // derived from jidctint -- DCT_ISLOW #define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ p2 = s2; \ p3 = s6; \ p1 = (p2+p3) * stbi__f2f(0.5411961f); \ t2 = p1 + p3*stbi__f2f(-1.847759065f); \ t3 = p1 + p2*stbi__f2f( 0.765366865f); \ p2 = s0; \ p3 = s4; \ t0 = stbi__fsh(p2+p3); \ t1 = stbi__fsh(p2-p3); \ x0 = t0+t3; \ x3 = t0-t3; \ x1 = t1+t2; \ x2 = t1-t2; \ t0 = s7; \ t1 = s5; \ t2 = s3; \ t3 = s1; \ p3 = t0+t2; \ p4 = t1+t3; \ p1 = t0+t3; \ p2 = t1+t2; \ p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ t0 = t0*stbi__f2f( 0.298631336f); \ t1 = t1*stbi__f2f( 2.053119869f); \ t2 = t2*stbi__f2f( 3.072711026f); \ t3 = t3*stbi__f2f( 1.501321110f); \ p1 = p5 + p1*stbi__f2f(-0.899976223f); \ p2 = p5 + p2*stbi__f2f(-2.562915447f); \ p3 = p3*stbi__f2f(-1.961570560f); \ p4 = p4*stbi__f2f(-0.390180644f); \ t3 += p1+p4; \ t2 += p2+p3; \ t1 += p2+p4; \ t0 += p1+p3; static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { int i,val[64],*v=val; stbi_uc *o; short *d = data; // columns for (i=0; i < 8; ++i,++d, ++v) { // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 && d[40]==0 && d[48]==0 && d[56]==0) { // no shortcut 0 seconds // (1|2|3|4|5|6|7)==0 0 seconds // all separate -0.047 seconds // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds int dcterm = d[0]*4; v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; } else { STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) // constants scaled things up by 1<<12; let's bring them back // down, but keep 2 extra bits of precision x0 += 512; x1 += 512; x2 += 512; x3 += 512; v[ 0] = (x0+t3) >> 10; v[56] = (x0-t3) >> 10; v[ 8] = (x1+t2) >> 10; v[48] = (x1-t2) >> 10; v[16] = (x2+t1) >> 10; v[40] = (x2-t1) >> 10; v[24] = (x3+t0) >> 10; v[32] = (x3-t0) >> 10; } } for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { // no fast case since the first 1D IDCT spread components out STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) // constants scaled things up by 1<<12, plus we had 1<<2 from first // loop, plus horizontal and vertical each scale by sqrt(8) so together // we've got an extra 1<<3, so 1<<17 total we need to remove. // so we want to round that, which means adding 0.5 * 1<<17, // aka 65536. Also, we'll end up with -128 to 127 that we want // to encode as 0..255 by adding 128, so we'll add that before the shift x0 += 65536 + (128<<17); x1 += 65536 + (128<<17); x2 += 65536 + (128<<17); x3 += 65536 + (128<<17); // tried computing the shifts into temps, or'ing the temps to see // if any were out of range, but that was slower o[0] = stbi__clamp((x0+t3) >> 17); o[7] = stbi__clamp((x0-t3) >> 17); o[1] = stbi__clamp((x1+t2) >> 17); o[6] = stbi__clamp((x1-t2) >> 17); o[2] = stbi__clamp((x2+t1) >> 17); o[5] = stbi__clamp((x2-t1) >> 17); o[3] = stbi__clamp((x3+t0) >> 17); o[4] = stbi__clamp((x3-t0) >> 17); } } #ifdef STBI_SSE2 // sse2 integer IDCT. not the fastest possible implementation but it // produces bit-identical results to the generic C version so it's // fully "transparent". static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { // This is constructed to match our regular (generic) integer IDCT exactly. __m128i row0, row1, row2, row3, row4, row5, row6, row7; __m128i tmp; // dot product constant: even elems=x, odd elems=y #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) // out(1) = c1[even]*x + c1[odd]*y #define dct_rot(out0,out1, x,y,c0,c1) \ __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) // out = in << 12 (in 16-bit, out 32-bit) #define dct_widen(out, in) \ __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) // wide add #define dct_wadd(out, a, b) \ __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ __m128i out##_h = _mm_add_epi32(a##_h, b##_h) // wide sub #define dct_wsub(out, a, b) \ __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) // butterfly a/b, add bias, then shift by "s" and pack #define dct_bfly32o(out0, out1, a,b,bias,s) \ { \ __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ dct_wadd(sum, abiased, b); \ dct_wsub(dif, abiased, b); \ out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ } // 8-bit interleave step (for transposes) #define dct_interleave8(a, b) \ tmp = a; \ a = _mm_unpacklo_epi8(a, b); \ b = _mm_unpackhi_epi8(tmp, b) // 16-bit interleave step (for transposes) #define dct_interleave16(a, b) \ tmp = a; \ a = _mm_unpacklo_epi16(a, b); \ b = _mm_unpackhi_epi16(tmp, b) #define dct_pass(bias,shift) \ { \ /* even part */ \ dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ __m128i sum04 = _mm_add_epi16(row0, row4); \ __m128i dif04 = _mm_sub_epi16(row0, row4); \ dct_widen(t0e, sum04); \ dct_widen(t1e, dif04); \ dct_wadd(x0, t0e, t3e); \ dct_wsub(x3, t0e, t3e); \ dct_wadd(x1, t1e, t2e); \ dct_wsub(x2, t1e, t2e); \ /* odd part */ \ dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ __m128i sum17 = _mm_add_epi16(row1, row7); \ __m128i sum35 = _mm_add_epi16(row3, row5); \ dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ dct_wadd(x4, y0o, y4o); \ dct_wadd(x5, y1o, y5o); \ dct_wadd(x6, y2o, y5o); \ dct_wadd(x7, y3o, y4o); \ dct_bfly32o(row0,row7, x0,x7,bias,shift); \ dct_bfly32o(row1,row6, x1,x6,bias,shift); \ dct_bfly32o(row2,row5, x2,x5,bias,shift); \ dct_bfly32o(row3,row4, x3,x4,bias,shift); \ } __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); // rounding biases in column/row passes, see stbi__idct_block for explanation. __m128i bias_0 = _mm_set1_epi32(512); __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); // load row0 = _mm_load_si128((const __m128i *) (data + 0*8)); row1 = _mm_load_si128((const __m128i *) (data + 1*8)); row2 = _mm_load_si128((const __m128i *) (data + 2*8)); row3 = _mm_load_si128((const __m128i *) (data + 3*8)); row4 = _mm_load_si128((const __m128i *) (data + 4*8)); row5 = _mm_load_si128((const __m128i *) (data + 5*8)); row6 = _mm_load_si128((const __m128i *) (data + 6*8)); row7 = _mm_load_si128((const __m128i *) (data + 7*8)); // column pass dct_pass(bias_0, 10); { // 16bit 8x8 transpose pass 1 dct_interleave16(row0, row4); dct_interleave16(row1, row5); dct_interleave16(row2, row6); dct_interleave16(row3, row7); // transpose pass 2 dct_interleave16(row0, row2); dct_interleave16(row1, row3); dct_interleave16(row4, row6); dct_interleave16(row5, row7); // transpose pass 3 dct_interleave16(row0, row1); dct_interleave16(row2, row3); dct_interleave16(row4, row5); dct_interleave16(row6, row7); } // row pass dct_pass(bias_1, 17); { // pack __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 __m128i p1 = _mm_packus_epi16(row2, row3); __m128i p2 = _mm_packus_epi16(row4, row5); __m128i p3 = _mm_packus_epi16(row6, row7); // 8bit 8x8 transpose pass 1 dct_interleave8(p0, p2); // a0e0a1e1... dct_interleave8(p1, p3); // c0g0c1g1... // transpose pass 2 dct_interleave8(p0, p1); // a0c0e0g0... dct_interleave8(p2, p3); // b0d0f0h0... // transpose pass 3 dct_interleave8(p0, p2); // a0b0c0d0... dct_interleave8(p1, p3); // a4b4c4d4... // store _mm_storel_epi64((__m128i *) out, p0); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p2); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p1); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p3); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); } #undef dct_const #undef dct_rot #undef dct_widen #undef dct_wadd #undef dct_wsub #undef dct_bfly32o #undef dct_interleave8 #undef dct_interleave16 #undef dct_pass } #endif // STBI_SSE2 #ifdef STBI_NEON // NEON integer IDCT. should produce bit-identical // results to the generic C version. static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); #define dct_long_mul(out, inq, coeff) \ int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) #define dct_long_mac(out, acc, inq, coeff) \ int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) #define dct_widen(out, inq) \ int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) // wide add #define dct_wadd(out, a, b) \ int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ int32x4_t out##_h = vaddq_s32(a##_h, b##_h) // wide sub #define dct_wsub(out, a, b) \ int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ int32x4_t out##_h = vsubq_s32(a##_h, b##_h) // butterfly a/b, then shift using "shiftop" by "s" and pack #define dct_bfly32o(out0,out1, a,b,shiftop,s) \ { \ dct_wadd(sum, a, b); \ dct_wsub(dif, a, b); \ out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ } #define dct_pass(shiftop, shift) \ { \ /* even part */ \ int16x8_t sum26 = vaddq_s16(row2, row6); \ dct_long_mul(p1e, sum26, rot0_0); \ dct_long_mac(t2e, p1e, row6, rot0_1); \ dct_long_mac(t3e, p1e, row2, rot0_2); \ int16x8_t sum04 = vaddq_s16(row0, row4); \ int16x8_t dif04 = vsubq_s16(row0, row4); \ dct_widen(t0e, sum04); \ dct_widen(t1e, dif04); \ dct_wadd(x0, t0e, t3e); \ dct_wsub(x3, t0e, t3e); \ dct_wadd(x1, t1e, t2e); \ dct_wsub(x2, t1e, t2e); \ /* odd part */ \ int16x8_t sum15 = vaddq_s16(row1, row5); \ int16x8_t sum17 = vaddq_s16(row1, row7); \ int16x8_t sum35 = vaddq_s16(row3, row5); \ int16x8_t sum37 = vaddq_s16(row3, row7); \ int16x8_t sumodd = vaddq_s16(sum17, sum35); \ dct_long_mul(p5o, sumodd, rot1_0); \ dct_long_mac(p1o, p5o, sum17, rot1_1); \ dct_long_mac(p2o, p5o, sum35, rot1_2); \ dct_long_mul(p3o, sum37, rot2_0); \ dct_long_mul(p4o, sum15, rot2_1); \ dct_wadd(sump13o, p1o, p3o); \ dct_wadd(sump24o, p2o, p4o); \ dct_wadd(sump23o, p2o, p3o); \ dct_wadd(sump14o, p1o, p4o); \ dct_long_mac(x4, sump13o, row7, rot3_0); \ dct_long_mac(x5, sump24o, row5, rot3_1); \ dct_long_mac(x6, sump23o, row3, rot3_2); \ dct_long_mac(x7, sump14o, row1, rot3_3); \ dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ } // load row0 = vld1q_s16(data + 0*8); row1 = vld1q_s16(data + 1*8); row2 = vld1q_s16(data + 2*8); row3 = vld1q_s16(data + 3*8); row4 = vld1q_s16(data + 4*8); row5 = vld1q_s16(data + 5*8); row6 = vld1q_s16(data + 6*8); row7 = vld1q_s16(data + 7*8); // add DC bias row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); // column pass dct_pass(vrshrn_n_s32, 10); // 16bit 8x8 transpose { // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. // whether compilers actually get this is another story, sadly. #define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } #define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } // pass 1 dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 dct_trn16(row2, row3); dct_trn16(row4, row5); dct_trn16(row6, row7); // pass 2 dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 dct_trn32(row1, row3); dct_trn32(row4, row6); dct_trn32(row5, row7); // pass 3 dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 dct_trn64(row1, row5); dct_trn64(row2, row6); dct_trn64(row3, row7); #undef dct_trn16 #undef dct_trn32 #undef dct_trn64 } // row pass // vrshrn_n_s32 only supports shifts up to 16, we need // 17. so do a non-rounding shift of 16 first then follow // up with a rounding shift by 1. dct_pass(vshrn_n_s32, 16); { // pack and round uint8x8_t p0 = vqrshrun_n_s16(row0, 1); uint8x8_t p1 = vqrshrun_n_s16(row1, 1); uint8x8_t p2 = vqrshrun_n_s16(row2, 1); uint8x8_t p3 = vqrshrun_n_s16(row3, 1); uint8x8_t p4 = vqrshrun_n_s16(row4, 1); uint8x8_t p5 = vqrshrun_n_s16(row5, 1); uint8x8_t p6 = vqrshrun_n_s16(row6, 1); uint8x8_t p7 = vqrshrun_n_s16(row7, 1); // again, these can translate into one instruction, but often don't. #define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } #define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } // sadly can't use interleaved stores here since we only write // 8 bytes to each scan line! // 8x8 8-bit transpose pass 1 dct_trn8_8(p0, p1); dct_trn8_8(p2, p3); dct_trn8_8(p4, p5); dct_trn8_8(p6, p7); // pass 2 dct_trn8_16(p0, p2); dct_trn8_16(p1, p3); dct_trn8_16(p4, p6); dct_trn8_16(p5, p7); // pass 3 dct_trn8_32(p0, p4); dct_trn8_32(p1, p5); dct_trn8_32(p2, p6); dct_trn8_32(p3, p7); // store vst1_u8(out, p0); out += out_stride; vst1_u8(out, p1); out += out_stride; vst1_u8(out, p2); out += out_stride; vst1_u8(out, p3); out += out_stride; vst1_u8(out, p4); out += out_stride; vst1_u8(out, p5); out += out_stride; vst1_u8(out, p6); out += out_stride; vst1_u8(out, p7); #undef dct_trn8_8 #undef dct_trn8_16 #undef dct_trn8_32 } #undef dct_long_mul #undef dct_long_mac #undef dct_widen #undef dct_wadd #undef dct_wsub #undef dct_bfly32o #undef dct_pass } #endif // STBI_NEON #define STBI__MARKER_none 0xff // if there's a pending marker from the entropy stream, return that // otherwise, fetch from the stream and get a marker. if there's no // marker, return 0xff, which is never a valid marker value static stbi_uc stbi__get_marker(stbi__jpeg *j) { stbi_uc x; if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } x = stbi__get8(j->s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) x = stbi__get8(j->s); // consume repeated 0xff fill bytes return x; } // in each scan, we'll have scan_n components, and the order // of the components is specified by order[] #define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) // after a restart interval, stbi__jpeg_reset the entropy decoder and // the dc prediction static void stbi__jpeg_reset(stbi__jpeg *j) { j->code_bits = 0; j->code_buffer = 0; j->nomore = 0; j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; j->marker = STBI__MARKER_none; j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; j->eob_run = 0; // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, // since we don't even allow 1<<30 pixels } static int stbi__parse_entropy_coded_data(stbi__jpeg *z) { stbi__jpeg_reset(z); if (!z->progressive) { if (z->scan_n == 1) { int i,j; STBI_SIMD_ALIGN(short, data[64]); int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); // if it's NOT a restart, then just bail, so we get corrupt data // rather than no data if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } else { // interleaved int i,j,k,x,y; STBI_SIMD_ALIGN(short, data[64]); for (j=0; j < z->img_mcu_y; ++j) { for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x)*8; int y2 = (j*z->img_comp[n].v + y)*8; int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } } else { if (z->scan_n == 1) { int i,j; int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); if (z->spec_start == 0) { if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; } else { int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) return 0; } // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } else { // interleaved int i,j,k,x,y; for (j=0; j < z->img_mcu_y; ++j) { for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x); int y2 = (j*z->img_comp[n].v + y); short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } } } static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { int i; for (i=0; i < 64; ++i) data[i] *= dequant[i]; } static void stbi__jpeg_finish(stbi__jpeg *z) { if (z->progressive) { // dequantize and idct the data int i,j,n; for (n=0; n < z->s->img_n; ++n) { int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); } } } } } static int stbi__process_marker(stbi__jpeg *z, int m) { int L; switch (m) { case STBI__MARKER_none: // no marker found return stbi__err("expected marker","Corrupt JPEG"); case 0xDD: // DRI - specify restart interval if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); z->restart_interval = stbi__get16be(z->s); return 1; case 0xDB: // DQT - define quantization table L = stbi__get16be(z->s)-2; while (L > 0) { int q = stbi__get8(z->s); int p = q >> 4, sixteen = (p != 0); int t = q & 15,i; if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); for (i=0; i < 64; ++i) z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); L -= (sixteen ? 129 : 65); } return L==0; case 0xC4: // DHT - define huffman table L = stbi__get16be(z->s)-2; while (L > 0) { stbi_uc *v; int sizes[16],i,n=0; int q = stbi__get8(z->s); int tc = q >> 4; int th = q & 15; if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); for (i=0; i < 16; ++i) { sizes[i] = stbi__get8(z->s); n += sizes[i]; } if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; v = z->huff_dc[th].values; } else { if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; v = z->huff_ac[th].values; } for (i=0; i < n; ++i) v[i] = stbi__get8(z->s); if (tc != 0) stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); L -= n; } return L==0; } // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { L = stbi__get16be(z->s); if (L < 2) { if (m == 0xFE) return stbi__err("bad COM len","Corrupt JPEG"); else return stbi__err("bad APP len","Corrupt JPEG"); } L -= 2; if (m == 0xE0 && L >= 5) { // JFIF APP0 segment static const unsigned char tag[5] = {'J','F','I','F','\0'}; int ok = 1; int i; for (i=0; i < 5; ++i) if (stbi__get8(z->s) != tag[i]) ok = 0; L -= 5; if (ok) z->jfif = 1; } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; int ok = 1; int i; for (i=0; i < 6; ++i) if (stbi__get8(z->s) != tag[i]) ok = 0; L -= 6; if (ok) { stbi__get8(z->s); // version stbi__get16be(z->s); // flags0 stbi__get16be(z->s); // flags1 z->app14_color_transform = stbi__get8(z->s); // color transform L -= 6; } } stbi__skip(z->s, L); return 1; } return stbi__err("unknown marker","Corrupt JPEG"); } // after we see SOS static int stbi__process_scan_header(stbi__jpeg *z) { int i; int Ls = stbi__get16be(z->s); z->scan_n = stbi__get8(z->s); if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); for (i=0; i < z->scan_n; ++i) { int id = stbi__get8(z->s), which; int q = stbi__get8(z->s); for (which = 0; which < z->s->img_n; ++which) if (z->img_comp[which].id == id) break; if (which == z->s->img_n) return 0; // no match z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); z->order[i] = which; } { int aa; z->spec_start = stbi__get8(z->s); z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 aa = stbi__get8(z->s); z->succ_high = (aa >> 4); z->succ_low = (aa & 15); if (z->progressive) { if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) return stbi__err("bad SOS", "Corrupt JPEG"); } else { if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); z->spec_end = 63; } } return 1; } static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) { int i; for (i=0; i < ncomp; ++i) { if (z->img_comp[i].raw_data) { STBI_FREE(z->img_comp[i].raw_data); z->img_comp[i].raw_data = NULL; z->img_comp[i].data = NULL; } if (z->img_comp[i].raw_coeff) { STBI_FREE(z->img_comp[i].raw_coeff); z->img_comp[i].raw_coeff = 0; z->img_comp[i].coeff = 0; } if (z->img_comp[i].linebuf) { STBI_FREE(z->img_comp[i].linebuf); z->img_comp[i].linebuf = NULL; } } return why; } static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; int Lf,p,i,q, h_max=1,v_max=1,c; Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); c = stbi__get8(s); if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; for (i=0; i < c; ++i) { z->img_comp[i].data = NULL; z->img_comp[i].linebuf = NULL; } if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); z->rgb = 0; for (i=0; i < s->img_n; ++i) { static const unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) ++z->rgb; q = stbi__get8(s); z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); } if (scan != STBI__SCAN_load) return 1; if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios // and I've never seen a non-corrupted JPEG file actually use them for (i=0; i < s->img_n; ++i) { if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); } // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; // these sizes can't be more than 17 bits z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; for (i=0; i < s->img_n; ++i) { // number of effective pixels (e.g. for non-interleaved MCU) z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; // to simplify generation, we'll allocate enough memory to decode // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion // // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) // so these muls can't overflow with 32-bit ints (which we require) z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; z->img_comp[i].coeff = 0; z->img_comp[i].raw_coeff = 0; z->img_comp[i].linebuf = NULL; z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); if (z->img_comp[i].raw_data == NULL) return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); if (z->progressive) { // w2, h2 are multiples of 8 (see above) z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); if (z->img_comp[i].raw_coeff == NULL) return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); } } return 1; } // use comparisons since in some cases we handle more than one case (e.g. SOF) #define stbi__DNL(x) ((x) == 0xdc) #define stbi__SOI(x) ((x) == 0xd8) #define stbi__EOI(x) ((x) == 0xd9) #define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) #define stbi__SOS(x) ((x) == 0xda) #define stbi__SOF_progressive(x) ((x) == 0xc2) static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { int m; z->jfif = 0; z->app14_color_transform = -1; // valid values are 0,1,2 z->marker = STBI__MARKER_none; // initialize cached marker to empty m = stbi__get_marker(z); if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); if (scan == STBI__SCAN_type) return 1; m = stbi__get_marker(z); while (!stbi__SOF(m)) { if (!stbi__process_marker(z,m)) return 0; m = stbi__get_marker(z); while (m == STBI__MARKER_none) { // some files have extra padding after their blocks, so ok, we'll scan if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); m = stbi__get_marker(z); } } z->progressive = stbi__SOF_progressive(m); if (!stbi__process_frame_header(z, scan)) return 0; return 1; } static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) { // some JPEGs have junk at end, skip over it but if we find what looks // like a valid marker, resume there while (!stbi__at_eof(j->s)) { int x = stbi__get8(j->s); while (x == 255) { // might be a marker if (stbi__at_eof(j->s)) return STBI__MARKER_none; x = stbi__get8(j->s); if (x != 0x00 && x != 0xff) { // not a stuffed zero or lead-in to another marker, looks // like an actual marker, return it return x; } // stuffed zero has x=0 now which ends the loop, meaning we go // back to regular scan loop. // repeated 0xff keeps trying to read the next byte of the marker. } } return STBI__MARKER_none; } // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { int m; for (m = 0; m < 4; m++) { j->img_comp[m].raw_data = NULL; j->img_comp[m].raw_coeff = NULL; } j->restart_interval = 0; if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; m = stbi__get_marker(j); while (!stbi__EOI(m)) { if (stbi__SOS(m)) { if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } m = stbi__get_marker(j); if (STBI__RESTART(m)) m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); m = stbi__get_marker(j); } else { if (!stbi__process_marker(j, m)) return 1; m = stbi__get_marker(j); } } if (j->progressive) stbi__jpeg_finish(j); return 1; } // static jfif-centered resampling (across block boundaries) typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, int w, int hs); #define stbi__div4(x) ((stbi_uc) ((x) >> 2)) static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { STBI_NOTUSED(out); STBI_NOTUSED(in_far); STBI_NOTUSED(w); STBI_NOTUSED(hs); return in_near; } static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate two samples vertically for every one in input int i; STBI_NOTUSED(hs); for (i=0; i < w; ++i) out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); return out; } static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate two samples horizontally for every one in input int i; stbi_uc *input = in_near; if (w == 1) { // if only one sample, can't do any interpolation out[0] = out[1] = input[0]; return out; } out[0] = input[0]; out[1] = stbi__div4(input[0]*3 + input[1] + 2); for (i=1; i < w-1; ++i) { int n = 3*input[i]+2; out[i*2+0] = stbi__div4(n+input[i-1]); out[i*2+1] = stbi__div4(n+input[i+1]); } out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); out[i*2+1] = input[w-1]; STBI_NOTUSED(in_far); STBI_NOTUSED(hs); return out; } #define stbi__div16(x) ((stbi_uc) ((x) >> 4)) static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input int i,t0,t1; if (w == 1) { out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); return out; } t1 = 3*in_near[0] + in_far[0]; out[0] = stbi__div4(t1+2); for (i=1; i < w; ++i) { t0 = t1; t1 = 3*in_near[i]+in_far[i]; out[i*2-1] = stbi__div16(3*t0 + t1 + 8); out[i*2 ] = stbi__div16(3*t1 + t0 + 8); } out[w*2-1] = stbi__div4(t1+2); STBI_NOTUSED(hs); return out; } #if defined(STBI_SSE2) || defined(STBI_NEON) static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input int i=0,t0,t1; if (w == 1) { out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); return out; } t1 = 3*in_near[0] + in_far[0]; // process groups of 8 pixels for as long as we can. // note we can't handle the last pixel in a row in this loop // because we need to handle the filter boundary conditions. for (; i < ((w-1) & ~7); i += 8) { #if defined(STBI_SSE2) // load and perform the vertical filtering pass // this uses 3*x + y = 4*x + (y - x) __m128i zero = _mm_setzero_si128(); __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); __m128i farw = _mm_unpacklo_epi8(farb, zero); __m128i nearw = _mm_unpacklo_epi8(nearb, zero); __m128i diff = _mm_sub_epi16(farw, nearw); __m128i nears = _mm_slli_epi16(nearw, 2); __m128i curr = _mm_add_epi16(nears, diff); // current row // horizontal filter works the same based on shifted vers of current // row. "prev" is current row shifted right by 1 pixel; we need to // insert the previous pixel value (from t1). // "next" is current row shifted left by 1 pixel, with first pixel // of next block of 8 pixels added in. __m128i prv0 = _mm_slli_si128(curr, 2); __m128i nxt0 = _mm_srli_si128(curr, 2); __m128i prev = _mm_insert_epi16(prv0, t1, 0); __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); // horizontal filter, polyphase implementation since it's convenient: // even pixels = 3*cur + prev = cur*4 + (prev - cur) // odd pixels = 3*cur + next = cur*4 + (next - cur) // note the shared term. __m128i bias = _mm_set1_epi16(8); __m128i curs = _mm_slli_epi16(curr, 2); __m128i prvd = _mm_sub_epi16(prev, curr); __m128i nxtd = _mm_sub_epi16(next, curr); __m128i curb = _mm_add_epi16(curs, bias); __m128i even = _mm_add_epi16(prvd, curb); __m128i odd = _mm_add_epi16(nxtd, curb); // interleave even and odd pixels, then undo scaling. __m128i int0 = _mm_unpacklo_epi16(even, odd); __m128i int1 = _mm_unpackhi_epi16(even, odd); __m128i de0 = _mm_srli_epi16(int0, 4); __m128i de1 = _mm_srli_epi16(int1, 4); // pack and write output __m128i outv = _mm_packus_epi16(de0, de1); _mm_storeu_si128((__m128i *) (out + i*2), outv); #elif defined(STBI_NEON) // load and perform the vertical filtering pass // this uses 3*x + y = 4*x + (y - x) uint8x8_t farb = vld1_u8(in_far + i); uint8x8_t nearb = vld1_u8(in_near + i); int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); int16x8_t curr = vaddq_s16(nears, diff); // current row // horizontal filter works the same based on shifted vers of current // row. "prev" is current row shifted right by 1 pixel; we need to // insert the previous pixel value (from t1). // "next" is current row shifted left by 1 pixel, with first pixel // of next block of 8 pixels added in. int16x8_t prv0 = vextq_s16(curr, curr, 7); int16x8_t nxt0 = vextq_s16(curr, curr, 1); int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); // horizontal filter, polyphase implementation since it's convenient: // even pixels = 3*cur + prev = cur*4 + (prev - cur) // odd pixels = 3*cur + next = cur*4 + (next - cur) // note the shared term. int16x8_t curs = vshlq_n_s16(curr, 2); int16x8_t prvd = vsubq_s16(prev, curr); int16x8_t nxtd = vsubq_s16(next, curr); int16x8_t even = vaddq_s16(curs, prvd); int16x8_t odd = vaddq_s16(curs, nxtd); // undo scaling and round, then store with even/odd phases interleaved uint8x8x2_t o; o.val[0] = vqrshrun_n_s16(even, 4); o.val[1] = vqrshrun_n_s16(odd, 4); vst2_u8(out + i*2, o); #endif // "previous" value for next iter t1 = 3*in_near[i+7] + in_far[i+7]; } t0 = t1; t1 = 3*in_near[i] + in_far[i]; out[i*2] = stbi__div16(3*t1 + t0 + 8); for (++i; i < w; ++i) { t0 = t1; t1 = 3*in_near[i]+in_far[i]; out[i*2-1] = stbi__div16(3*t0 + t1 + 8); out[i*2 ] = stbi__div16(3*t1 + t0 + 8); } out[w*2-1] = stbi__div4(t1+2); STBI_NOTUSED(hs); return out; } #endif static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // resample with nearest-neighbor int i,j; STBI_NOTUSED(in_far); for (i=0; i < w; ++i) for (j=0; j < hs; ++j) out[i*hs+j] = in_near[i]; return out; } // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar #define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; for (i=0; i < count; ++i) { int y_fixed = (y[i] << 20) + (1<<19); // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr* stbi__float2fixed(1.40200f); g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #if defined(STBI_SSE2) || defined(STBI_NEON) static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) { int i = 0; #ifdef STBI_SSE2 // step == 3 is pretty ugly on the final interleave, and i'm not convinced // it's useful in practice (you wouldn't use it for textures, for example). // so just accelerate step == 4 case. if (step == 4) { // this is a fairly straightforward implementation and not super-optimized. __m128i signflip = _mm_set1_epi8(-0x80); __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); __m128i xw = _mm_set1_epi16(255); // alpha channel for (; i+7 < count; i += 8) { // load __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 // unpack to short (and left-shift cr, cb by 8) __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); // color transform __m128i yws = _mm_srli_epi16(yw, 4); __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); __m128i rws = _mm_add_epi16(cr0, yws); __m128i gwt = _mm_add_epi16(cb0, yws); __m128i bws = _mm_add_epi16(yws, cb1); __m128i gws = _mm_add_epi16(gwt, cr1); // descale __m128i rw = _mm_srai_epi16(rws, 4); __m128i bw = _mm_srai_epi16(bws, 4); __m128i gw = _mm_srai_epi16(gws, 4); // back to byte, set up for transpose __m128i brb = _mm_packus_epi16(rw, bw); __m128i gxb = _mm_packus_epi16(gw, xw); // transpose to interleave channels __m128i t0 = _mm_unpacklo_epi8(brb, gxb); __m128i t1 = _mm_unpackhi_epi8(brb, gxb); __m128i o0 = _mm_unpacklo_epi16(t0, t1); __m128i o1 = _mm_unpackhi_epi16(t0, t1); // store _mm_storeu_si128((__m128i *) (out + 0), o0); _mm_storeu_si128((__m128i *) (out + 16), o1); out += 32; } } #endif #ifdef STBI_NEON // in this version, step=3 support would be easy to add. but is there demand? if (step == 4) { // this is a fairly straightforward implementation and not super-optimized. uint8x8_t signflip = vdup_n_u8(0x80); int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); for (; i+7 < count; i += 8) { // load uint8x8_t y_bytes = vld1_u8(y + i); uint8x8_t cr_bytes = vld1_u8(pcr + i); uint8x8_t cb_bytes = vld1_u8(pcb + i); int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); // expand to s16 int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); int16x8_t crw = vshll_n_s8(cr_biased, 7); int16x8_t cbw = vshll_n_s8(cb_biased, 7); // color transform int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); int16x8_t rws = vaddq_s16(yws, cr0); int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); int16x8_t bws = vaddq_s16(yws, cb1); // undo scaling, round, convert to byte uint8x8x4_t o; o.val[0] = vqrshrun_n_s16(rws, 4); o.val[1] = vqrshrun_n_s16(gws, 4); o.val[2] = vqrshrun_n_s16(bws, 4); o.val[3] = vdup_n_u8(255); // store, interleaving r/g/b/a vst4_u8(out, o); out += 8*4; } } #endif for (; i < count; ++i) { int y_fixed = (y[i] << 20) + (1<<19); // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr* stbi__float2fixed(1.40200f); g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #endif // set up the kernels static void stbi__setup_jpeg(stbi__jpeg *j) { j->idct_block_kernel = stbi__idct_block; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; #ifdef STBI_SSE2 if (stbi__sse2_available()) { j->idct_block_kernel = stbi__idct_simd; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct { resample_row_func resample; stbi_uc *line0,*line1; int hs,vs; // expansion factor in each axis int w_lores; // horizontal pixels pre-expansion int ystep; // how far through vertical expansion we are int ypos; // which pre-expansion row we're on } stbi__resample; // fast 0..255 * 0..255 => 0..255 rounded multiplication static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) { unsigned int t = x*y + 128; return (stbi_uc) ((t + (t >>8)) >> 8); } static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { int n, decode_n, is_rgb; z->s->img_n = 0; // make stbi__cleanup_jpeg safe // validate req_comp if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); // load a jpeg image from whichever source, but leave in YCbCr format if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } // determine actual number of components to generate n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); if (z->s->img_n == 3 && n < 3 && !is_rgb) decode_n = 1; else decode_n = z->s->img_n; // nothing to do if no components requested; check this now to avoid // accessing uninitialized coutput[0] later if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } // resample and color-convert { int k; unsigned int i,j; stbi_uc *output; stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; stbi__resample res_comp[4]; for (k=0; k < decode_n; ++k) { stbi__resample *r = &res_comp[k]; // allocate line buffer big enough for upsampling off the edges // with upsample factor of 4 z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } r->hs = z->img_h_max / z->img_comp[k].h; r->vs = z->img_v_max / z->img_comp[k].v; r->ystep = r->vs >> 1; r->w_lores = (z->s->img_x + r->hs-1) / r->hs; r->ypos = 0; r->line0 = r->line1 = z->img_comp[k].data; if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; else r->resample = stbi__resample_row_generic; } // can't error after this so, this is safe output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample for (j=0; j < z->s->img_y; ++j) { stbi_uc *out = output + n * z->s->img_x * j; for (k=0; k < decode_n; ++k) { stbi__resample *r = &res_comp[k]; int y_bot = r->ystep >= (r->vs >> 1); coutput[k] = r->resample(z->img_comp[k].linebuf, y_bot ? r->line1 : r->line0, y_bot ? r->line0 : r->line1, r->w_lores, r->hs); if (++r->ystep >= r->vs) { r->ystep = 0; r->line0 = r->line1; if (++r->ypos < z->img_comp[k].y) r->line1 += z->img_comp[k].w2; } } if (n >= 3) { stbi_uc *y = coutput[0]; if (z->s->img_n == 3) { if (is_rgb) { for (i=0; i < z->s->img_x; ++i) { out[0] = y[i]; out[1] = coutput[1][i]; out[2] = coutput[2][i]; out[3] = 255; out += n; } } else { z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } } else if (z->s->img_n == 4) { if (z->app14_color_transform == 0) { // CMYK for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; out[0] = stbi__blinn_8x8(coutput[0][i], m); out[1] = stbi__blinn_8x8(coutput[1][i], m); out[2] = stbi__blinn_8x8(coutput[2][i], m); out[3] = 255; out += n; } } else if (z->app14_color_transform == 2) { // YCCK z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; out[0] = stbi__blinn_8x8(255 - out[0], m); out[1] = stbi__blinn_8x8(255 - out[1], m); out[2] = stbi__blinn_8x8(255 - out[2], m); out += n; } } else { // YCbCr + alpha? Ignore the fourth channel for now z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } } else for (i=0; i < z->s->img_x; ++i) { out[0] = out[1] = out[2] = y[i]; out[3] = 255; // not used if n==3 out += n; } } else { if (is_rgb) { if (n == 1) for (i=0; i < z->s->img_x; ++i) *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); else { for (i=0; i < z->s->img_x; ++i, out += 2) { out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); out[1] = 255; } } } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); out[0] = stbi__compute_y(r, g, b); out[1] = 255; out += n; } } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { for (i=0; i < z->s->img_x; ++i) { out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); out[1] = 255; out += n; } } else { stbi_uc *y = coutput[0]; if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } } } } stbi__cleanup_jpeg(z); *out_x = z->s->img_x; *out_y = z->s->img_y; if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output return output; } } static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); if (!j) return stbi__errpuc("outofmem", "Out of memory"); memset(j, 0, sizeof(stbi__jpeg)); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); result = load_jpeg_image(j, x,y,comp,req_comp); STBI_FREE(j); return result; } static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); if (!j) return stbi__err("outofmem", "Out of memory"); memset(j, 0, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); stbi__rewind(s); STBI_FREE(j); return r; } static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) { if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { stbi__rewind( j->s ); return 0; } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; return 1; } static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); if (!j) return stbi__err("outofmem", "Out of memory"); memset(j, 0, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); return result; } #endif // public domain zlib decode v0.2 Sean Barrett 2006-11-18 // simple implementation // - all input must be provided in an upfront buffer // - all output is written to a single output buffer (can malloc/realloc) // performance // - fast huffman #ifndef STBI_NO_ZLIB // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) #define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) typedef struct { stbi__uint16 fast[1 << STBI__ZFAST_BITS]; stbi__uint16 firstcode[16]; int maxcode[17]; stbi__uint16 firstsymbol[16]; stbi_uc size[STBI__ZNSYMS]; stbi__uint16 value[STBI__ZNSYMS]; } stbi__zhuffman; stbi_inline static int stbi__bitreverse16(int n) { n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); return n; } stbi_inline static int stbi__bit_reverse(int v, int bits) { STBI_ASSERT(bits <= 16); // to bit reverse n bits, reverse 16 and shift // e.g. 11 bits, bit reverse and shift away 5 return stbi__bitreverse16(v) >> (16-bits); } static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) { int i,k=0; int code, next_code[16], sizes[17]; // DEFLATE spec for generating codes memset(sizes, 0, sizeof(sizes)); memset(z->fast, 0, sizeof(z->fast)); for (i=0; i < num; ++i) ++sizes[sizelist[i]]; sizes[0] = 0; for (i=1; i < 16; ++i) if (sizes[i] > (1 << i)) return stbi__err("bad sizes", "Corrupt PNG"); code = 0; for (i=1; i < 16; ++i) { next_code[i] = code; z->firstcode[i] = (stbi__uint16) code; z->firstsymbol[i] = (stbi__uint16) k; code = (code + sizes[i]); if (sizes[i]) if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); z->maxcode[i] = code << (16-i); // preshift for inner loop code <<= 1; k += sizes[i]; } z->maxcode[16] = 0x10000; // sentinel for (i=0; i < num; ++i) { int s = sizelist[i]; if (s) { int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); z->size [c] = (stbi_uc ) s; z->value[c] = (stbi__uint16) i; if (s <= STBI__ZFAST_BITS) { int j = stbi__bit_reverse(next_code[s],s); while (j < (1 << STBI__ZFAST_BITS)) { z->fast[j] = fastv; j += (1 << s); } } ++next_code[s]; } } return 1; } // zlib-from-memory implementation for PNG reading // because PNG allows splitting the zlib stream arbitrarily, // and it's annoying structurally to have PNG call ZLIB call PNG, // we require PNG read all the IDATs and combine them into a single // memory buffer typedef struct { stbi_uc *zbuffer, *zbuffer_end; int num_bits; stbi__uint32 code_buffer; char *zout; char *zout_start; char *zout_end; int z_expandable; stbi__zhuffman z_length, z_distance; } stbi__zbuf; stbi_inline static int stbi__zeof(stbi__zbuf *z) { return (z->zbuffer >= z->zbuffer_end); } stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { if (z->code_buffer >= (1U << z->num_bits)) { z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ return; } z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); } stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) { unsigned int k; if (z->num_bits < n) stbi__fill_bits(z); k = z->code_buffer & ((1 << n) - 1); z->code_buffer >>= n; z->num_bits -= n; return k; } static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { int b,s,k; // not resolved by fast table, so compute it the slow way // use jpeg approach, which requires MSbits at top k = stbi__bit_reverse(a->code_buffer, 16); for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; } stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; if (a->num_bits < 16) { if (stbi__zeof(a)) { return -1; /* report error for unexpected end of data. */ } stbi__fill_bits(a); } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; a->code_buffer >>= s; a->num_bits -= s; return b & 511; } return stbi__zhuffman_decode_slowpath(a, z); } static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); cur = (unsigned int) (z->zout - z->zout_start); limit = old_limit = (unsigned) (z->zout_end - z->zout_start); if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); while (cur + n > limit) { if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); limit *= 2; } q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); z->zout_start = q; z->zout = q + cur; z->zout_end = q + limit; return 1; } static const int stbi__zlength_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static const int stbi__zlength_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static const int stbi__zdist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static int stbi__parse_huffman_block(stbi__zbuf *a) { char *zout = a->zout; for(;;) { int z = stbi__zhuffman_decode(a, &a->z_length); if (z < 256) { if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes if (zout >= a->zout_end) { if (!stbi__zexpand(a, zout, 1)) return 0; zout = a->zout; } *zout++ = (char) z; } else { stbi_uc *p; int len,dist; if (z == 256) { a->zout = zout; return 1; } if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); if (zout + len > a->zout_end) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } p = (stbi_uc *) (zout - dist); if (dist == 1) { // run of one byte; common in images. stbi_uc v = *p; if (len) { do *zout++ = v; while (--len); } } else { if (len) { do *zout++ = *p++; while (--len); } } } } } static int stbi__compute_huffman_codes(stbi__zbuf *a) { static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; int i,n; int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; int ntot = hlit + hdist; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { int s = stbi__zreceive(a,3); codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; } if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; while (n < ntot) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; else { stbi_uc fill = 0; if (c == 16) { c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; } else if (c == 17) { c = stbi__zreceive(a,3)+3; } else if (c == 18) { c = stbi__zreceive(a,7)+11; } else { return stbi__err("bad codelengths", "Corrupt PNG"); } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); n += c; } } if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; } static int stbi__parse_uncompressed_block(stbi__zbuf *a) { stbi_uc header[4]; int len,nlen,k; if (a->num_bits & 7) stbi__zreceive(a, a->num_bits & 7); // discard // drain the bit-packed data into header k = 0; while (a->num_bits > 0) { header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check a->code_buffer >>= 8; a->num_bits -= 8; } if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); len = header[1] * 256 + header[0]; nlen = header[3] * 256 + header[2]; if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); if (a->zout + len > a->zout_end) if (!stbi__zexpand(a, a->zout, len)) return 0; memcpy(a->zout, a->zbuffer, len); a->zbuffer += len; a->zout += len; return 1; } static int stbi__parse_zlib_header(stbi__zbuf *a) { int cmf = stbi__zget8(a); int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png // window = 1 << (8 + cinfo)... but who cares, we fully buffer output return 1; } static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = { 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 }; static const stbi_uc stbi__zdefault_distance[32] = { 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 }; /* Init algorithm: { int i; // use <= to match clearly with spec for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; } */ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { int final, type; if (parse_header) if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; do { final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); if (type == 0) { if (!stbi__parse_uncompressed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { if (!stbi__compute_huffman_codes(a)) return 0; } if (!stbi__parse_huffman_block(a)) return 0; } } while (!final); return 1; } static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { a->zout_start = obuf; a->zout = obuf; a->zout_end = obuf + olen; a->z_expandable = exp; return stbi__parse_zlib(a, parse_header); } STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) { stbi__zbuf a; char *p = (char *) stbi__malloc(initial_size); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer + len; if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) { return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); } STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { stbi__zbuf a; char *p = (char *) stbi__malloc(initial_size); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer + len; if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) { stbi__zbuf a; a.zbuffer = (stbi_uc *) ibuffer; a.zbuffer_end = (stbi_uc *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) return (int) (a.zout - a.zout_start); else return -1; } STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) { stbi__zbuf a; char *p = (char *) stbi__malloc(16384); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer+len; if (stbi__do_zlib(&a, p, 16384, 1, 0)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) { stbi__zbuf a; a.zbuffer = (stbi_uc *) ibuffer; a.zbuffer_end = (stbi_uc *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) return (int) (a.zout - a.zout_start); else return -1; } #endif // public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 // simple implementation // - only 8-bit samples // - no CRC checking // - allocates lots of intermediate memory // - avoids problem of streaming data between subsystems // - avoids explicit window management // performance // - uses stb_zlib, a PD zlib implementation with fast huffman decoding #ifndef STBI_NO_PNG typedef struct { stbi__uint32 length; stbi__uint32 type; } stbi__pngchunk; static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) { stbi__pngchunk c; c.length = stbi__get32be(s); c.type = stbi__get32be(s); return c; } static int stbi__check_png_header(stbi__context *s) { static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); return 1; } typedef struct { stbi__context *s; stbi_uc *idata, *expanded, *out; int depth; } stbi__png; enum { STBI__F_none=0, STBI__F_sub=1, STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, // synthetic filters used for first scanline to avoid needing a dummy row of 0s STBI__F_avg_first, STBI__F_paeth_first }; static stbi_uc first_row_filter[5] = { STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_paeth_first }; static int stbi__paeth(int a, int b, int c) { int p = a + b - c; int pa = abs(p-a); int pb = abs(p-b); int pc = abs(p-c); if (pa <= pb && pa <= pc) return a; if (pb <= pc) return b; return c; } static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { int bytes = (depth == 16? 2 : 1); stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; int k; int img_n = s->img_n; // copy it into a local for later int output_bytes = out_n*bytes; int filter_bytes = img_n*bytes; int width = x; STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), // so just check for raw_len < img_len always. if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; stbi_uc *prior; int filter = *raw++; if (filter > 4) return stbi__err("invalid filter","Corrupt PNG"); if (depth < 8) { if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place filter_bytes = 1; width = img_width_bytes; } prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; // handle first byte explicitly for (k=0; k < filter_bytes; ++k) { switch (filter) { case STBI__F_none : cur[k] = raw[k]; break; case STBI__F_sub : cur[k] = raw[k]; break; case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; case STBI__F_avg_first : cur[k] = raw[k]; break; case STBI__F_paeth_first: cur[k] = raw[k]; break; } } if (depth == 8) { if (img_n != out_n) cur[img_n] = 255; // first pixel raw += img_n; cur += out_n; prior += out_n; } else if (depth == 16) { if (img_n != out_n) { cur[filter_bytes] = 255; // first pixel top byte cur[filter_bytes+1] = 255; // first pixel bottom byte } raw += filter_bytes; cur += output_bytes; prior += output_bytes; } else { raw += 1; cur += 1; prior += 1; } // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { int nk = (width - 1)*filter_bytes; #define STBI__CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; } #undef STBI__CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); #define STBI__CASE(f) \ case f: \ for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ for (k=0; k < filter_bytes; ++k) switch (filter) { STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; } #undef STBI__CASE // the loop above sets the high byte of the pixels' alpha, but for // 16 bit png files we also need the low byte set. we'll do that here. if (depth == 16) { cur = a->out + stride*j; // start at the beginning of the row again for (i=0; i < x; ++i,cur+=output_bytes) { cur[filter_bytes+1] = 255; } } } } // we make a separate pass to expand bits to pixels; for performance, // this could run two scanlines behind the above code, so it won't // intefere with filtering but will still be in the cache. if (depth < 8) { for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range // note that the final byte might overshoot and write more data than desired. // we can allocate enough data that this never writes out of memory, but it // could also overwrite the next scanline. can it overwrite non-empty data // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. // so we need to explicitly clamp the final ones if (depth == 4) { for (k=x*img_n; k >= 2; k-=2, ++in) { *cur++ = scale * ((*in >> 4) ); *cur++ = scale * ((*in ) & 0x0f); } if (k > 0) *cur++ = scale * ((*in >> 4) ); } else if (depth == 2) { for (k=x*img_n; k >= 4; k-=4, ++in) { *cur++ = scale * ((*in >> 6) ); *cur++ = scale * ((*in >> 4) & 0x03); *cur++ = scale * ((*in >> 2) & 0x03); *cur++ = scale * ((*in ) & 0x03); } if (k > 0) *cur++ = scale * ((*in >> 6) ); if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); } else if (depth == 1) { for (k=x*img_n; k >= 8; k-=8, ++in) { *cur++ = scale * ((*in >> 7) ); *cur++ = scale * ((*in >> 6) & 0x01); *cur++ = scale * ((*in >> 5) & 0x01); *cur++ = scale * ((*in >> 4) & 0x01); *cur++ = scale * ((*in >> 3) & 0x01); *cur++ = scale * ((*in >> 2) & 0x01); *cur++ = scale * ((*in >> 1) & 0x01); *cur++ = scale * ((*in ) & 0x01); } if (k > 0) *cur++ = scale * ((*in >> 7) ); if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } if (img_n != out_n) { int q; // insert alpha = 255 cur = a->out + stride*j; if (img_n == 1) { for (q=x-1; q >= 0; --q) { cur[q*2+1] = 255; cur[q*2+0] = cur[q]; } } else { STBI_ASSERT(img_n == 3); for (q=x-1; q >= 0; --q) { cur[q*4+3] = 255; cur[q*4+2] = cur[q*3+2]; cur[q*4+1] = cur[q*3+1]; cur[q*4+0] = cur[q*3+0]; } } } } } else if (depth == 16) { // force the image data from big-endian to platform-native. // this is done in a separate pass due to the decoding relying // on the data being untouched, but could probably be done // per-line during decode if care is taken. stbi_uc *cur = a->out; stbi__uint16 *cur16 = (stbi__uint16*)cur; for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { *cur16 = (cur[0] << 8) | cur[1]; } } return 1; } static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { int bytes = (depth == 16 ? 2 : 1); int out_bytes = out_n * bytes; stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); if (!final) return stbi__err("outofmem", "Out of memory"); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; int xspc[] = { 8,8,4,4,2,2,1 }; int yspc[] = { 8,8,8,4,4,2,2 }; int i,j,x,y; // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; if (x && y) { stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { STBI_FREE(final); return 0; } for (j=0; j < y; ++j) { for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, a->out + (j*x+i)*out_bytes, out_bytes); } } STBI_FREE(a->out); image_data += img_len; image_data_len -= img_len; } } a->out = final; return 1; } static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi_uc *p = z->out; // compute color-based transparency, assuming we've // already got 255 as the alpha value in the output STBI_ASSERT(out_n == 2 || out_n == 4); if (out_n == 2) { for (i=0; i < pixel_count; ++i) { p[1] = (p[0] == tc[0] ? 0 : 255); p += 2; } } else { for (i=0; i < pixel_count; ++i) { if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) p[3] = 0; p += 4; } } return 1; } static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi__uint16 *p = (stbi__uint16*) z->out; // compute color-based transparency, assuming we've // already got 65535 as the alpha value in the output STBI_ASSERT(out_n == 2 || out_n == 4); if (out_n == 2) { for (i = 0; i < pixel_count; ++i) { p[1] = (p[0] == tc[0] ? 0 : 65535); p += 2; } } else { for (i = 0; i < pixel_count; ++i) { if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) p[3] = 0; p += 4; } } return 1; } static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak temp_out = p; if (pal_img_n == 3) { for (i=0; i < pixel_count; ++i) { int n = orig[i]*4; p[0] = palette[n ]; p[1] = palette[n+1]; p[2] = palette[n+2]; p += 3; } } else { for (i=0; i < pixel_count; ++i) { int n = orig[i]*4; p[0] = palette[n ]; p[1] = palette[n+1]; p[2] = palette[n+2]; p[3] = palette[n+3]; p += 4; } } STBI_FREE(a->out); a->out = temp_out; STBI_NOTUSED(len); return 1; } static int stbi__unpremultiply_on_load_global = 0; static int stbi__de_iphone_flag_global = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { stbi__de_iphone_flag_global = flag_true_if_should_convert; } #ifndef STBI_THREAD_LOCAL #define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global #define stbi__de_iphone_flag stbi__de_iphone_flag_global #else static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; stbi__unpremultiply_on_load_set = 1; } STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) { stbi__de_iphone_flag_local = flag_true_if_should_convert; stbi__de_iphone_flag_set = 1; } #define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ ? stbi__unpremultiply_on_load_local \ : stbi__unpremultiply_on_load_global) #define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ ? stbi__de_iphone_flag_local \ : stbi__de_iphone_flag_global) #endif // STBI_THREAD_LOCAL static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi_uc *p = z->out; if (s->img_out_n == 3) { // convert bgr to rgb for (i=0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; p += 3; } } else { STBI_ASSERT(s->img_out_n == 4); if (stbi__unpremultiply_on_load) { // convert bgr to rgb and unpremultiply for (i=0; i < pixel_count; ++i) { stbi_uc a = p[3]; stbi_uc t = p[0]; if (a) { stbi_uc half = a / 2; p[0] = (p[2] * 255 + half) / a; p[1] = (p[1] * 255 + half) / a; p[2] = ( t * 255 + half) / a; } else { p[0] = p[2]; p[2] = t; } p += 4; } } else { // convert bgr to rgb for (i=0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; p += 4; } } } } #define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; stbi_uc has_trans=0, tc[3]={0}; stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, is_iphone=0; stbi__context *s = z->s; z->expanded = NULL; z->idata = NULL; z->out = NULL; if (!stbi__check_png_header(s)) return 0; if (scan == STBI__SCAN_type) return 1; for (;;) { stbi__pngchunk c = stbi__get_chunk_header(s); switch (c.type) { case STBI__PNG_TYPE('C','g','B','I'): is_iphone = 1; stbi__skip(s, c.length); break; case STBI__PNG_TYPE('I','H','D','R'): { int comp,filter; if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); s->img_x = stbi__get32be(s); s->img_y = stbi__get32be(s); if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); } // even with SCAN_header, have to scan to see if we have a tRNS break; } case STBI__PNG_TYPE('P','L','T','E'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); pal_len = c.length / 3; if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); for (i=0; i < pal_len; ++i) { palette[i*4+0] = stbi__get8(s); palette[i*4+1] = stbi__get8(s); palette[i*4+2] = stbi__get8(s); palette[i*4+3] = 255; } break; } case STBI__PNG_TYPE('t','R','N','S'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); if (pal_img_n) { if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); pal_img_n = 4; for (i=0; i < c.length; ++i) palette[i*4+3] = stbi__get8(s); } else { if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } if (z->depth == 16) { for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger } } break; } case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); if (scan == STBI__SCAN_header) { // header scan definitely stops at first IDAT if (pal_img_n) s->img_n = pal_img_n; return 1; } if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = idata_limit; stbi_uc *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; while (ioff + c.length > idata_limit) idata_limit *= 2; STBI_NOTUSED(idata_limit_old); p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); z->idata = p; } if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); ioff += c.length; break; } case STBI__PNG_TYPE('I','E','N','D'): { stbi__uint32 raw_len, bpl; if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (scan != STBI__SCAN_load) return 1; if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); // initial guess for decoded data size to avoid unnecessary reallocs bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); if (z->expanded == NULL) return 0; // zlib should set error STBI_FREE(z->idata); z->idata = NULL; if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) s->img_out_n = s->img_n+1; else s->img_out_n = s->img_n; if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; if (has_trans) { if (z->depth == 16) { if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; } else { if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; } } if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) stbi__de_iphone(z); if (pal_img_n) { // pal_img_n == 3 or 4 s->img_n = pal_img_n; // record the actual colors we had s->img_out_n = pal_img_n; if (req_comp >= 3) s->img_out_n = req_comp; if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) return 0; } else if (has_trans) { // non-paletted image with tRNS -> source image has (constant) alpha ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; // end of PNG chunk, read and skip CRC stbi__get32be(s); return 1; } default: // if critical, fail if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if ((c.type & (1 << 29)) == 0) { #ifndef STBI_NO_FAILURE_STRINGS // not threadsafe static char invalid_chunk[] = "XXXX PNG chunk not known"; invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); #endif return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); } stbi__skip(s, c.length); break; } // end of PNG chunk, read and skip CRC stbi__get32be(s); } } static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { if (p->depth <= 8) ri->bits_per_channel = 8; else if (p->depth == 16) ri->bits_per_channel = 16; else return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { if (ri->bits_per_channel == 8) result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); else result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } *x = p->s->img_x; *y = p->s->img_y; if (n) *n = p->s->img_n; } STBI_FREE(p->out); p->out = NULL; STBI_FREE(p->expanded); p->expanded = NULL; STBI_FREE(p->idata); p->idata = NULL; return result; } static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi__png p; p.s = s; return stbi__do_png(&p, x,y,comp,req_comp, ri); } static int stbi__png_test(stbi__context *s) { int r; r = stbi__check_png_header(s); stbi__rewind(s); return r; } static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) { if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { stbi__rewind( p->s ); return 0; } if (x) *x = p->s->img_x; if (y) *y = p->s->img_y; if (comp) *comp = p->s->img_n; return 1; } static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) { stbi__png p; p.s = s; return stbi__png_info_raw(&p, x, y, comp); } static int stbi__png_is16(stbi__context *s) { stbi__png p; p.s = s; if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) return 0; if (p.depth != 16) { stbi__rewind(p.s); return 0; } return 1; } #endif // Microsoft/Windows BMP image #ifndef STBI_NO_BMP static int stbi__bmp_test_raw(stbi__context *s) { int r; int sz; if (stbi__get8(s) != 'B') return 0; if (stbi__get8(s) != 'M') return 0; stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved stbi__get32le(s); // discard data offset sz = stbi__get32le(s); r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); return r; } static int stbi__bmp_test(stbi__context *s) { int r = stbi__bmp_test_raw(s); stbi__rewind(s); return r; } // returns 0..31 for the highest set bit static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; if (z >= 0x10000) { n += 16; z >>= 16; } if (z >= 0x00100) { n += 8; z >>= 8; } if (z >= 0x00010) { n += 4; z >>= 4; } if (z >= 0x00004) { n += 2; z >>= 2; } if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } static int stbi__bitcount(unsigned int a) { a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits a = (a + (a >> 8)); // max 16 per 8 bits a = (a + (a >> 16)); // max 32 per 8 bits return a & 0xff; } // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { 0, 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, }; static unsigned int shift_table[9] = { 0, 0,0,1,0,2,4,6,0, }; if (shift < 0) v <<= -shift; else v >>= shift; STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; } typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; int extra_read; } stbi__bmp_data; static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) { // BI_BITFIELDS specifies masks explicitly, don't override if (compress == 3) return 1; if (compress == 0) { if (info->bpp == 16) { info->mr = 31u << 10; info->mg = 31u << 5; info->mb = 31u << 0; } else if (info->bpp == 32) { info->mr = 0xffu << 16; info->mg = 0xffu << 8; info->mb = 0xffu << 0; info->ma = 0xffu << 24; info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 } else { // otherwise, use defaults, which is all-0 info->mr = info->mg = info->mb = info->ma = 0; } return 1; } return 0; // error } static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int hsz; if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; info->extra_read = 14; if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { s->img_x = stbi__get16le(s); s->img_y = stbi__get16le(s); } else { s->img_x = stbi__get32le(s); s->img_y = stbi__get32le(s); } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); info->bpp = stbi__get16le(s); if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres stbi__get32le(s); // discard colorsused stbi__get32le(s); // discard max important if (hsz == 40 || hsz == 56) { if (hsz == 56) { stbi__get32le(s); stbi__get32le(s); stbi__get32le(s); stbi__get32le(s); } if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { stbi__bmp_set_mask_defaults(info, compress); } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? return stbi__errpuc("bad BMP", "bad BMP"); } } else return stbi__errpuc("bad BMP", "bad BMP"); } } else { // V4/V5 header int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->ma = stbi__get32le(s); if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs stbi__bmp_set_mask_defaults(info, compress); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters if (hsz == 124) { stbi__get32le(s); // discard rendering intent stbi__get32le(s); // discard offset of profile data stbi__get32le(s); // discard size of profile data stbi__get32le(s); // discard reserved } } } return (void *) 1; } static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; unsigned int mr=0,mg=0,mb=0,ma=0, all_a; stbi_uc pal[256][4]; int psize=0,i,j,width; int flip_vertically, pad, target; stbi__bmp_data info; STBI_NOTUSED(ri); info.all_a = 255; if (stbi__bmp_parse_header(s, &info) == NULL) return NULL; // error code already set flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); mr = info.mr; mg = info.mg; mb = info.mb; ma = info.ma; all_a = info.all_a; if (info.hsz == 12) { if (info.bpp < 24) psize = (info.offset - info.extra_read - 24) / 3; } else { if (info.bpp < 16) psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { // accept some number of extra bytes after the header, but if the offset points either to before // the header ends or implies a large amount of extra data, reject the file as malformed int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); int header_limit = 1024; // max we actually read is below 256 bytes currently. int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { return stbi__errpuc("bad header", "Corrupt BMP"); } // we established that bytes_read_so_far is positive and sensible. // the first half of this test rejects offsets that are either too small positives, or // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn // ensures the number computed in the second half of the test can't overflow. if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { return stbi__errpuc("bad offset", "Corrupt BMP"); } else { stbi__skip(s, info.offset - bytes_read_so_far); } } if (info.bpp == 24 && ma == 0xff000000) s->img_n = 3; else s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else target = s->img_n; // if they want monochrome, we'll post-convert // sanity-check size if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) return stbi__errpuc("too large", "Corrupt BMP"); out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (info.bpp < 16) { int z=0; if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } for (i=0; i < psize; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; if (info.bpp == 1) { for (j=0; j < (int) s->img_y; ++j) { int bit_offset = 7, v = stbi__get8(s); for (i=0; i < (int) s->img_x; ++i) { int color = (v>>bit_offset)&0x1; out[z++] = pal[color][0]; out[z++] = pal[color][1]; out[z++] = pal[color][2]; if (target == 4) out[z++] = 255; if (i+1 == (int) s->img_x) break; if((--bit_offset) < 0) { bit_offset = 7; v = stbi__get8(s); } } stbi__skip(s, pad); } } else { for (j=0; j < (int) s->img_y; ++j) { for (i=0; i < (int) s->img_x; i += 2) { int v=stbi__get8(s),v2=0; if (info.bpp == 4) { v2 = v & 15; v >>= 4; } out[z++] = pal[v][0]; out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; if (i+1 == (int) s->img_x) break; v = (info.bpp == 8) ? stbi__get8(s) : v2; out[z++] = pal[v][0]; out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; } stbi__skip(s, pad); } } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; stbi__skip(s, info.offset - info.extra_read - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; pad = (-width) & 3; if (info.bpp == 24) { easy = 1; } else if (info.bpp == 32) { if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) easy = 2; } if (!easy) { if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } // right shift amt to put high bit in position #7 rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } } for (j=0; j < (int) s->img_y; ++j) { if (easy) { for (i=0; i < (int) s->img_x; ++i) { unsigned char a; out[z+2] = stbi__get8(s); out[z+1] = stbi__get8(s); out[z+0] = stbi__get8(s); z += 3; a = (easy == 2 ? stbi__get8(s) : 255); all_a |= a; if (target == 4) out[z++] = a; } } else { int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); unsigned int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); all_a |= a; if (target == 4) out[z++] = STBI__BYTECAST(a); } } stbi__skip(s, pad); } } // if alpha channel is all 0s, replace with all 255s if (target == 4 && all_a == 0) for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) out[i] = 255; if (flip_vertically) { stbi_uc t; for (j=0; j < (int) s->img_y>>1; ++j) { stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { t = p1[i]; p1[i] = p2[i]; p2[i] = t; } } } if (req_comp && req_comp != target) { out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); if (out == NULL) return out; // stbi__convert_format frees input on failure } *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; return out; } #endif // Targa Truevision - TGA // by Jonathan Dummer #ifndef STBI_NO_TGA // returns STBI_rgb or whatever, 0 on error static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) { // only RGB or RGBA (incl. 16bit) or grey allowed if (is_rgb16) *is_rgb16 = 0; switch(bits_per_pixel) { case 8: return STBI_grey; case 16: if(is_grey) return STBI_grey_alpha; // fallthrough case 15: if(is_rgb16) *is_rgb16 = 1; return STBI_rgb; case 24: // fallthrough case 32: return bits_per_pixel/8; default: return 0; } } static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; int sz, tga_colormap_type; stbi__get8(s); // discard Offset tga_colormap_type = stbi__get8(s); // colormap type if( tga_colormap_type > 1 ) { stbi__rewind(s); return 0; // only RGB or indexed allowed } tga_image_type = stbi__get8(s); // image type if ( tga_colormap_type == 1 ) { // colormapped (paletted) image if (tga_image_type != 1 && tga_image_type != 9) { stbi__rewind(s); return 0; } stbi__skip(s,4); // skip index of first colormap entry and number of entries sz = stbi__get8(s); // check bits per palette color entry if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { stbi__rewind(s); return 0; } stbi__skip(s,4); // skip image x and y origin tga_colormap_bpp = sz; } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { stbi__rewind(s); return 0; // only RGB or grey allowed, +/- RLE } stbi__skip(s,9); // skip colormap specification and image x/y origin tga_colormap_bpp = 0; } tga_w = stbi__get16le(s); if( tga_w < 1 ) { stbi__rewind(s); return 0; // test width } tga_h = stbi__get16le(s); if( tga_h < 1 ) { stbi__rewind(s); return 0; // test height } tga_bits_per_pixel = stbi__get8(s); // bits per pixel stbi__get8(s); // ignore alpha bits if (tga_colormap_bpp != 0) { if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { // when using a colormap, tga_bits_per_pixel is the size of the indexes // I don't think anything but 8 or 16bit indexes makes sense stbi__rewind(s); return 0; } tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); } else { tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); } if(!tga_comp) { stbi__rewind(s); return 0; } if (x) *x = tga_w; if (y) *y = tga_h; if (comp) *comp = tga_comp; return 1; // seems to have passed everything } static int stbi__tga_test(stbi__context *s) { int res = 0; int sz, tga_color_type; stbi__get8(s); // discard Offset tga_color_type = stbi__get8(s); // color type if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed sz = stbi__get8(s); // image type if ( tga_color_type == 1 ) { // colormapped (paletted) image if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 stbi__skip(s,4); // skip index of first colormap entry and number of entries sz = stbi__get8(s); // check bits per palette color entry if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; stbi__skip(s,4); // skip image x and y origin } else { // "normal" image w/o colormap if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE stbi__skip(s,9); // skip colormap specification and image x/y origin } if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height sz = stbi__get8(s); // bits per pixel if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; res = 1; // if we got this far, everything's good and we can return 1 instead of 0 errorEnd: stbi__rewind(s); return res; } // read 16bit value and convert to 24bit RGB static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) { stbi__uint16 px = (stbi__uint16)stbi__get16le(s); stbi__uint16 fiveBitMask = 31; // we have 3 channels with 5bits each int r = (px >> 10) & fiveBitMask; int g = (px >> 5) & fiveBitMask; int b = px & fiveBitMask; // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later out[0] = (stbi_uc)((r * 255)/31); out[1] = (stbi_uc)((g * 255)/31); out[2] = (stbi_uc)((b * 255)/31); // some people claim that the most significant bit might be used for alpha // (possibly if an alpha-bit is set in the "image descriptor byte") // but that only made 16bit test images completely translucent.. // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { // read in the TGA header stuff int tga_offset = stbi__get8(s); int tga_indexed = stbi__get8(s); int tga_image_type = stbi__get8(s); int tga_is_RLE = 0; int tga_palette_start = stbi__get16le(s); int tga_palette_len = stbi__get16le(s); int tga_palette_bits = stbi__get8(s); int tga_x_origin = stbi__get16le(s); int tga_y_origin = stbi__get16le(s); int tga_width = stbi__get16le(s); int tga_height = stbi__get16le(s); int tga_bits_per_pixel = stbi__get8(s); int tga_comp, tga_rgb16=0; int tga_inverted = stbi__get8(s); // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) // image data unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; unsigned char raw_data[4] = {0}; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); STBI_NOTUSED(tga_x_origin); // @TODO STBI_NOTUSED(tga_y_origin); // @TODO if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // do a tiny bit of precessing if ( tga_image_type >= 8 ) { tga_image_type -= 8; tga_is_RLE = 1; } tga_inverted = 1 - ((tga_inverted >> 5) & 1); // If I'm paletted, then I'll use the number of bits from the palette if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); // tga info *x = tga_width; *y = tga_height; if (comp) *comp = tga_comp; if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) return stbi__errpuc("too large", "Corrupt TGA"); tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) stbi__skip(s, tga_offset ); if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { for (i=0; i < tga_height; ++i) { int row = tga_inverted ? tga_height -i - 1 : i; stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; stbi__getn(s, tga_row, tga_width * tga_comp); } } else { // do I need to load a palette? if ( tga_indexed) { if (tga_palette_len == 0) { /* you have to have at least one entry! */ STBI_FREE(tga_data); return stbi__errpuc("bad palette", "Corrupt TGA"); } // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); } if (tga_rgb16) { stbi_uc *pal_entry = tga_palette; STBI_ASSERT(tga_comp == STBI_rgb); for (i=0; i < tga_palette_len; ++i) { stbi__tga_read_rgb16(s, pal_entry); pal_entry += tga_comp; } } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { STBI_FREE(tga_data); STBI_FREE(tga_palette); return stbi__errpuc("bad palette", "Corrupt TGA"); } } // load the data for (i=0; i < tga_width * tga_height; ++i) { // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? if ( tga_is_RLE ) { if ( RLE_count == 0 ) { // yep, get the next byte as a RLE command int RLE_cmd = stbi__get8(s); RLE_count = 1 + (RLE_cmd & 127); RLE_repeating = RLE_cmd >> 7; read_next_pixel = 1; } else if ( !RLE_repeating ) { read_next_pixel = 1; } } else { read_next_pixel = 1; } // OK, if I need to read a pixel, do it now if ( read_next_pixel ) { // load however much data we did have if ( tga_indexed ) { // read in index, then perform the lookup int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); if ( pal_idx >= tga_palette_len ) { // invalid index pal_idx = 0; } pal_idx *= tga_comp; for (j = 0; j < tga_comp; ++j) { raw_data[j] = tga_palette[pal_idx+j]; } } else if(tga_rgb16) { STBI_ASSERT(tga_comp == STBI_rgb); stbi__tga_read_rgb16(s, raw_data); } else { // read in the data raw for (j = 0; j < tga_comp; ++j) { raw_data[j] = stbi__get8(s); } } // clear the reading flag for the next pixel read_next_pixel = 0; } // end of reading a pixel // copy data for (j = 0; j < tga_comp; ++j) tga_data[i*tga_comp+j] = raw_data[j]; // in case we're in RLE mode, keep counting down --RLE_count; } // do I need to invert the image? if ( tga_inverted ) { for (j = 0; j*2 < tga_height; ++j) { int index1 = j * tga_width * tga_comp; int index2 = (tga_height - 1 - j) * tga_width * tga_comp; for (i = tga_width * tga_comp; i > 0; --i) { unsigned char temp = tga_data[index1]; tga_data[index1] = tga_data[index2]; tga_data[index2] = temp; ++index1; ++index2; } } } // clear my palette, if I had one if ( tga_palette != NULL ) { STBI_FREE( tga_palette ); } } // swap RGB - if the source data was RGB16, it already is in the right order if (tga_comp >= 3 && !tga_rgb16) { unsigned char* tga_pixel = tga_data; for (i=0; i < tga_width * tga_height; ++i) { unsigned char temp = tga_pixel[0]; tga_pixel[0] = tga_pixel[2]; tga_pixel[2] = temp; tga_pixel += tga_comp; } } // convert to target component count if (req_comp && req_comp != tga_comp) tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); // the things I do to get rid of an error message, and yet keep // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } #endif // ************************************************************************************************* // Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s) { int r = (stbi__get32be(s) == 0x38425053); stbi__rewind(s); return r; } static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { int count, nleft, len; count = 0; while ((nleft = pixelCount - count) > 0) { len = stbi__get8(s); if (len == 128) { // No-op. } else if (len < 128) { // Copy next len+1 bytes literally. len++; if (len > nleft) return 0; // corrupt data count += len; while (len) { *p = stbi__get8(s); p += 4; len--; } } else if (len > 128) { stbi_uc val; // Next -len+1 bytes in the dest are replicated from next source byte. // (Interpret len as a negative 8-bit int.) len = 257 - len; if (len > nleft) return 0; // corrupt data val = stbi__get8(s); count += len; while (len) { *p = val; p += 4; len--; } } } return 1; } static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { int pixelCount; int channelCount, compression; int channel, i; int bitdepth; int w,h; stbi_uc *out; STBI_NOTUSED(ri); // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" return stbi__errpuc("not PSD", "Corrupt PSD image"); // Check file type version. if (stbi__get16be(s) != 1) return stbi__errpuc("wrong version", "Unsupported version of PSD image"); // Skip 6 reserved bytes. stbi__skip(s, 6 ); // Read the number of channels (R, G, B, A, etc). channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); // Read the rows and columns of the image. h = stbi__get32be(s); w = stbi__get32be(s); if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // Make sure the depth is 8 bits. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); // Make sure the color mode is RGB. // Valid options are: // 0: Bitmap // 1: Grayscale // 2: Indexed color // 3: RGB color // 4: CMYK color // 7: Multichannel // 8: Duotone // 9: Lab color if (stbi__get16be(s) != 3) return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) stbi__skip(s,stbi__get32be(s) ); // Skip the image resources. (resolution, pen tool paths, etc) stbi__skip(s, stbi__get32be(s) ); // Skip the reserved data. stbi__skip(s, stbi__get32be(s) ); // Find out if the data is compressed. // Known values: // 0: no compression // 1: RLE compressed compression = stbi__get16be(s); if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); // Check size if (!stbi__mad3sizes_valid(4, w, h, 0)) return stbi__errpuc("too large", "Corrupt PSD"); // Create the destination image. if (!compression && bitdepth == 16 && bpc == 16) { out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); ri->bits_per_channel = 16; } else out = (stbi_uc *) stbi__malloc(4 * w*h); if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; // Initialize the data to zero. //memset( out, 0, pixelCount * 4 ); // Finally, the image data. if (compression) { // RLE as used by .PSD and .TIFF // Loop until you get the number of unpacked bytes you are expecting: // Read the next source byte into n. // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. // Else if n is 128, noop. // Endloop // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); // Read the RLE data by channel. for (channel = 0; channel < 4; channel++) { stbi_uc *p; p = out+channel; if (channel >= channelCount) { // Fill this channel with default data. for (i = 0; i < pixelCount; i++, p += 4) *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. if (!stbi__psd_decode_rle(s, p, pixelCount)) { STBI_FREE(out); return stbi__errpuc("corrupt", "bad RLE data"); } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { if (channel >= channelCount) { // Fill this channel with default data. if (bitdepth == 16 && bpc == 16) { stbi__uint16 *q = ((stbi__uint16 *) out) + channel; stbi__uint16 val = channel == 3 ? 65535 : 0; for (i = 0; i < pixelCount; i++, q += 4) *q = val; } else { stbi_uc *p = out+channel; stbi_uc val = channel == 3 ? 255 : 0; for (i = 0; i < pixelCount; i++, p += 4) *p = val; } } else { if (ri->bits_per_channel == 16) { // output bpc stbi__uint16 *q = ((stbi__uint16 *) out) + channel; for (i = 0; i < pixelCount; i++, q += 4) *q = (stbi__uint16) stbi__get16be(s); } else { stbi_uc *p = out+channel; if (bitdepth == 16) { // input bpc for (i = 0; i < pixelCount; i++, p += 4) *p = (stbi_uc) (stbi__get16be(s) >> 8); } else { for (i = 0; i < pixelCount; i++, p += 4) *p = stbi__get8(s); } } } } } // remove weird white matte from PSD if (channelCount >= 4) { if (ri->bits_per_channel == 16) { for (i=0; i < w*h; ++i) { stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; if (pixel[3] != 0 && pixel[3] != 65535) { float a = pixel[3] / 65535.0f; float ra = 1.0f / a; float inv_a = 65535.0f * (1 - ra); pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); } } } else { for (i=0; i < w*h; ++i) { unsigned char *pixel = out + 4*i; if (pixel[3] != 0 && pixel[3] != 255) { float a = pixel[3] / 255.0f; float ra = 1.0f / a; float inv_a = 255.0f * (1 - ra); pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); } } } } // convert to desired output format if (req_comp && req_comp != 4) { if (ri->bits_per_channel == 16) out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); else out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } if (comp) *comp = 4; *y = h; *x = w; return out; } #endif // ************************************************************************************************* // Softimage PIC loader // by Tom Seddon // // See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format // See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ #ifndef STBI_NO_PIC static int stbi__pic_is4(stbi__context *s,const char *str) { int i; for (i=0; i<4; ++i) if (stbi__get8(s) != (stbi_uc)str[i]) return 0; return 1; } static int stbi__pic_test_core(stbi__context *s) { int i; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) return 0; for(i=0;i<84;++i) stbi__get8(s); if (!stbi__pic_is4(s,"PICT")) return 0; return 1; } typedef struct { stbi_uc size,type,channel; } stbi__pic_packet; static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) { int mask=0x80, i; for (i=0; i<4; ++i, mask>>=1) { if (channel & mask) { if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); dest[i]=stbi__get8(s); } } return dest; } static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) { int mask=0x80,i; for (i=0;i<4; ++i, mask>>=1) if (channel&mask) dest[i]=src[i]; } static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) { int act_comp=0,num_packets=0,y,chained; stbi__pic_packet packets[10]; // this will (should...) cater for even some bizarre stuff like having data // for the same channel in multiple packets. do { stbi__pic_packet *packet; if (num_packets==sizeof(packets)/sizeof(packets[0])) return stbi__errpuc("bad format","too many packets"); packet = &packets[num_packets++]; chained = stbi__get8(s); packet->size = stbi__get8(s); packet->type = stbi__get8(s); packet->channel = stbi__get8(s); act_comp |= packet->channel; if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? for(y=0; ytype) { default: return stbi__errpuc("bad format","packet has bad compression type"); case 0: {//uncompressed int x; for(x=0;xchannel,dest)) return 0; break; } case 1://Pure RLE { int left=width, i; while (left>0) { stbi_uc count,value[4]; count=stbi__get8(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); if (count > left) count = (stbi_uc) left; if (!stbi__readval(s,packet->channel,value)) return 0; for(i=0; ichannel,dest,value); left -= count; } } break; case 2: {//Mixed RLE int left=width; while (left>0) { int count = stbi__get8(s), i; if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); if (count >= 128) { // Repeated stbi_uc value[4]; if (count==128) count = stbi__get16be(s); else count -= 127; if (count > left) return stbi__errpuc("bad file","scanline overrun"); if (!stbi__readval(s,packet->channel,value)) return 0; for(i=0;ichannel,dest,value); } else { // Raw ++count; if (count>left) return stbi__errpuc("bad file","scanline overrun"); for(i=0;ichannel,dest)) return 0; } left-=count; } break; } } } } return result; } static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) { stbi_uc *result; int i, x,y, internal_comp; STBI_NOTUSED(ri); if (!comp) comp = &internal_comp; for (i=0; i<92; ++i) stbi__get8(s); x = stbi__get16be(s); y = stbi__get16be(s); if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); if (!result) return stbi__errpuc("outofmem", "Out of memory"); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { STBI_FREE(result); result=0; } *px = x; *py = y; if (req_comp == 0) req_comp = *comp; result=stbi__convert_format(result,4,req_comp,x,y); return result; } static int stbi__pic_test(stbi__context *s) { int r = stbi__pic_test_core(s); stbi__rewind(s); return r; } #endif // ************************************************************************************************* // GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb #ifndef STBI_NO_GIF typedef struct { stbi__int16 prefix; stbi_uc first; stbi_uc suffix; } stbi__gif_lzw; typedef struct { int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; stbi__gif_lzw codes[8192]; stbi_uc *color_table; int parse, step; int lflags; int start_x, start_y; int max_x, max_y; int cur_x, cur_y; int line_size; int delay; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) { int sz; if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; sz = stbi__get8(s); if (sz != '9' && sz != '7') return 0; if (stbi__get8(s) != 'a') return 0; return 1; } static int stbi__gif_test(stbi__context *s) { int r = stbi__gif_test_raw(s); stbi__rewind(s); return r; } static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) { int i; for (i=0; i < num_entries; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); pal[i][3] = transp == i ? 0 : 255; } } static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) { stbi_uc version; if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return stbi__err("not GIF", "Corrupt GIF"); version = stbi__get8(s); if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); stbi__g_failure_reason = ""; g->w = stbi__get16le(s); g->h = stbi__get16le(s); g->flags = stbi__get8(s); g->bgindex = stbi__get8(s); g->ratio = stbi__get8(s); g->transparent = -1; if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; if (g->flags & 0x80) stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); return 1; } static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); if (!g) return stbi__err("outofmem", "Out of memory"); if (!stbi__gif_header(s, g, comp, 1)) { STBI_FREE(g); stbi__rewind( s ); return 0; } if (x) *x = g->w; if (y) *y = g->h; STBI_FREE(g); return 1; } static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty if (g->codes[code].prefix >= 0) stbi__out_gif_code(g, g->codes[code].prefix); if (g->cur_y >= g->max_y) return; idx = g->cur_x + g->cur_y; p = &g->out[idx]; g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; p[3] = c[3]; } g->cur_x += 4; if (g->cur_x >= g->max_x) { g->cur_x = g->start_x; g->cur_y += g->step; while (g->cur_y >= g->max_y && g->parse > 0) { g->step = (1 << g->parse) * g->line_size; g->cur_y = g->start_y + (g->step >> 1); --g->parse; } } } static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { stbi_uc lzw_cs; stbi__int32 len, init_code; stbi__uint32 first; stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; stbi__gif_lzw *p; lzw_cs = stbi__get8(s); if (lzw_cs > 12) return NULL; clear = 1 << lzw_cs; first = 1; codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; bits = 0; valid_bits = 0; for (init_code = 0; init_code < clear; init_code++) { g->codes[init_code].prefix = -1; g->codes[init_code].first = (stbi_uc) init_code; g->codes[init_code].suffix = (stbi_uc) init_code; } // support no starting clear code avail = clear+2; oldcode = -1; len = 0; for(;;) { if (valid_bits < codesize) { if (len == 0) { len = stbi__get8(s); // start new block if (len == 0) return g->out; } --len; bits |= (stbi__int32) stbi__get8(s) << valid_bits; valid_bits += 8; } else { stbi__int32 code = bits & codemask; bits >>= codesize; valid_bits -= codesize; // @OPTIMIZE: is there some way we can accelerate the non-clear path? if (code == clear) { // clear code codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; avail = clear + 2; oldcode = -1; first = 0; } else if (code == clear + 1) { // end of stream code stbi__skip(s, len); while ((len = stbi__get8(s)) > 0) stbi__skip(s,len); return g->out; } else if (code <= avail) { if (first) { return stbi__errpuc("no clear code", "Corrupt GIF"); } if (oldcode >= 0) { p = &g->codes[avail++]; if (avail > 8192) { return stbi__errpuc("too many codes", "Corrupt GIF"); } p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; } else if (code == avail) return stbi__errpuc("illegal code in raster", "Corrupt GIF"); stbi__out_gif_code(g, (stbi__uint16) code); if ((avail & codemask) == 0 && avail <= 0x0FFF) { codesize++; codemask = (1 << codesize) - 1; } oldcode = code; } else { return stbi__errpuc("illegal code in raster", "Corrupt GIF"); } } } } // this function is designed to support animated gifs, although stb_image doesn't support it // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { int dispose; int first_frame; int pi; int pcount; STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) first_frame = 0; if (g->out == 0) { if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) return stbi__errpuc("too large", "GIF image is too large"); pcount = g->w * g->h; g->out = (stbi_uc *) stbi__malloc(4 * pcount); g->background = (stbi_uc *) stbi__malloc(4 * pcount); g->history = (stbi_uc *) stbi__malloc(pcount); if (!g->out || !g->background || !g->history) return stbi__errpuc("outofmem", "Out of memory"); // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" // color refers to the color that was there the previous frame. memset(g->out, 0x00, 4 * pcount); memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) memset(g->history, 0x00, pcount); // pixels that were affected previous frame first_frame = 1; } else { // second frame - how do we dispose of the previous one? dispose = (g->eflags & 0x1C) >> 2; pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background } if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } } else if (dispose == 2) { // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } // background is what out is after the undoing of the previou frame; memcpy( g->background, g->out, 4 * g->w * g->h ); } // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { stbi__int32 x, y, w, h; stbi_uc *o; x = stbi__get16le(s); y = stbi__get16le(s); w = stbi__get16le(s); h = stbi__get16le(s); if (((x + w) > (g->w)) || ((y + h) > (g->h))) return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); g->line_size = g->w * 4; g->start_x = x * 4; g->start_y = y * g->line_size; g->max_x = g->start_x + w * 4; g->max_y = g->start_y + h * g->line_size; g->cur_x = g->start_x; g->cur_y = g->start_y; // if the width of the specified rectangle is 0, that means // we may not see *any* pixels or the image is malformed; // to make sure this is caught, move the current y down to // max_y (which is what out_gif_code checks). if (w == 0) g->cur_y = g->max_y; g->lflags = stbi__get8(s); if (g->lflags & 0x40) { g->step = 8 * g->line_size; // first interlaced spacing g->parse = 3; } else { g->step = g->line_size; g->parse = 0; } if (g->lflags & 0x80) { stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { g->color_table = (stbi_uc *) g->pal; } else return stbi__errpuc("missing color table", "Corrupt GIF"); o = stbi__process_gif_raster(s, g); if (!o) return NULL; // if this was the first frame, pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } return o; } case 0x21: // Comment Extension. { int len; int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. // unset old transparent if (g->transparent >= 0) { g->pal[g->transparent][3] = 255; } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { g->pal[g->transparent][3] = 0; } } else { // don't need transparent stbi__skip(s, 1); g->transparent = -1; } } else { stbi__skip(s, len); break; } } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } break; } case 0x3B: // gif stream termination code return (stbi_uc *) s; // using '1' causes warning on some compilers default: return stbi__errpuc("unknown code", "Corrupt GIF"); } } } static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) { STBI_FREE(g->out); STBI_FREE(g->history); STBI_FREE(g->background); if (out) STBI_FREE(out); if (delays && *delays) STBI_FREE(*delays); return stbi__errpuc("outofmem", "Out of memory"); } static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; stbi_uc *two_back = 0; stbi__gif g; int stride; int out_size = 0; int delays_size = 0; STBI_NOTUSED(out_size); STBI_NOTUSED(delays_size); memset(&g, 0, sizeof(g)); if (delays) { *delays = 0; } do { u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; ++layers; stride = g.w * g.h * 4; if (out) { void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); if (!tmp) return stbi__load_gif_main_outofmem(&g, out, delays); else { out = (stbi_uc*) tmp; out_size = layers * stride; } if (delays) { int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); if (!new_delays) return stbi__load_gif_main_outofmem(&g, out, delays); *delays = new_delays; delays_size = layers * sizeof(int); } } else { out = (stbi_uc*)stbi__malloc( layers * stride ); if (!out) return stbi__load_gif_main_outofmem(&g, out, delays); out_size = layers * stride; if (delays) { *delays = (int*) stbi__malloc( layers * sizeof(int) ); if (!*delays) return stbi__load_gif_main_outofmem(&g, out, delays); delays_size = layers * sizeof(int); } } memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { two_back = out - 2 * stride; } if (delays) { (*delays)[layers - 1U] = g.delay; } } } while (u != 0); // free temp buffer; STBI_FREE(g.out); STBI_FREE(g.history); STBI_FREE(g.background); // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); *z = layers; return out; } else { return stbi__errpuc("not GIF", "Image was not as a gif type."); } } static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); STBI_NOTUSED(ri); u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; // moved conversion to after successful load so that the same // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } else if (g.out) { // if there was an error and we allocated an image buffer, free it! STBI_FREE(g.out); } // free buffers needed for multiple frame loading; STBI_FREE(g.history); STBI_FREE(g.background); return u; } static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) { return stbi__gif_info_raw(s,x,y,comp); } #endif // ************************************************************************************************* // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR static int stbi__hdr_test_core(stbi__context *s, const char *signature) { int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) return 0; stbi__rewind(s); return 1; } static int stbi__hdr_test(stbi__context* s) { int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); if(!r) { r = stbi__hdr_test_core(s, "#?RGBE\n"); stbi__rewind(s); } return r; } #define STBI__HDR_BUFLEN 1024 static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) { int len=0; char c = '\0'; c = (char) stbi__get8(z); while (!stbi__at_eof(z) && c != '\n') { buffer[len++] = c; if (len == STBI__HDR_BUFLEN-1) { // flush to end of line while (!stbi__at_eof(z) && stbi__get8(z) != '\n') ; break; } c = (char) stbi__get8(z); } buffer[len] = 0; return buffer; } static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) { if ( input[3] != 0 ) { float f1; // Exponent f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); if (req_comp <= 2) output[0] = (input[0] + input[1] + input[2]) * f1 / 3; else { output[0] = input[0] * f1; output[1] = input[1] * f1; output[2] = input[2] * f1; } if (req_comp == 2) output[1] = 1; if (req_comp == 4) output[3] = 1; } else { switch (req_comp) { case 4: output[3] = 1; /* fallthrough */ case 3: output[0] = output[1] = output[2] = 0; break; case 2: output[1] = 1; /* fallthrough */ case 1: output[0] = 0; break; } } } static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; int width, height; stbi_uc *scanline; float *hdr_data; int len; unsigned char count, value; int i, j, k, c1,c2, z; const char *headerToken; STBI_NOTUSED(ri); // Check identifier headerToken = stbi__hdr_gettoken(s,buffer); if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header for(;;) { token = stbi__hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); // Parse width and height // can't use sscanf() if we're not using stdio! token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); token += 3; height = (int) strtol(token, &token, 10); while (*token == ' ') ++token; if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); token += 3; width = (int) strtol(token, NULL, 10); if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); *x = width; *y = height; if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) return stbi__errpf("too large", "HDR image is too large"); // Read data hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); if (!hdr_data) return stbi__errpf("outofmem", "Out of memory"); // Load image data // image data is stored as some number of sca if ( width < 8 || width >= 32768) { // Read flat data for (j=0; j < height; ++j) { for (i=0; i < width; ++i) { stbi_uc rgbe[4]; main_decode_loop: stbi__getn(s, rgbe, 4); stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); } } } else { // Read RLE-encoded data scanline = NULL; for (j = 0; j < height; ++j) { c1 = stbi__get8(s); c2 = stbi__get8(s); len = stbi__get8(s); if (c1 != 2 || c2 != 2 || (len & 0x80)) { // not run-length encoded, so we have to actually use THIS data as a decoded // pixel (note this can't be a valid pixel--one of RGB must be >= 128) stbi_uc rgbe[4]; rgbe[0] = (stbi_uc) c1; rgbe[1] = (stbi_uc) c2; rgbe[2] = (stbi_uc) len; rgbe[3] = (stbi_uc) stbi__get8(s); stbi__hdr_convert(hdr_data, rgbe, req_comp); i = 1; j = 0; STBI_FREE(scanline); goto main_decode_loop; // yes, this makes no sense } len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } if (scanline == NULL) { scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); if (!scanline) { STBI_FREE(hdr_data); return stbi__errpf("outofmem", "Out of memory"); } } for (k = 0; k < 4; ++k) { int nleft; i = 0; while ((nleft = width - i) > 0) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } } } for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } if (scanline) STBI_FREE(scanline); } return hdr_data; } static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) { char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; int dummy; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; if (stbi__hdr_test(s) == 0) { stbi__rewind( s ); return 0; } for(;;) { token = stbi__hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } if (!valid) { stbi__rewind( s ); return 0; } token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) { stbi__rewind( s ); return 0; } token += 3; *y = (int) strtol(token, &token, 10); while (*token == ' ') ++token; if (strncmp(token, "+X ", 3)) { stbi__rewind( s ); return 0; } token += 3; *x = (int) strtol(token, NULL, 10); *comp = 3; return 1; } #endif // STBI_NO_HDR #ifndef STBI_NO_BMP static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) { void *p; stbi__bmp_data info; info.all_a = 255; p = stbi__bmp_parse_header(s, &info); if (p == NULL) { stbi__rewind( s ); return 0; } if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) { if (info.bpp == 24 && info.ma == 0xff000000) *comp = 3; else *comp = info.ma ? 4 : 3; } return 1; } #endif #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { int channelCount, dummy, depth; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 1) { stbi__rewind( s ); return 0; } stbi__skip(s, 6); channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) { stbi__rewind( s ); return 0; } *y = stbi__get32be(s); *x = stbi__get32be(s); depth = stbi__get16be(s); if (depth != 8 && depth != 16) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 3) { stbi__rewind( s ); return 0; } *comp = 4; return 1; } static int stbi__psd_is16(stbi__context *s) { int channelCount, depth; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 1) { stbi__rewind( s ); return 0; } stbi__skip(s, 6); channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) { stbi__rewind( s ); return 0; } STBI_NOTUSED(stbi__get32be(s)); STBI_NOTUSED(stbi__get32be(s)); depth = stbi__get16be(s); if (depth != 16) { stbi__rewind( s ); return 0; } return 1; } #endif #ifndef STBI_NO_PIC static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { int act_comp=0,num_packets=0,chained,dummy; stbi__pic_packet packets[10]; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { stbi__rewind(s); return 0; } stbi__skip(s, 88); *x = stbi__get16be(s); *y = stbi__get16be(s); if (stbi__at_eof(s)) { stbi__rewind( s); return 0; } if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { stbi__rewind( s ); return 0; } stbi__skip(s, 8); do { stbi__pic_packet *packet; if (num_packets==sizeof(packets)/sizeof(packets[0])) return 0; packet = &packets[num_packets++]; chained = stbi__get8(s); packet->size = stbi__get8(s); packet->type = stbi__get8(s); packet->channel = stbi__get8(s); act_comp |= packet->channel; if (stbi__at_eof(s)) { stbi__rewind( s ); return 0; } if (packet->size != 8) { stbi__rewind( s ); return 0; } } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); return 1; } #endif // ************************************************************************************************* // Portable Gray Map and Portable Pixel Map loader // by Ken Miller // // PGM: http://netpbm.sourceforge.net/doc/pgm.html // PPM: http://netpbm.sourceforge.net/doc/ppm.html // // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s) { char p, t; p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { stbi__rewind( s ); return 0; } return 1; } static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; STBI_NOTUSED(ri); ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); if (ri->bits_per_channel == 0) return 0; if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) return stbi__errpuc("too large", "PNM too large"); out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { STBI_FREE(out); return stbi__errpuc("bad PNM", "PNM file truncated"); } if (req_comp && req_comp != s->img_n) { if (ri->bits_per_channel == 16) { out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); } else { out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); } if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; } static int stbi__pnm_isspace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; } static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) { for (;;) { while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) *c = (char) stbi__get8(s); if (stbi__at_eof(s) || *c != '#') break; while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) *c = (char) stbi__get8(s); } } static int stbi__pnm_isdigit(char c) { return c >= '0' && c <= '9'; } static int stbi__pnm_getinteger(stbi__context *s, char *c) { int value = 0; while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); if((value > 214748364) || (value == 214748364 && *c > '7')) return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); } return value; } static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { int maxv, dummy; char c, p, t; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; stbi__rewind(s); // Get identifier p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { stbi__rewind(s); return 0; } *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm c = (char) stbi__get8(s); stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width if(*x == 0) return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height if (*y == 0) return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value if (maxv > 65535) return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); else if (maxv > 255) return 16; else return 8; } static int stbi__pnm_is16(stbi__context *s) { if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) return 1; return 0; } #endif static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { #ifndef STBI_NO_JPEG if (stbi__jpeg_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PNG if (stbi__png_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_GIF if (stbi__gif_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_BMP if (stbi__bmp_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PSD if (stbi__psd_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PIC if (stbi__pic_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PNM if (stbi__pnm_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_HDR if (stbi__hdr_info(s, x, y, comp)) return 1; #endif // test tga last because it's a crappy test! #ifndef STBI_NO_TGA if (stbi__tga_info(s, x, y, comp)) return 1; #endif return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } static int stbi__is_16_main(stbi__context *s) { #ifndef STBI_NO_PNG if (stbi__png_is16(s)) return 1; #endif #ifndef STBI_NO_PSD if (stbi__psd_is16(s)) return 1; #endif #ifndef STBI_NO_PNM if (stbi__pnm_is16(s)) return 1; #endif return 0; } #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { FILE *f = stbi__fopen(filename, "rb"); int result; if (!f) return stbi__err("can't fopen", "Unable to open file"); result = stbi_info_from_file(f, x, y, comp); fclose(f); return result; } STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) { int r; stbi__context s; long pos = ftell(f); stbi__start_file(&s, f); r = stbi__info_main(&s,x,y,comp); fseek(f,pos,SEEK_SET); return r; } STBIDEF int stbi_is_16_bit(char const *filename) { FILE *f = stbi__fopen(filename, "rb"); int result; if (!f) return stbi__err("can't fopen", "Unable to open file"); result = stbi_is_16_bit_from_file(f); fclose(f); return result; } STBIDEF int stbi_is_16_bit_from_file(FILE *f) { int r; stbi__context s; long pos = ftell(f); stbi__start_file(&s, f); r = stbi__is_16_main(&s); fseek(f,pos,SEEK_SET); return r; } #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__info_main(&s,x,y,comp); } STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); return stbi__info_main(&s,x,y,comp); } STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__is_16_main(&s); } STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); return stbi__is_16_main(&s); } #endif // STB_IMAGE_IMPLEMENTATION /* revision history: 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug 1-bit BMP *_is_16_bit api avoid warnings 2.16 (2017-07-23) all functions have 16-bit variants; STBI_NO_STDIO works again; compilation fixes; fix rounding in unpremultiply; optimize vertical flip; disable raw_len validation; documentation fixes 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; warning fixes; disable run-time SSE detection on gcc; uniform handling of optional "return" values; thread-safe initialization of zlib tables 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) allocate large structures on the stack remove white matting for transparent PSD fix reported channel count for PNG & BMP re-enable SSE2 in non-gcc 64-bit support RGB-formatted JPEG read 16-bit PNGs (only as 8-bit) 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED 2.09 (2016-01-16) allow comments in PNM files 16-bit-per-pixel TGA (not bit-per-component) info() for TGA could break due to .hdr handling info() for BMP to shares code instead of sloppy parse can use STBI_REALLOC_SIZED if allocator doesn't support realloc code cleanup 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA 2.07 (2015-09-13) fix compiler warnings partial animated GIF support limited 16-bpc PSD support #ifdef unused functions bug with < 92 byte PIC,PNM,HDR,TGA 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit 2.03 (2015-04-12) extra corruption checking (mmozeiko) stbi_set_flip_vertically_on_load (nguillemot) fix NEON support; fix mingw support 2.02 (2015-01-19) fix incorrect assert, fix warning 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) progressive JPEG (stb) PGM/PPM support (Ken Miller) STBI_MALLOC,STBI_REALLOC,STBI_FREE GIF bugfix -- seemingly never worked STBI_NO_*, STBI_ONLY_* 1.48 (2014-12-14) fix incorrectly-named assert() 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) optimize PNG (ryg) fix bug in interlaced PNG with user-specified channel count (stb) 1.46 (2014-08-26) fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG 1.45 (2014-08-16) fix MSVC-ARM internal compiler error by wrapping malloc 1.44 (2014-08-07) various warning fixes from Ronny Chevalier 1.43 (2014-07-15) fix MSVC-only compiler problem in code changed in 1.42 1.42 (2014-07-09) don't define _CRT_SECURE_NO_WARNINGS (affects user code) fixes to stbi__cleanup_jpeg path added STBI_ASSERT to avoid requiring assert.h 1.41 (2014-06-25) fix search&replace from 1.36 that messed up comments/error messages 1.40 (2014-06-22) fix gcc struct-initialization warning 1.39 (2014-06-15) fix to TGA optimization when req_comp != number of components in TGA; fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) add support for BMP version 5 (more ignored fields) 1.38 (2014-06-06) suppress MSVC warnings on integer casts truncating values fix accidental rename of 'skip' field of I/O 1.37 (2014-06-04) remove duplicate typedef 1.36 (2014-06-03) convert to header file single-file library if de-iphone isn't set, load iphone images color-swapped instead of returning NULL 1.35 (2014-05-27) various warnings fix broken STBI_SIMD path fix bug where stbi_load_from_file no longer left file pointer in correct place fix broken non-easy path for 32-bit BMP (possibly never used) TGA optimization by Arseny Kapoulkine 1.34 (unknown) use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case 1.33 (2011-07-14) make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements 1.32 (2011-07-13) support for "info" function for all supported filetypes (SpartanJ) 1.31 (2011-06-20) a few more leak fixes, bug in PNG handling (SpartanJ) 1.30 (2011-06-11) added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) removed deprecated format-specific test/load functions removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) fix inefficiency in decoding 32-bit BMP (David Woo) 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) 1.27 (2010-08-01) cast-to-stbi_uc to fix warnings 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ 1.25 (2010-07-17) refix trans_data warning (Won Chun) 1.24 (2010-07-12) perf improvements reading from files on platforms with lock-heavy fgetc() minor perf improvements for jpeg deprecated type-specific functions so we'll get feedback if they're needed attempt to fix trans_data warning (Won Chun) 1.23 fixed bug in iPhone support 1.22 (2010-07-10) removed image *writing* support stbi_info support from Jetro Lauha GIF support from Jean-Marc Lienher iPhone PNG-extensions from James Brown warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) 1.21 fix use of 'stbi_uc' in header (reported by jon blow) 1.20 added support for Softimage PIC, by Tom Seddon 1.19 bug in interlaced PNG corruption check (found by ryg) 1.18 (2008-08-02) fix a threading bug (local mutable static) 1.17 support interlaced PNG 1.16 major bugfix - stbi__convert_format converted one too many pixels 1.15 initialize some fields for thread safety 1.14 fix threadsafe conversion bug header-file-only version (#define STBI_HEADER_FILE_ONLY before including) 1.13 threadsafe 1.12 const qualifiers in the API 1.11 Support installable IDCT, colorspace conversion routines 1.10 Fixes for 64-bit (don't use "unsigned long") optimized upsampling by Fabian "ryg" Giesen 1.09 Fix format-conversion for PSD code (bad global variables!) 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz 1.07 attempt to fix C++ warning/errors again 1.06 attempt to fix C++ warning/errors again 1.05 fix TGA loading to return correct *comp and use good luminance calc 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR 1.02 support for (subset of) HDR files, float interface for preferred access to them 1.01 fix bug: possible bug in handling right-side up bmps... not sure fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all 1.00 interface to zlib that skips zlib header 0.99 correct handling of alpha in palette 0.98 TGA loader by lonesock; dynamically add loaders (untested) 0.97 jpeg errors on too large a file; also catch another malloc failure 0.96 fix detection of invalid v value - particleman@mollyrocket forum 0.95 during header scan, seek to markers in case of padding 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same 0.93 handle jpegtran output; verbose errors 0.92 read 4,8,16,24,32-bit BMP files of several formats 0.91 output 24-bit Windows 3.0 BMP files 0.90 fix a few more warnings; bump version number to approach 1.0 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd 0.60 fix compiling as c++ 0.59 fix warnings: merge Dave Moore's -Wall fixes 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available 0.56 fix bug: zlib uncompressed mode len vs. nlen 0.55 fix bug: restart_interval not initialized to 0 0.54 allow NULL for 'int *comp' 0.53 fix bug in png 3->4; speedup png decoding 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments 0.51 obey req_comp requests, 1-component jpegs return as 1-component, on 'test' only check type, not whether we support this variant 0.50 (2006-11-19) first released version */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ================================================ FILE: lib/third_party/imgui/backend/source/imgui_impl_glfw.cpp ================================================ // dear imgui: Platform Backend for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (Requires: GLFW 3.0+. Prefer GLFW 3.3+/3.4+ for full feature support.) // Implemented features: // [X] Platform: Clipboard support. // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors) with GLFW 3.1+. Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // [X] Multiple Dear ImGui contexts support. // Missing features or Issues: // [ ] Platform: Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. // [ ] Platform: Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. // [ ] Platform: Multi-viewport: Missing ImGuiBackendFlags_HasParentViewport support. The viewport->ParentViewportID field is ignored, and therefore io.ConfigViewportsNoDefaultParent has no effect either. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq // - Getting Started https://dearimgui.com/getting-started // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). // - Introduction, links and more at the top of imgui.cpp // About Emscripten support: // - Emscripten provides its own GLFW (3.2.1) implementation (syntax: "-sUSE_GLFW=3"), but Joystick is broken and several features are not supported (multiple windows, clipboard, timer, etc.) // - A third-party Emscripten GLFW (3.4.0) implementation (syntax: "--use-port=contrib.glfw3") fixes the Joystick issue and implements all relevant features for the browser. // See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Comparison.md for details. // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2026-02-10: Try to set IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND automatically if corresponding headers are not accessible. (#9225) // 2026-01-25: [Docking] Improve workarounds for cases where GLFW is unable to provide any reliable monitor info. Preserve existing monitor list when none of the new one is valid. (#9195, #7902, #5683) // 2026-01-18: [Docking] Dynamically load X11 functions to avoid -lx11 linking requirement introduced on 2025-09-10. // 2025-12-12: Added IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND to forcefully disable either. // 2025-12-10: Avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. Lowers overhead for very high framerates (e.g. 10k+ FPS). // 2025-11-06: Lower minimum requirement to GLFW 3.0. Though a recent version e.g GLFW 3.4 is highly recommended. // 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. // 2025-09-15: Content Scales are always reported as 1.0 on Wayland. FramebufferScale are always reported as 1.0 on X11. (#8920, #8921) // 2025-09-10: [Docking] Improve multi-viewport behavior in tiling WMs on X11 via the ImGui_ImplGlfw_SetWindowFloating() function. Note: using GLFW backend on Linux/BSD etc. requires linking with -lX11. (#8884, #8474, #8289) // 2025-07-08: Made ImGui_ImplGlfw_GetContentScaleForWindow(), ImGui_ImplGlfw_GetContentScaleForMonitor() helpers return 1.0f on Emscripten and Android platforms, matching macOS logic. (#8742, #8733) // 2025-06-18: Added support for multiple Dear ImGui contexts. (#8676, #8239, #8069) // 2025-06-11: Added ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) and ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) helper to facilitate making DPI-aware apps. // 2025-05-15: [Docking] Add Platform_GetWindowFramebufferScale() handler, to allow varying Retina display density on multiple monitors. // 2025-04-26: [Docking] Disable multi-viewports under Wayland. (#8587) // 2025-03-10: Map GLFW_KEY_WORLD_1 and GLFW_KEY_WORLD_2 into ImGuiKey_Oem102. // 2025-03-03: Fixed clipboard handler assertion when using GLFW <= 3.2.1 compiled with asserts enabled. // 2025-02-21: [Docking] Update monitors and work areas information every frame, as the later may change regardless of monitor changes. (#8415) // 2024-11-05: [Docking] Added Linux workaround for spurious mouse up events emitted while dragging and creating new viewport. (#3158, #7733, #7922) // 2024-08-22: Moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn // - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn // 2024-07-31: Added ImGui_ImplGlfw_Sleep() helper function for usage by our examples app, since GLFW doesn't provide one. // 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. // 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) // 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491) // 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702) // 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034) // 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240) // 2023-02-03: Emscripten: Registering custom low-level mouse wheel handler to get more accurate scrolling impulses on Emscripten. (#4019, #6096) // 2023-01-18: Handle unsupported glfwGetVideoMode() call on e.g. Emscripten. // 2023-01-04: Inputs: Fixed mods state on Linux when using Alt-GR text input (e.g. German keyboard layout), could lead to broken text input. Revert a 2022/01/17 change were we resumed using mods provided by GLFW, turns out they were faulty. // 2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908) // 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). // 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position *EDIT* Reverted 2023-07-18. // 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX. // 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11. // 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend. // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). // 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. // 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). // 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. // 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. // 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). // 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. // 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. // 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. // 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. // 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. #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_impl_glfw.h" // Clang warnings with -Weverything #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness #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 is. #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #endif #if defined(__has_include) #if !__has_include() || !__has_include() #define IMGUI_IMPL_GLFW_DISABLE_X11 #endif #if !__has_include() #define IMGUI_IMPL_GLFW_DISABLE_WAYLAND #endif #endif // GLFW #if !defined(IMGUI_IMPL_GLFW_DISABLE_X11) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)) #define GLFW_HAS_X11 1 #else #define GLFW_HAS_X11 0 #endif #if !defined(IMGUI_IMPL_GLFW_DISABLE_WAYLAND) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)) #define GLFW_HAS_WAYLAND 1 #else #define GLFW_HAS_WAYLAND 0 #endif #include #ifdef _WIN32 #undef APIENTRY #ifndef GLFW_EXPOSE_NATIVE_WIN32 // for glfwGetWin32Window() #define GLFW_EXPOSE_NATIVE_WIN32 #endif #include #elif defined(__APPLE__) #ifndef GLFW_EXPOSE_NATIVE_COCOA // for glfwGetCocoaWindow() #define GLFW_EXPOSE_NATIVE_COCOA #endif #include #elif GLFW_HAS_X11 #ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.) #define GLFW_EXPOSE_NATIVE_X11 #include #include // for dlopen() #endif #include #undef Status // X11 headers are leaking this. #endif #ifndef _WIN32 #include // for usleep() // IMHEX PATCH BEGIN #include // for std::string_view #include // for std::getenv() // IMHEX PATCH END #endif #include // for snprintf() #ifdef __EMSCRIPTEN__ #include #include // IMHEX PATCH BEGIN #include static std::string clipboardContent; // IMHEX PATCH END #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 #include #else #define EMSCRIPTEN_USE_EMBEDDED_GLFW3 #endif #endif // We gather version tests as define in order to easily see which features are version-dependent. #define GLFW_VERSION_COMBINED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION) #define GLFW_HAS_CREATECURSOR (GLFW_VERSION_COMBINED >= 3100) // 3.1+ glfwCreateCursor() #define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_COMBINED >= 3200) // 3.2+ GLFW_FLOATING #define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_HOVERED #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwSetWindowOpacity #define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorContentScale #if defined(__EMSCRIPTEN__) || defined(__SWITCH__) // no Vulkan support in GLFW for Emscripten or homebrew Nintendo Switch #define GLFW_HAS_VULKAN (0) #else #define GLFW_HAS_VULKAN (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwCreateWindowSurface #endif #define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwFocusWindow #define GLFW_HAS_FOCUS_ON_SHOW (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW #define GLFW_HAS_MONITOR_WORK_AREA (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorWorkarea #define GLFW_HAS_OSX_WINDOW_POS_FIX (GLFW_VERSION_COMBINED >= 3301) // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553 #ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? #define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR #else #define GLFW_HAS_NEW_CURSORS (0) #endif #ifdef GLFW_MOUSE_PASSTHROUGH // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2020-07-17 (passthrough) #define GLFW_HAS_MOUSE_PASSTHROUGH (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_MOUSE_PASSTHROUGH #else #define GLFW_HAS_MOUSE_PASSTHROUGH (0) #endif #define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api #define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() #define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError() #define GLFW_HAS_GETPLATFORM (GLFW_VERSION_COMBINED >= 3400) // 3.4+ glfwGetPlatform() // Map GLFWWindow* to ImGuiContext*. // - Would be simpler if we could use glfwSetWindowUserPointer()/glfwGetWindowUserPointer(), but this is a single and shared resource. // - Would be simpler if we could use e.g. std::map<> as well. But we don't. // - This is not particularly optimized as we expect size to be small and queries to be rare. struct ImGui_ImplGlfw_WindowToContext { GLFWwindow* Window; ImGuiContext* Context; }; static ImVector g_ContextMap; static void ImGui_ImplGlfw_ContextMap_Add(GLFWwindow* window, ImGuiContext* ctx) { g_ContextMap.push_back(ImGui_ImplGlfw_WindowToContext{ window, ctx }); } static void ImGui_ImplGlfw_ContextMap_Remove(GLFWwindow* window) { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) { g_ContextMap.erase_unsorted(&entry); if (g_ContextMap.empty()) g_ContextMap.clear(); return; } } static ImGuiContext* ImGui_ImplGlfw_ContextMap_Get(GLFWwindow* window) { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) return entry.Context; return nullptr; } enum GlfwClientApi { GlfwClientApi_OpenGL, GlfwClientApi_Vulkan, GlfwClientApi_Unknown, // Anything else fits here. }; #if GLFW_HAS_X11 typedef Atom (*PFN_XInternAtom)(Display*, const char* ,Bool); typedef int (*PFN_XChangeProperty)(Display*, Window, Atom, Atom, int, int, const unsigned char*, int); typedef int (*PFN_XChangeWindowAttributes)(Display*, Window, unsigned long, XSetWindowAttributes*); typedef int (*PFN_XFlush)(Display*); #endif // GLFW data struct ImGui_ImplGlfw_Data { ImGuiContext* Context; GLFWwindow* Window; GlfwClientApi ClientApi; double Time; GLFWwindow* MouseWindow; #if GLFW_HAS_CREATECURSOR GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; GLFWcursor* LastMouseCursor; #endif bool MouseIgnoreButtonUpWaitForFocusLoss; bool MouseIgnoreButtonUp; ImVec2 LastValidMousePos; GLFWwindow* KeyOwnerWindows[GLFW_KEY_LAST]; bool IsWayland; bool InstalledCallbacks; bool CallbacksChainForAllWindows; char BackendPlatformName[32]; #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const char* CanvasSelector; #endif // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. GLFWwindowfocusfun PrevUserCallbackWindowFocus; GLFWcursorposfun PrevUserCallbackCursorPos; GLFWcursorenterfun PrevUserCallbackCursorEnter; GLFWmousebuttonfun PrevUserCallbackMousebutton; GLFWscrollfun PrevUserCallbackScroll; GLFWkeyfun PrevUserCallbackKey; GLFWcharfun PrevUserCallbackChar; GLFWmonitorfun PrevUserCallbackMonitor; #ifdef _WIN32 WNDPROC PrevWndProc; #endif #if GLFW_HAS_X11 // Module and function pointers loaded at initialization to avoid linking statically with X11. void* X11Module; PFN_XInternAtom XInternAtom; PFN_XChangeProperty XChangeProperty; PFN_XChangeWindowAttributes XChangeWindowAttributes; PFN_XFlush XFlush; #endif ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); } }; // Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts // It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. // FIXME: multi-context support is not well tested and probably dysfunctional in this backend. // - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks // (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. // - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. // FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. namespace ImGui { extern ImGuiIO& GetIO(ImGuiContext*); } static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() { // Get data for current context return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(GLFWwindow* window) { // Get data for a given GLFW window, regardless of current context (since GLFW events are sent together) ImGuiContext* ctx = ImGui_ImplGlfw_ContextMap_Get(window); return (ImGui_ImplGlfw_Data*)ImGui::GetIO(ctx).BackendPlatformUserData; } // Forward Declarations static void ImGui_ImplGlfw_UpdateMonitors(); static void ImGui_ImplGlfw_InitMultiViewportSupport(); static void ImGui_ImplGlfw_ShutdownMultiViewportSupport(); // Functions static bool ImGui_ImplGlfw_IsWayland() { #if !GLFW_HAS_WAYLAND return false; #elif GLFW_HAS_GETPLATFORM return glfwGetPlatform() == GLFW_PLATFORM_WAYLAND; #else const char* version = glfwGetVersionString(); if (strstr(version, "Wayland") == nullptr) // e.g. Ubuntu 22.04 ships with GLFW 3.3.6 compiled without Wayland return false; #ifdef GLFW_EXPOSE_NATIVE_X11 if (glfwGetX11Display() != nullptr) return false; #endif return true; #endif } // Not static to allow third-party code to use that if they want to (but undocumented) ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode); ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode) { IM_UNUSED(scancode); switch (keycode) { case GLFW_KEY_TAB: return ImGuiKey_Tab; case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; case GLFW_KEY_UP: return ImGuiKey_UpArrow; case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; case GLFW_KEY_HOME: return ImGuiKey_Home; case GLFW_KEY_END: return ImGuiKey_End; case GLFW_KEY_INSERT: return ImGuiKey_Insert; case GLFW_KEY_DELETE: return ImGuiKey_Delete; case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; case GLFW_KEY_SPACE: return ImGuiKey_Space; case GLFW_KEY_ENTER: return ImGuiKey_Enter; case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; case GLFW_KEY_COMMA: return ImGuiKey_Comma; case GLFW_KEY_MINUS: return ImGuiKey_Minus; case GLFW_KEY_PERIOD: return ImGuiKey_Period; case GLFW_KEY_SLASH: return ImGuiKey_Slash; case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; case GLFW_KEY_EQUAL: return ImGuiKey_Equal; case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; case GLFW_KEY_WORLD_1: return ImGuiKey_Oem102; case GLFW_KEY_WORLD_2: return ImGuiKey_Oem102; case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; case GLFW_KEY_PAUSE: return ImGuiKey_Pause; case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; case GLFW_KEY_MENU: return ImGuiKey_Menu; case GLFW_KEY_0: return ImGuiKey_0; case GLFW_KEY_1: return ImGuiKey_1; case GLFW_KEY_2: return ImGuiKey_2; case GLFW_KEY_3: return ImGuiKey_3; case GLFW_KEY_4: return ImGuiKey_4; case GLFW_KEY_5: return ImGuiKey_5; case GLFW_KEY_6: return ImGuiKey_6; case GLFW_KEY_7: return ImGuiKey_7; case GLFW_KEY_8: return ImGuiKey_8; case GLFW_KEY_9: return ImGuiKey_9; case GLFW_KEY_A: return ImGuiKey_A; case GLFW_KEY_B: return ImGuiKey_B; case GLFW_KEY_C: return ImGuiKey_C; case GLFW_KEY_D: return ImGuiKey_D; case GLFW_KEY_E: return ImGuiKey_E; case GLFW_KEY_F: return ImGuiKey_F; case GLFW_KEY_G: return ImGuiKey_G; case GLFW_KEY_H: return ImGuiKey_H; case GLFW_KEY_I: return ImGuiKey_I; case GLFW_KEY_J: return ImGuiKey_J; case GLFW_KEY_K: return ImGuiKey_K; case GLFW_KEY_L: return ImGuiKey_L; case GLFW_KEY_M: return ImGuiKey_M; case GLFW_KEY_N: return ImGuiKey_N; case GLFW_KEY_O: return ImGuiKey_O; case GLFW_KEY_P: return ImGuiKey_P; case GLFW_KEY_Q: return ImGuiKey_Q; case GLFW_KEY_R: return ImGuiKey_R; case GLFW_KEY_S: return ImGuiKey_S; case GLFW_KEY_T: return ImGuiKey_T; case GLFW_KEY_U: return ImGuiKey_U; case GLFW_KEY_V: return ImGuiKey_V; case GLFW_KEY_W: return ImGuiKey_W; case GLFW_KEY_X: return ImGuiKey_X; case GLFW_KEY_Y: return ImGuiKey_Y; case GLFW_KEY_Z: return ImGuiKey_Z; case GLFW_KEY_F1: return ImGuiKey_F1; case GLFW_KEY_F2: return ImGuiKey_F2; case GLFW_KEY_F3: return ImGuiKey_F3; case GLFW_KEY_F4: return ImGuiKey_F4; case GLFW_KEY_F5: return ImGuiKey_F5; case GLFW_KEY_F6: return ImGuiKey_F6; case GLFW_KEY_F7: return ImGuiKey_F7; case GLFW_KEY_F8: return ImGuiKey_F8; case GLFW_KEY_F9: return ImGuiKey_F9; case GLFW_KEY_F10: return ImGuiKey_F10; case GLFW_KEY_F11: return ImGuiKey_F11; case GLFW_KEY_F12: return ImGuiKey_F12; case GLFW_KEY_F13: return ImGuiKey_F13; case GLFW_KEY_F14: return ImGuiKey_F14; case GLFW_KEY_F15: return ImGuiKey_F15; case GLFW_KEY_F16: return ImGuiKey_F16; case GLFW_KEY_F17: return ImGuiKey_F17; case GLFW_KEY_F18: return ImGuiKey_F18; case GLFW_KEY_F19: return ImGuiKey_F19; case GLFW_KEY_F20: return ImGuiKey_F20; case GLFW_KEY_F21: return ImGuiKey_F21; case GLFW_KEY_F22: return ImGuiKey_F22; case GLFW_KEY_F23: return ImGuiKey_F23; case GLFW_KEY_F24: return ImGuiKey_F24; default: return ImGuiKey_None; } } // X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW // See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630 static void ImGui_ImplGlfw_UpdateKeyModifiers(ImGuiIO& io, GLFWwindow* window, int mods) { // IMHEX PATCH BEGIN #ifdef __linux__ static bool isX11 = [] { const auto sessionType = std::getenv("XDG_SESSION_TYPE"); if (sessionType == nullptr) return false; return std::string_view(sessionType) == "x11"; }(); if (isX11) { io.AddKeyEvent(ImGuiMod_Ctrl, (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); io.AddKeyEvent(ImGuiMod_Shift, (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); io.AddKeyEvent(ImGuiMod_Alt, (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); io.AddKeyEvent(ImGuiMod_Super, (glfwGetKey(window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); } else #endif { io.AddKeyEvent(ImGuiMod_Ctrl, (mods & GLFW_MOD_CONTROL) != 0); io.AddKeyEvent(ImGuiMod_Shift, (mods & GLFW_MOD_SHIFT) != 0); io.AddKeyEvent(ImGuiMod_Alt, (mods & GLFW_MOD_ALT) != 0); io.AddKeyEvent(ImGuiMod_Super, (mods & GLFW_MOD_SUPER) != 0); } // IMHEX PATCH END } static bool ImGui_ImplGlfw_ShouldChainCallback(ImGui_ImplGlfw_Data* bd, GLFWwindow* window) { return bd->CallbacksChainForAllWindows ? true : (window == bd->Window); } void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackMousebutton(window, button, action, mods); // Workaround for Linux: ignore mouse up events which are following an focus loss following a viewport creation if (bd->MouseIgnoreButtonUp && action == GLFW_RELEASE) return; ImGuiIO& io = ImGui::GetIO(bd->Context); // IMHEX PATCH BEGIN ImGui_ImplGlfw_UpdateKeyModifiers(io, window, mods); // IMHEX PATCH END if (button >= 0 && button < ImGuiMouseButton_COUNT) io.AddMouseButtonEvent(button, action == GLFW_PRESS); } void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackScroll(window, xoffset, yoffset); #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 // Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback(). return; #endif ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddMouseWheelEvent((float)xoffset, (float)yoffset); } // FIXME: should this be baked into ImGui_ImplGlfw_KeyToImGuiKey()? then what about the values passed to io.SetKeyEventNativeData()? static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) { #if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) // See https://github.com/glfw/glfw/issues/1502 for details. // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). // This won't cover edge cases but this is at least going to cover common cases. if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) return key; GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); const char* key_name = glfwGetKeyName(key, scancode); glfwSetErrorCallback(prev_error_callback); #if GLFW_HAS_GETERROR && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // Eat errors (see #5908) (void)glfwGetError(nullptr); #endif if (key_name && key_name[0] != 0 && key_name[1] == 0) { const char char_names[] = "`-=[]\\,;\'./"; const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; IM_ASSERT(IM_COUNTOF(char_names) == IM_COUNTOF(char_keys)); if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); } else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } } // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); #else IM_UNUSED(scancode); #endif return key; } void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); if (action != GLFW_PRESS && action != GLFW_RELEASE) return; ImGuiIO& io = ImGui::GetIO(bd->Context); // IMHEX PATCH BEGIN ImGui_ImplGlfw_UpdateKeyModifiers(io, window, mods); // IMHEX PATCH END if (keycode >= 0 && keycode < IM_COUNTOF(bd->KeyOwnerWindows)) bd->KeyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : nullptr; keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode); io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) } void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackWindowFocus(window, focused); // Workaround for Linux: when losing focus with MouseIgnoreButtonUpWaitForFocusLoss set, we will temporarily ignore subsequent Mouse Up events bd->MouseIgnoreButtonUp = (bd->MouseIgnoreButtonUpWaitForFocusLoss && focused == 0); bd->MouseIgnoreButtonUpWaitForFocusLoss = false; ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddFocusEvent(focused != 0); } void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackCursorPos(window, x, y); ImGuiIO& io = ImGui::GetIO(bd->Context); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { int window_x, window_y; glfwGetWindowPos(window, &window_x, &window_y); x += window_x; y += window_y; } io.AddMousePosEvent((float)x, (float)y); bd->LastValidMousePos = ImVec2((float)x, (float)y); } // Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, // so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackCursorEnter(window, entered); ImGuiIO& io = ImGui::GetIO(bd->Context); if (entered) { bd->MouseWindow = window; io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y); } else if (!entered && bd->MouseWindow == window) { bd->LastValidMousePos = io.MousePos; bd->MouseWindow = nullptr; io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } } void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackChar(window, c); ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddInputCharacter(c); } void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) { // This function is technically part of the API even if we stopped using the callback, so leaving it around. } #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void* user_data) { // Mimic Emscripten_HandleWheel() in SDL. // Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096 ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; float multiplier = 0.0f; if (ev->deltaMode == DOM_DELTA_PIXEL) { multiplier = 1.0f / 100.0f; } // 100 pixels make up a step. else if (ev->deltaMode == DOM_DELTA_LINE) { multiplier = 1.0f / 3.0f; } // 3 lines make up a step. else if (ev->deltaMode == DOM_DELTA_PAGE) { multiplier = 80.0f; } // A page makes up 80 steps. float wheel_x = ev->deltaX * -multiplier; float wheel_y = ev->deltaY * -multiplier; ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddMouseWheelEvent(wheel_x, wheel_y); //IMGUI_DEBUG_LOG("[Emsc] mode %d dx: %.2f, dy: %.2f, dz: %.2f --> feed %.2f %.2f\n", (int)ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ, wheel_x, wheel_y); return EM_TRUE; } #endif #ifdef _WIN32 static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); #endif void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); IM_ASSERT(bd->Window == window); bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); bd->InstalledCallbacks = true; } void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); IM_ASSERT(bd->Window == window); glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus); glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter); glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos); glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton); glfwSetScrollCallback(window, bd->PrevUserCallbackScroll); glfwSetKeyCallback(window, bd->PrevUserCallbackKey); glfwSetCharCallback(window, bd->PrevUserCallbackChar); glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); bd->InstalledCallbacks = false; bd->PrevUserCallbackWindowFocus = nullptr; bd->PrevUserCallbackCursorEnter = nullptr; bd->PrevUserCallbackCursorPos = nullptr; bd->PrevUserCallbackMousebutton = nullptr; bd->PrevUserCallbackScroll = nullptr; bd->PrevUserCallbackKey = nullptr; bd->PrevUserCallbackChar = nullptr; bd->PrevUserCallbackMonitor = nullptr; } // Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user). // This is 'false' by default meaning we only chain callbacks for the main viewport. // We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback. // If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter. void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); bd->CallbacksChainForAllWindows = chain_for_all_windows; } #ifdef __EMSCRIPTEN__ #if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 void ImGui_ImplGlfw_EmscriptenOpenURL(const char* url) { if (url) emscripten::glfw3::OpenURL(url); } #else EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (const char* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); }); #endif #endif static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { ImGuiIO& io = ImGui::GetIO(); IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); //printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED); // Setup backend capabilities flags ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)(); snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_glfw (%d)", GLFW_VERSION_COMBINED); io.BackendPlatformUserData = (void*)bd; io.BackendPlatformName = bd->BackendPlatformName; #if GLFW_HAS_CREATECURSOR io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) #endif io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) bool has_viewports = false; #ifndef __EMSCRIPTEN__ has_viewports = true; #if GLFW_HAS_GETPLATFORM if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) has_viewports = false; #endif if (has_viewports) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #endif #if GLFW_HAS_MOUSE_PASSTHROUGH || GLFW_HAS_WINDOW_HOVERED io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) #endif bd->Context = ImGui::GetCurrentContext(); bd->Window = window; bd->Time = 0.0; bd->IsWayland = ImGui_ImplGlfw_IsWayland(); ImGui_ImplGlfw_ContextMap_Add(window, bd->Context); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); #if GLFW_VERSION_COMBINED < 3300 platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window, text); }; platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window); }; #else platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(nullptr, text); }; platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(nullptr); }; #endif #ifdef __EMSCRIPTEN__ platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; #endif // IMHEX PATCH BEGIN #ifdef __EMSCRIPTEN__ // Callback to handle clipboard paste from browser emscripten_browser_clipboard::paste([](std::string const &paste_data, void *callback_data [[maybe_unused]]) { clipboardContent = std::move(paste_data); }); #endif // IMHEX PATCH END // Create mouse cursors // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. // Missing cursors will return nullptr and our _UpdateMouseCursor() function will use the Arrow cursor instead.) #if GLFW_HAS_CREATECURSOR GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); #if GLFW_HAS_NEW_CURSORS bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); #else bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); #endif glfwSetErrorCallback(prev_error_callback); #endif #if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) (void)glfwGetError(nullptr); #endif // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. if (install_callbacks) ImGui_ImplGlfw_InstallCallbacks(window); // Update monitor a first time during init // (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784) ImGui_ImplGlfw_UpdateMonitors(); glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); // Set platform dependent data in viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)bd->Window; #ifdef _WIN32 main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window); #elif defined(__APPLE__) main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window); #else IM_UNUSED(main_viewport); #endif if (has_viewports) ImGui_ImplGlfw_InitMultiViewportSupport(); // Windows: register a WndProc hook so we can intercept some messages. #ifdef _WIN32 HWND hwnd = (HWND)main_viewport->PlatformHandleRaw; ::SetPropA(hwnd, "IMGUI_BACKEND_DATA", bd); bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC); IM_ASSERT(bd->PrevWndProc != nullptr); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); #endif #if GLFW_HAS_X11 if (!bd->IsWayland) { // Load X11 module dynamically. Copied from the way that GLFW does it in x11_init.c #if defined(__CYGWIN__) const char* x11_module_path = "libX11-6.so"; #elif defined(__OpenBSD__) || defined(__NetBSD__) const char* x11_module_path = "libX11.so"; #else const char* x11_module_path = "libX11.so.6"; #endif bd->X11Module = dlopen(x11_module_path, RTLD_LAZY | RTLD_LOCAL); bd->XInternAtom = (PFN_XInternAtom)dlsym(bd->X11Module, "XInternAtom"); bd->XChangeProperty = (PFN_XChangeProperty)dlsym(bd->X11Module, "XChangeProperty"); bd->XChangeWindowAttributes = (PFN_XChangeWindowAttributes)dlsym(bd->X11Module, "XChangeWindowAttributes"); bd->XFlush = (PFN_XFlush)dlsym(bd->X11Module, "XFlush"); IM_ASSERT(bd->XInternAtom != nullptr && bd->XChangeProperty != nullptr && bd->XChangeWindowAttributes != nullptr && bd->XFlush != nullptr); } #endif // Emscripten: the same application can run on various platforms, so we detect the Apple platform at runtime // to override io.ConfigMacOSXBehaviors from its default (which is always false in Emscripten). #ifdef __EMSCRIPTEN__ #if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 if (emscripten::glfw3::IsRuntimePlatformApple()) { io.ConfigMacOSXBehaviors = true; // Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used. // This means that Meta + V only registers a single key-press, even if the keys are held. // This is a compromise for dealing with this issue in ImGui since ImGui implements key repeat itself. // See https://github.com/pongasoft/emscripten-glfw/blob/v3.4.0.20240817/docs/Usage.md#the-problem-of-the-super-key emscripten::glfw3::SetSuperPlusKeyTimeouts(10, 10); } #endif #endif bd->ClientApi = client_api; return true; } bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) { return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); } bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) { return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); } bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) { return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); } void ImGui_ImplGlfw_Shutdown() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplGlfw_ShutdownMultiViewportSupport(); if (bd->InstalledCallbacks) ImGui_ImplGlfw_RestoreCallbacks(bd->Window); #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 if (bd->CanvasSelector) emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr); #endif #if GLFW_HAS_CREATECURSOR for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) glfwDestroyCursor(bd->MouseCursors[cursor_n]); #endif // Windows: restore our WndProc hook #ifdef _WIN32 ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ::SetPropA((HWND)main_viewport->PlatformHandleRaw, "IMGUI_BACKEND_DATA", nullptr); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc); bd->PrevWndProc = nullptr; #endif #if GLFW_HAS_X11 if (bd->X11Module != nullptr) dlclose(bd->X11Module); #endif io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport); platform_io.ClearPlatformHandlers(); ImGui_ImplGlfw_ContextMap_Remove(bd->Window); IM_DELETE(bd); } static void ImGui_ImplGlfw_UpdateMouseData() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGuiID mouse_viewport_id = 0; const ImVec2 mouse_pos_prev = io.MousePos; for (int n = 0; n < platform_io.Viewports.Size; n++) { ImGuiViewport* viewport = platform_io.Viewports[n]; GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const bool is_window_focused = true; #else const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; #endif if (is_window_focused) { // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions. if (io.WantSetMousePos) glfwSetCursorPos(window, (double)(mouse_pos_prev.x - viewport->Pos.x), (double)(mouse_pos_prev.y - viewport->Pos.y)); // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) if (bd->MouseWindow == nullptr) { double mouse_x, mouse_y; glfwGetCursorPos(window, &mouse_x, &mouse_y); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) int window_x, window_y; glfwGetWindowPos(window, &window_x, &window_y); mouse_x += window_x; mouse_y += window_y; } bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } } // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag (since we implement hit via our WndProc hook) // On other platforms we rely on the library fallbacking to its own search when reporting a viewport with _NoInputs flag. // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported // by the backend, and use its flawed heuristic to guess the viewport behind. // - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). // FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. #if GLFW_HAS_MOUSE_PASSTHROUGH const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0; glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input); #endif #if GLFW_HAS_MOUSE_PASSTHROUGH || GLFW_HAS_WINDOW_HOVERED if (glfwGetWindowAttrib(window, GLFW_HOVERED)) mouse_viewport_id = viewport->ID; #else // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. #endif } if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) io.AddMouseViewportEvent(mouse_viewport_id); } static void ImGui_ImplGlfw_UpdateMouseCursor() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) return; ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); for (int n = 0; n < platform_io.Viewports.Size; n++) { GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle; if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { if (bd->LastMouseCursor != nullptr) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); bd->LastMouseCursor = nullptr; } } else { // Show OS mouse cursor // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. // IMHEX PATCH BEGIN #if GLFW_HAS_CREATECURSOR && !defined(_WIN32) // IMHEX PATCH END GLFWcursor* cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]; if (bd->LastMouseCursor != cursor) { glfwSetCursor(window, cursor); bd->LastMouseCursor = cursor; } #endif glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } } } // Update gamepad inputs static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } static void ImGui_ImplGlfw_UpdateGamepads() { ImGuiIO& io = ImGui::GetIO(); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs, but see #8075 return; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; #if GLFW_HAS_GAMEPAD_API && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) GLFWgamepadstate gamepad; if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) return; #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0) #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) #else 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); if (axes_count == 0 || buttons_count == 0) return; #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0) #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) #endif io.BackendFlags |= ImGuiBackendFlags_HasGamepad; MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); #undef MAP_BUTTON #undef MAP_ANALOG } static void ImGui_ImplGlfw_UpdateMonitors() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); int monitors_count = 0; GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); bool updated_monitors = false; for (int n = 0; n < monitors_count; n++) { ImGuiPlatformMonitor monitor; int x, y; glfwGetMonitorPos(glfw_monitors[n], &x, &y); const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); if (vid_mode == nullptr) continue; // Failed to get Video mode (e.g. Emscripten does not support this function) if (vid_mode->width <= 0 || vid_mode->height <= 0) continue; // Failed to query suitable monitor info (#9195) monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); #if GLFW_HAS_MONITOR_WORK_AREA int w, h; glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h); if (w > 0 && h > 0) // Workaround a small GLFW issue reporting zero on monitor changes: https://github.com/glfw/glfw/pull/1761 { monitor.WorkPos = ImVec2((float)x, (float)y); monitor.WorkSize = ImVec2((float)w, (float)h); } #endif float scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfw_monitors[n]); if (scale == 0.0f) continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0 (#7902) monitor.DpiScale = scale; monitor.PlatformHandle = (void*)glfw_monitors[n]; // [...] GLFW doc states: "guaranteed to be valid only until the monitor configuration changes" // Preserve existing monitor list until a valid one is added. // Happens on macOS sleeping (#5683) and seemingly occasionally on Windows (#9195) if (!updated_monitors) platform_io.Monitors.resize(0); updated_monitors = true; platform_io.Monitors.push_back(monitor); } } // - On Windows the process needs to be marked DPI-aware!! SDL2 doesn't do it by default. You can call ::SetProcessDPIAware() or call ImGui_ImplWin32_EnableDpiAwareness() from Win32 backend. // - Apple platforms use FramebufferScale so we always return 1.0f. // - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle. float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) { #if GLFW_HAS_WAYLAND if (ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window)) if (bd->IsWayland) return 1.0f; #endif #if GLFW_HAS_PER_MONITOR_DPI && !(defined(__APPLE__) || defined(__EMSCRIPTEN__) || defined(__ANDROID__)) float x_scale, y_scale; glfwGetWindowContentScale(window, &x_scale, &y_scale); return x_scale; #else IM_UNUSED(window); return 1.0f; #endif } float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) { #if GLFW_HAS_WAYLAND if (ImGui_ImplGlfw_IsWayland()) // We can't access our bd->IsWayland cache for a monitor. return 1.0f; #endif #if GLFW_HAS_PER_MONITOR_DPI && !(defined(__APPLE__) || defined(__EMSCRIPTEN__) || defined(__ANDROID__)) float x_scale, y_scale; glfwGetMonitorContentScale(monitor, &x_scale, &y_scale); return x_scale; #else IM_UNUSED(monitor); return 1.0f; #endif } static void ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(GLFWwindow* window, ImVec2* out_size, ImVec2* out_framebuffer_scale) { int w, h; int display_w, display_h; glfwGetWindowSize(window, &w, &h); glfwGetFramebufferSize(window, &display_w, &display_h); float fb_scale_x = (w > 0) ? (float)display_w / (float)w : 1.0f; float fb_scale_y = (h > 0) ? (float)display_h / (float)h : 1.0f; #if GLFW_HAS_WAYLAND ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (!bd->IsWayland) fb_scale_x = fb_scale_y = 1.0f; #endif if (out_size != nullptr) *out_size = ImVec2((float)w, (float)h); if (out_framebuffer_scale != nullptr) *out_framebuffer_scale = ImVec2(fb_scale_x, fb_scale_y); } void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?"); // Setup main viewport size (every frame to accommodate for window resizing) ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale); ImGui_ImplGlfw_UpdateMonitors(); // Setup time step // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) double current_time = glfwGetTime(); if (current_time <= bd->Time) current_time = bd->Time + 0.00001f; io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; bd->MouseIgnoreButtonUp = false; ImGui_ImplGlfw_UpdateMouseData(); ImGui_ImplGlfw_UpdateMouseCursor(); // Update game controllers (if enabled and available) ImGui_ImplGlfw_UpdateGamepads(); } // GLFW doesn't provide a portable sleep function void ImGui_ImplGlfw_Sleep(int milliseconds) { #ifdef _WIN32 ::Sleep(milliseconds); #else usleep(milliseconds * 1000); #endif } #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) { ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; double canvas_width, canvas_height; emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); return true; } static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, const EmscriptenFullscreenChangeEvent* event, void* user_data) { ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; double canvas_width, canvas_height; emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); return true; } // 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. // STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_selector) { IM_ASSERT(canvas_selector != nullptr); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?"); bd->CanvasSelector = canvas_selector; emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange); emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, bd, false, ImGui_ImplEmscripten_FullscreenChangeCallback); // Change the size of the GLFW window according to the size of the canvas ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd); // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. // FIXME: May break chaining in case user registered their own Emscripten callback? emscripten_set_wheel_callback(bd->CanvasSelector, bd, false, ImGui_ImplEmscripten_WheelCallback); } #elif defined(EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3) // When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call // by invoking emscripten_glfw_make_canvas_resizable afterward. // See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Usage.md#how-to-make-the-canvas-resizable-by-the-user for an explanation void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector) { GLFWwindow* w = (GLFWwindow*)(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector)); IM_ASSERT(window == w); // Sanity check IM_UNUSED(w); emscripten_glfw_make_canvas_resizable(window, "window", nullptr); } #endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 //-------------------------------------------------------------------------------------------------------- // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT // This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- // Helper structure we store in the void* PlatformUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGui_ImplGlfw_ViewportData { GLFWwindow* Window; // Stored in ImGuiViewport::PlatformHandle bool WindowOwned; int IgnoreWindowPosEventFrame; int IgnoreWindowSizeEventFrame; #ifdef _WIN32 WNDPROC PrevWndProc; #endif ImGui_ImplGlfw_ViewportData() { memset((void*)this, 0, sizeof(*this)); IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } ~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == nullptr); } }; static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) viewport->PlatformRequestClose = true; } // GLFW may dispatch window pos/size events after calling glfwSetWindowPos()/glfwSetWindowSize(). // However: depending on the platform the callback may be invoked at different time: // - on Windows it appears to be called within the glfwSetWindowPos()/glfwSetWindowSize() call // - on Linux it is queued and invoked during glfwPollEvents() // Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only // ignore recent glfwSetWindowXXX() calls. static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) { if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowPosEventFrame + 1); //data->IgnoreWindowPosEventFrame = -1; if (ignore_event) return; } viewport->PlatformRequestMove = true; } } static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) { if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowSizeEventFrame + 1); //data->IgnoreWindowSizeEventFrame = -1; if (ignore_event) return; } viewport->PlatformRequestResize = true; } } #if !defined(__APPLE__) && !defined(_WIN32) && !defined(__EMSCRIPTEN__) && GLFW_HAS_GETPLATFORM #define IMGUI_GLFW_HAS_SETWINDOWFLOATING static void ImGui_ImplGlfw_SetWindowFloating(ImGui_ImplGlfw_Data* bd, GLFWwindow* window) { #ifdef GLFW_EXPOSE_NATIVE_X11 if (glfwGetPlatform() == GLFW_PLATFORM_X11) { Display* display = glfwGetX11Display(); Window xwindow = glfwGetX11Window(window); Atom wm_type = bd->XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); Atom wm_type_dialog = bd->XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); bd->XChangeProperty(display, xwindow, wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&wm_type_dialog, 1); XSetWindowAttributes attrs; attrs.override_redirect = False; bd->XChangeWindowAttributes(display, xwindow, CWOverrideRedirect, &attrs); bd->XFlush(display); } #endif // GLFW_EXPOSE_NATIVE_X11 #ifdef GLFW_EXPOSE_NATIVE_WAYLAND // FIXME: Help needed, see #8884, #8474 for discussions about this. #endif // GLFW_EXPOSE_NATIVE_X11 } #endif // IMGUI_GLFW_HAS_SETWINDOWFLOATING static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); viewport->PlatformUserData = vd; // Workaround for Linux: ignore mouse up events corresponding to losing focus of the previously focused window (#7733, #3158, #7922) #ifdef __linux__ bd->MouseIgnoreButtonUpWaitForFocusLoss = true; #endif // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem glfwWindowHint(GLFW_VISIBLE, false); glfwWindowHint(GLFW_FOCUSED, false); #if GLFW_HAS_FOCUS_ON_SHOW glfwWindowHint(GLFW_FOCUS_ON_SHOW, false); #endif glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); #if GLFW_HAS_WINDOW_TOPMOST glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false); #endif GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : nullptr; vd->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", nullptr, share_window); vd->WindowOwned = true; ImGui_ImplGlfw_ContextMap_Add(vd->Window, bd->Context); viewport->PlatformHandle = (void*)vd->Window; #ifdef IMGUI_GLFW_HAS_SETWINDOWFLOATING ImGui_ImplGlfw_SetWindowFloating(bd, vd->Window); #endif #ifdef _WIN32 viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window); ::SetPropA((HWND)viewport->PlatformHandleRaw, "IMGUI_BACKEND_DATA", bd); // IMHEX PATCH BEGIN // REASON: The patch with #include // #elif defined(__APPLE__) // viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(vd->Window); // IMHEX PATCH END #endif glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); // Install GLFW callbacks for secondary viewports glfwSetWindowFocusCallback(vd->Window, ImGui_ImplGlfw_WindowFocusCallback); glfwSetCursorEnterCallback(vd->Window, ImGui_ImplGlfw_CursorEnterCallback); glfwSetCursorPosCallback(vd->Window, ImGui_ImplGlfw_CursorPosCallback); glfwSetMouseButtonCallback(vd->Window, ImGui_ImplGlfw_MouseButtonCallback); glfwSetScrollCallback(vd->Window, ImGui_ImplGlfw_ScrollCallback); glfwSetKeyCallback(vd->Window, ImGui_ImplGlfw_KeyCallback); glfwSetCharCallback(vd->Window, ImGui_ImplGlfw_CharCallback); glfwSetWindowCloseCallback(vd->Window, ImGui_ImplGlfw_WindowCloseCallback); glfwSetWindowPosCallback(vd->Window, ImGui_ImplGlfw_WindowPosCallback); glfwSetWindowSizeCallback(vd->Window, ImGui_ImplGlfw_WindowSizeCallback); if (bd->ClientApi == GlfwClientApi_OpenGL) { glfwMakeContextCurrent(vd->Window); glfwSwapInterval(0); } } static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { if (vd->WindowOwned) { #if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) HWND hwnd = (HWND)viewport->PlatformHandleRaw; ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); #endif // Release any keys that were pressed in the window being destroyed and are still held down, // because we will not receive any release events after window is destroyed. for (int i = 0; i < IM_COUNTOF(bd->KeyOwnerWindows); i++) if (bd->KeyOwnerWindows[i] == vd->Window) ImGui_ImplGlfw_KeyCallback(vd->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called. ImGui_ImplGlfw_ContextMap_Remove(vd->Window); glfwDestroyWindow(vd->Window); } vd->Window = nullptr; IM_DELETE(vd); } viewport->PlatformUserData = viewport->PlatformHandle = nullptr; } static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; #if defined(_WIN32) // GLFW hack: Hide icon from task bar HWND hwnd = (HWND)viewport->PlatformHandleRaw; if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); ex_style &= ~WS_EX_APPWINDOW; ex_style |= WS_EX_TOOLWINDOW; ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); } // GLFW hack: install WndProc for mouse source event and WM_NCHITTEST message handler. ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); vd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC); ::SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); #if !GLFW_HAS_FOCUS_ON_SHOW // GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window. // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3 via a GLFW_FOCUS_ON_SHOW window attribute. // See https://github.com/glfw/glfw/issues/1189 // FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile. if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) { ::ShowWindow(hwnd, SW_SHOWNA); return; } #endif #endif glfwShowWindow(vd->Window); } static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; int x = 0, y = 0; glfwGetWindowPos(vd->Window, &x, &y); return ImVec2((float)x, (float)y); } static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; vd->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); glfwSetWindowPos(vd->Window, (int)pos.x, (int)pos.y); } static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; int w = 0, h = 0; glfwGetWindowSize(vd->Window, &w, &h); return ImVec2((float)w, (float)h); } static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; #if defined(__APPLE__) && !GLFW_HAS_OSX_WINDOW_POS_FIX // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based // on the upper-left corner. int x, y, width, height; glfwGetWindowPos(vd->Window, &x, &y); glfwGetWindowSize(vd->Window, &width, &height); glfwSetWindowPos(vd->Window, x, y - height + size.y); #endif vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y); } static ImVec2 ImGui_ImplGlfw_GetWindowFramebufferScale(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; ImVec2 framebuffer_scale; ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(vd->Window, nullptr, &framebuffer_scale); return framebuffer_scale; } static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; glfwSetWindowTitle(vd->Window, title); } static void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport) { #if GLFW_HAS_FOCUS_WINDOW ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; glfwFocusWindow(vd->Window); #else // FIXME: What are the effect of not having this function? At the moment imgui doesn't actually call SetWindowFocus - we set that up ahead, will answer that question later. (void)viewport; #endif } static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; return glfwGetWindowAttrib(vd->Window, GLFW_FOCUSED) != 0; } static bool ImGui_ImplGlfw_GetWindowMinimized(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; return glfwGetWindowAttrib(vd->Window, GLFW_ICONIFIED) != 0; } #if GLFW_HAS_WINDOW_ALPHA static void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; glfwSetWindowOpacity(vd->Window, alpha); } #endif static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; if (bd->ClientApi == GlfwClientApi_OpenGL) glfwMakeContextCurrent(vd->Window); } static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; if (bd->ClientApi == GlfwClientApi_OpenGL) { glfwMakeContextCurrent(vd->Window); glfwSwapBuffers(vd->Window); } } //-------------------------------------------------------------------------------------------------------- // Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) //-------------------------------------------------------------------------------------------------------- // Avoid including so we can build without it #if GLFW_HAS_VULKAN #ifndef VULKAN_H_ #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; #if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; #else #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; #endif VK_DEFINE_HANDLE(VkInstance) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) struct VkAllocationCallbacks; enum VkResult { VK_RESULT_MAX_ENUM = 0x7FFFFFFF }; #endif // VULKAN_H_ extern "C" { extern GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); } static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; IM_UNUSED(bd); IM_ASSERT(bd->ClientApi == GlfwClientApi_Vulkan); VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, vd->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); return (int)err; } #endif // GLFW_HAS_VULKAN static void ImGui_ImplGlfw_InitMultiViewportSupport() { // Register platform interface (will be coupled with a renderer interface) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_CreateWindow = ImGui_ImplGlfw_CreateWindow; platform_io.Platform_DestroyWindow = ImGui_ImplGlfw_DestroyWindow; platform_io.Platform_ShowWindow = ImGui_ImplGlfw_ShowWindow; platform_io.Platform_SetWindowPos = ImGui_ImplGlfw_SetWindowPos; platform_io.Platform_GetWindowPos = ImGui_ImplGlfw_GetWindowPos; platform_io.Platform_SetWindowSize = ImGui_ImplGlfw_SetWindowSize; platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize; platform_io.Platform_GetWindowFramebufferScale = ImGui_ImplGlfw_GetWindowFramebufferScale; platform_io.Platform_SetWindowFocus = ImGui_ImplGlfw_SetWindowFocus; platform_io.Platform_GetWindowFocus = ImGui_ImplGlfw_GetWindowFocus; platform_io.Platform_GetWindowMinimized = ImGui_ImplGlfw_GetWindowMinimized; platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow; platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers; #if GLFW_HAS_WINDOW_ALPHA platform_io.Platform_SetWindowAlpha = ImGui_ImplGlfw_SetWindowAlpha; #endif #if GLFW_HAS_VULKAN platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; #endif // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); vd->Window = bd->Window; vd->WindowOwned = false; main_viewport->PlatformUserData = vd; main_viewport->PlatformHandle = (void*)bd->Window; } static void ImGui_ImplGlfw_ShutdownMultiViewportSupport() { ImGui::DestroyPlatformWindows(); } //----------------------------------------------------------------------------- // WndProc hook (declared here because we will need access to ImGui_ImplGlfw_ViewportData) #ifdef _WIN32 static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() { LPARAM extra_info = ::GetMessageExtraInfo(); if ((extra_info & 0xFFFFFF80) == 0xFF515700) return ImGuiMouseSource_Pen; if ((extra_info & 0xFFFFFF80) == 0xFF515780) return ImGuiMouseSource_TouchScreen; return ImGuiMouseSource_Mouse; } static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)::GetPropA(hWnd, "IMGUI_BACKEND_DATA"); ImGuiIO& io = ImGui::GetIO(bd->Context); WNDPROC prev_wndproc = bd->PrevWndProc; ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT"); if (viewport != NULL) if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) prev_wndproc = vd->PrevWndProc; switch (msg) { // GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen. // Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently. case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP: io.AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); break; // We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs". // In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!) #if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED case WM_NCHITTEST: { // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. if (viewport && (viewport->Flags & ImGuiViewportFlags_NoInputs)) return HTTRANSPARENT; break; } #endif default: break; } return ::CallWindowProcW(prev_wndproc, hWnd, msg, wParam, lParam); } #endif // #ifdef _WIN32 //----------------------------------------------------------------------------- #if defined(__clang__) #pragma clang diagnostic pop #endif #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/backend/source/imgui_impl_opengl3.cpp ================================================ // dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline // - Desktop GL: 2.x 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 Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). // [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // About WebGL/ES: // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. // - This is done automatically on iOS, Android and Emscripten targets. // - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq // - Getting Started https://dearimgui.com/getting-started // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). // - Introduction, links and more at the top of imgui.cpp // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-07-22: OpenGL: Add and call embedded loader shutdown during ImGui_ImplOpenGL3_Shutdown() to facilitate multiple init/shutdown cycles in same process. (#8792) // 2025-07-15: OpenGL: Set GL_UNPACK_ALIGNMENT to 1 before updating textures (#8802) + restore non-WebGL/ES update path that doesn't require a CPU-side copy. // 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL3_CreateFontsTexture() and ImGui_ImplOpenGL3_DestroyFontsTexture(). // 2025-06-04: OpenGL: Made GLES 3.20 contexts not access GL_CONTEXT_PROFILE_MASK nor GL_PRIMITIVE_RESTART. (#8664) // 2025-02-18: OpenGL: Lazily reinitialize embedded GL loader for when calling backend from e.g. other DLL boundaries. (#8406) // 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) // 2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562) // 2024-04-16: OpenGL: Detect ES3 contexts on desktop based on version string, to e.g. avoid calling glPolygonMode() on them. (#7447) // 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink. // 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983) // 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445) // 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333) // 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375) // 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333) // 2023-03-23: OpenGL: Properly restoring "no shader program bound" if it was the case prior to running the rendering function. (#6267, #6220, #6224) // 2023-03-15: OpenGL: Fixed GL loader crash when GL_VERSION returns nullptr. (#6154, #4445, #3530) // 2023-03-06: OpenGL: Fixed restoration of a potentially deleted OpenGL program, by calling glIsProgram(). (#6220, #6224) // 2022-11-09: OpenGL: Reverted use of glBufferSubData(), too many corruptions issues + old issues seemingly can't be reproed with Intel drivers nowadays (revert 2021-12-15 and 2022-05-23 changes). // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-09-27: OpenGL: Added ability to '#define IMGUI_IMPL_OPENGL_DEBUG'. // 2022-05-23: OpenGL: Reworking 2021-12-15 "Using buffer orphaning" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127). // 2022-05-13: OpenGL: Fixed state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states. // 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers. // 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions. // 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state. // 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader. // 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version. // 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater. // 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer. // 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state. // 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state. // 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x) // 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre-3.3 context which have the defines set by a loader. // 2020-07-10: OpenGL: Added support for glad2 OpenGL loader. // 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX. // 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix. // 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset. // 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader. // 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader. // 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders. // 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility. // 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call. // 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. // 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop. // 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early. // 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. // 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link. // 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. // 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. // 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. // 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a nullptr pointer. // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". // 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. // 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. // 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. // 2017-05-01: OpenGL: Fixed save and restore of current blend func state. // 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. // 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. // 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) //---------------------------------------- // OpenGL GLSL GLSL // version version string //---------------------------------------- // 2.0 110 "#version 110" // 2.1 120 "#version 120" // 3.0 130 "#version 130" // 3.1 140 "#version 140" // 3.2 150 "#version 150" // 3.3 330 "#version 330 core" // 4.0 400 "#version 400 core" // 4.1 410 "#version 410 core" // 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) #define _CRT_SECURE_NO_WARNINGS #endif // IMHEX PATCH BEGIN #if defined(WIN32) #include #include #endif // IMHEX PATCH END #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_impl_opengl3.h" #include #include // intptr_t #if defined(__APPLE__) #include #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: ignore unknown flags #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness #pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used #pragma clang diagnostic ignored "-Wnonportable-system-include-path" #pragma clang diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) #endif #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 #endif // GL includes #if defined(IMGUI_IMPL_OPENGL_ES2) #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) #include // Use GL ES 2 #else #include // Use GL ES 2 #endif #if defined(__EMSCRIPTEN__) #ifndef GL_GLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES #endif #include #endif #elif defined(IMGUI_IMPL_OPENGL_ES3) #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) #include // Use GL ES 3 #else #include // Use GL ES 3 #endif #elif !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) // 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 using our own minimal custom loader based on gl3w. // In the rest of your app/engine, you can use another loader of your choice (gl3w, glew, glad, glbinding, glext, glLoadGen, etc.). // If you happen to be developing a new feature for this backend (imgui_impl_opengl3.cpp): // - You may need to regenerate imgui_impl_opengl3_loader.h to add new symbols. See https://github.com/dearimgui/gl3w_stripped // Typically you would run: python3 ./gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt // - You can temporarily use an unstripped version. See https://github.com/dearimgui/gl3w_stripped/releases // Changes to this backend using new APIs should be accompanied by a regenerated stripped loader version. #define IMGL3W_IMPL #define IMGUI_IMPL_OPENGL_LOADER_IMGL3W #include "imgui_impl_opengl3_loader.h" #endif // Vertex arrays are not supported on ES2/WebGL1 unless Emscripten which uses an extension #ifndef IMGUI_IMPL_OPENGL_ES2 #define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #elif defined(__EMSCRIPTEN__) #define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #define glBindVertexArray glBindVertexArrayOES #define glGenVertexArrays glGenVertexArraysOES #define glDeleteVertexArrays glDeleteVertexArraysOES #define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES #endif // Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have.. // A desktop ES context can technically compile fine with our loader, so we also perform a runtime checks #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) #define IMGUI_IMPL_OPENGL_HAS_EXTENSIONS // has glGetIntegerv(GL_NUM_EXTENSIONS) #define IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE // may have glPolygonMode() #endif // Desktop GL 2.1+ and GL ES 3.0+ have glBindBuffer() with GL_PIXEL_UNPACK_BUFFER target. #if !defined(IMGUI_IMPL_OPENGL_ES2) #define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK #endif // Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1) #define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART #endif // Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have. #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2) #define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET #endif // Desktop GL 3.3+ and GL ES 3.0+ have glBindSampler() #if !defined(IMGUI_IMPL_OPENGL_ES2) && (defined(IMGUI_IMPL_OPENGL_ES3) || defined(GL_VERSION_3_3)) #define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER #endif // [Debugging] //#define IMGUI_IMPL_OPENGL_DEBUG #ifdef IMGUI_IMPL_OPENGL_DEBUG #include #define GL_CALL(_CALL) do { _CALL; GLenum gl_err = glGetError(); if (gl_err != 0) fprintf(stderr, "GL error 0x%x returned from '%s'.\n", gl_err, #_CALL); } while (0) // Call with error check #else #define GL_CALL(_CALL) _CALL // Call without error check #endif // OpenGL Data struct ImGui_ImplOpenGL3_Data { GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2) char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings. bool GlProfileIsES2; bool GlProfileIsES3; bool GlProfileIsCompat; GLint GlProfileMask; GLint MaxTextureSize; GLuint ShaderHandle; GLint AttribLocationTex; // Uniforms location // IMHEX PATCH BEGIN GLint AttribLocationGammaCorrected; // IMHEX PATCH END GLint AttribLocationProjMtx; GLuint AttribLocationVtxPos; // Vertex attributes location GLuint AttribLocationVtxUV; GLuint AttribLocationVtxColor; unsigned int VboHandle, ElementsHandle; GLsizeiptr VertexBufferSize; GLsizeiptr IndexBufferSize; bool HasPolygonMode; bool HasBindSampler; bool HasClipOrigin; bool UseBufferSubData; ImVector TempBuffer; ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); } }; // Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts // It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } // Forward Declarations static void ImGui_ImplOpenGL3_InitMultiViewportSupport(); static void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport(); // OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only) #ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY struct ImGui_ImplOpenGL3_VtxAttribState { GLint Enabled, Size, Type, Normalized, Stride; GLvoid* Ptr; void GetState(GLint index) { glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &Enabled); glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_SIZE, &Size); glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_TYPE, &Type); glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &Normalized); glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &Stride); glGetVertexAttribPointerv(index, GL_VERTEX_ATTRIB_ARRAY_POINTER, &Ptr); } void SetState(GLint index) { glVertexAttribPointer(index, Size, Type, (GLboolean)Normalized, Stride, Ptr); if (Enabled) glEnableVertexAttribArray(index); else glDisableVertexAttribArray(index); } }; #endif // Not static to allow third-party code to use that if they want to (but undocumented) bool ImGui_ImplOpenGL3_InitLoader(); bool ImGui_ImplOpenGL3_InitLoader() { // Lazily initialize our loader if not already done // (to facilitate handling multiple DLL boundaries and multiple context shutdowns we call this from all main entry points) #ifdef IMGUI_IMPL_OPENGL_LOADER_IMGL3W if (glGetIntegerv == nullptr && imgl3wInit() != 0) { fprintf(stderr, "Failed to initialize OpenGL loader!\n"); return false; } #endif return true; } static void ImGui_ImplOpenGL3_ShutdownLoader() { #ifdef IMGUI_IMPL_OPENGL_LOADER_IMGL3W imgl3wShutdown(); #endif } // Functions bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { ImGuiIO& io = ImGui::GetIO(); IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); // Initialize loader if (!ImGui_ImplOpenGL3_InitLoader()) return false; // Setup backend capabilities flags ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)(); io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_opengl3"; // Query for GL version (e.g. 320 for GL 3.2) const char* gl_version_str = (const char*)glGetString(GL_VERSION); #if defined(IMGUI_IMPL_OPENGL_ES2) // GLES 2 bd->GlVersion = 200; bd->GlProfileIsES2 = true; IM_UNUSED(gl_version_str); #else // Desktop or GLES 3 GLint major = 0; GLint minor = 0; glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MINOR_VERSION, &minor); if (major == 0 && minor == 0) sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "." bd->GlVersion = (GLuint)(major * 100 + minor * 10); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &bd->MaxTextureSize); #if defined(IMGUI_IMPL_OPENGL_ES3) bd->GlProfileIsES3 = true; #else if (strncmp(gl_version_str, "OpenGL ES 3", 11) == 0) bd->GlProfileIsES3 = true; #endif #if defined(GL_CONTEXT_PROFILE_MASK) if (!bd->GlProfileIsES3 && bd->GlVersion >= 320) glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask); bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; #endif bd->UseBufferSubData = false; /* // Query vendor to enable glBufferSubData kludge #ifdef _WIN32 if (const char* vendor = (const char*)glGetString(GL_VENDOR)) if (strncmp(vendor, "Intel", 5) == 0) bd->UseBufferSubData = true; #endif */ #endif #ifdef IMGUI_IMPL_OPENGL_DEBUG printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2/IsEs3 = %d/%d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET if (bd->GlVersion >= 320) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. #endif io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = (int)bd->MaxTextureSize; // 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 nullptr if unsure. if (glsl_version == nullptr) { #if defined(IMGUI_IMPL_OPENGL_ES2) glsl_version = "#version 100"; #elif defined(IMGUI_IMPL_OPENGL_ES3) glsl_version = "#version 300 es"; #elif defined(__APPLE__) glsl_version = "#version 150"; #else glsl_version = "#version 130"; #endif } IM_ASSERT((int)strlen(glsl_version) + 2 < IM_COUNTOF(bd->GlslVersionString)); strcpy(bd->GlslVersionString, glsl_version); strcat(bd->GlslVersionString, "\n"); // Make an arbitrary GL call (we don't actually need the result) // IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know! GLint current_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); // Detect extensions we support #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3); #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER bd->HasBindSampler = (bd->GlVersion >= 330 || bd->GlProfileIsES3); #endif bd->HasClipOrigin = (bd->GlVersion >= 450); #ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS GLint num_extensions = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); for (GLint i = 0; i < num_extensions; i++) { const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); if (extension != nullptr && strcmp(extension, "GL_ARB_clip_control") == 0) bd->HasClipOrigin = true; } #endif ImGui_ImplOpenGL3_InitMultiViewportSupport(); return true; } void ImGui_ImplOpenGL3_Shutdown() { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplOpenGL3_ShutdownMultiViewportSupport(); ImGui_ImplOpenGL3_DestroyDeviceObjects(); io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports); platform_io.ClearRendererHandlers(); IM_DELETE(bd); ImGui_ImplOpenGL3_ShutdownLoader(); } void ImGui_ImplOpenGL3_NewFrame() { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL3_Init()?"); ImGui_ImplOpenGL3_InitLoader(); if (!bd->ShaderHandle) if (!ImGui_ImplOpenGL3_CreateDeviceObjects()) IM_ASSERT(0 && "ImGui_ImplOpenGL3_CreateDeviceObjects() failed!"); } // IMHEX PATCH BEGIN static bool useFontShaders = false; void ImGui_ImplOpenGL3_TurnFontShadersOn(const ImDrawList *parent_list, const ImDrawCmd *cmd) { useFontShaders = true; } void ImGui_ImplOpenGL3_TurnFontShadersOff(const ImDrawList *parent_list, const ImDrawCmd *cmd) { useFontShaders = false; } // IMHEX PATCH END static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); // IMHEX PATCH BEGIN #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) if (useFontShaders) { glBlendFuncSeparate(GL_SRC1_COLOR, GL_ONE_MINUS_SRC1_COLOR,GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } else { glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } #else // IMHEX PATCH END glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // IMHEX PATCH BEGIN #endif // IMHEX PATCH END glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); glEnable(GL_SCISSOR_TEST); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART if (!bd->GlProfileIsES3 && bd->GlVersion >= 310) glDisable(GL_PRIMITIVE_RESTART); #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE if (bd->HasPolygonMode) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) #if defined(GL_CLIP_ORIGIN) bool clip_origin_lower_left = true; if (bd->HasClipOrigin) { GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin); if (current_clip_origin == GL_UPPER_LEFT) clip_origin_lower_left = false; } #endif // 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). DisplayPos is (0,0) for single viewport apps. GL_CALL(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; float T = draw_data->DisplayPos.y; float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; #if defined(GL_CLIP_ORIGIN) if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left #endif const float ortho_projection[4][4] = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, { 0.0f, 0.0f, -1.0f, 0.0f }, { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, }; glUseProgram(bd->ShaderHandle); glUniform1i(bd->AttribLocationTex, 0); glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); // IMHEX PATCH BEGIN glUniform1i(bd->AttribLocationGammaCorrected, useFontShaders); // IMHEX PATCH END #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER if (bd->HasBindSampler) glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise. #endif (void)vertex_array_object; #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glBindVertexArray(vertex_array_object); #endif // Bind vertex/index buffers and setup attributes for ImDrawVert GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle)); GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle)); GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos)); GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV)); GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor)); GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos))); GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, uv))); GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col))); } // OpenGL3 Render function. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. // This is in order to be able to run within an OpenGL engine that doesn't do so. void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) 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; ImGui_ImplOpenGL3_InitLoader(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). if (draw_data->Textures != nullptr) for (ImTextureData* tex : *draw_data->Textures) if (tex->Status != ImTextureStatus_OK) ImGui_ImplOpenGL3_UpdateTexture(tex); // Backup GL state GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); glActiveTexture(GL_TEXTURE0); GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program); GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER GLuint last_sampler; if (bd->HasBindSampler) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; } #endif GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer); #ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY // This is part of VAO on OpenGL 3.0+ and OpenGL ES 3.0+. GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_pos; last_vtx_attrib_state_pos.GetState(bd->AttribLocationVtxPos); ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_uv; last_vtx_attrib_state_uv.GetState(bd->AttribLocationVtxUV); ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_color; last_vtx_attrib_state_color.GetState(bd->AttribLocationVtxColor); #endif #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object); #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE GLint last_polygon_mode[2]; if (bd->HasPolygonMode) { glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); } #endif GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); GLboolean last_enable_blend = glIsEnabled(GL_BLEND); GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST); GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART GLboolean last_enable_primitive_restart = (!bd->GlProfileIsES3 && bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE; #endif // Setup desired GL state // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. GLuint vertex_array_object = 0; #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY GL_CALL(glGenVertexArrays(1, &vertex_array_object)); #endif ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); // 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 (const ImDrawList* draw_list : draw_data->CmdLists) { // Upload vertex/index buffers // - OpenGL drivers are in a very sorry state nowadays.... // During 2021 we attempted to switch from glBufferData() to orphaning+glBufferSubData() following reports // of leaks on Intel GPU when using multi-viewports on Windows. // - After this we kept hearing of various display corruptions issues. We started disabling on non-Intel GPU, but issues still got reported on Intel. // - We are now back to using exclusively glBufferData(). So bd->UseBufferSubData IS ALWAYS FALSE in this code. // We are keeping the old code path for a while in case people finding new issues may want to test the bd->UseBufferSubData path. // - See https://github.com/ocornut/imgui/issues/4468 and please report any corruption issues. const GLsizeiptr vtx_buffer_size = (GLsizeiptr)draw_list->VtxBuffer.Size * (int)sizeof(ImDrawVert); const GLsizeiptr idx_buffer_size = (GLsizeiptr)draw_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx); if (bd->UseBufferSubData) { if (bd->VertexBufferSize < vtx_buffer_size) { bd->VertexBufferSize = vtx_buffer_size; GL_CALL(glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, nullptr, GL_STREAM_DRAW)); } if (bd->IndexBufferSize < idx_buffer_size) { bd->IndexBufferSize = idx_buffer_size; GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, nullptr, GL_STREAM_DRAW)); } GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data)); GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data)); } else { GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data, GL_STREAM_DRAW)); GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data, GL_STREAM_DRAW)); } for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); else pcmd->UserCallback(draw_list, pcmd); } else { // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle (Y is inverted in OpenGL) GL_CALL(glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y))); // Bind texture, Draw GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID())); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET if (bd->GlVersion >= 320) GL_CALL(glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset)); else #endif GL_CALL(glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)))); } } } // Destroy the temporary VAO #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY GL_CALL(glDeleteVertexArrays(1, &vertex_array_object)); #endif // Restore modified GL state // This "glIsProgram()" check is required because if the program is "pending deletion" at the time of binding backup, it will have been deleted by now and will cause an OpenGL error. See #6220. if (last_program == 0 || glIsProgram(last_program)) glUseProgram(last_program); glBindTexture(GL_TEXTURE_2D, last_texture); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER if (bd->HasBindSampler) glBindSampler(0, last_sampler); #endif glActiveTexture(last_active_texture); #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glBindVertexArray(last_vertex_array_object); #endif glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); #ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); last_vtx_attrib_state_pos.SetState(bd->AttribLocationVtxPos); last_vtx_attrib_state_uv.SetState(bd->AttribLocationVtxUV); last_vtx_attrib_state_color.SetState(bd->AttribLocationVtxColor); #endif 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); if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST); if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART if (!bd->GlProfileIsES3 && bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); } #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE // Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons if (bd->HasPolygonMode) { if (bd->GlVersion <= 310 || bd->GlProfileIsCompat) { glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); } else { glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); } } #endif // IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE 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]); (void)bd; // Not all compilation paths use this } static void ImGui_ImplOpenGL3_DestroyTexture(ImTextureData* tex) { GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID; glDeleteTextures(1, &gl_tex_id); // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) tex->SetTexID(ImTextureID_Invalid); tex->SetStatus(ImTextureStatus_Destroyed); } void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) { // FIXME: Consider backing up and restoring if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) { #ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); #endif #ifdef GL_UNPACK_ALIGNMENT GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); #endif } if (tex->Status == ImTextureStatus_WantCreate) { // Create and upload new texture to graphics system //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); const void* pixels = tex->GetPixels(); GLuint gl_texture_id = 0; // Upload texture to graphics system // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) GLint last_texture; GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); GL_CALL(glGenTextures(1, &gl_texture_id)); GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_texture_id)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); // Store identifiers tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id); tex->SetStatus(ImTextureStatus_OK); // Restore state GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); } else if (tex->Status == ImTextureStatus_WantUpdates) { // Update selected blocks. We only ever write to textures regions which have never been used before! // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. GLint last_texture; GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID; GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_tex_id)); #if GL_UNPACK_ROW_LENGTH // Not on WebGL/ES GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width)); for (ImTextureRect& r : tex->Updates) GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y))); GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); #else // GL ES doesn't have GL_UNPACK_ROW_LENGTH, so we need to (A) copy to a contiguous buffer or (B) upload line by line. ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); for (ImTextureRect& r : tex->Updates) { const int src_pitch = r.w * tex->BytesPerPixel; bd->TempBuffer.resize(r.h * src_pitch); char* out_p = bd->TempBuffer.Data; for (int y = 0; y < r.h; y++, out_p += src_pitch) memcpy(out_p, tex->GetPixelsAt(r.x, r.y + y), src_pitch); IM_ASSERT(out_p == bd->TempBuffer.end()); GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, bd->TempBuffer.Data)); } #endif tex->SetStatus(ImTextureStatus_OK); GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); // Restore state } else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) ImGui_ImplOpenGL3_DestroyTexture(tex); } // 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) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); GLint status = 0, log_length = 0; glGetShaderiv(handle, GL_COMPILE_STATUS, &status); glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); if ((GLboolean)status == GL_FALSE) fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString); if (log_length > 1) { ImVector buf; buf.resize((int)(log_length + 1)); glGetShaderInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin()); fprintf(stderr, "%s\n", buf.begin()); } return (GLboolean)status == GL_TRUE; } // 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) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); GLint status = 0, log_length = 0; glGetProgramiv(handle, GL_LINK_STATUS, &status); glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); if ((GLboolean)status == GL_FALSE) fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString); if (log_length > 1) { ImVector buf; buf.resize((int)(log_length + 1)); glGetProgramInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin()); fprintf(stderr, "%s\n", buf.begin()); } return (GLboolean)status == GL_TRUE; } bool ImGui_ImplOpenGL3_CreateDeviceObjects() { ImGui_ImplOpenGL3_InitLoader(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); // Backup GL state GLint last_texture, last_array_buffer; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK GLint last_pixel_unpack_buffer = 0; if (bd->GlVersion >= 210) { glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_pixel_unpack_buffer); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } #endif #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); #endif // Parse GLSL version string int glsl_version = 130; sscanf(bd->GlslVersionString, "#version %d", &glsl_version); const GLchar* vertex_shader_glsl_120 = "uniform mat4 ProjMtx;\n" "attribute vec2 Position;\n" "attribute vec2 UV;\n" "attribute vec4 Color;\n" "varying vec2 Frag_UV;\n" "varying vec4 Frag_Color;\n" "void main()\n" "{\n" " Frag_UV = UV;\n" " Frag_Color = Color;\n" " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" "}\n"; const GLchar* vertex_shader_glsl_130 = "uniform mat4 ProjMtx;\n" "in vec2 Position;\n" "in vec2 UV;\n" "in vec4 Color;\n" "out vec2 Frag_UV;\n" "out vec4 Frag_Color;\n" "void main()\n" "{\n" " Frag_UV = UV;\n" " Frag_Color = Color;\n" " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" "}\n"; const GLchar* vertex_shader_glsl_300_es = "precision highp float;\n" "layout (location = 0) in vec2 Position;\n" "layout (location = 1) in vec2 UV;\n" "layout (location = 2) in vec4 Color;\n" "uniform mat4 ProjMtx;\n" "out vec2 Frag_UV;\n" "out vec4 Frag_Color;\n" "void main()\n" "{\n" " Frag_UV = UV;\n" " Frag_Color = Color;\n" " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" "}\n"; const GLchar* vertex_shader_glsl_410_core = "layout (location = 0) in vec2 Position;\n" "layout (location = 1) in vec2 UV;\n" "layout (location = 2) in vec4 Color;\n" "uniform mat4 ProjMtx;\n" "out vec2 Frag_UV;\n" "out vec4 Frag_Color;\n" "void main()\n" "{\n" " Frag_UV = UV;\n" " Frag_Color = Color;\n" " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" "}\n"; const GLchar* fragment_shader_glsl_120 = "#ifdef GL_ES\n" " precision mediump float;\n" "#endif\n" "uniform sampler2D Texture;\n" "varying vec2 Frag_UV;\n" "varying vec4 Frag_Color;\n" "void main()\n" "{\n" " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" "}\n"; const GLchar* fragment_shader_glsl_130 = "uniform sampler2D Texture;\n" "in vec2 Frag_UV;\n" "in vec4 Frag_Color;\n" "out vec4 Out_Color;\n" "void main()\n" "{\n" " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" "}\n"; const GLchar* fragment_shader_glsl_300_es = "precision mediump float;\n" "uniform sampler2D Texture;\n" "in vec2 Frag_UV;\n" "in vec4 Frag_Color;\n" "layout (location = 0) out vec4 Out_Color;\n" "void main()\n" "{\n" " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" "}\n"; const GLchar* fragment_shader_glsl_410_core = "in vec2 Frag_UV;\n" "in vec4 Frag_Color;\n" "uniform sampler2D Texture;\n" "layout (location = 0) out vec4 Out_Color;\n" // IMHEX PATCH BEGIN "layout (location = 0, index = 1) out vec4 SRC1_Color;\n" "uniform bool GammaCorrected;\n" // IMHEX PATCH END "void main()\n" "{\n" // IMHEX PATCH BEGIN " if (!GammaCorrected)\n" " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" " else {\n" " Out_Color = Frag_Color;\n" " SRC1_Color = vec4(texture(Texture, Frag_UV.st).rgb * Frag_Color.aaa,1.0);\n" " }\n" // IMHEX PATCH END "}\n"; // Select shaders matching our GLSL versions const GLchar* vertex_shader = nullptr; const GLchar* fragment_shader = nullptr; if (glsl_version < 130) { vertex_shader = vertex_shader_glsl_120; fragment_shader = fragment_shader_glsl_120; } else if (glsl_version >= 410) { vertex_shader = vertex_shader_glsl_410_core; fragment_shader = fragment_shader_glsl_410_core; } else if (glsl_version == 300) { vertex_shader = vertex_shader_glsl_300_es; fragment_shader = fragment_shader_glsl_300_es; } else { vertex_shader = vertex_shader_glsl_130; fragment_shader = fragment_shader_glsl_130; } // Create shaders const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader }; GLuint vert_handle; GL_CALL(vert_handle = glCreateShader(GL_VERTEX_SHADER)); glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr); glCompileShader(vert_handle); if (!CheckShader(vert_handle, "vertex shader")) return false; const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader }; GLuint frag_handle; GL_CALL(frag_handle = glCreateShader(GL_FRAGMENT_SHADER)); glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr); glCompileShader(frag_handle); if (!CheckShader(frag_handle, "fragment shader")) return false; // Link bd->ShaderHandle = glCreateProgram(); glAttachShader(bd->ShaderHandle, vert_handle); glAttachShader(bd->ShaderHandle, frag_handle); glLinkProgram(bd->ShaderHandle); if (!CheckProgram(bd->ShaderHandle, "shader program")) return false; glDetachShader(bd->ShaderHandle, vert_handle); glDetachShader(bd->ShaderHandle, frag_handle); glDeleteShader(vert_handle); glDeleteShader(frag_handle); // IMHEX PATCH BEGIN bd->AttribLocationGammaCorrected = (GLuint)glGetUniformLocation(bd->ShaderHandle, "GammaCorrected"); // IMHEX PATCH END bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture"); bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx"); bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position"); bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV"); bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color"); // Create buffers glGenBuffers(1, &bd->VboHandle); glGenBuffers(1, &bd->ElementsHandle); // Restore modified GL state glBindTexture(GL_TEXTURE_2D, last_texture); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK if (bd->GlVersion >= 210) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, last_pixel_unpack_buffer); } #endif #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glBindVertexArray(last_vertex_array); #endif return true; } void ImGui_ImplOpenGL3_DestroyDeviceObjects() { ImGui_ImplOpenGL3_InitLoader(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; } if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; } if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; } // Destroy all textures for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) if (tex->RefCount == 1) ImGui_ImplOpenGL3_DestroyTexture(tex); } //-------------------------------------------------------------------------------------------------------- // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT // This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) { if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) { ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); } ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData); } static void ImGui_ImplOpenGL3_InitMultiViewportSupport() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow; } static void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport() { ImGui::DestroyPlatformWindows(); } //----------------------------------------------------------------------------- #if defined(__GNUC__) #pragma GCC diagnostic pop #endif #if defined(__clang__) #pragma clang diagnostic pop #endif #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/cimgui/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) # https://github.com/cimgui/cimgui project(imgui_cimgui) set(CMAKE_CXX_STANDARD 23) if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD) add_library(imgui_cimgui OBJECT source/cimgui.cpp ) target_include_directories(imgui_cimgui PUBLIC include ) target_link_libraries(imgui_cimgui PRIVATE imgui_includes) add_dependencies(imhex_all imgui_cimgui) endif() ================================================ FILE: lib/third_party/imgui/cimgui/include/cimgui.h ================================================ //This file is automatically generated by generator.lua from https://github.com/cimgui/cimgui //based on imgui.h file version "1.92.0" 19200 from Dear ImGui https://github.com/ocornut/imgui //with imgui_internal.h api //with imgui_freetype.h api //docking branch #ifndef CIMGUI_INCLUDED #define CIMGUI_INCLUDED #include #include #if defined _WIN32 || defined __CYGWIN__ #ifdef CIMGUI_NO_EXPORT #define API #else #define API __declspec(dllexport) #endif #else #ifdef __GNUC__ #define API __attribute__((__visibility__("default"))) #else #define API #endif #endif #if defined __cplusplus #define EXTERN extern "C" #else #include #include #define EXTERN extern #endif #define CIMGUI_API EXTERN API #define CONST const #ifdef _MSC_VER typedef unsigned __int64 ImU64; #else //typedef unsigned long long ImU64; #endif #ifndef CIMGUI_DEFINE_ENUMS_AND_STRUCTS #ifdef IMGUI_ENABLE_FREETYPE #include "misc/freetype/imgui_freetype.h" #endif #endif #ifdef CIMGUI_DEFINE_ENUMS_AND_STRUCTS typedef struct ImDrawChannel ImDrawChannel; typedef struct ImDrawCmd ImDrawCmd; typedef struct ImDrawData ImDrawData; typedef struct ImDrawList ImDrawList; typedef struct ImDrawListSharedData ImDrawListSharedData; typedef struct ImDrawListSplitter ImDrawListSplitter; typedef struct ImDrawVert ImDrawVert; typedef struct ImFont ImFont; typedef struct ImFontAtlas ImFontAtlas; typedef struct ImFontAtlasBuilder ImFontAtlasBuilder; typedef struct ImFontAtlasRect ImFontAtlasRect; typedef struct ImFontBaked ImFontBaked; typedef struct ImFontConfig ImFontConfig; typedef struct ImFontGlyph ImFontGlyph; typedef struct ImFontGlyphRangesBuilder ImFontGlyphRangesBuilder; typedef struct ImFontLoader ImFontLoader; typedef struct ImTextureData ImTextureData; typedef struct ImTextureRect ImTextureRect; typedef struct ImColor ImColor; typedef struct ImGuiContext ImGuiContext; typedef struct ImGuiIO ImGuiIO; typedef struct ImGuiInputTextCallbackData ImGuiInputTextCallbackData; typedef struct ImGuiKeyData ImGuiKeyData; typedef struct ImGuiListClipper ImGuiListClipper; typedef struct ImGuiMultiSelectIO ImGuiMultiSelectIO; typedef struct ImGuiOnceUponAFrame ImGuiOnceUponAFrame; typedef struct ImGuiPayload ImGuiPayload; typedef struct ImGuiPlatformIO ImGuiPlatformIO; typedef struct ImGuiPlatformImeData ImGuiPlatformImeData; typedef struct ImGuiPlatformMonitor ImGuiPlatformMonitor; typedef struct ImGuiSelectionBasicStorage ImGuiSelectionBasicStorage; typedef struct ImGuiSelectionExternalStorage ImGuiSelectionExternalStorage; typedef struct ImGuiSelectionRequest ImGuiSelectionRequest; typedef struct ImGuiSizeCallbackData ImGuiSizeCallbackData; typedef struct ImGuiStorage ImGuiStorage; typedef struct ImGuiStoragePair ImGuiStoragePair; typedef struct ImGuiStyle ImGuiStyle; typedef struct ImGuiTableSortSpecs ImGuiTableSortSpecs; typedef struct ImGuiTableColumnSortSpecs ImGuiTableColumnSortSpecs; typedef struct ImGuiTextBuffer ImGuiTextBuffer; typedef struct ImGuiTextFilter ImGuiTextFilter; typedef struct ImGuiViewport ImGuiViewport; typedef struct ImGuiWindowClass ImGuiWindowClass; typedef struct ImBitVector ImBitVector; typedef struct ImRect ImRect; typedef struct ImGuiTextIndex ImGuiTextIndex; typedef struct ImDrawDataBuilder ImDrawDataBuilder; typedef struct ImFontAtlasPostProcessData ImFontAtlasPostProcessData; typedef struct ImFontAtlasRectEntry ImFontAtlasRectEntry; typedef struct ImGuiBoxSelectState ImGuiBoxSelectState; typedef struct ImGuiColorMod ImGuiColorMod; typedef struct ImGuiContextHook ImGuiContextHook; typedef struct ImGuiDataTypeInfo ImGuiDataTypeInfo; typedef struct ImGuiDeactivatedItemData ImGuiDeactivatedItemData; typedef struct ImGuiDockContext ImGuiDockContext; typedef struct ImGuiDockRequest ImGuiDockRequest; typedef struct ImGuiDockNode ImGuiDockNode; typedef struct ImGuiDockNodeSettings ImGuiDockNodeSettings; typedef struct ImGuiErrorRecoveryState ImGuiErrorRecoveryState; typedef struct ImGuiGroupData ImGuiGroupData; typedef struct ImGuiInputTextState ImGuiInputTextState; typedef struct ImGuiInputTextDeactivateData ImGuiInputTextDeactivateData; typedef struct ImGuiLastItemData ImGuiLastItemData; typedef struct ImGuiLocEntry ImGuiLocEntry; typedef struct ImGuiMenuColumns ImGuiMenuColumns; typedef struct ImGuiMultiSelectState ImGuiMultiSelectState; typedef struct ImGuiMultiSelectTempData ImGuiMultiSelectTempData; typedef struct ImGuiNavItemData ImGuiNavItemData; typedef struct ImGuiMetricsConfig ImGuiMetricsConfig; typedef struct ImGuiNextWindowData ImGuiNextWindowData; typedef struct ImGuiNextItemData ImGuiNextItemData; typedef struct ImGuiOldColumnData ImGuiOldColumnData; typedef struct ImGuiOldColumns ImGuiOldColumns; typedef struct ImGuiPopupData ImGuiPopupData; typedef struct ImGuiSettingsHandler ImGuiSettingsHandler; typedef struct ImGuiStyleMod ImGuiStyleMod; typedef struct ImGuiStyleVarInfo ImGuiStyleVarInfo; typedef struct ImGuiTabBar ImGuiTabBar; typedef struct ImGuiTabItem ImGuiTabItem; typedef struct ImGuiTable ImGuiTable; typedef struct ImGuiTableHeaderData ImGuiTableHeaderData; typedef struct ImGuiTableColumn ImGuiTableColumn; typedef struct ImGuiTableInstanceData ImGuiTableInstanceData; typedef struct ImGuiTableTempData ImGuiTableTempData; typedef struct ImGuiTableSettings ImGuiTableSettings; typedef struct ImGuiTableColumnsSettings ImGuiTableColumnsSettings; typedef struct ImGuiTreeNodeStackData ImGuiTreeNodeStackData; typedef struct ImGuiTypingSelectState ImGuiTypingSelectState; typedef struct ImGuiTypingSelectRequest ImGuiTypingSelectRequest; typedef struct ImGuiWindow ImGuiWindow; typedef struct ImGuiWindowDockStyle ImGuiWindowDockStyle; typedef struct ImGuiWindowTempData ImGuiWindowTempData; typedef struct ImGuiWindowSettings ImGuiWindowSettings; typedef struct STB_TexteditState STB_TexteditState; typedef struct stbrp_node stbrp_node; typedef struct ImVector_const_charPtr {int Size;int Capacity;const char** Data;} ImVector_const_charPtr; typedef unsigned int ImGuiID; typedef signed char ImS8; typedef unsigned char ImU8; typedef signed short ImS16; typedef unsigned short ImU16; typedef signed int ImS32; typedef unsigned int ImU32; typedef signed long long ImS64; typedef unsigned long long ImU64; struct ImDrawChannel; struct ImDrawCmd; struct ImDrawData; struct ImDrawList; struct ImDrawListSharedData; struct ImDrawListSplitter; struct ImDrawVert; struct ImFont; struct ImFontAtlas; struct ImFontAtlasBuilder; struct ImFontAtlasRect; struct ImFontBaked; struct ImFontConfig; struct ImFontGlyph; struct ImFontGlyphRangesBuilder; struct ImFontLoader; struct ImTextureData; struct ImTextureRect; struct ImColor; struct ImGuiContext; struct ImGuiIO; struct ImGuiInputTextCallbackData; struct ImGuiKeyData; struct ImGuiListClipper; struct ImGuiMultiSelectIO; struct ImGuiOnceUponAFrame; struct ImGuiPayload; struct ImGuiPlatformIO; struct ImGuiPlatformImeData; struct ImGuiPlatformMonitor; struct ImGuiSelectionBasicStorage; struct ImGuiSelectionExternalStorage; struct ImGuiSelectionRequest; struct ImGuiSizeCallbackData; struct ImGuiStorage; struct ImGuiStoragePair; struct ImGuiStyle; struct ImGuiTableSortSpecs; struct ImGuiTableColumnSortSpecs; struct ImGuiTextBuffer; struct ImGuiTextFilter; struct ImGuiViewport; struct ImGuiWindowClass; typedef int ImGuiCol; typedef int ImGuiCond; typedef int ImGuiDataType; typedef int ImGuiMouseButton; typedef int ImGuiMouseCursor; typedef int ImGuiStyleVar; typedef int ImGuiTableBgTarget; typedef int ImDrawFlags; typedef int ImDrawListFlags; typedef int ImFontFlags; typedef int ImFontAtlasFlags; typedef int ImGuiBackendFlags; typedef int ImGuiButtonFlags; typedef int ImGuiChildFlags; typedef int ImGuiColorEditFlags; typedef int ImGuiConfigFlags; typedef int ImGuiComboFlags; typedef int ImGuiDockNodeFlags; typedef int ImGuiDragDropFlags; typedef int ImGuiFocusedFlags; typedef int ImGuiHoveredFlags; typedef int ImGuiInputFlags; typedef int ImGuiInputTextFlags; typedef int ImGuiItemFlags; typedef int ImGuiKeyChord; typedef int ImGuiPopupFlags; typedef int ImGuiMultiSelectFlags; typedef int ImGuiSelectableFlags; typedef int ImGuiSliderFlags; typedef int ImGuiTabBarFlags; typedef int ImGuiTabItemFlags; typedef int ImGuiTableFlags; typedef int ImGuiTableColumnFlags; typedef int ImGuiTableRowFlags; typedef int ImGuiTreeNodeFlags; typedef int ImGuiViewportFlags; typedef int ImGuiWindowFlags; typedef unsigned int ImWchar32; typedef unsigned short ImWchar16; #ifdef IMGUI_USE_WCHAR32 typedef ImWchar32 ImWchar; #else typedef ImWchar16 ImWchar; #endif #ifdef IMGUI_USE_WCHAR32 #define IM_UNICODE_CODEPOINT_MAX 0x10FFFF #else #define IM_UNICODE_CODEPOINT_MAX 0xFFFF #endif typedef ImS64 ImGuiSelectionUserData; typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); typedef struct ImVec2 ImVec2; struct ImVec2 { float x, y; }; typedef struct ImVec4 ImVec4; struct ImVec4 { float x, y, z, w; }; typedef ImU64 ImTextureID; typedef struct ImTextureRef ImTextureRef; struct ImTextureRef { ImTextureData* _TexData; ImTextureID _TexID; }; typedef enum { ImGuiWindowFlags_None = 0, ImGuiWindowFlags_NoTitleBar = 1 << 0, ImGuiWindowFlags_NoResize = 1 << 1, ImGuiWindowFlags_NoMove = 1 << 2, ImGuiWindowFlags_NoScrollbar = 1 << 3, ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, ImGuiWindowFlags_NoCollapse = 1 << 5, ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, ImGuiWindowFlags_NoBackground = 1 << 7, ImGuiWindowFlags_NoSavedSettings = 1 << 8, ImGuiWindowFlags_NoMouseInputs = 1 << 9, ImGuiWindowFlags_MenuBar = 1 << 10, ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, ImGuiWindowFlags_NoNavInputs = 1 << 16, ImGuiWindowFlags_NoNavFocus = 1 << 17, ImGuiWindowFlags_UnsavedDocument = 1 << 18, ImGuiWindowFlags_NoDocking = 1 << 19, ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_DockNodeHost = 1 << 23, ImGuiWindowFlags_ChildWindow = 1 << 24, ImGuiWindowFlags_Tooltip = 1 << 25, ImGuiWindowFlags_Popup = 1 << 26, ImGuiWindowFlags_Modal = 1 << 27, ImGuiWindowFlags_ChildMenu = 1 << 28, }ImGuiWindowFlags_; typedef enum { ImGuiChildFlags_None = 0, ImGuiChildFlags_Borders = 1 << 0, ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1, ImGuiChildFlags_ResizeX = 1 << 2, ImGuiChildFlags_ResizeY = 1 << 3, ImGuiChildFlags_AutoResizeX = 1 << 4, ImGuiChildFlags_AutoResizeY = 1 << 5, ImGuiChildFlags_AlwaysAutoResize = 1 << 6, ImGuiChildFlags_FrameStyle = 1 << 7, ImGuiChildFlags_NavFlattened = 1 << 8, }ImGuiChildFlags_; typedef enum { ImGuiItemFlags_None = 0, ImGuiItemFlags_NoTabStop = 1 << 0, ImGuiItemFlags_NoNav = 1 << 1, ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, ImGuiItemFlags_ButtonRepeat = 1 << 3, ImGuiItemFlags_AutoClosePopups = 1 << 4, ImGuiItemFlags_AllowDuplicateId = 1 << 5, }ImGuiItemFlags_; typedef enum { ImGuiInputTextFlags_None = 0, ImGuiInputTextFlags_CharsDecimal = 1 << 0, ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, ImGuiInputTextFlags_CharsScientific = 1 << 2, ImGuiInputTextFlags_CharsUppercase = 1 << 3, ImGuiInputTextFlags_CharsNoBlank = 1 << 4, ImGuiInputTextFlags_AllowTabInput = 1 << 5, ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6, ImGuiInputTextFlags_EscapeClearsAll = 1 << 7, ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8, ImGuiInputTextFlags_ReadOnly = 1 << 9, ImGuiInputTextFlags_Password = 1 << 10, ImGuiInputTextFlags_AlwaysOverwrite = 1 << 11, ImGuiInputTextFlags_AutoSelectAll = 1 << 12, ImGuiInputTextFlags_ParseEmptyRefVal = 1 << 13, ImGuiInputTextFlags_DisplayEmptyRefVal = 1 << 14, ImGuiInputTextFlags_NoHorizontalScroll = 1 << 15, ImGuiInputTextFlags_NoUndoRedo = 1 << 16, ImGuiInputTextFlags_ElideLeft = 1 << 17, ImGuiInputTextFlags_CallbackCompletion = 1 << 18, ImGuiInputTextFlags_CallbackHistory = 1 << 19, ImGuiInputTextFlags_CallbackAlways = 1 << 20, ImGuiInputTextFlags_CallbackCharFilter = 1 << 21, ImGuiInputTextFlags_CallbackResize = 1 << 22, ImGuiInputTextFlags_CallbackEdit = 1 << 23, }ImGuiInputTextFlags_; typedef enum { ImGuiTreeNodeFlags_None = 0, ImGuiTreeNodeFlags_Selected = 1 << 0, ImGuiTreeNodeFlags_Framed = 1 << 1, ImGuiTreeNodeFlags_AllowOverlap = 1 << 2, ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, ImGuiTreeNodeFlags_Leaf = 1 << 8, ImGuiTreeNodeFlags_Bullet = 1 << 9, ImGuiTreeNodeFlags_FramePadding = 1 << 10, ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, ImGuiTreeNodeFlags_SpanLabelWidth = 1 << 13, ImGuiTreeNodeFlags_SpanAllColumns = 1 << 14, ImGuiTreeNodeFlags_LabelSpanAllColumns = 1 << 15, ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17, ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog, ImGuiTreeNodeFlags_DrawLinesNone = 1 << 18, ImGuiTreeNodeFlags_DrawLinesFull = 1 << 19, ImGuiTreeNodeFlags_DrawLinesToNodes = 1 << 20, }ImGuiTreeNodeFlags_; typedef enum { ImGuiPopupFlags_None = 0, ImGuiPopupFlags_MouseButtonLeft = 0, ImGuiPopupFlags_MouseButtonRight = 1, ImGuiPopupFlags_MouseButtonMiddle = 2, ImGuiPopupFlags_MouseButtonMask_ = 0x1F, ImGuiPopupFlags_MouseButtonDefault_ = 1, ImGuiPopupFlags_NoReopen = 1 << 5, ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 7, ImGuiPopupFlags_NoOpenOverItems = 1 << 8, ImGuiPopupFlags_AnyPopupId = 1 << 10, ImGuiPopupFlags_AnyPopupLevel = 1 << 11, ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel, }ImGuiPopupFlags_; typedef enum { ImGuiSelectableFlags_None = 0, ImGuiSelectableFlags_NoAutoClosePopups = 1 << 0, ImGuiSelectableFlags_SpanAllColumns = 1 << 1, ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, ImGuiSelectableFlags_Disabled = 1 << 3, ImGuiSelectableFlags_AllowOverlap = 1 << 4, ImGuiSelectableFlags_Highlight = 1 << 5, }ImGuiSelectableFlags_; typedef enum { ImGuiComboFlags_None = 0, ImGuiComboFlags_PopupAlignLeft = 1 << 0, ImGuiComboFlags_HeightSmall = 1 << 1, ImGuiComboFlags_HeightRegular = 1 << 2, ImGuiComboFlags_HeightLarge = 1 << 3, ImGuiComboFlags_HeightLargest = 1 << 4, ImGuiComboFlags_NoArrowButton = 1 << 5, ImGuiComboFlags_NoPreview = 1 << 6, ImGuiComboFlags_WidthFitPreview = 1 << 7, ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest, }ImGuiComboFlags_; typedef enum { ImGuiTabBarFlags_None = 0, ImGuiTabBarFlags_Reorderable = 1 << 0, ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1, ImGuiTabBarFlags_TabListPopupButton = 1 << 2, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, ImGuiTabBarFlags_NoTooltip = 1 << 5, ImGuiTabBarFlags_DrawSelectedOverline = 1 << 6, ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 7, ImGuiTabBarFlags_FittingPolicyScroll = 1 << 8, ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll, ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown, }ImGuiTabBarFlags_; typedef enum { ImGuiTabItemFlags_None = 0, ImGuiTabItemFlags_UnsavedDocument = 1 << 0, ImGuiTabItemFlags_SetSelected = 1 << 1, ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, ImGuiTabItemFlags_NoPushId = 1 << 3, ImGuiTabItemFlags_NoTooltip = 1 << 4, ImGuiTabItemFlags_NoReorder = 1 << 5, ImGuiTabItemFlags_Leading = 1 << 6, ImGuiTabItemFlags_Trailing = 1 << 7, ImGuiTabItemFlags_NoAssumedClosure = 1 << 8, }ImGuiTabItemFlags_; typedef enum { ImGuiFocusedFlags_None = 0, ImGuiFocusedFlags_ChildWindows = 1 << 0, ImGuiFocusedFlags_RootWindow = 1 << 1, ImGuiFocusedFlags_AnyWindow = 1 << 2, ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, ImGuiFocusedFlags_DockHierarchy = 1 << 4, ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows, }ImGuiFocusedFlags_; typedef enum { ImGuiHoveredFlags_None = 0, ImGuiHoveredFlags_ChildWindows = 1 << 0, ImGuiHoveredFlags_RootWindow = 1 << 1, ImGuiHoveredFlags_AnyWindow = 1 << 2, ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, ImGuiHoveredFlags_DockHierarchy = 1 << 4, ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, ImGuiHoveredFlags_AllowWhenOverlappedByItem = 1 << 8, ImGuiHoveredFlags_AllowWhenOverlappedByWindow = 1 << 9, ImGuiHoveredFlags_AllowWhenDisabled = 1 << 10, ImGuiHoveredFlags_NoNavOverride = 1 << 11, ImGuiHoveredFlags_AllowWhenOverlapped = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow, ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, ImGuiHoveredFlags_ForTooltip = 1 << 12, ImGuiHoveredFlags_Stationary = 1 << 13, ImGuiHoveredFlags_DelayNone = 1 << 14, ImGuiHoveredFlags_DelayShort = 1 << 15, ImGuiHoveredFlags_DelayNormal = 1 << 16, ImGuiHoveredFlags_NoSharedDelay = 1 << 17, }ImGuiHoveredFlags_; typedef enum { ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, ImGuiDockNodeFlags_NoDockingOverCentralNode = 1 << 2, ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, ImGuiDockNodeFlags_NoDockingSplit = 1 << 4, ImGuiDockNodeFlags_NoResize = 1 << 5, ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6, ImGuiDockNodeFlags_NoUndocking = 1 << 7, }ImGuiDockNodeFlags_; typedef enum { ImGuiDragDropFlags_None = 0, ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2, ImGuiDragDropFlags_SourceAllowNullID = 1 << 3, ImGuiDragDropFlags_SourceExtern = 1 << 4, ImGuiDragDropFlags_PayloadAutoExpire = 1 << 5, ImGuiDragDropFlags_PayloadNoCrossContext = 1 << 6, ImGuiDragDropFlags_PayloadNoCrossProcess = 1 << 7, ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, }ImGuiDragDropFlags_; typedef enum { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double, ImGuiDataType_Bool, ImGuiDataType_String, ImGuiDataType_COUNT }ImGuiDataType_; typedef enum { ImGuiDir_None=-1, ImGuiDir_Left=0, ImGuiDir_Right=1, ImGuiDir_Up=2, ImGuiDir_Down=3, ImGuiDir_COUNT=4, }ImGuiDir; typedef enum { ImGuiSortDirection_None=0, ImGuiSortDirection_Ascending=1, ImGuiSortDirection_Descending=2, }ImGuiSortDirection; typedef enum { ImGuiKey_None=0, ImGuiKey_NamedKey_BEGIN=512, ImGuiKey_Tab=512, ImGuiKey_LeftArrow=513, ImGuiKey_RightArrow=514, ImGuiKey_UpArrow=515, ImGuiKey_DownArrow=516, ImGuiKey_PageUp=517, ImGuiKey_PageDown=518, ImGuiKey_Home=519, ImGuiKey_End=520, ImGuiKey_Insert=521, ImGuiKey_Delete=522, ImGuiKey_Backspace=523, ImGuiKey_Space=524, ImGuiKey_Enter=525, ImGuiKey_Escape=526, ImGuiKey_LeftCtrl=527, ImGuiKey_LeftShift=528, ImGuiKey_LeftAlt=529, ImGuiKey_LeftSuper=530, ImGuiKey_RightCtrl=531, ImGuiKey_RightShift=532, ImGuiKey_RightAlt=533, ImGuiKey_RightSuper=534, ImGuiKey_Menu=535, ImGuiKey_0=536, ImGuiKey_1=537, ImGuiKey_2=538, ImGuiKey_3=539, ImGuiKey_4=540, ImGuiKey_5=541, ImGuiKey_6=542, ImGuiKey_7=543, ImGuiKey_8=544, ImGuiKey_9=545, ImGuiKey_A=546, ImGuiKey_B=547, ImGuiKey_C=548, ImGuiKey_D=549, ImGuiKey_E=550, ImGuiKey_F=551, ImGuiKey_G=552, ImGuiKey_H=553, ImGuiKey_I=554, ImGuiKey_J=555, ImGuiKey_K=556, ImGuiKey_L=557, ImGuiKey_M=558, ImGuiKey_N=559, ImGuiKey_O=560, ImGuiKey_P=561, ImGuiKey_Q=562, ImGuiKey_R=563, ImGuiKey_S=564, ImGuiKey_T=565, ImGuiKey_U=566, ImGuiKey_V=567, ImGuiKey_W=568, ImGuiKey_X=569, ImGuiKey_Y=570, ImGuiKey_Z=571, ImGuiKey_F1=572, ImGuiKey_F2=573, ImGuiKey_F3=574, ImGuiKey_F4=575, ImGuiKey_F5=576, ImGuiKey_F6=577, ImGuiKey_F7=578, ImGuiKey_F8=579, ImGuiKey_F9=580, ImGuiKey_F10=581, ImGuiKey_F11=582, ImGuiKey_F12=583, ImGuiKey_F13=584, ImGuiKey_F14=585, ImGuiKey_F15=586, ImGuiKey_F16=587, ImGuiKey_F17=588, ImGuiKey_F18=589, ImGuiKey_F19=590, ImGuiKey_F20=591, ImGuiKey_F21=592, ImGuiKey_F22=593, ImGuiKey_F23=594, ImGuiKey_F24=595, ImGuiKey_Apostrophe=596, ImGuiKey_Comma=597, ImGuiKey_Minus=598, ImGuiKey_Period=599, ImGuiKey_Slash=600, ImGuiKey_Semicolon=601, ImGuiKey_Equal=602, ImGuiKey_LeftBracket=603, ImGuiKey_Backslash=604, ImGuiKey_RightBracket=605, ImGuiKey_GraveAccent=606, ImGuiKey_CapsLock=607, ImGuiKey_ScrollLock=608, ImGuiKey_NumLock=609, ImGuiKey_PrintScreen=610, ImGuiKey_Pause=611, ImGuiKey_Keypad0=612, ImGuiKey_Keypad1=613, ImGuiKey_Keypad2=614, ImGuiKey_Keypad3=615, ImGuiKey_Keypad4=616, ImGuiKey_Keypad5=617, ImGuiKey_Keypad6=618, ImGuiKey_Keypad7=619, ImGuiKey_Keypad8=620, ImGuiKey_Keypad9=621, ImGuiKey_KeypadDecimal=622, ImGuiKey_KeypadDivide=623, ImGuiKey_KeypadMultiply=624, ImGuiKey_KeypadSubtract=625, ImGuiKey_KeypadAdd=626, ImGuiKey_KeypadEnter=627, ImGuiKey_KeypadEqual=628, ImGuiKey_AppBack=629, ImGuiKey_AppForward=630, ImGuiKey_Oem102=631, ImGuiKey_GamepadStart=632, ImGuiKey_GamepadBack=633, ImGuiKey_GamepadFaceLeft=634, ImGuiKey_GamepadFaceRight=635, ImGuiKey_GamepadFaceUp=636, ImGuiKey_GamepadFaceDown=637, ImGuiKey_GamepadDpadLeft=638, ImGuiKey_GamepadDpadRight=639, ImGuiKey_GamepadDpadUp=640, ImGuiKey_GamepadDpadDown=641, ImGuiKey_GamepadL1=642, ImGuiKey_GamepadR1=643, ImGuiKey_GamepadL2=644, ImGuiKey_GamepadR2=645, ImGuiKey_GamepadL3=646, ImGuiKey_GamepadR3=647, ImGuiKey_GamepadLStickLeft=648, ImGuiKey_GamepadLStickRight=649, ImGuiKey_GamepadLStickUp=650, ImGuiKey_GamepadLStickDown=651, ImGuiKey_GamepadRStickLeft=652, ImGuiKey_GamepadRStickRight=653, ImGuiKey_GamepadRStickUp=654, ImGuiKey_GamepadRStickDown=655, ImGuiKey_MouseLeft=656, ImGuiKey_MouseRight=657, ImGuiKey_MouseMiddle=658, ImGuiKey_MouseX1=659, ImGuiKey_MouseX2=660, ImGuiKey_MouseWheelX=661, ImGuiKey_MouseWheelY=662, ImGuiKey_ReservedForModCtrl=663, ImGuiKey_ReservedForModShift=664, ImGuiKey_ReservedForModAlt=665, ImGuiKey_ReservedForModSuper=666, ImGuiKey_NamedKey_END=667, ImGuiKey_NamedKey_COUNT=ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, ImGuiMod_None=0, ImGuiMod_Ctrl=1 << 12, ImGuiMod_Shift=1 << 13, ImGuiMod_Alt=1 << 14, ImGuiMod_Super=1 << 15, ImGuiMod_Mask_=0xF000, }ImGuiKey; typedef enum { ImGuiInputFlags_None = 0, ImGuiInputFlags_Repeat = 1 << 0, ImGuiInputFlags_RouteActive = 1 << 10, ImGuiInputFlags_RouteFocused = 1 << 11, ImGuiInputFlags_RouteGlobal = 1 << 12, ImGuiInputFlags_RouteAlways = 1 << 13, ImGuiInputFlags_RouteOverFocused = 1 << 14, ImGuiInputFlags_RouteOverActive = 1 << 15, ImGuiInputFlags_RouteUnlessBgFocused = 1 << 16, ImGuiInputFlags_RouteFromRootWindow = 1 << 17, ImGuiInputFlags_Tooltip = 1 << 18, }ImGuiInputFlags_; typedef enum { ImGuiConfigFlags_None = 0, ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, ImGuiConfigFlags_NavEnableGamepad = 1 << 1, ImGuiConfigFlags_NoMouse = 1 << 4, ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, ImGuiConfigFlags_NoKeyboard = 1 << 6, ImGuiConfigFlags_DockingEnable = 1 << 7, ImGuiConfigFlags_ViewportsEnable = 1 << 10, ImGuiConfigFlags_IsSRGB = 1 << 20, ImGuiConfigFlags_IsTouchScreen = 1 << 21, }ImGuiConfigFlags_; typedef enum { ImGuiBackendFlags_None = 0, ImGuiBackendFlags_HasGamepad = 1 << 0, ImGuiBackendFlags_HasMouseCursors = 1 << 1, ImGuiBackendFlags_HasSetMousePos = 1 << 2, ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3, ImGuiBackendFlags_RendererHasTextures = 1 << 4, ImGuiBackendFlags_PlatformHasViewports = 1 << 10, ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, ImGuiBackendFlags_RendererHasViewports = 1 << 12, }ImGuiBackendFlags_; typedef enum { ImGuiCol_Text, ImGuiCol_TextDisabled, ImGuiCol_WindowBg, ImGuiCol_ChildBg, ImGuiCol_PopupBg, ImGuiCol_Border, ImGuiCol_BorderShadow, ImGuiCol_FrameBg, ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive, ImGuiCol_TitleBg, ImGuiCol_TitleBgActive, ImGuiCol_TitleBgCollapsed, ImGuiCol_MenuBarBg, ImGuiCol_ScrollbarBg, ImGuiCol_ScrollbarGrab, ImGuiCol_ScrollbarGrabHovered, ImGuiCol_ScrollbarGrabActive, ImGuiCol_CheckMark, ImGuiCol_SliderGrab, ImGuiCol_SliderGrabActive, ImGuiCol_Button, ImGuiCol_ButtonHovered, ImGuiCol_ButtonActive, ImGuiCol_Header, ImGuiCol_HeaderHovered, ImGuiCol_HeaderActive, ImGuiCol_Separator, ImGuiCol_SeparatorHovered, ImGuiCol_SeparatorActive, ImGuiCol_ResizeGrip, ImGuiCol_ResizeGripHovered, ImGuiCol_ResizeGripActive, ImGuiCol_InputTextCursor, ImGuiCol_TabHovered, ImGuiCol_Tab, ImGuiCol_TabSelected, ImGuiCol_TabSelectedOverline, ImGuiCol_TabDimmed, ImGuiCol_TabDimmedSelected, ImGuiCol_TabDimmedSelectedOverline, ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg, ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, ImGuiCol_PlotHistogramHovered, ImGuiCol_TableHeaderBg, ImGuiCol_TableBorderStrong, ImGuiCol_TableBorderLight, ImGuiCol_TableRowBg, ImGuiCol_TableRowBgAlt, ImGuiCol_TextLink, ImGuiCol_TextSelectedBg, ImGuiCol_TreeLines, ImGuiCol_DragDropTarget, ImGuiCol_NavCursor, ImGuiCol_NavWindowingHighlight, ImGuiCol_NavWindowingDimBg, ImGuiCol_ModalWindowDimBg, ImGuiCol_COUNT, }ImGuiCol_; typedef enum { ImGuiStyleVar_Alpha, ImGuiStyleVar_DisabledAlpha, ImGuiStyleVar_WindowPadding, ImGuiStyleVar_WindowRounding, ImGuiStyleVar_WindowBorderSize, ImGuiStyleVar_WindowMinSize, ImGuiStyleVar_WindowTitleAlign, ImGuiStyleVar_ChildRounding, ImGuiStyleVar_ChildBorderSize, ImGuiStyleVar_PopupRounding, ImGuiStyleVar_PopupBorderSize, ImGuiStyleVar_FramePadding, ImGuiStyleVar_FrameRounding, ImGuiStyleVar_FrameBorderSize, ImGuiStyleVar_ItemSpacing, ImGuiStyleVar_ItemInnerSpacing, ImGuiStyleVar_IndentSpacing, ImGuiStyleVar_CellPadding, ImGuiStyleVar_ScrollbarSize, ImGuiStyleVar_ScrollbarRounding, ImGuiStyleVar_GrabMinSize, ImGuiStyleVar_GrabRounding, ImGuiStyleVar_ImageBorderSize, ImGuiStyleVar_TabRounding, ImGuiStyleVar_TabBorderSize, ImGuiStyleVar_TabBarBorderSize, ImGuiStyleVar_TabBarOverlineSize, ImGuiStyleVar_TableAngledHeadersAngle, ImGuiStyleVar_TableAngledHeadersTextAlign, ImGuiStyleVar_TreeLinesSize, ImGuiStyleVar_TreeLinesRounding, ImGuiStyleVar_ButtonTextAlign, ImGuiStyleVar_SelectableTextAlign, ImGuiStyleVar_SeparatorTextBorderSize, ImGuiStyleVar_SeparatorTextAlign, ImGuiStyleVar_SeparatorTextPadding, ImGuiStyleVar_DockingSeparatorSize, ImGuiStyleVar_COUNT }ImGuiStyleVar_; typedef enum { ImGuiButtonFlags_None = 0, ImGuiButtonFlags_MouseButtonLeft = 1 << 0, ImGuiButtonFlags_MouseButtonRight = 1 << 1, ImGuiButtonFlags_MouseButtonMiddle = 1 << 2, ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, ImGuiButtonFlags_EnableNav = 1 << 3, }ImGuiButtonFlags_; typedef enum { ImGuiColorEditFlags_None = 0, ImGuiColorEditFlags_NoAlpha = 1 << 1, ImGuiColorEditFlags_NoPicker = 1 << 2, ImGuiColorEditFlags_NoOptions = 1 << 3, ImGuiColorEditFlags_NoSmallPreview = 1 << 4, ImGuiColorEditFlags_NoInputs = 1 << 5, ImGuiColorEditFlags_NoTooltip = 1 << 6, ImGuiColorEditFlags_NoLabel = 1 << 7, ImGuiColorEditFlags_NoSidePreview = 1 << 8, ImGuiColorEditFlags_NoDragDrop = 1 << 9, ImGuiColorEditFlags_NoBorder = 1 << 10, ImGuiColorEditFlags_AlphaOpaque = 1 << 11, ImGuiColorEditFlags_AlphaNoBg = 1 << 12, ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 13, ImGuiColorEditFlags_AlphaBar = 1 << 16, ImGuiColorEditFlags_HDR = 1 << 19, ImGuiColorEditFlags_DisplayRGB = 1 << 20, ImGuiColorEditFlags_DisplayHSV = 1 << 21, ImGuiColorEditFlags_DisplayHex = 1 << 22, ImGuiColorEditFlags_Uint8 = 1 << 23, ImGuiColorEditFlags_Float = 1 << 24, ImGuiColorEditFlags_PickerHueBar = 1 << 25, ImGuiColorEditFlags_PickerHueWheel = 1 << 26, ImGuiColorEditFlags_InputRGB = 1 << 27, ImGuiColorEditFlags_InputHSV = 1 << 28, ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, ImGuiColorEditFlags_AlphaMask_ = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque | ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf, ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV, }ImGuiColorEditFlags_; typedef enum { ImGuiSliderFlags_None = 0, ImGuiSliderFlags_Logarithmic = 1 << 5, ImGuiSliderFlags_NoRoundToFormat = 1 << 6, ImGuiSliderFlags_NoInput = 1 << 7, ImGuiSliderFlags_WrapAround = 1 << 8, ImGuiSliderFlags_ClampOnInput = 1 << 9, ImGuiSliderFlags_ClampZeroRange = 1 << 10, ImGuiSliderFlags_NoSpeedTweaks = 1 << 11, ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange, ImGuiSliderFlags_InvalidMask_ = 0x7000000F, }ImGuiSliderFlags_; typedef enum { ImGuiMouseButton_Left = 0, ImGuiMouseButton_Right = 1, ImGuiMouseButton_Middle = 2, ImGuiMouseButton_COUNT = 5 }ImGuiMouseButton_; typedef enum { ImGuiMouseCursor_None = -1, ImGuiMouseCursor_Arrow = 0, ImGuiMouseCursor_TextInput, ImGuiMouseCursor_ResizeAll, ImGuiMouseCursor_ResizeNS, ImGuiMouseCursor_ResizeEW, ImGuiMouseCursor_ResizeNESW, ImGuiMouseCursor_ResizeNWSE, ImGuiMouseCursor_Hand, ImGuiMouseCursor_Wait, ImGuiMouseCursor_Progress, ImGuiMouseCursor_NotAllowed, ImGuiMouseCursor_COUNT }ImGuiMouseCursor_; typedef enum { ImGuiMouseSource_Mouse=0, ImGuiMouseSource_TouchScreen=1, ImGuiMouseSource_Pen=2, ImGuiMouseSource_COUNT=3, }ImGuiMouseSource; typedef enum { ImGuiCond_None = 0, ImGuiCond_Always = 1 << 0, ImGuiCond_Once = 1 << 1, ImGuiCond_FirstUseEver = 1 << 2, ImGuiCond_Appearing = 1 << 3, }ImGuiCond_; typedef enum { ImGuiTableFlags_None = 0, ImGuiTableFlags_Resizable = 1 << 0, ImGuiTableFlags_Reorderable = 1 << 1, ImGuiTableFlags_Hideable = 1 << 2, ImGuiTableFlags_Sortable = 1 << 3, ImGuiTableFlags_NoSavedSettings = 1 << 4, ImGuiTableFlags_ContextMenuInBody = 1 << 5, ImGuiTableFlags_RowBg = 1 << 6, ImGuiTableFlags_BordersInnerH = 1 << 7, ImGuiTableFlags_BordersOuterH = 1 << 8, ImGuiTableFlags_BordersInnerV = 1 << 9, ImGuiTableFlags_BordersOuterV = 1 << 10, ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, ImGuiTableFlags_NoBordersInBody = 1 << 11, ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, ImGuiTableFlags_SizingFixedFit = 1 << 13, ImGuiTableFlags_SizingFixedSame = 2 << 13, ImGuiTableFlags_SizingStretchProp = 3 << 13, ImGuiTableFlags_SizingStretchSame = 4 << 13, ImGuiTableFlags_NoHostExtendX = 1 << 16, ImGuiTableFlags_NoHostExtendY = 1 << 17, ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, ImGuiTableFlags_PreciseWidths = 1 << 19, ImGuiTableFlags_NoClip = 1 << 20, ImGuiTableFlags_PadOuterX = 1 << 21, ImGuiTableFlags_NoPadOuterX = 1 << 22, ImGuiTableFlags_NoPadInnerX = 1 << 23, ImGuiTableFlags_ScrollX = 1 << 24, ImGuiTableFlags_ScrollY = 1 << 25, ImGuiTableFlags_SortMulti = 1 << 26, ImGuiTableFlags_SortTristate = 1 << 27, ImGuiTableFlags_HighlightHoveredColumn = 1 << 28, ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame, }ImGuiTableFlags_; typedef enum { ImGuiTableColumnFlags_None = 0, ImGuiTableColumnFlags_Disabled = 1 << 0, ImGuiTableColumnFlags_DefaultHide = 1 << 1, ImGuiTableColumnFlags_DefaultSort = 1 << 2, ImGuiTableColumnFlags_WidthStretch = 1 << 3, ImGuiTableColumnFlags_WidthFixed = 1 << 4, ImGuiTableColumnFlags_NoResize = 1 << 5, ImGuiTableColumnFlags_NoReorder = 1 << 6, ImGuiTableColumnFlags_NoHide = 1 << 7, ImGuiTableColumnFlags_NoClip = 1 << 8, ImGuiTableColumnFlags_NoSort = 1 << 9, ImGuiTableColumnFlags_NoSortAscending = 1 << 10, ImGuiTableColumnFlags_NoSortDescending = 1 << 11, ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, ImGuiTableColumnFlags_IndentEnable = 1 << 16, ImGuiTableColumnFlags_IndentDisable = 1 << 17, ImGuiTableColumnFlags_AngledHeader = 1 << 18, ImGuiTableColumnFlags_IsEnabled = 1 << 24, ImGuiTableColumnFlags_IsVisible = 1 << 25, ImGuiTableColumnFlags_IsSorted = 1 << 26, ImGuiTableColumnFlags_IsHovered = 1 << 27, ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable, ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered, ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30, }ImGuiTableColumnFlags_; typedef enum { ImGuiTableRowFlags_None = 0, ImGuiTableRowFlags_Headers = 1 << 0, }ImGuiTableRowFlags_; typedef enum { ImGuiTableBgTarget_None = 0, ImGuiTableBgTarget_RowBg0 = 1, ImGuiTableBgTarget_RowBg1 = 2, ImGuiTableBgTarget_CellBg = 3, }ImGuiTableBgTarget_; struct ImGuiTableSortSpecs { const ImGuiTableColumnSortSpecs* Specs; int SpecsCount; bool SpecsDirty; }; struct ImGuiTableColumnSortSpecs { ImGuiID ColumnUserID; ImS16 ColumnIndex; ImS16 SortOrder; ImGuiSortDirection SortDirection; }; struct ImGuiStyle { float FontSizeBase; float FontScaleMain; float FontScaleDpi; float Alpha; float DisabledAlpha; ImVec2 WindowPadding; float WindowRounding; float WindowBorderSize; float WindowBorderHoverPadding; ImVec2 WindowMinSize; ImVec2 WindowTitleAlign; ImGuiDir WindowMenuButtonPosition; float ChildRounding; float ChildBorderSize; float PopupRounding; float PopupBorderSize; ImVec2 FramePadding; float FrameRounding; float FrameBorderSize; ImVec2 ItemSpacing; ImVec2 ItemInnerSpacing; ImVec2 CellPadding; ImVec2 TouchExtraPadding; float IndentSpacing; float ColumnsMinSpacing; float ScrollbarSize; float ScrollbarRounding; float GrabMinSize; float GrabRounding; float LogSliderDeadzone; float ImageBorderSize; float TabRounding; float TabBorderSize; float TabCloseButtonMinWidthSelected; float TabCloseButtonMinWidthUnselected; float TabBarBorderSize; float TabBarOverlineSize; float TableAngledHeadersAngle; ImVec2 TableAngledHeadersTextAlign; ImGuiTreeNodeFlags TreeLinesFlags; float TreeLinesSize; float TreeLinesRounding; ImGuiDir ColorButtonPosition; ImVec2 ButtonTextAlign; ImVec2 SelectableTextAlign; float SeparatorTextBorderSize; ImVec2 SeparatorTextAlign; ImVec2 SeparatorTextPadding; ImVec2 DisplayWindowPadding; ImVec2 DisplaySafeAreaPadding; float DockingSeparatorSize; float MouseCursorScale; bool AntiAliasedLines; bool AntiAliasedLinesUseTex; bool AntiAliasedFill; float CurveTessellationTol; float CircleTessellationMaxError; ImVec4 Colors[ImGuiCol_COUNT]; float HoverStationaryDelay; float HoverDelayShort; float HoverDelayNormal; ImGuiHoveredFlags HoverFlagsForTooltipMouse; ImGuiHoveredFlags HoverFlagsForTooltipNav; float _MainScale; float _NextFrameFontSizeBase; }; struct ImGuiKeyData { bool Down; float DownDuration; float DownDurationPrev; float AnalogValue; }; typedef struct ImVector_ImWchar {int Size;int Capacity;ImWchar* Data;} ImVector_ImWchar; struct ImGuiIO { ImGuiConfigFlags ConfigFlags; ImGuiBackendFlags BackendFlags; ImVec2 DisplaySize; ImVec2 DisplayFramebufferScale; float DeltaTime; float IniSavingRate; const char* IniFilename; const char* LogFilename; void* UserData; ImFontAtlas*Fonts; ImFont* FontDefault; bool FontAllowUserScaling; bool ConfigNavSwapGamepadButtons; bool ConfigNavMoveSetMousePos; bool ConfigNavCaptureKeyboard; bool ConfigNavEscapeClearFocusItem; bool ConfigNavEscapeClearFocusWindow; bool ConfigNavCursorVisibleAuto; bool ConfigNavCursorVisibleAlways; bool ConfigDockingNoSplit; bool ConfigDockingWithShift; bool ConfigDockingAlwaysTabBar; bool ConfigDockingTransparentPayload; bool ConfigViewportsNoAutoMerge; bool ConfigViewportsNoTaskBarIcon; bool ConfigViewportsNoDecoration; bool ConfigViewportsNoDefaultParent; bool ConfigDpiScaleFonts; bool ConfigDpiScaleViewports; bool MouseDrawCursor; bool ConfigMacOSXBehaviors; bool ConfigInputTrickleEventQueue; bool ConfigInputTextCursorBlink; bool ConfigInputTextEnterKeepActive; bool ConfigDragClickToInputText; bool ConfigWindowsResizeFromEdges; bool ConfigWindowsMoveFromTitleBarOnly; bool ConfigWindowsCopyContentsWithCtrlC; bool ConfigScrollbarScrollByPage; float ConfigMemoryCompactTimer; float MouseDoubleClickTime; float MouseDoubleClickMaxDist; float MouseDragThreshold; float KeyRepeatDelay; float KeyRepeatRate; bool ConfigErrorRecovery; bool ConfigErrorRecoveryEnableAssert; bool ConfigErrorRecoveryEnableDebugLog; bool ConfigErrorRecoveryEnableTooltip; bool ConfigDebugIsDebuggerPresent; bool ConfigDebugHighlightIdConflicts; bool ConfigDebugHighlightIdConflictsShowItemPicker; bool ConfigDebugBeginReturnValueOnce; bool ConfigDebugBeginReturnValueLoop; bool ConfigDebugIgnoreFocusLoss; bool ConfigDebugIniSettings; const char* BackendPlatformName; const char* BackendRendererName; void* BackendPlatformUserData; void* BackendRendererUserData; void* BackendLanguageUserData; bool WantCaptureMouse; bool WantCaptureKeyboard; bool WantTextInput; bool WantSetMousePos; bool WantSaveIniSettings; bool NavActive; bool NavVisible; float Framerate; int MetricsRenderVertices; int MetricsRenderIndices; int MetricsRenderWindows; int MetricsActiveWindows; ImVec2 MouseDelta; ImGuiContext* Ctx; ImVec2 MousePos; bool MouseDown[5]; float MouseWheel; float MouseWheelH; ImGuiMouseSource MouseSource; ImGuiID MouseHoveredViewport; bool KeyCtrl; bool KeyShift; bool KeyAlt; bool KeySuper; ImGuiKeyChord KeyMods; ImGuiKeyData KeysData[ImGuiKey_NamedKey_COUNT]; bool WantCaptureMouseUnlessPopupClose; ImVec2 MousePosPrev; ImVec2 MouseClickedPos[5]; double MouseClickedTime[5]; bool MouseClicked[5]; bool MouseDoubleClicked[5]; ImU16 MouseClickedCount[5]; ImU16 MouseClickedLastCount[5]; bool MouseReleased[5]; double MouseReleasedTime[5]; bool MouseDownOwned[5]; bool MouseDownOwnedUnlessPopupClose[5]; bool MouseWheelRequestAxisSwap; bool MouseCtrlLeftAsRightClick; float MouseDownDuration[5]; float MouseDownDurationPrev[5]; ImVec2 MouseDragMaxDistanceAbs[5]; float MouseDragMaxDistanceSqr[5]; float PenPressure; bool AppFocusLost; bool AppAcceptingEvents; ImWchar16 InputQueueSurrogate; ImVector_ImWchar InputQueueCharacters; }; struct ImGuiInputTextCallbackData { ImGuiContext* Ctx; ImGuiInputTextFlags EventFlag; ImGuiInputTextFlags Flags; void* UserData; ImWchar EventChar; ImGuiKey EventKey; char* Buf; int BufTextLen; int BufSize; bool BufDirty; int CursorPos; int SelectionStart; int SelectionEnd; }; struct ImGuiSizeCallbackData { void* UserData; ImVec2 Pos; ImVec2 CurrentSize; ImVec2 DesiredSize; }; struct ImGuiWindowClass { ImGuiID ClassId; ImGuiID ParentViewportId; ImGuiID FocusRouteParentWindowId; ImGuiViewportFlags ViewportFlagsOverrideSet; ImGuiViewportFlags ViewportFlagsOverrideClear; ImGuiTabItemFlags TabItemFlagsOverrideSet; ImGuiDockNodeFlags DockNodeFlagsOverrideSet; bool DockingAlwaysTabBar; bool DockingAllowUnclassed; }; struct ImGuiPayload { void* Data; int DataSize; ImGuiID SourceId; ImGuiID SourceParentId; int DataFrameCount; char DataType[32 + 1]; bool Preview; bool Delivery; }; struct ImGuiOnceUponAFrame { int RefFrame; }; struct ImGuiTextRange { const char* b; const char* e; }; typedef struct ImGuiTextRange ImGuiTextRange; typedef struct ImVector_ImGuiTextRange {int Size;int Capacity;ImGuiTextRange* Data;} ImVector_ImGuiTextRange; struct ImGuiTextFilter { char InputBuf[256]; ImVector_ImGuiTextRange Filters; int CountGrep; }; typedef struct ImGuiTextRange ImGuiTextRange; typedef struct ImVector_char {int Size;int Capacity;char* Data;} ImVector_char; struct ImGuiTextBuffer { ImVector_char Buf; }; struct ImGuiStoragePair { ImGuiID key; union { int val_i; float val_f; void* val_p; }; }; typedef struct ImVector_ImGuiStoragePair {int Size;int Capacity;ImGuiStoragePair* Data;} ImVector_ImGuiStoragePair; struct ImGuiStorage { ImVector_ImGuiStoragePair Data; }; struct ImGuiListClipper { ImGuiContext* Ctx; int DisplayStart; int DisplayEnd; int ItemsCount; float ItemsHeight; double StartPosY; double StartSeekOffsetY; void* TempData; }; struct ImColor { ImVec4 Value; }; typedef enum { ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5, ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, ImGuiMultiSelectFlags_BoxSelect2d = 1 << 7, ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 8, ImGuiMultiSelectFlags_ClearOnEscape = 1 << 9, ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 10, ImGuiMultiSelectFlags_ScopeWindow = 1 << 11, ImGuiMultiSelectFlags_ScopeRect = 1 << 12, ImGuiMultiSelectFlags_SelectOnClick = 1 << 13, ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, ImGuiMultiSelectFlags_NavWrapX = 1 << 16, }ImGuiMultiSelectFlags_; typedef struct ImVector_ImGuiSelectionRequest {int Size;int Capacity;ImGuiSelectionRequest* Data;} ImVector_ImGuiSelectionRequest; struct ImGuiMultiSelectIO { ImVector_ImGuiSelectionRequest Requests; ImGuiSelectionUserData RangeSrcItem; ImGuiSelectionUserData NavIdItem; bool NavIdSelected; bool RangeSrcReset; int ItemsCount; }; typedef enum { ImGuiSelectionRequestType_None = 0, ImGuiSelectionRequestType_SetAll, ImGuiSelectionRequestType_SetRange, }ImGuiSelectionRequestType; struct ImGuiSelectionRequest { ImGuiSelectionRequestType Type; bool Selected; ImS8 RangeDirection; ImGuiSelectionUserData RangeFirstItem; ImGuiSelectionUserData RangeLastItem; }; struct ImGuiSelectionBasicStorage { int Size; bool PreserveOrder; void* UserData; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); int _SelectionOrder; ImGuiStorage _Storage; }; struct ImGuiSelectionExternalStorage { void* UserData; void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); }; typedef unsigned short ImDrawIdx; typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); struct ImDrawCmd { ImVec4 ClipRect; ImTextureRef TexRef; unsigned int VtxOffset; unsigned int IdxOffset; unsigned int ElemCount; ImDrawCallback UserCallback; void* UserCallbackData; int UserCallbackDataSize; int UserCallbackDataOffset; }; struct ImDrawVert { ImVec2 pos; ImVec2 uv; ImU32 col; }; typedef struct ImDrawCmdHeader ImDrawCmdHeader; struct ImDrawCmdHeader { ImVec4 ClipRect; ImTextureRef TexRef; unsigned int VtxOffset; }; typedef struct ImVector_ImDrawCmd {int Size;int Capacity;ImDrawCmd* Data;} ImVector_ImDrawCmd; typedef struct ImVector_ImDrawIdx {int Size;int Capacity;ImDrawIdx* Data;} ImVector_ImDrawIdx; struct ImDrawChannel { ImVector_ImDrawCmd _CmdBuffer; ImVector_ImDrawIdx _IdxBuffer; }; typedef struct ImVector_ImDrawChannel {int Size;int Capacity;ImDrawChannel* Data;} ImVector_ImDrawChannel; struct ImDrawListSplitter { int _Current; int _Count; ImVector_ImDrawChannel _Channels; }; typedef enum { ImDrawFlags_None = 0, ImDrawFlags_Closed = 1 << 0, ImDrawFlags_RoundCornersTopLeft = 1 << 4, ImDrawFlags_RoundCornersTopRight = 1 << 5, ImDrawFlags_RoundCornersBottomLeft = 1 << 6, ImDrawFlags_RoundCornersBottomRight = 1 << 7, ImDrawFlags_RoundCornersNone = 1 << 8, ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft, ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, }ImDrawFlags_; typedef enum { ImDrawListFlags_None = 0, ImDrawListFlags_AntiAliasedLines = 1 << 0, ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, ImDrawListFlags_AntiAliasedFill = 1 << 2, ImDrawListFlags_AllowVtxOffset = 1 << 3, }ImDrawListFlags_; typedef struct ImVector_ImDrawVert {int Size;int Capacity;ImDrawVert* Data;} ImVector_ImDrawVert; typedef struct ImVector_ImVec2 {int Size;int Capacity;ImVec2* Data;} ImVector_ImVec2; typedef struct ImVector_ImVec4 {int Size;int Capacity;ImVec4* Data;} ImVector_ImVec4; typedef struct ImVector_ImTextureRef {int Size;int Capacity;ImTextureRef* Data;} ImVector_ImTextureRef; typedef struct ImVector_ImU8 {int Size;int Capacity;ImU8* Data;} ImVector_ImU8; struct ImDrawList { ImVector_ImDrawCmd CmdBuffer; ImVector_ImDrawIdx IdxBuffer; ImVector_ImDrawVert VtxBuffer; ImDrawListFlags Flags; unsigned int _VtxCurrentIdx; ImDrawListSharedData* _Data; ImDrawVert* _VtxWritePtr; ImDrawIdx* _IdxWritePtr; ImVector_ImVec2 _Path; ImDrawCmdHeader _CmdHeader; ImDrawListSplitter _Splitter; ImVector_ImVec4 _ClipRectStack; ImVector_ImTextureRef _TextureStack; ImVector_ImU8 _CallbacksDataBuf; float _FringeScale; const char* _OwnerName; }; typedef struct ImVector_ImDrawListPtr {int Size;int Capacity;ImDrawList** Data;} ImVector_ImDrawListPtr; typedef struct ImVector_ImTextureDataPtr {int Size;int Capacity;ImTextureData** Data;} ImVector_ImTextureDataPtr; struct ImDrawData { bool Valid; int CmdListsCount; int TotalIdxCount; int TotalVtxCount; ImVector_ImDrawListPtr CmdLists; ImVec2 DisplayPos; ImVec2 DisplaySize; ImVec2 FramebufferScale; ImGuiViewport* OwnerViewport; ImVector_ImTextureDataPtr* Textures; }; typedef enum { ImTextureFormat_RGBA32, ImTextureFormat_Alpha8, }ImTextureFormat; typedef enum { ImTextureStatus_OK, ImTextureStatus_Destroyed, ImTextureStatus_WantCreate, ImTextureStatus_WantUpdates, ImTextureStatus_WantDestroy, }ImTextureStatus; struct ImTextureRect { unsigned short x, y; unsigned short w, h; }; typedef struct ImVector_ImTextureRect {int Size;int Capacity;ImTextureRect* Data;} ImVector_ImTextureRect; struct ImTextureData { int UniqueID; ImTextureStatus Status; void* BackendUserData; ImTextureID TexID; ImTextureFormat Format; int Width; int Height; int BytesPerPixel; unsigned char* Pixels; ImTextureRect UsedRect; ImTextureRect UpdateRect; ImVector_ImTextureRect Updates; int UnusedFrames; unsigned short RefCount; bool UseColors; bool WantDestroyNextFrame; }; struct ImFontConfig { char Name[40]; void* FontData; int FontDataSize; bool FontDataOwnedByAtlas; bool MergeMode; bool PixelSnapH; bool PixelSnapV; ImS8 FontNo; ImS8 OversampleH; ImS8 OversampleV; float SizePixels; const ImWchar* GlyphRanges; const ImWchar* GlyphExcludeRanges; ImVec2 GlyphOffset; float GlyphMinAdvanceX; float GlyphMaxAdvanceX; float GlyphExtraAdvanceX; unsigned int FontLoaderFlags; float RasterizerMultiply; float RasterizerDensity; ImWchar EllipsisChar; ImFontFlags Flags; ImFont* DstFont; const ImFontLoader* FontLoader; void* FontLoaderData; }; struct ImFontGlyph { unsigned int Colored : 1; unsigned int Visible : 1; unsigned int SourceIdx : 4; unsigned int Codepoint : 26; float AdvanceX; float X0, Y0, X1, Y1; float U0, V0, U1, V1; int PackId; }; typedef struct ImVector_ImU32 {int Size;int Capacity;ImU32* Data;} ImVector_ImU32; struct ImFontGlyphRangesBuilder { ImVector_ImU32 UsedChars; }; typedef int ImFontAtlasRectId; struct ImFontAtlasRect { unsigned short x, y; unsigned short w, h; ImVec2 uv0, uv1; }; typedef enum { ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, ImFontAtlasFlags_NoMouseCursors = 1 << 1, ImFontAtlasFlags_NoBakedLines = 1 << 2, }ImFontAtlasFlags_; typedef struct ImVector_ImFontPtr {int Size;int Capacity;ImFont** Data;} ImVector_ImFontPtr; typedef struct ImVector_ImFontConfig {int Size;int Capacity;ImFontConfig* Data;} ImVector_ImFontConfig; typedef struct ImVector_ImDrawListSharedDataPtr {int Size;int Capacity;ImDrawListSharedData** Data;} ImVector_ImDrawListSharedDataPtr; struct ImFontAtlas { ImFontAtlasFlags Flags; ImTextureFormat TexDesiredFormat; int TexGlyphPadding; int TexMinWidth; int TexMinHeight; int TexMaxWidth; int TexMaxHeight; void* UserData; ImTextureRef TexRef; ImTextureData* TexData; ImVector_ImTextureDataPtr TexList; bool Locked; bool RendererHasTextures; bool TexIsBuilt; bool TexPixelsUseColors; ImVec2 TexUvScale; ImVec2 TexUvWhitePixel; ImVector_ImFontPtr Fonts; ImVector_ImFontConfig Sources; ImVec4 TexUvLines[(32) + 1]; int TexNextUniqueID; int FontNextUniqueID; ImVector_ImDrawListSharedDataPtr DrawListSharedDatas; ImFontAtlasBuilder* Builder; const ImFontLoader* FontLoader; const char* FontLoaderName; void* FontLoaderData; unsigned int FontLoaderFlags; int RefCount; ImGuiContext* OwnerContext; }; typedef struct ImVector_float {int Size;int Capacity;float* Data;} ImVector_float; typedef struct ImVector_ImU16 {int Size;int Capacity;ImU16* Data;} ImVector_ImU16; typedef struct ImVector_ImFontGlyph {int Size;int Capacity;ImFontGlyph* Data;} ImVector_ImFontGlyph; struct ImFontBaked { ImVector_float IndexAdvanceX; float FallbackAdvanceX; float Size; float RasterizerDensity; ImVector_ImU16 IndexLookup; ImVector_ImFontGlyph Glyphs; int FallbackGlyphIndex; float Ascent, Descent; unsigned int MetricsTotalSurface:26; unsigned int WantDestroy:1; unsigned int LockLoadingFallback:1; int LastUsedFrame; ImGuiID BakedId; ImFont* ContainerFont; void* FontLoaderDatas; }; typedef enum { ImFontFlags_None = 0, ImFontFlags_NoLoadError = 1 << 1, ImFontFlags_NoLoadGlyphs = 1 << 2, ImFontFlags_LockBakedSizes = 1 << 3, }ImFontFlags_; typedef struct ImVector_ImFontConfigPtr {int Size;int Capacity;ImFontConfig** Data;} ImVector_ImFontConfigPtr; struct ImFont { ImFontBaked* LastBaked; ImFontAtlas* ContainerAtlas; ImFontFlags Flags; float CurrentRasterizerDensity; ImGuiID FontId; float LegacySize; ImVector_ImFontConfigPtr Sources; ImWchar EllipsisChar; ImWchar FallbackChar; ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX +1)/8192/8]; bool EllipsisAutoBake; ImGuiStorage RemapPairs; }; typedef enum { ImGuiViewportFlags_None = 0, ImGuiViewportFlags_IsPlatformWindow = 1 << 0, ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, ImGuiViewportFlags_OwnedByApp = 1 << 2, ImGuiViewportFlags_NoDecoration = 1 << 3, ImGuiViewportFlags_NoTaskBarIcon = 1 << 4, ImGuiViewportFlags_NoFocusOnAppearing = 1 << 5, ImGuiViewportFlags_NoFocusOnClick = 1 << 6, ImGuiViewportFlags_NoInputs = 1 << 7, ImGuiViewportFlags_NoRendererClear = 1 << 8, ImGuiViewportFlags_NoAutoMerge = 1 << 9, ImGuiViewportFlags_TopMost = 1 << 10, ImGuiViewportFlags_CanHostOtherWindows = 1 << 11, ImGuiViewportFlags_IsMinimized = 1 << 12, ImGuiViewportFlags_IsFocused = 1 << 13, }ImGuiViewportFlags_; struct ImGuiViewport { ImGuiID ID; ImGuiViewportFlags Flags; ImVec2 Pos; ImVec2 Size; ImVec2 FramebufferScale; ImVec2 WorkPos; ImVec2 WorkSize; float DpiScale; ImGuiID ParentViewportId; ImDrawData* DrawData; void* RendererUserData; void* PlatformUserData; void* PlatformHandle; void* PlatformHandleRaw; bool PlatformWindowCreated; bool PlatformRequestMove; bool PlatformRequestResize; bool PlatformRequestClose; }; typedef struct ImVector_ImGuiPlatformMonitor {int Size;int Capacity;ImGuiPlatformMonitor* Data;} ImVector_ImGuiPlatformMonitor; typedef struct ImVector_ImGuiViewportPtr {int Size;int Capacity;ImGuiViewport** Data;} ImVector_ImGuiViewportPtr; struct ImGuiPlatformIO { const char* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx); void (*Platform_SetClipboardTextFn)(ImGuiContext* ctx, const char* text); void* Platform_ClipboardUserData; bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path); void* Platform_OpenInShellUserData; void (*Platform_SetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); void* Platform_ImeUserData; ImWchar Platform_LocaleDecimalPoint; int Renderer_TextureMaxWidth; int Renderer_TextureMaxHeight; void* Renderer_RenderState; void (*Platform_CreateWindow)(ImGuiViewport* vp); void (*Platform_DestroyWindow)(ImGuiViewport* vp); void (*Platform_ShowWindow)(ImGuiViewport* vp); void (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos); ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp); void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); ImVec2 (*Platform_GetWindowFramebufferScale)(ImGuiViewport* vp); void (*Platform_SetWindowFocus)(ImGuiViewport* vp); bool (*Platform_GetWindowFocus)(ImGuiViewport* vp); bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str); void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); void (*Platform_UpdateWindow)(ImGuiViewport* vp); void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); void (*Platform_OnChangedViewport)(ImGuiViewport* vp); ImVec4 (*Platform_GetWindowWorkAreaInsets)(ImGuiViewport* vp); int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); void (*Renderer_CreateWindow)(ImGuiViewport* vp); void (*Renderer_DestroyWindow)(ImGuiViewport* vp); void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); ImVector_ImGuiPlatformMonitor Monitors; ImVector_ImTextureDataPtr Textures; ImVector_ImGuiViewportPtr Viewports; }; struct ImGuiPlatformMonitor { ImVec2 MainPos, MainSize; ImVec2 WorkPos, WorkSize; float DpiScale; void* PlatformHandle; }; struct ImGuiPlatformImeData { bool WantVisible; bool WantTextInput; ImVec2 InputPos; float InputLineHeight; ImGuiID ViewportId; }; struct ImBitVector; struct ImRect; struct ImGuiTextIndex; struct ImDrawDataBuilder; struct ImDrawListSharedData; struct ImFontAtlasBuilder; struct ImFontAtlasPostProcessData; struct ImFontAtlasRectEntry; struct ImGuiBoxSelectState; struct ImGuiColorMod; struct ImGuiContext; struct ImGuiContextHook; struct ImGuiDataTypeInfo; struct ImGuiDeactivatedItemData; struct ImGuiDockContext; struct ImGuiDockRequest; struct ImGuiDockNode; struct ImGuiDockNodeSettings; struct ImGuiErrorRecoveryState; struct ImGuiGroupData; struct ImGuiInputTextState; struct ImGuiInputTextDeactivateData; struct ImGuiLastItemData; struct ImGuiLocEntry; struct ImGuiMenuColumns; struct ImGuiMultiSelectState; struct ImGuiMultiSelectTempData; struct ImGuiNavItemData; struct ImGuiMetricsConfig; struct ImGuiNextWindowData; struct ImGuiNextItemData; struct ImGuiOldColumnData; struct ImGuiOldColumns; struct ImGuiPopupData; struct ImGuiSettingsHandler; struct ImGuiStyleMod; struct ImGuiStyleVarInfo; struct ImGuiTabBar; struct ImGuiTabItem; struct ImGuiTable; struct ImGuiTableHeaderData; struct ImGuiTableColumn; struct ImGuiTableInstanceData; struct ImGuiTableTempData; struct ImGuiTableSettings; struct ImGuiTableColumnsSettings; struct ImGuiTreeNodeStackData; struct ImGuiTypingSelectState; struct ImGuiTypingSelectRequest; struct ImGuiWindow; struct ImGuiWindowDockStyle; struct ImGuiWindowTempData; struct ImGuiWindowSettings; typedef int ImGuiDataAuthority; typedef int ImGuiLayoutType; typedef int ImGuiActivateFlags; typedef int ImGuiDebugLogFlags; typedef int ImGuiFocusRequestFlags; typedef int ImGuiItemStatusFlags; typedef int ImGuiOldColumnFlags; typedef int ImGuiLogFlags; typedef int ImGuiNavRenderCursorFlags; typedef int ImGuiNavMoveFlags; typedef int ImGuiNextItemDataFlags; typedef int ImGuiNextWindowDataFlags; typedef int ImGuiScrollFlags; typedef int ImGuiSeparatorFlags; typedef int ImGuiTextFlags; typedef int ImGuiTooltipFlags; typedef int ImGuiTypingSelectFlags; typedef int ImGuiWindowRefreshFlags; typedef ImS16 ImGuiTableColumnIdx; typedef ImU16 ImGuiTableDrawChannelIdx; extern ImGuiContext* GImGui; typedef FILE* ImFileHandle; typedef struct ImVec1 ImVec1; struct ImVec1 { float x; }; typedef struct ImVec2i ImVec2i; struct ImVec2i { int x, y; }; typedef struct ImVec2ih ImVec2ih; struct ImVec2ih { short x, y; }; struct ImRect { ImVec2 Min; ImVec2 Max; }; typedef ImU32* ImBitArrayPtr; struct ImBitVector { ImVector_ImU32 Storage; }; typedef int ImPoolIdx; typedef struct ImVector_int {int Size;int Capacity;int* Data;} ImVector_int; struct ImGuiTextIndex { ImVector_int LineOffsets; int EndOffset; }; struct ImDrawListSharedData { ImVec2 TexUvWhitePixel; const ImVec4* TexUvLines; ImFontAtlas* FontAtlas; ImFont* Font; float FontSize; float FontScale; float CurveTessellationTol; float CircleSegmentMaxError; float InitialFringeScale; ImDrawListFlags InitialFlags; ImVec4 ClipRectFullscreen; ImVector_ImVec2 TempBuffer; ImVector_ImDrawListPtr DrawLists; ImGuiContext* Context; ImVec2 ArcFastVtx[48]; float ArcFastRadiusCutoff; ImU8 CircleSegmentCounts[64]; }; struct ImDrawDataBuilder { ImVector_ImDrawListPtr* Layers[2]; ImVector_ImDrawListPtr LayerData1; }; typedef struct ImFontStackData ImFontStackData; struct ImFontStackData { ImFont* Font; float FontSizeBeforeScaling; float FontSizeAfterScaling; }; struct ImGuiStyleVarInfo { ImU32 Count : 8; ImGuiDataType DataType : 8; ImU32 Offset : 16; }; struct ImGuiColorMod { ImGuiCol Col; ImVec4 BackupValue; }; struct ImGuiStyleMod { ImGuiStyleVar VarIdx; union { int BackupInt[2]; float BackupFloat[2]; }; }; typedef struct ImGuiDataTypeStorage ImGuiDataTypeStorage; struct ImGuiDataTypeStorage { ImU8 Data[8]; }; struct ImGuiDataTypeInfo { size_t Size; const char* Name; const char* PrintFmt; const char* ScanFmt; }; typedef enum { ImGuiDataType_Pointer = ImGuiDataType_COUNT, ImGuiDataType_ID, }ImGuiDataTypePrivate_; typedef enum { ImGuiItemFlags_Disabled = 1 << 10, ImGuiItemFlags_ReadOnly = 1 << 11, ImGuiItemFlags_MixedValue = 1 << 12, ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, ImGuiItemFlags_AllowOverlap = 1 << 14, ImGuiItemFlags_NoNavDisableMouseHover = 1 << 15, ImGuiItemFlags_NoMarkEdited = 1 << 16, ImGuiItemFlags_NoFocus = 1 << 17, ImGuiItemFlags_Inputable = 1 << 20, ImGuiItemFlags_HasSelectionUserData = 1 << 21, ImGuiItemFlags_IsMultiSelect = 1 << 22, ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups, }ImGuiItemFlagsPrivate_; typedef enum { ImGuiItemStatusFlags_None = 0, ImGuiItemStatusFlags_HoveredRect = 1 << 0, ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, ImGuiItemStatusFlags_Edited = 1 << 2, ImGuiItemStatusFlags_ToggledSelection = 1 << 3, ImGuiItemStatusFlags_ToggledOpen = 1 << 4, ImGuiItemStatusFlags_HasDeactivated = 1 << 5, ImGuiItemStatusFlags_Deactivated = 1 << 6, ImGuiItemStatusFlags_HoveredWindow = 1 << 7, ImGuiItemStatusFlags_Visible = 1 << 8, ImGuiItemStatusFlags_HasClipRect = 1 << 9, ImGuiItemStatusFlags_HasShortcut = 1 << 10, }ImGuiItemStatusFlags_; typedef enum { ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay, ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary, ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_, }ImGuiHoveredFlagsPrivate_; typedef enum { ImGuiInputTextFlags_Multiline = 1 << 26, ImGuiInputTextFlags_MergedItem = 1 << 27, ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 28, }ImGuiInputTextFlagsPrivate_; typedef enum { ImGuiButtonFlags_PressedOnClick = 1 << 4, ImGuiButtonFlags_PressedOnClickRelease = 1 << 5, ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, ImGuiButtonFlags_PressedOnRelease = 1 << 7, ImGuiButtonFlags_PressedOnDoubleClick = 1 << 8, ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, ImGuiButtonFlags_FlattenChildren = 1 << 11, ImGuiButtonFlags_AllowOverlap = 1 << 12, ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, ImGuiButtonFlags_NoKeyModsAllowed = 1 << 16, ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, ImGuiButtonFlags_NoNavFocus = 1 << 18, ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, ImGuiButtonFlags_NoFocus = 1 << 22, ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold, ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease, }ImGuiButtonFlagsPrivate_; typedef enum { ImGuiComboFlags_CustomPreview = 1 << 20, }ImGuiComboFlagsPrivate_; typedef enum { ImGuiSliderFlags_Vertical = 1 << 20, ImGuiSliderFlags_ReadOnly = 1 << 21, }ImGuiSliderFlagsPrivate_; typedef enum { ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, ImGuiSelectableFlags_SelectOnNav = 1 << 21, ImGuiSelectableFlags_SelectOnClick = 1 << 22, ImGuiSelectableFlags_SelectOnRelease = 1 << 23, ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26, ImGuiSelectableFlags_NoSetKeyOwner = 1 << 27, }ImGuiSelectableFlagsPrivate_; typedef enum { ImGuiTreeNodeFlags_NoNavFocus = 1 << 27, ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28, ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29, ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow, ImGuiTreeNodeFlags_DrawLinesMask_ = ImGuiTreeNodeFlags_DrawLinesNone | ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes, }ImGuiTreeNodeFlagsPrivate_; typedef enum { ImGuiSeparatorFlags_None = 0, ImGuiSeparatorFlags_Horizontal = 1 << 0, ImGuiSeparatorFlags_Vertical = 1 << 1, ImGuiSeparatorFlags_SpanAllColumns = 1 << 2, }ImGuiSeparatorFlags_; typedef enum { ImGuiFocusRequestFlags_None = 0, ImGuiFocusRequestFlags_RestoreFocusedChild = 1 << 0, ImGuiFocusRequestFlags_UnlessBelowModal = 1 << 1, }ImGuiFocusRequestFlags_; typedef enum { ImGuiTextFlags_None = 0, ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0, }ImGuiTextFlags_; typedef enum { ImGuiTooltipFlags_None = 0, ImGuiTooltipFlags_OverridePrevious = 1 << 1, }ImGuiTooltipFlags_; typedef enum { ImGuiLayoutType_Horizontal = 0, ImGuiLayoutType_Vertical = 1 }ImGuiLayoutType_; typedef enum { ImGuiLogFlags_None = 0, ImGuiLogFlags_OutputTTY = 1 << 0, ImGuiLogFlags_OutputFile = 1 << 1, ImGuiLogFlags_OutputBuffer = 1 << 2, ImGuiLogFlags_OutputClipboard = 1 << 3, ImGuiLogFlags_OutputMask_ = ImGuiLogFlags_OutputTTY | ImGuiLogFlags_OutputFile | ImGuiLogFlags_OutputBuffer | ImGuiLogFlags_OutputClipboard, }ImGuiLogFlags_; typedef enum { ImGuiAxis_None = -1, ImGuiAxis_X = 0, ImGuiAxis_Y = 1 }ImGuiAxis; typedef enum { ImGuiPlotType_Lines, ImGuiPlotType_Histogram, }ImGuiPlotType; typedef struct ImGuiComboPreviewData ImGuiComboPreviewData; struct ImGuiComboPreviewData { ImRect PreviewRect; ImVec2 BackupCursorPos; ImVec2 BackupCursorMaxPos; ImVec2 BackupCursorPosPrevLine; float BackupPrevLineTextBaseOffset; ImGuiLayoutType BackupLayout; }; struct ImGuiGroupData { ImGuiID WindowID; ImVec2 BackupCursorPos; ImVec2 BackupCursorMaxPos; ImVec2 BackupCursorPosPrevLine; ImVec1 BackupIndent; ImVec1 BackupGroupOffset; ImVec2 BackupCurrLineSize; float BackupCurrLineTextBaseOffset; ImGuiID BackupActiveIdIsAlive; bool BackupDeactivatedIdIsAlive; bool BackupHoveredIdIsAlive; bool BackupIsSameLine; bool EmitItem; }; struct ImGuiMenuColumns { ImU32 TotalWidth; ImU32 NextTotalWidth; ImU16 Spacing; ImU16 OffsetIcon; ImU16 OffsetLabel; ImU16 OffsetShortcut; ImU16 OffsetMark; ImU16 Widths[4]; }; typedef struct ImGuiInputTextDeactivatedState ImGuiInputTextDeactivatedState; struct ImGuiInputTextDeactivatedState { ImGuiID ID; ImVector_char TextA; }; struct STB_TexteditState; typedef STB_TexteditState ImStbTexteditState; struct ImGuiInputTextState { ImGuiContext* Ctx; ImStbTexteditState* Stb; ImGuiInputTextFlags Flags; ImGuiID ID; int TextLen; const char* TextSrc; ImVector_char TextA; ImVector_char TextToRevertTo; ImVector_char CallbackTextBackup; int BufCapacity; ImVec2 Scroll; float CursorAnim; bool CursorFollow; bool SelectedAllMouseLock; bool Edited; bool WantReloadUserBuf; int ReloadSelectionStart; int ReloadSelectionEnd; }; typedef enum { ImGuiWindowRefreshFlags_None = 0, ImGuiWindowRefreshFlags_TryToAvoidRefresh = 1 << 0, ImGuiWindowRefreshFlags_RefreshOnHover = 1 << 1, ImGuiWindowRefreshFlags_RefreshOnFocus = 1 << 2, }ImGuiWindowRefreshFlags_; typedef enum { ImGuiNextWindowDataFlags_None = 0, ImGuiNextWindowDataFlags_HasPos = 1 << 0, ImGuiNextWindowDataFlags_HasSize = 1 << 1, ImGuiNextWindowDataFlags_HasContentSize = 1 << 2, ImGuiNextWindowDataFlags_HasCollapsed = 1 << 3, ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4, ImGuiNextWindowDataFlags_HasFocus = 1 << 5, ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6, ImGuiNextWindowDataFlags_HasScroll = 1 << 7, ImGuiNextWindowDataFlags_HasWindowFlags = 1 << 8, ImGuiNextWindowDataFlags_HasChildFlags = 1 << 9, ImGuiNextWindowDataFlags_HasRefreshPolicy = 1 << 10, ImGuiNextWindowDataFlags_HasViewport = 1 << 11, ImGuiNextWindowDataFlags_HasDock = 1 << 12, ImGuiNextWindowDataFlags_HasWindowClass = 1 << 13, }ImGuiNextWindowDataFlags_; struct ImGuiNextWindowData { ImGuiNextWindowDataFlags HasFlags; ImGuiCond PosCond; ImGuiCond SizeCond; ImGuiCond CollapsedCond; ImGuiCond DockCond; ImVec2 PosVal; ImVec2 PosPivotVal; ImVec2 SizeVal; ImVec2 ContentSizeVal; ImVec2 ScrollVal; ImGuiWindowFlags WindowFlags; ImGuiChildFlags ChildFlags; bool PosUndock; bool CollapsedVal; ImRect SizeConstraintRect; ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; ImGuiID ViewportId; ImGuiID DockId; ImGuiWindowClass WindowClass; ImVec2 MenuBarOffsetMinVal; ImGuiWindowRefreshFlags RefreshFlagsVal; }; typedef enum { ImGuiNextItemDataFlags_None = 0, ImGuiNextItemDataFlags_HasWidth = 1 << 0, ImGuiNextItemDataFlags_HasOpen = 1 << 1, ImGuiNextItemDataFlags_HasShortcut = 1 << 2, ImGuiNextItemDataFlags_HasRefVal = 1 << 3, ImGuiNextItemDataFlags_HasStorageID = 1 << 4, }ImGuiNextItemDataFlags_; struct ImGuiNextItemData { ImGuiNextItemDataFlags HasFlags; ImGuiItemFlags ItemFlags; ImGuiID FocusScopeId; ImGuiSelectionUserData SelectionUserData; float Width; ImGuiKeyChord Shortcut; ImGuiInputFlags ShortcutFlags; bool OpenVal; ImU8 OpenCond; ImGuiDataTypeStorage RefVal; ImGuiID StorageId; }; struct ImGuiLastItemData { ImGuiID ID; ImGuiItemFlags ItemFlags; ImGuiItemStatusFlags StatusFlags; ImRect Rect; ImRect NavRect; ImRect DisplayRect; ImRect ClipRect; ImGuiKeyChord Shortcut; }; struct ImGuiTreeNodeStackData { ImGuiID ID; ImGuiTreeNodeFlags TreeFlags; ImGuiItemFlags ItemFlags; ImRect NavRect; float DrawLinesX1; float DrawLinesToNodesY2; ImGuiTableColumnIdx DrawLinesTableColumn; }; struct ImGuiErrorRecoveryState { short SizeOfWindowStack; short SizeOfIDStack; short SizeOfTreeStack; short SizeOfColorStack; short SizeOfStyleVarStack; short SizeOfFontStack; short SizeOfFocusScopeStack; short SizeOfGroupStack; short SizeOfItemFlagsStack; short SizeOfBeginPopupStack; short SizeOfDisabledStack; }; typedef struct ImGuiWindowStackData ImGuiWindowStackData; struct ImGuiWindowStackData { ImGuiWindow* Window; ImGuiLastItemData ParentLastItemDataBackup; ImGuiErrorRecoveryState StackSizesInBegin; bool DisabledOverrideReenable; float DisabledOverrideReenableAlphaBackup; }; typedef struct ImGuiShrinkWidthItem ImGuiShrinkWidthItem; struct ImGuiShrinkWidthItem { int Index; float Width; float InitialWidth; }; typedef struct ImGuiPtrOrIndex ImGuiPtrOrIndex; struct ImGuiPtrOrIndex { void* Ptr; int Index; }; struct ImGuiDeactivatedItemData { ImGuiID ID; int ElapseFrame; bool HasBeenEditedBefore; bool IsAlive; }; typedef enum { ImGuiPopupPositionPolicy_Default, ImGuiPopupPositionPolicy_ComboBox, ImGuiPopupPositionPolicy_Tooltip, }ImGuiPopupPositionPolicy; struct ImGuiPopupData { ImGuiID PopupId; ImGuiWindow* Window; ImGuiWindow* RestoreNavWindow; int ParentNavLayer; int OpenFrameCount; ImGuiID OpenParentId; ImVec2 OpenPopupPos; ImVec2 OpenMousePos; }; typedef struct ImBitArray_ImGuiKey_NamedKey_COUNT__lessImGuiKey_NamedKey_BEGIN {ImU32 Storage[(ImGuiKey_NamedKey_COUNT+31)>>5];} ImBitArray_ImGuiKey_NamedKey_COUNT__lessImGuiKey_NamedKey_BEGIN; typedef ImBitArray_ImGuiKey_NamedKey_COUNT__lessImGuiKey_NamedKey_BEGIN ImBitArrayForNamedKeys; typedef enum { ImGuiInputEventType_None = 0, ImGuiInputEventType_MousePos, ImGuiInputEventType_MouseWheel, ImGuiInputEventType_MouseButton, ImGuiInputEventType_MouseViewport, ImGuiInputEventType_Key, ImGuiInputEventType_Text, ImGuiInputEventType_Focus, ImGuiInputEventType_COUNT }ImGuiInputEventType; typedef enum { ImGuiInputSource_None = 0, ImGuiInputSource_Mouse, ImGuiInputSource_Keyboard, ImGuiInputSource_Gamepad, ImGuiInputSource_COUNT }ImGuiInputSource; typedef struct ImGuiInputEventMousePos ImGuiInputEventMousePos; struct ImGuiInputEventMousePos { float PosX, PosY; ImGuiMouseSource MouseSource; }; typedef struct ImGuiInputEventMouseWheel ImGuiInputEventMouseWheel; struct ImGuiInputEventMouseWheel { float WheelX, WheelY; ImGuiMouseSource MouseSource; }; typedef struct ImGuiInputEventMouseButton ImGuiInputEventMouseButton; struct ImGuiInputEventMouseButton { int Button; bool Down; ImGuiMouseSource MouseSource; }; typedef struct ImGuiInputEventMouseViewport ImGuiInputEventMouseViewport; struct ImGuiInputEventMouseViewport { ImGuiID HoveredViewportID; }; typedef struct ImGuiInputEventKey ImGuiInputEventKey; struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; typedef struct ImGuiInputEventText ImGuiInputEventText; struct ImGuiInputEventText { unsigned int Char; }; typedef struct ImGuiInputEventAppFocused ImGuiInputEventAppFocused; struct ImGuiInputEventAppFocused { bool Focused; }; typedef struct ImGuiInputEvent ImGuiInputEvent; struct ImGuiInputEvent { ImGuiInputEventType Type; ImGuiInputSource Source; ImU32 EventId; union { ImGuiInputEventMousePos MousePos; ImGuiInputEventMouseWheel MouseWheel; ImGuiInputEventMouseButton MouseButton; ImGuiInputEventMouseViewport MouseViewport; ImGuiInputEventKey Key; ImGuiInputEventText Text; ImGuiInputEventAppFocused AppFocused; }; bool AddedByTestEngine; }; typedef ImS16 ImGuiKeyRoutingIndex; typedef struct ImGuiKeyRoutingData ImGuiKeyRoutingData; struct ImGuiKeyRoutingData { ImGuiKeyRoutingIndex NextEntryIndex; ImU16 Mods; ImU8 RoutingCurrScore; ImU8 RoutingNextScore; ImGuiID RoutingCurr; ImGuiID RoutingNext; }; typedef struct ImGuiKeyRoutingTable ImGuiKeyRoutingTable; typedef struct ImVector_ImGuiKeyRoutingData {int Size;int Capacity;ImGuiKeyRoutingData* Data;} ImVector_ImGuiKeyRoutingData; struct ImGuiKeyRoutingTable { ImGuiKeyRoutingIndex Index[ImGuiKey_NamedKey_COUNT]; ImVector_ImGuiKeyRoutingData Entries; ImVector_ImGuiKeyRoutingData EntriesNext; }; typedef struct ImGuiKeyOwnerData ImGuiKeyOwnerData; struct ImGuiKeyOwnerData { ImGuiID OwnerCurr; ImGuiID OwnerNext; bool LockThisFrame; bool LockUntilRelease; }; typedef enum { ImGuiInputFlags_RepeatRateDefault = 1 << 1, ImGuiInputFlags_RepeatRateNavMove = 1 << 2, ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, ImGuiInputFlags_RepeatUntilRelease = 1 << 4, ImGuiInputFlags_RepeatUntilKeyModsChange = 1 << 5, ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6, ImGuiInputFlags_RepeatUntilOtherKeyPress = 1 << 7, ImGuiInputFlags_LockThisFrame = 1 << 20, ImGuiInputFlags_LockUntilRelease = 1 << 21, ImGuiInputFlags_CondHovered = 1 << 22, ImGuiInputFlags_CondActive = 1 << 23, ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak, ImGuiInputFlags_RepeatUntilMask_ = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress, ImGuiInputFlags_RepeatMask_ = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_, ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, ImGuiInputFlags_RouteTypeMask_ = ImGuiInputFlags_RouteActive | ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteAlways, ImGuiInputFlags_RouteOptionsMask_ = ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused | ImGuiInputFlags_RouteFromRootWindow, ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_RepeatMask_, ImGuiInputFlags_SupportedByIsMouseClicked = ImGuiInputFlags_Repeat, ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_, ImGuiInputFlags_SupportedBySetNextItemShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_ | ImGuiInputFlags_Tooltip, ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease, ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_, }ImGuiInputFlagsPrivate_; typedef struct ImGuiListClipperRange ImGuiListClipperRange; struct ImGuiListClipperRange { int Min; int Max; bool PosToIndexConvert; ImS8 PosToIndexOffsetMin; ImS8 PosToIndexOffsetMax; }; typedef struct ImGuiListClipperData ImGuiListClipperData; typedef struct ImVector_ImGuiListClipperRange {int Size;int Capacity;ImGuiListClipperRange* Data;} ImVector_ImGuiListClipperRange; struct ImGuiListClipperData { ImGuiListClipper* ListClipper; float LossynessOffset; int StepNo; int ItemsFrozen; ImVector_ImGuiListClipperRange Ranges; }; typedef enum { ImGuiActivateFlags_None = 0, ImGuiActivateFlags_PreferInput = 1 << 0, ImGuiActivateFlags_PreferTweak = 1 << 1, ImGuiActivateFlags_TryToPreserveState = 1 << 2, ImGuiActivateFlags_FromTabbing = 1 << 3, ImGuiActivateFlags_FromShortcut = 1 << 4, }ImGuiActivateFlags_; typedef enum { ImGuiScrollFlags_None = 0, ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, ImGuiScrollFlags_AlwaysCenterX = 1 << 4, ImGuiScrollFlags_AlwaysCenterY = 1 << 5, ImGuiScrollFlags_NoScrollParent = 1 << 6, ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY, }ImGuiScrollFlags_; typedef enum { ImGuiNavRenderCursorFlags_None = 0, ImGuiNavRenderCursorFlags_Compact = 1 << 1, ImGuiNavRenderCursorFlags_AlwaysDraw = 1 << 2, ImGuiNavRenderCursorFlags_NoRounding = 1 << 3, }ImGuiNavRenderCursorFlags_; typedef enum { ImGuiNavMoveFlags_None = 0, ImGuiNavMoveFlags_LoopX = 1 << 0, ImGuiNavMoveFlags_LoopY = 1 << 1, ImGuiNavMoveFlags_WrapX = 1 << 2, ImGuiNavMoveFlags_WrapY = 1 << 3, ImGuiNavMoveFlags_WrapMask_ = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY, ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, ImGuiNavMoveFlags_Forwarded = 1 << 7, ImGuiNavMoveFlags_DebugNoResult = 1 << 8, ImGuiNavMoveFlags_FocusApi = 1 << 9, ImGuiNavMoveFlags_IsTabbing = 1 << 10, ImGuiNavMoveFlags_IsPageMove = 1 << 11, ImGuiNavMoveFlags_Activate = 1 << 12, ImGuiNavMoveFlags_NoSelect = 1 << 13, ImGuiNavMoveFlags_NoSetNavCursorVisible = 1 << 14, ImGuiNavMoveFlags_NoClearActiveId = 1 << 15, }ImGuiNavMoveFlags_; typedef enum { ImGuiNavLayer_Main = 0, ImGuiNavLayer_Menu = 1, ImGuiNavLayer_COUNT }ImGuiNavLayer; struct ImGuiNavItemData { ImGuiWindow* Window; ImGuiID ID; ImGuiID FocusScopeId; ImRect RectRel; ImGuiItemFlags ItemFlags; float DistBox; float DistCenter; float DistAxial; ImGuiSelectionUserData SelectionUserData; }; typedef struct ImGuiFocusScopeData ImGuiFocusScopeData; struct ImGuiFocusScopeData { ImGuiID ID; ImGuiID WindowID; }; typedef enum { ImGuiTypingSelectFlags_None = 0, ImGuiTypingSelectFlags_AllowBackspace = 1 << 0, ImGuiTypingSelectFlags_AllowSingleCharMode = 1 << 1, }ImGuiTypingSelectFlags_; struct ImGuiTypingSelectRequest { ImGuiTypingSelectFlags Flags; int SearchBufferLen; const char* SearchBuffer; bool SelectRequest; bool SingleCharMode; ImS8 SingleCharSize; }; struct ImGuiTypingSelectState { ImGuiTypingSelectRequest Request; char SearchBuffer[64]; ImGuiID FocusScope; int LastRequestFrame; float LastRequestTime; bool SingleCharModeLock; }; typedef enum { ImGuiOldColumnFlags_None = 0, ImGuiOldColumnFlags_NoBorder = 1 << 0, ImGuiOldColumnFlags_NoResize = 1 << 1, ImGuiOldColumnFlags_NoPreserveWidths = 1 << 2, ImGuiOldColumnFlags_NoForceWithinWindow = 1 << 3, ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4, }ImGuiOldColumnFlags_; struct ImGuiOldColumnData { float OffsetNorm; float OffsetNormBeforeResize; ImGuiOldColumnFlags Flags; ImRect ClipRect; }; typedef struct ImVector_ImGuiOldColumnData {int Size;int Capacity;ImGuiOldColumnData* Data;} ImVector_ImGuiOldColumnData; struct ImGuiOldColumns { ImGuiID ID; ImGuiOldColumnFlags Flags; bool IsFirstFrame; bool IsBeingResized; int Current; int Count; float OffMinX, OffMaxX; float LineMinY, LineMaxY; float HostCursorPosY; float HostCursorMaxPosX; ImRect HostInitialClipRect; ImRect HostBackupClipRect; ImRect HostBackupParentWorkRect; ImVector_ImGuiOldColumnData Columns; ImDrawListSplitter Splitter; }; struct ImGuiBoxSelectState { ImGuiID ID; bool IsActive; bool IsStarting; bool IsStartedFromVoid; bool IsStartedSetNavIdOnce; bool RequestClear; ImGuiKeyChord KeyMods : 16; ImVec2 StartPosRel; ImVec2 EndPosRel; ImVec2 ScrollAccum; ImGuiWindow* Window; bool UnclipMode; ImRect UnclipRect; ImRect BoxSelectRectPrev; ImRect BoxSelectRectCurr; }; struct ImGuiMultiSelectTempData { ImGuiMultiSelectIO IO; ImGuiMultiSelectState* Storage; ImGuiID FocusScopeId; ImGuiMultiSelectFlags Flags; ImVec2 ScopeRectMin; ImVec2 BackupCursorMaxPos; ImGuiSelectionUserData LastSubmittedItem; ImGuiID BoxSelectId; ImGuiKeyChord KeyMods; ImS8 LoopRequestSetAll; bool IsEndIO; bool IsFocused; bool IsKeyboardSetRange; bool NavIdPassedBy; bool RangeSrcPassedBy; bool RangeDstPassedBy; }; struct ImGuiMultiSelectState { ImGuiWindow* Window; ImGuiID ID; int LastFrameActive; int LastSelectionSize; ImS8 RangeSelected; ImS8 NavIdSelected; ImGuiSelectionUserData RangeSrcItem; ImGuiSelectionUserData NavIdItem; }; typedef enum { ImGuiDockNodeFlags_DockSpace = 1 << 10, ImGuiDockNodeFlags_CentralNode = 1 << 11, ImGuiDockNodeFlags_NoTabBar = 1 << 12, ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, ImGuiDockNodeFlags_NoCloseButton = 1 << 15, ImGuiDockNodeFlags_NoResizeX = 1 << 16, ImGuiDockNodeFlags_NoResizeY = 1 << 17, ImGuiDockNodeFlags_DockedWindowsInFocusRoute= 1 << 18, ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 19, ImGuiDockNodeFlags_NoDockingOverMe = 1 << 20, ImGuiDockNodeFlags_NoDockingOverOther = 1 << 21, ImGuiDockNodeFlags_NoDockingOverEmpty = 1 << 22, ImGuiDockNodeFlags_NoDocking = ImGuiDockNodeFlags_NoDockingOverMe | ImGuiDockNodeFlags_NoDockingOverOther | ImGuiDockNodeFlags_NoDockingOverEmpty | ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoDockingSplitOther, ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, ImGuiDockNodeFlags_NoResizeFlagsMask_ = (int)ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, ImGuiDockNodeFlags_LocalFlagsTransferMask_ = (int)ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | (int)ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, }ImGuiDockNodeFlagsPrivate_; typedef enum { ImGuiDataAuthority_Auto, ImGuiDataAuthority_DockNode, ImGuiDataAuthority_Window, }ImGuiDataAuthority_; typedef enum { ImGuiDockNodeState_Unknown, ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow, ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing, ImGuiDockNodeState_HostWindowVisible, }ImGuiDockNodeState; typedef struct ImVector_ImGuiWindowPtr {int Size;int Capacity;ImGuiWindow** Data;} ImVector_ImGuiWindowPtr; struct ImGuiDockNode { ImGuiID ID; ImGuiDockNodeFlags SharedFlags; ImGuiDockNodeFlags LocalFlags; ImGuiDockNodeFlags LocalFlagsInWindows; ImGuiDockNodeFlags MergedFlags; ImGuiDockNodeState State; ImGuiDockNode* ParentNode; ImGuiDockNode* ChildNodes[2]; ImVector_ImGuiWindowPtr Windows; ImGuiTabBar* TabBar; ImVec2 Pos; ImVec2 Size; ImVec2 SizeRef; ImGuiAxis SplitAxis; ImGuiWindowClass WindowClass; ImU32 LastBgColor; ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; ImGuiDockNode* CentralNode; ImGuiDockNode* OnlyNodeWithWindows; int CountNodeWithWindows; int LastFrameAlive; int LastFrameActive; int LastFrameFocused; ImGuiID LastFocusedNodeId; ImGuiID SelectedTabId; ImGuiID WantCloseTabId; ImGuiID RefViewportId; ImGuiDataAuthority AuthorityForPos :3; ImGuiDataAuthority AuthorityForSize :3; ImGuiDataAuthority AuthorityForViewport :3; bool IsVisible :1; bool IsFocused :1; bool IsBgDrawnThisFrame :1; bool HasCloseButton :1; bool HasWindowMenuButton :1; bool HasCentralNodeChild :1; bool WantCloseAll :1; bool WantLockSizeOnce :1; bool WantMouseMove :1; bool WantHiddenTabBarUpdate :1; bool WantHiddenTabBarToggle :1; }; typedef enum { ImGuiWindowDockStyleCol_Text, ImGuiWindowDockStyleCol_TabHovered, ImGuiWindowDockStyleCol_TabFocused, ImGuiWindowDockStyleCol_TabSelected, ImGuiWindowDockStyleCol_TabSelectedOverline, ImGuiWindowDockStyleCol_TabDimmed, ImGuiWindowDockStyleCol_TabDimmedSelected, ImGuiWindowDockStyleCol_TabDimmedSelectedOverline, ImGuiWindowDockStyleCol_COUNT }ImGuiWindowDockStyleCol; struct ImGuiWindowDockStyle { ImU32 Colors[ImGuiWindowDockStyleCol_COUNT]; }; typedef struct ImVector_ImGuiDockRequest {int Size;int Capacity;ImGuiDockRequest* Data;} ImVector_ImGuiDockRequest; typedef struct ImVector_ImGuiDockNodeSettings {int Size;int Capacity;ImGuiDockNodeSettings* Data;} ImVector_ImGuiDockNodeSettings; struct ImGuiDockContext { ImGuiStorage Nodes; ImVector_ImGuiDockRequest Requests; ImVector_ImGuiDockNodeSettings NodesSettings; bool WantFullRebuild; }; typedef struct ImGuiViewportP ImGuiViewportP; struct ImGuiViewportP { ImGuiViewport _ImGuiViewport; ImGuiWindow* Window; int Idx; int LastFrameActive; int LastFocusedStampCount; ImGuiID LastNameHash; ImVec2 LastPos; ImVec2 LastSize; float Alpha; float LastAlpha; bool LastFocusedHadNavWindow; short PlatformMonitor; int BgFgDrawListsLastFrame[2]; ImDrawList* BgFgDrawLists[2]; ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; ImVec2 LastPlatformPos; ImVec2 LastPlatformSize; ImVec2 LastRendererSize; ImVec2 WorkInsetMin; ImVec2 WorkInsetMax; ImVec2 BuildWorkInsetMin; ImVec2 BuildWorkInsetMax; }; struct ImGuiWindowSettings { ImGuiID ID; ImVec2ih Pos; ImVec2ih Size; ImVec2ih ViewportPos; ImGuiID ViewportId; ImGuiID DockId; ImGuiID ClassId; short DockOrder; bool Collapsed; bool IsChild; bool WantApply; bool WantDelete; }; struct ImGuiSettingsHandler { const char* TypeName; ImGuiID TypeHash; void (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); void (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); void* UserData; }; typedef enum { ImGuiLocKey_VersionStr=0, ImGuiLocKey_TableSizeOne=1, ImGuiLocKey_TableSizeAllFit=2, ImGuiLocKey_TableSizeAllDefault=3, ImGuiLocKey_TableResetOrder=4, ImGuiLocKey_WindowingMainMenuBar=5, ImGuiLocKey_WindowingPopup=6, ImGuiLocKey_WindowingUntitled=7, ImGuiLocKey_OpenLink_s=8, ImGuiLocKey_CopyLink=9, ImGuiLocKey_DockingHideTabBar=10, ImGuiLocKey_DockingHoldShiftToDock=11, ImGuiLocKey_DockingDragToUndockOrMoveNode=12, ImGuiLocKey_COUNT=13, }ImGuiLocKey; struct ImGuiLocEntry { ImGuiLocKey Key; const char* Text; }; typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); typedef enum { ImGuiDebugLogFlags_None = 0, ImGuiDebugLogFlags_EventError = 1 << 0, ImGuiDebugLogFlags_EventActiveId = 1 << 1, ImGuiDebugLogFlags_EventFocus = 1 << 2, ImGuiDebugLogFlags_EventPopup = 1 << 3, ImGuiDebugLogFlags_EventNav = 1 << 4, ImGuiDebugLogFlags_EventClipper = 1 << 5, ImGuiDebugLogFlags_EventSelection = 1 << 6, ImGuiDebugLogFlags_EventIO = 1 << 7, ImGuiDebugLogFlags_EventFont = 1 << 8, ImGuiDebugLogFlags_EventInputRouting = 1 << 9, ImGuiDebugLogFlags_EventDocking = 1 << 10, ImGuiDebugLogFlags_EventViewport = 1 << 11, ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, ImGuiDebugLogFlags_OutputToTTY = 1 << 20, ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, }ImGuiDebugLogFlags_; typedef struct ImGuiDebugAllocEntry ImGuiDebugAllocEntry; struct ImGuiDebugAllocEntry { int FrameCount; ImS16 AllocCount; ImS16 FreeCount; }; typedef struct ImGuiDebugAllocInfo ImGuiDebugAllocInfo; struct ImGuiDebugAllocInfo { int TotalAllocCount; int TotalFreeCount; ImS16 LastEntriesIdx; ImGuiDebugAllocEntry LastEntriesBuf[6]; }; struct ImGuiMetricsConfig { bool ShowDebugLog; bool ShowIDStackTool; bool ShowWindowsRects; bool ShowWindowsBeginOrder; bool ShowTablesRects; bool ShowDrawCmdMesh; bool ShowDrawCmdBoundingBoxes; bool ShowTextEncodingViewer; bool ShowTextureUsedRect; bool ShowDockingNodes; int ShowWindowsRectsType; int ShowTablesRectsType; int HighlightMonitorIdx; ImGuiID HighlightViewportID; bool ShowFontPreview; }; typedef struct ImGuiStackLevelInfo ImGuiStackLevelInfo; struct ImGuiStackLevelInfo { ImGuiID ID; ImS8 QueryFrameCount; bool QuerySuccess; ImGuiDataType DataType : 8; char Desc[57]; }; typedef struct ImGuiIDStackTool ImGuiIDStackTool; typedef struct ImVector_ImGuiStackLevelInfo {int Size;int Capacity;ImGuiStackLevelInfo* Data;} ImVector_ImGuiStackLevelInfo; struct ImGuiIDStackTool { int LastActiveFrame; int StackLevel; ImGuiID QueryId; ImVector_ImGuiStackLevelInfo Results; bool CopyToClipboardOnCtrlC; float CopyToClipboardLastTime; ImGuiTextBuffer ResultPathBuf; }; typedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook); typedef enum { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ }ImGuiContextHookType; struct ImGuiContextHook { ImGuiID HookId; ImGuiContextHookType Type; ImGuiID Owner; ImGuiContextHookCallback Callback; void* UserData; }; typedef struct ImVector_ImFontAtlasPtr {int Size;int Capacity;ImFontAtlas** Data;} ImVector_ImFontAtlasPtr; typedef struct ImVector_ImGuiInputEvent {int Size;int Capacity;ImGuiInputEvent* Data;} ImVector_ImGuiInputEvent; typedef struct ImVector_ImGuiWindowStackData {int Size;int Capacity;ImGuiWindowStackData* Data;} ImVector_ImGuiWindowStackData; typedef struct ImVector_ImGuiColorMod {int Size;int Capacity;ImGuiColorMod* Data;} ImVector_ImGuiColorMod; typedef struct ImVector_ImGuiStyleMod {int Size;int Capacity;ImGuiStyleMod* Data;} ImVector_ImGuiStyleMod; typedef struct ImVector_ImFontStackData {int Size;int Capacity;ImFontStackData* Data;} ImVector_ImFontStackData; typedef struct ImVector_ImGuiFocusScopeData {int Size;int Capacity;ImGuiFocusScopeData* Data;} ImVector_ImGuiFocusScopeData; typedef struct ImVector_ImGuiItemFlags {int Size;int Capacity;ImGuiItemFlags* Data;} ImVector_ImGuiItemFlags; typedef struct ImVector_ImGuiGroupData {int Size;int Capacity;ImGuiGroupData* Data;} ImVector_ImGuiGroupData; typedef struct ImVector_ImGuiPopupData {int Size;int Capacity;ImGuiPopupData* Data;} ImVector_ImGuiPopupData; typedef struct ImVector_ImGuiTreeNodeStackData {int Size;int Capacity;ImGuiTreeNodeStackData* Data;} ImVector_ImGuiTreeNodeStackData; typedef struct ImVector_ImGuiViewportPPtr {int Size;int Capacity;ImGuiViewportP** Data;} ImVector_ImGuiViewportPPtr; typedef struct ImVector_unsigned_char {int Size;int Capacity;unsigned char* Data;} ImVector_unsigned_char; typedef struct ImVector_ImGuiListClipperData {int Size;int Capacity;ImGuiListClipperData* Data;} ImVector_ImGuiListClipperData; typedef struct ImVector_ImGuiTableTempData {int Size;int Capacity;ImGuiTableTempData* Data;} ImVector_ImGuiTableTempData; typedef struct ImVector_ImGuiTable {int Size;int Capacity;ImGuiTable* Data;} ImVector_ImGuiTable; typedef struct ImPool_ImGuiTable {ImVector_ImGuiTable Buf;ImGuiStorage Map;ImPoolIdx FreeIdx;ImPoolIdx AliveCount;} ImPool_ImGuiTable; typedef struct ImVector_ImGuiTabBar {int Size;int Capacity;ImGuiTabBar* Data;} ImVector_ImGuiTabBar; typedef struct ImPool_ImGuiTabBar {ImVector_ImGuiTabBar Buf;ImGuiStorage Map;ImPoolIdx FreeIdx;ImPoolIdx AliveCount;} ImPool_ImGuiTabBar; typedef struct ImVector_ImGuiPtrOrIndex {int Size;int Capacity;ImGuiPtrOrIndex* Data;} ImVector_ImGuiPtrOrIndex; typedef struct ImVector_ImGuiShrinkWidthItem {int Size;int Capacity;ImGuiShrinkWidthItem* Data;} ImVector_ImGuiShrinkWidthItem; typedef struct ImVector_ImGuiMultiSelectTempData {int Size;int Capacity;ImGuiMultiSelectTempData* Data;} ImVector_ImGuiMultiSelectTempData; typedef struct ImVector_ImGuiMultiSelectState {int Size;int Capacity;ImGuiMultiSelectState* Data;} ImVector_ImGuiMultiSelectState; typedef struct ImPool_ImGuiMultiSelectState {ImVector_ImGuiMultiSelectState Buf;ImGuiStorage Map;ImPoolIdx FreeIdx;ImPoolIdx AliveCount;} ImPool_ImGuiMultiSelectState; typedef struct ImVector_ImGuiID {int Size;int Capacity;ImGuiID* Data;} ImVector_ImGuiID; typedef struct ImVector_ImGuiSettingsHandler {int Size;int Capacity;ImGuiSettingsHandler* Data;} ImVector_ImGuiSettingsHandler; typedef struct ImChunkStream_ImGuiWindowSettings {ImVector_char Buf;} ImChunkStream_ImGuiWindowSettings; typedef struct ImChunkStream_ImGuiTableSettings {ImVector_char Buf;} ImChunkStream_ImGuiTableSettings; typedef struct ImVector_ImGuiContextHook {int Size;int Capacity;ImGuiContextHook* Data;} ImVector_ImGuiContextHook; struct ImGuiContext { bool Initialized; ImGuiIO IO; ImGuiPlatformIO PlatformIO; ImGuiStyle Style; ImGuiConfigFlags ConfigFlagsCurrFrame; ImGuiConfigFlags ConfigFlagsLastFrame; ImVector_ImFontAtlasPtr FontAtlases; ImFont* Font; ImFontBaked* FontBaked; float FontSize; float FontSizeBase; float FontBakedScale; float FontRasterizerDensity; float CurrentDpiScale; ImDrawListSharedData DrawListSharedData; double Time; int FrameCount; int FrameCountEnded; int FrameCountPlatformEnded; int FrameCountRendered; ImGuiID WithinEndChildID; bool WithinFrameScope; bool WithinFrameScopeWithImplicitWindow; bool GcCompactAll; bool TestEngineHookItems; void* TestEngine; char ContextName[16]; ImVector_ImGuiInputEvent InputEventsQueue; ImVector_ImGuiInputEvent InputEventsTrail; ImGuiMouseSource InputEventsNextMouseSource; ImU32 InputEventsNextEventId; ImVector_ImGuiWindowPtr Windows; ImVector_ImGuiWindowPtr WindowsFocusOrder; ImVector_ImGuiWindowPtr WindowsTempSortBuffer; ImVector_ImGuiWindowStackData CurrentWindowStack; ImGuiStorage WindowsById; int WindowsActiveCount; float WindowsBorderHoverPadding; ImGuiID DebugBreakInWindow; ImGuiWindow* CurrentWindow; ImGuiWindow* HoveredWindow; ImGuiWindow* HoveredWindowUnderMovingWindow; ImGuiWindow* HoveredWindowBeforeClear; ImGuiWindow* MovingWindow; ImGuiWindow* WheelingWindow; ImVec2 WheelingWindowRefMousePos; int WheelingWindowStartFrame; int WheelingWindowScrolledFrame; float WheelingWindowReleaseTimer; ImVec2 WheelingWindowWheelRemainder; ImVec2 WheelingAxisAvg; ImGuiID DebugDrawIdConflicts; ImGuiID DebugHookIdInfo; ImGuiID HoveredId; ImGuiID HoveredIdPreviousFrame; int HoveredIdPreviousFrameItemCount; float HoveredIdTimer; float HoveredIdNotActiveTimer; bool HoveredIdAllowOverlap; bool HoveredIdIsDisabled; bool ItemUnclipByLog; ImGuiID ActiveId; ImGuiID ActiveIdIsAlive; float ActiveIdTimer; bool ActiveIdIsJustActivated; bool ActiveIdAllowOverlap; bool ActiveIdNoClearOnFocusLoss; bool ActiveIdHasBeenPressedBefore; bool ActiveIdHasBeenEditedBefore; bool ActiveIdHasBeenEditedThisFrame; bool ActiveIdFromShortcut; int ActiveIdMouseButton : 8; ImVec2 ActiveIdClickOffset; ImGuiWindow* ActiveIdWindow; ImGuiInputSource ActiveIdSource; ImGuiID ActiveIdPreviousFrame; ImGuiDeactivatedItemData DeactivatedItemData; ImGuiDataTypeStorage ActiveIdValueOnActivation; ImGuiID LastActiveId; float LastActiveIdTimer; double LastKeyModsChangeTime; double LastKeyModsChangeFromNoneTime; double LastKeyboardKeyPressTime; ImBitArrayForNamedKeys KeysMayBeCharInput; ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT]; ImGuiKeyRoutingTable KeysRoutingTable; ImU32 ActiveIdUsingNavDirMask; bool ActiveIdUsingAllKeyboardKeys; ImGuiKeyChord DebugBreakInShortcutRouting; ImGuiID CurrentFocusScopeId; ImGuiItemFlags CurrentItemFlags; ImGuiID DebugLocateId; ImGuiNextItemData NextItemData; ImGuiLastItemData LastItemData; ImGuiNextWindowData NextWindowData; bool DebugShowGroupRects; ImGuiCol DebugFlashStyleColorIdx; ImVector_ImGuiColorMod ColorStack; ImVector_ImGuiStyleMod StyleVarStack; ImVector_ImFontStackData FontStack; ImVector_ImGuiFocusScopeData FocusScopeStack; ImVector_ImGuiItemFlags ItemFlagsStack; ImVector_ImGuiGroupData GroupStack; ImVector_ImGuiPopupData OpenPopupStack; ImVector_ImGuiPopupData BeginPopupStack; ImVector_ImGuiTreeNodeStackData TreeNodeStack; ImVector_ImGuiViewportPPtr Viewports; ImGuiViewportP* CurrentViewport; ImGuiViewportP* MouseViewport; ImGuiViewportP* MouseLastHoveredViewport; ImGuiID PlatformLastFocusedViewportId; ImGuiPlatformMonitor FallbackMonitor; ImRect PlatformMonitorsFullWorkRect; int ViewportCreatedCount; int PlatformWindowsCreatedCount; int ViewportFocusedStampCount; bool NavCursorVisible; bool NavHighlightItemUnderNav; bool NavMousePosDirty; bool NavIdIsAlive; ImGuiID NavId; ImGuiWindow* NavWindow; ImGuiID NavFocusScopeId; ImGuiNavLayer NavLayer; ImGuiID NavActivateId; ImGuiID NavActivateDownId; ImGuiID NavActivatePressedId; ImGuiActivateFlags NavActivateFlags; ImVector_ImGuiFocusScopeData NavFocusRoute; ImGuiID NavHighlightActivatedId; float NavHighlightActivatedTimer; ImGuiID NavNextActivateId; ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; ImGuiSelectionUserData NavLastValidSelectionUserData; ImS8 NavCursorHideFrames; bool NavAnyRequest; bool NavInitRequest; bool NavInitRequestFromMove; ImGuiNavItemData NavInitResult; bool NavMoveSubmitted; bool NavMoveScoringItems; bool NavMoveForwardToNextFrame; ImGuiNavMoveFlags NavMoveFlags; ImGuiScrollFlags NavMoveScrollFlags; ImGuiKeyChord NavMoveKeyMods; ImGuiDir NavMoveDir; ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; ImRect NavScoringRect; ImRect NavScoringNoClipRect; int NavScoringDebugCount; int NavTabbingDir; int NavTabbingCounter; ImGuiNavItemData NavMoveResultLocal; ImGuiNavItemData NavMoveResultLocalVisible; ImGuiNavItemData NavMoveResultOther; ImGuiNavItemData NavTabbingResultFirst; ImGuiID NavJustMovedFromFocusScopeId; ImGuiID NavJustMovedToId; ImGuiID NavJustMovedToFocusScopeId; ImGuiKeyChord NavJustMovedToKeyMods; bool NavJustMovedToIsTabbing; bool NavJustMovedToHasSelectionData; bool ConfigNavWindowingWithGamepad; ImGuiKeyChord ConfigNavWindowingKeyNext; ImGuiKeyChord ConfigNavWindowingKeyPrev; ImGuiWindow* NavWindowingTarget; ImGuiWindow* NavWindowingTargetAnim; ImGuiWindow* NavWindowingListWindow; float NavWindowingTimer; float NavWindowingHighlightAlpha; ImGuiInputSource NavWindowingInputSource; bool NavWindowingToggleLayer; ImGuiKey NavWindowingToggleKey; ImVec2 NavWindowingAccumDeltaPos; ImVec2 NavWindowingAccumDeltaSize; float DimBgRatio; bool DragDropActive; bool DragDropWithinSource; bool DragDropWithinTarget; ImGuiDragDropFlags DragDropSourceFlags; int DragDropSourceFrameCount; int DragDropMouseButton; ImGuiPayload DragDropPayload; ImRect DragDropTargetRect; ImRect DragDropTargetClipRect; ImGuiID DragDropTargetId; ImGuiDragDropFlags DragDropAcceptFlags; float DragDropAcceptIdCurrRectSurface; ImGuiID DragDropAcceptIdCurr; ImGuiID DragDropAcceptIdPrev; int DragDropAcceptFrameCount; ImGuiID DragDropHoldJustPressedId; ImVector_unsigned_char DragDropPayloadBufHeap; unsigned char DragDropPayloadBufLocal[16]; int ClipperTempDataStacked; ImVector_ImGuiListClipperData ClipperTempData; ImGuiTable* CurrentTable; ImGuiID DebugBreakInTable; int TablesTempDataStacked; ImVector_ImGuiTableTempData TablesTempData; ImPool_ImGuiTable Tables; ImVector_float TablesLastTimeActive; ImVector_ImDrawChannel DrawChannelsTempMergeBuffer; ImGuiTabBar* CurrentTabBar; ImPool_ImGuiTabBar TabBars; ImVector_ImGuiPtrOrIndex CurrentTabBarStack; ImVector_ImGuiShrinkWidthItem ShrinkWidthBuffer; ImGuiBoxSelectState BoxSelectState; ImGuiMultiSelectTempData* CurrentMultiSelect; int MultiSelectTempDataStacked; ImVector_ImGuiMultiSelectTempData MultiSelectTempData; ImPool_ImGuiMultiSelectState MultiSelectStorage; ImGuiID HoverItemDelayId; ImGuiID HoverItemDelayIdPreviousFrame; float HoverItemDelayTimer; float HoverItemDelayClearTimer; ImGuiID HoverItemUnlockedStationaryId; ImGuiID HoverWindowUnlockedStationaryId; ImGuiMouseCursor MouseCursor; float MouseStationaryTimer; ImVec2 MouseLastValidPos; ImGuiInputTextState InputTextState; ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFontBaked InputTextPasswordFontBackupBaked; ImFontFlags InputTextPasswordFontBackupFlags; ImGuiID TempInputId; ImGuiDataTypeStorage DataTypeZeroValue; int BeginMenuDepth; int BeginComboDepth; ImGuiColorEditFlags ColorEditOptions; ImGuiID ColorEditCurrentID; ImGuiID ColorEditSavedID; float ColorEditSavedHue; float ColorEditSavedSat; ImU32 ColorEditSavedColor; ImVec4 ColorPickerRef; ImGuiComboPreviewData ComboPreviewData; ImRect WindowResizeBorderExpectedRect; bool WindowResizeRelativeMode; short ScrollbarSeekMode; float ScrollbarClickDeltaToGrabCenter; float SliderGrabClickOffset; float SliderCurrentAccum; bool SliderCurrentAccumDirty; bool DragCurrentAccumDirty; float DragCurrentAccum; float DragSpeedDefaultRatio; float DisabledAlphaBackup; short DisabledStackSize; short TooltipOverrideCount; ImGuiWindow* TooltipPreviousWindow; ImVector_char ClipboardHandlerData; ImVector_ImGuiID MenusIdSubmittedThisFrame; ImGuiTypingSelectState TypingSelectState; ImGuiPlatformImeData PlatformImeData; ImGuiPlatformImeData PlatformImeDataPrev; ImVector_ImTextureDataPtr UserTextures; ImGuiDockContext DockContext; void (*DockNodeWindowMenuHandler)(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar); bool SettingsLoaded; float SettingsDirtyTimer; ImGuiTextBuffer SettingsIniData; ImVector_ImGuiSettingsHandler SettingsHandlers; ImChunkStream_ImGuiWindowSettings SettingsWindows; ImChunkStream_ImGuiTableSettings SettingsTables; ImVector_ImGuiContextHook Hooks; ImGuiID HookIdNext; const char* LocalizationTable[ImGuiLocKey_COUNT]; bool LogEnabled; ImGuiLogFlags LogFlags; ImGuiWindow* LogWindow; ImFileHandle LogFile; ImGuiTextBuffer LogBuffer; const char* LogNextPrefix; const char* LogNextSuffix; float LogLinePosY; bool LogLineFirstItem; int LogDepthRef; int LogDepthToExpand; int LogDepthToExpandDefault; ImGuiErrorCallback ErrorCallback; void* ErrorCallbackUserData; ImVec2 ErrorTooltipLockedPos; bool ErrorFirst; int ErrorCountCurrentFrame; ImGuiErrorRecoveryState StackSizesInNewFrame; ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow; int DebugDrawIdConflictsCount; ImGuiDebugLogFlags DebugLogFlags; ImGuiTextBuffer DebugLogBuf; ImGuiTextIndex DebugLogIndex; int DebugLogSkippedErrors; ImGuiDebugLogFlags DebugLogAutoDisableFlags; ImU8 DebugLogAutoDisableFrames; ImU8 DebugLocateFrames; bool DebugBreakInLocateId; ImGuiKeyChord DebugBreakKeyChord; ImS8 DebugBeginReturnValueCullDepth; bool DebugItemPickerActive; ImU8 DebugItemPickerMouseButton; ImGuiID DebugItemPickerBreakId; float DebugFlashStyleColorTime; ImVec4 DebugFlashStyleColorBackup; ImGuiMetricsConfig DebugMetricsConfig; ImGuiIDStackTool DebugIDStackTool; ImGuiDebugAllocInfo DebugAllocInfo; ImGuiDockNode* DebugHoveredDockNode; float FramerateSecPerFrame[60]; int FramerateSecPerFrameIdx; int FramerateSecPerFrameCount; float FramerateSecPerFrameAccum; int WantCaptureMouseNextFrame; int WantCaptureKeyboardNextFrame; int WantTextInputNextFrame; ImVector_char TempBuffer; char TempKeychordName[64]; }; struct ImGuiWindowTempData { ImVec2 CursorPos; ImVec2 CursorPosPrevLine; ImVec2 CursorStartPos; ImVec2 CursorMaxPos; ImVec2 IdealMaxPos; ImVec2 CurrLineSize; ImVec2 PrevLineSize; float CurrLineTextBaseOffset; float PrevLineTextBaseOffset; bool IsSameLine; bool IsSetPos; ImVec1 Indent; ImVec1 ColumnsOffset; ImVec1 GroupOffset; ImVec2 CursorStartPosLossyness; ImGuiNavLayer NavLayerCurrent; short NavLayersActiveMask; short NavLayersActiveMaskNext; bool NavIsScrollPushableX; bool NavHideHighlightOneFrame; bool NavWindowHasScrollY; bool MenuBarAppending; ImVec2 MenuBarOffset; ImGuiMenuColumns MenuColumns; int TreeDepth; ImU32 TreeHasStackDataDepthMask; ImU32 TreeRecordsClippedNodesY2Mask; ImVector_ImGuiWindowPtr ChildWindows; ImGuiStorage* StateStorage; ImGuiOldColumns* CurrentColumns; int CurrentTableIdx; ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; ImU32 ModalDimBgColor; ImGuiItemStatusFlags WindowItemStatusFlags; ImGuiItemStatusFlags ChildItemStatusFlags; ImGuiItemStatusFlags DockTabItemStatusFlags; ImRect DockTabItemRect; float ItemWidth; float TextWrapPos; ImVector_float ItemWidthStack; ImVector_float TextWrapPosStack; }; typedef struct ImVector_ImGuiOldColumns {int Size;int Capacity;ImGuiOldColumns* Data;} ImVector_ImGuiOldColumns; struct ImGuiWindow { ImGuiContext* Ctx; char* Name; ImGuiID ID; ImGuiWindowFlags Flags, FlagsPreviousFrame; ImGuiChildFlags ChildFlags; ImGuiWindowClass WindowClass; ImGuiViewportP* Viewport; ImGuiID ViewportId; ImVec2 ViewportPos; int ViewportAllowPlatformMonitorExtend; ImVec2 Pos; ImVec2 Size; ImVec2 SizeFull; ImVec2 ContentSize; ImVec2 ContentSizeIdeal; ImVec2 ContentSizeExplicit; ImVec2 WindowPadding; float WindowRounding; float WindowBorderSize; float TitleBarHeight, MenuBarHeight; float DecoOuterSizeX1, DecoOuterSizeY1; float DecoOuterSizeX2, DecoOuterSizeY2; float DecoInnerSizeX1, DecoInnerSizeY1; int NameBufLen; ImGuiID MoveId; ImGuiID TabId; ImGuiID ChildId; ImGuiID PopupId; ImVec2 Scroll; ImVec2 ScrollMax; ImVec2 ScrollTarget; ImVec2 ScrollTargetCenterRatio; ImVec2 ScrollTargetEdgeSnapDist; ImVec2 ScrollbarSizes; bool ScrollbarX, ScrollbarY; bool ScrollbarXStabilizeEnabled; ImU8 ScrollbarXStabilizeToggledHistory; bool ViewportOwned; bool Active; bool WasActive; bool WriteAccessed; bool Collapsed; bool WantCollapseToggle; bool SkipItems; bool SkipRefresh; bool Appearing; bool Hidden; bool IsFallbackWindow; bool IsExplicitChild; bool HasCloseButton; signed char ResizeBorderHovered; signed char ResizeBorderHeld; short BeginCount; short BeginCountPreviousFrame; short BeginOrderWithinParent; short BeginOrderWithinContext; short FocusOrder; ImS8 AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; ImGuiDir AutoPosLastDirection; ImS8 HiddenFramesCanSkipItems; ImS8 HiddenFramesCannotSkipItems; ImS8 HiddenFramesForRenderOnly; ImS8 DisableInputsFrames; ImGuiCond SetWindowPosAllowFlags : 8; ImGuiCond SetWindowSizeAllowFlags : 8; ImGuiCond SetWindowCollapsedAllowFlags : 8; ImGuiCond SetWindowDockAllowFlags : 8; ImVec2 SetWindowPosVal; ImVec2 SetWindowPosPivot; ImVector_ImGuiID IDStack; ImGuiWindowTempData DC; ImRect OuterRectClipped; ImRect InnerRect; ImRect InnerClipRect; ImRect WorkRect; ImRect ParentWorkRect; ImRect ClipRect; ImRect ContentRegionRect; ImVec2ih HitTestHoleSize; ImVec2ih HitTestHoleOffset; int LastFrameActive; int LastFrameJustFocused; float LastTimeActive; float ItemWidthDefault; ImGuiStorage StateStorage; ImVector_ImGuiOldColumns ColumnsStorage; float FontWindowScale; float FontWindowScaleParents; float FontRefSize; int SettingsOffset; ImDrawList* DrawList; ImDrawList DrawListInst; ImGuiWindow* ParentWindow; ImGuiWindow* ParentWindowInBeginStack; ImGuiWindow* RootWindow; ImGuiWindow* RootWindowPopupTree; ImGuiWindow* RootWindowDockTree; ImGuiWindow* RootWindowForTitleBarHighlight; ImGuiWindow* RootWindowForNav; ImGuiWindow* ParentWindowForFocusRoute; ImGuiWindow* NavLastChildNavWindow; ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; ImRect NavRectRel[ImGuiNavLayer_COUNT]; ImVec2 NavPreferredScoringPosRel[ImGuiNavLayer_COUNT]; ImGuiID NavRootFocusScopeId; int MemoryDrawListIdxCapacity; int MemoryDrawListVtxCapacity; bool MemoryCompacted; bool DockIsActive :1; bool DockNodeIsVisible :1; bool DockTabIsVisible :1; bool DockTabWantClose :1; short DockOrder; ImGuiWindowDockStyle DockStyle; ImGuiDockNode* DockNode; ImGuiDockNode* DockNodeAsHost; ImGuiID DockId; }; typedef enum { ImGuiTabBarFlags_DockNode = 1 << 20, ImGuiTabBarFlags_IsFocused = 1 << 21, ImGuiTabBarFlags_SaveSettings = 1 << 22, }ImGuiTabBarFlagsPrivate_; typedef enum { ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, ImGuiTabItemFlags_Button = 1 << 21, ImGuiTabItemFlags_Invisible = 1 << 22, ImGuiTabItemFlags_Unsorted = 1 << 23, }ImGuiTabItemFlagsPrivate_; struct ImGuiTabItem { ImGuiID ID; ImGuiTabItemFlags Flags; ImGuiWindow* Window; int LastFrameVisible; int LastFrameSelected; float Offset; float Width; float ContentWidth; float RequestedWidth; ImS32 NameOffset; ImS16 BeginOrder; ImS16 IndexDuringLayout; bool WantClose; }; typedef struct ImVector_ImGuiTabItem {int Size;int Capacity;ImGuiTabItem* Data;} ImVector_ImGuiTabItem; struct ImGuiTabBar { ImGuiWindow* Window; ImVector_ImGuiTabItem Tabs; ImGuiTabBarFlags Flags; ImGuiID ID; ImGuiID SelectedTabId; ImGuiID NextSelectedTabId; ImGuiID VisibleTabId; int CurrFrameVisible; int PrevFrameVisible; ImRect BarRect; float CurrTabsContentsHeight; float PrevTabsContentsHeight; float WidthAllTabs; float WidthAllTabsIdeal; float ScrollingAnim; float ScrollingTarget; float ScrollingTargetDistToVisibility; float ScrollingSpeed; float ScrollingRectMinX; float ScrollingRectMaxX; float SeparatorMinX; float SeparatorMaxX; ImGuiID ReorderRequestTabId; ImS16 ReorderRequestOffset; ImS8 BeginCount; bool WantLayout; bool VisibleTabWasSubmitted; bool TabsAddedNew; ImS16 TabsActiveCount; ImS16 LastTabItemIdx; float ItemSpacingY; ImVec2 FramePadding; ImVec2 BackupCursorPos; ImGuiTextBuffer TabsNames; }; struct ImGuiTableColumn { ImGuiTableColumnFlags Flags; float WidthGiven; float MinX; float MaxX; float WidthRequest; float WidthAuto; float WidthMax; float StretchWeight; float InitStretchWeightOrWidth; ImRect ClipRect; ImGuiID UserID; float WorkMinX; float WorkMaxX; float ItemWidth; float ContentMaxXFrozen; float ContentMaxXUnfrozen; float ContentMaxXHeadersUsed; float ContentMaxXHeadersIdeal; ImS16 NameOffset; ImGuiTableColumnIdx DisplayOrder; ImGuiTableColumnIdx IndexWithinEnabledSet; ImGuiTableColumnIdx PrevEnabledColumn; ImGuiTableColumnIdx NextEnabledColumn; ImGuiTableColumnIdx SortOrder; ImGuiTableDrawChannelIdx DrawChannelCurrent; ImGuiTableDrawChannelIdx DrawChannelFrozen; ImGuiTableDrawChannelIdx DrawChannelUnfrozen; bool IsEnabled; bool IsUserEnabled; bool IsUserEnabledNextFrame; bool IsVisibleX; bool IsVisibleY; bool IsRequestOutput; bool IsSkipItems; bool IsPreserveWidthAuto; ImS8 NavLayerCurrent; ImU8 AutoFitQueue; ImU8 CannotSkipItemsQueue; ImU8 SortDirection : 2; ImU8 SortDirectionsAvailCount : 2; ImU8 SortDirectionsAvailMask : 4; ImU8 SortDirectionsAvailList; }; typedef struct ImGuiTableCellData ImGuiTableCellData; struct ImGuiTableCellData { ImU32 BgColor; ImGuiTableColumnIdx Column; }; struct ImGuiTableHeaderData { ImGuiTableColumnIdx Index; ImU32 TextColor; ImU32 BgColor0; ImU32 BgColor1; }; struct ImGuiTableInstanceData { ImGuiID TableInstanceID; float LastOuterHeight; float LastTopHeadersRowHeight; float LastFrozenHeight; int HoveredRowLast; int HoveredRowNext; }; typedef struct ImSpan_ImGuiTableColumn {ImGuiTableColumn* Data;ImGuiTableColumn* DataEnd;} ImSpan_ImGuiTableColumn; typedef struct ImSpan_ImGuiTableColumnIdx {ImGuiTableColumnIdx* Data;ImGuiTableColumnIdx* DataEnd;} ImSpan_ImGuiTableColumnIdx; typedef struct ImSpan_ImGuiTableCellData {ImGuiTableCellData* Data;ImGuiTableCellData* DataEnd;} ImSpan_ImGuiTableCellData; typedef struct ImVector_ImGuiTableInstanceData {int Size;int Capacity;ImGuiTableInstanceData* Data;} ImVector_ImGuiTableInstanceData; typedef struct ImVector_ImGuiTableColumnSortSpecs {int Size;int Capacity;ImGuiTableColumnSortSpecs* Data;} ImVector_ImGuiTableColumnSortSpecs; struct ImGuiTable { ImGuiID ID; ImGuiTableFlags Flags; void* RawData; ImGuiTableTempData* TempData; ImSpan_ImGuiTableColumn Columns; ImSpan_ImGuiTableColumnIdx DisplayOrderToIndex; ImSpan_ImGuiTableCellData RowCellData; ImBitArrayPtr EnabledMaskByDisplayOrder; ImBitArrayPtr EnabledMaskByIndex; ImBitArrayPtr VisibleMaskByIndex; ImGuiTableFlags SettingsLoadedFlags; int SettingsOffset; int LastFrameActive; int ColumnsCount; int CurrentRow; int CurrentColumn; ImS16 InstanceCurrent; ImS16 InstanceInteracted; float RowPosY1; float RowPosY2; float RowMinHeight; float RowCellPaddingY; float RowTextBaseline; float RowIndentOffsetX; ImGuiTableRowFlags RowFlags : 16; ImGuiTableRowFlags LastRowFlags : 16; int RowBgColorCounter; ImU32 RowBgColor[2]; ImU32 BorderColorStrong; ImU32 BorderColorLight; float BorderX1; float BorderX2; float HostIndentX; float MinColumnWidth; float OuterPaddingX; float CellPaddingX; float CellSpacingX1; float CellSpacingX2; float InnerWidth; float ColumnsGivenWidth; float ColumnsAutoFitWidth; float ColumnsStretchSumWeights; float ResizedColumnNextWidth; float ResizeLockMinContentsX2; float RefScale; float AngledHeadersHeight; float AngledHeadersSlope; ImRect OuterRect; ImRect InnerRect; ImRect WorkRect; ImRect InnerClipRect; ImRect BgClipRect; ImRect Bg0ClipRectForDrawCmd; ImRect Bg2ClipRectForDrawCmd; ImRect HostClipRect; ImRect HostBackupInnerClipRect; ImGuiWindow* OuterWindow; ImGuiWindow* InnerWindow; ImGuiTextBuffer ColumnsNames; ImDrawListSplitter* DrawSplitter; ImGuiTableInstanceData InstanceDataFirst; ImVector_ImGuiTableInstanceData InstanceDataExtra; ImGuiTableColumnSortSpecs SortSpecsSingle; ImVector_ImGuiTableColumnSortSpecs SortSpecsMulti; ImGuiTableSortSpecs SortSpecs; ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx ColumnsEnabledCount; ImGuiTableColumnIdx ColumnsEnabledFixedCount; ImGuiTableColumnIdx DeclColumnsCount; ImGuiTableColumnIdx AngledHeadersCount; ImGuiTableColumnIdx HoveredColumnBody; ImGuiTableColumnIdx HoveredColumnBorder; ImGuiTableColumnIdx HighlightColumnHeader; ImGuiTableColumnIdx AutoFitSingleColumn; ImGuiTableColumnIdx ResizedColumn; ImGuiTableColumnIdx LastResizedColumn; ImGuiTableColumnIdx HeldHeaderColumn; ImGuiTableColumnIdx ReorderColumn; ImGuiTableColumnIdx ReorderColumnDir; ImGuiTableColumnIdx LeftMostEnabledColumn; ImGuiTableColumnIdx RightMostEnabledColumn; ImGuiTableColumnIdx LeftMostStretchedColumn; ImGuiTableColumnIdx RightMostStretchedColumn; ImGuiTableColumnIdx ContextPopupColumn; ImGuiTableColumnIdx FreezeRowsRequest; ImGuiTableColumnIdx FreezeRowsCount; ImGuiTableColumnIdx FreezeColumnsRequest; ImGuiTableColumnIdx FreezeColumnsCount; ImGuiTableColumnIdx RowCellDataCurrent; ImGuiTableDrawChannelIdx DummyDrawChannel; ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen; ImS8 NavLayer; bool IsLayoutLocked; bool IsInsideRow; bool IsInitializing; bool IsSortSpecsDirty; bool IsUsingHeaders; bool IsContextPopupOpen; bool DisableDefaultContextMenu; bool IsSettingsRequestLoad; bool IsSettingsDirty; bool IsDefaultDisplayOrder; bool IsResetAllRequest; bool IsResetDisplayOrderRequest; bool IsUnfrozenRows; bool IsDefaultSizingPolicy; bool IsActiveIdAliveBeforeTable; bool IsActiveIdInTable; bool HasScrollbarYCurr; bool HasScrollbarYPrev; bool MemoryCompacted; bool HostSkipItems; }; typedef struct ImVector_ImGuiTableHeaderData {int Size;int Capacity;ImGuiTableHeaderData* Data;} ImVector_ImGuiTableHeaderData; struct ImGuiTableTempData { int TableIndex; float LastTimeActive; float AngledHeadersExtraWidth; ImVector_ImGuiTableHeaderData AngledHeadersRequests; ImVec2 UserOuterSize; ImDrawListSplitter DrawSplitter; ImRect HostBackupWorkRect; ImRect HostBackupParentWorkRect; ImVec2 HostBackupPrevLineSize; ImVec2 HostBackupCurrLineSize; ImVec2 HostBackupCursorMaxPos; ImVec1 HostBackupColumnsOffset; float HostBackupItemWidth; int HostBackupItemWidthStackSize; }; typedef struct ImGuiTableColumnSettings ImGuiTableColumnSettings; struct ImGuiTableColumnSettings { float WidthOrWeight; ImGuiID UserID; ImGuiTableColumnIdx Index; ImGuiTableColumnIdx DisplayOrder; ImGuiTableColumnIdx SortOrder; ImU8 SortDirection : 2; ImS8 IsEnabled : 2; ImU8 IsStretch : 1; }; struct ImGuiTableSettings { ImGuiID ID; ImGuiTableFlags SaveFlags; float RefScale; ImGuiTableColumnIdx ColumnsCount; ImGuiTableColumnIdx ColumnsCountMax; bool WantApply; }; struct ImFontLoader { const char* Name; bool (*LoaderInit)(ImFontAtlas* atlas); void (*LoaderShutdown)(ImFontAtlas* atlas); bool (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src); void (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src); bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint); bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph); size_t FontBakedSrcLoaderDataSize; }; struct ImFontAtlasRectEntry { int TargetIndex : 20; int Generation : 10; unsigned int IsUsed : 1; }; struct ImFontAtlasPostProcessData { ImFontAtlas* FontAtlas; ImFont* Font; ImFontConfig* FontSrc; ImFontBaked* FontBaked; ImFontGlyph* Glyph; void* Pixels; ImTextureFormat Format; int Pitch; int Width; int Height; }; struct stbrp_node; typedef stbrp_node stbrp_node_im; typedef struct stbrp_context_opaque stbrp_context_opaque; struct stbrp_context_opaque { char data[80]; }; typedef struct ImVector_stbrp_node_im {int Size;int Capacity;stbrp_node_im* Data;} ImVector_stbrp_node_im; typedef struct ImVector_ImFontAtlasRectEntry {int Size;int Capacity;ImFontAtlasRectEntry* Data;} ImVector_ImFontAtlasRectEntry; typedef struct ImVector_ImFontBakedPtr {int Size;int Capacity;ImFontBaked** Data;} ImVector_ImFontBakedPtr; typedef struct ImStableVector_ImFontBaked__32 {int Size;int Capacity;ImVector_ImFontBakedPtr Blocks;} ImStableVector_ImFontBaked__32; struct ImFontAtlasBuilder { stbrp_context_opaque PackContext; ImVector_stbrp_node_im PackNodes; ImVector_ImTextureRect Rects; ImVector_ImFontAtlasRectEntry RectsIndex; ImVector_unsigned_char TempBuffer; int RectsIndexFreeListStart; int RectsPackedCount; int RectsPackedSurface; int RectsDiscardedCount; int RectsDiscardedSurface; int FrameCount; ImVec2i MaxRectSize; ImVec2i MaxRectBounds; bool LockDisableResize; bool PreloadedAllGlyphsRanges; ImStableVector_ImFontBaked__32 BakedPool; ImGuiStorage BakedMap; int BakedDiscardedCount; ImFontAtlasRectId PackIdMouseCursors; ImFontAtlasRectId PackIdLinesTexData; }; #ifdef IMGUI_ENABLE_FREETYPE struct ImFontAtlas; struct ImFontLoader; typedef unsigned int ImGuiFreeTypeLoaderFlags; typedef enum { ImGuiFreeTypeLoaderFlags_NoHinting = 1 << 0, ImGuiFreeTypeLoaderFlags_NoAutoHint = 1 << 1, ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2, ImGuiFreeTypeLoaderFlags_LightHinting = 1 << 3, ImGuiFreeTypeLoaderFlags_MonoHinting = 1 << 4, ImGuiFreeTypeLoaderFlags_Bold = 1 << 5, ImGuiFreeTypeLoaderFlags_Oblique = 1 << 6, ImGuiFreeTypeLoaderFlags_Monochrome = 1 << 7, ImGuiFreeTypeLoaderFlags_LoadColor = 1 << 8, ImGuiFreeTypeLoaderFlags_Bitmap = 1 << 9, }ImGuiFreeTypeLoaderFlags_; #endif #define IMGUI_HAS_DOCK 1 #define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8) #else struct GLFWwindow; struct SDL_Window; typedef union SDL_Event SDL_Event; #endif // CIMGUI_DEFINE_ENUMS_AND_STRUCTS #ifndef CIMGUI_DEFINE_ENUMS_AND_STRUCTS typedef struct ImGuiTextFilter::ImGuiTextRange ImGuiTextRange; typedef ImStb::STB_TexteditState STB_TexteditState; typedef ImChunkStream ImChunkStream_ImGuiTableSettings; typedef ImChunkStream ImChunkStream_ImGuiWindowSettings; typedef ImPool ImPool_ImGuiMultiSelectState; typedef ImPool ImPool_ImGuiTabBar; typedef ImPool ImPool_ImGuiTable; typedef ImSpan ImSpan_ImGuiTableCellData; typedef ImSpan ImSpan_ImGuiTableColumn; typedef ImSpan ImSpan_ImGuiTableColumnIdx; typedef ImStableVector ImStableVector_ImFontBaked__32; typedef ImVector ImVector_ImDrawChannel; typedef ImVector ImVector_ImDrawCmd; typedef ImVector ImVector_ImDrawIdx; typedef ImVector ImVector_ImDrawListPtr; typedef ImVector ImVector_ImDrawListSharedDataPtr; typedef ImVector ImVector_ImDrawVert; typedef ImVector ImVector_ImFontPtr; typedef ImVector ImVector_ImFontAtlasPtr; typedef ImVector ImVector_ImFontAtlasRectEntry; typedef ImVector ImVector_ImFontConfig; typedef ImVector ImVector_ImFontConfigPtr; typedef ImVector ImVector_ImFontGlyph; typedef ImVector ImVector_ImFontStackData; typedef ImVector ImVector_ImGuiColorMod; typedef ImVector ImVector_ImGuiContextHook; typedef ImVector ImVector_ImGuiDockNodeSettings; typedef ImVector ImVector_ImGuiDockRequest; typedef ImVector ImVector_ImGuiFocusScopeData; typedef ImVector ImVector_ImGuiGroupData; typedef ImVector ImVector_ImGuiID; typedef ImVector ImVector_ImGuiInputEvent; typedef ImVector ImVector_ImGuiItemFlags; typedef ImVector ImVector_ImGuiKeyRoutingData; typedef ImVector ImVector_ImGuiListClipperData; typedef ImVector ImVector_ImGuiListClipperRange; typedef ImVector ImVector_ImGuiMultiSelectTempData; typedef ImVector ImVector_ImGuiOldColumnData; typedef ImVector ImVector_ImGuiOldColumns; typedef ImVector ImVector_ImGuiPlatformMonitor; typedef ImVector ImVector_ImGuiPopupData; typedef ImVector ImVector_ImGuiPtrOrIndex; typedef ImVector ImVector_ImGuiSelectionRequest; typedef ImVector ImVector_ImGuiSettingsHandler; typedef ImVector ImVector_ImGuiShrinkWidthItem; typedef ImVector ImVector_ImGuiStackLevelInfo; typedef ImVector ImVector_ImGuiStoragePair; typedef ImVector ImVector_ImGuiStyleMod; typedef ImVector ImVector_ImGuiTabItem; typedef ImVector ImVector_ImGuiTableColumnSortSpecs; typedef ImVector ImVector_ImGuiTableHeaderData; typedef ImVector ImVector_ImGuiTableInstanceData; typedef ImVector ImVector_ImGuiTableTempData; typedef ImVector ImVector_ImGuiTextRange; typedef ImVector ImVector_ImGuiTreeNodeStackData; typedef ImVector ImVector_ImGuiViewportPtr; typedef ImVector ImVector_ImGuiViewportPPtr; typedef ImVector ImVector_ImGuiWindowPtr; typedef ImVector ImVector_ImGuiWindowStackData; typedef ImVector ImVector_ImTextureDataPtr; typedef ImVector ImVector_ImTextureRect; typedef ImVector ImVector_ImTextureRef; typedef ImVector ImVector_ImU16; typedef ImVector ImVector_ImU32; typedef ImVector ImVector_ImU8; typedef ImVector ImVector_ImVec2; typedef ImVector ImVector_ImVec4; typedef ImVector ImVector_ImWchar; typedef ImVector ImVector_char; typedef ImVector ImVector_const_charPtr; typedef ImVector ImVector_float; typedef ImVector ImVector_int; typedef ImVector ImVector_stbrp_node_im; typedef ImVector ImVector_unsigned_char; #endif //CIMGUI_DEFINE_ENUMS_AND_STRUCTS CIMGUI_API ImVec2* ImVec2_ImVec2_Nil(void); CIMGUI_API void ImVec2_destroy(ImVec2* self); CIMGUI_API ImVec2* ImVec2_ImVec2_Float(float _x,float _y); CIMGUI_API ImVec4* ImVec4_ImVec4_Nil(void); CIMGUI_API void ImVec4_destroy(ImVec4* self); CIMGUI_API ImVec4* ImVec4_ImVec4_Float(float _x,float _y,float _z,float _w); CIMGUI_API ImTextureRef* ImTextureRef_ImTextureRef_Nil(void); CIMGUI_API void ImTextureRef_destroy(ImTextureRef* self); CIMGUI_API ImTextureRef* ImTextureRef_ImTextureRef_TextureID(ImTextureID tex_id); CIMGUI_API ImTextureID ImTextureRef_GetTexID(ImTextureRef* self); CIMGUI_API ImGuiContext* igCreateContext(ImFontAtlas* shared_font_atlas); CIMGUI_API void igDestroyContext(ImGuiContext* ctx); CIMGUI_API ImGuiContext* igGetCurrentContext(void); CIMGUI_API void igSetCurrentContext(ImGuiContext* ctx); CIMGUI_API ImGuiIO* igGetIO_Nil(void); CIMGUI_API ImGuiPlatformIO* igGetPlatformIO_Nil(void); CIMGUI_API ImGuiStyle* igGetStyle(void); CIMGUI_API void igNewFrame(void); CIMGUI_API void igEndFrame(void); CIMGUI_API void igRender(void); CIMGUI_API ImDrawData* igGetDrawData(void); CIMGUI_API void igShowDemoWindow(bool* p_open); CIMGUI_API void igShowMetricsWindow(bool* p_open); CIMGUI_API void igShowDebugLogWindow(bool* p_open); CIMGUI_API void igShowIDStackToolWindow(bool* p_open); CIMGUI_API void igShowAboutWindow(bool* p_open); CIMGUI_API void igShowStyleEditor(ImGuiStyle* ref); CIMGUI_API bool igShowStyleSelector(const char* label); CIMGUI_API void igShowFontSelector(const char* label); CIMGUI_API void igShowUserGuide(void); CIMGUI_API const char* igGetVersion(void); CIMGUI_API void igStyleColorsDark(ImGuiStyle* dst); CIMGUI_API void igStyleColorsLight(ImGuiStyle* dst); CIMGUI_API void igStyleColorsClassic(ImGuiStyle* dst); CIMGUI_API bool igBegin(const char* name,bool* p_open,ImGuiWindowFlags flags); CIMGUI_API void igEnd(void); CIMGUI_API bool igBeginChild_Str(const char* str_id,const ImVec2 size,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags); CIMGUI_API bool igBeginChild_ID(ImGuiID id,const ImVec2 size,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags); CIMGUI_API void igEndChild(void); CIMGUI_API bool igIsWindowAppearing(void); CIMGUI_API bool igIsWindowCollapsed(void); CIMGUI_API bool igIsWindowFocused(ImGuiFocusedFlags flags); CIMGUI_API bool igIsWindowHovered(ImGuiHoveredFlags flags); CIMGUI_API ImDrawList* igGetWindowDrawList(void); CIMGUI_API float igGetWindowDpiScale(void); CIMGUI_API void igGetWindowPos(ImVec2 *pOut); CIMGUI_API void igGetWindowSize(ImVec2 *pOut); CIMGUI_API float igGetWindowWidth(void); CIMGUI_API float igGetWindowHeight(void); CIMGUI_API ImGuiViewport* igGetWindowViewport(void); CIMGUI_API void igSetNextWindowPos(const ImVec2 pos,ImGuiCond cond,const ImVec2 pivot); CIMGUI_API void igSetNextWindowSize(const ImVec2 size,ImGuiCond cond); CIMGUI_API void igSetNextWindowSizeConstraints(const ImVec2 size_min,const ImVec2 size_max,ImGuiSizeCallback custom_callback,void* custom_callback_data); CIMGUI_API void igSetNextWindowContentSize(const ImVec2 size); CIMGUI_API void igSetNextWindowCollapsed(bool collapsed,ImGuiCond cond); CIMGUI_API void igSetNextWindowFocus(void); CIMGUI_API void igSetNextWindowScroll(const ImVec2 scroll); CIMGUI_API void igSetNextWindowBgAlpha(float alpha); CIMGUI_API void igSetNextWindowViewport(ImGuiID viewport_id); CIMGUI_API void igSetWindowPos_Vec2(const ImVec2 pos,ImGuiCond cond); CIMGUI_API void igSetWindowSize_Vec2(const ImVec2 size,ImGuiCond cond); CIMGUI_API void igSetWindowCollapsed_Bool(bool collapsed,ImGuiCond cond); CIMGUI_API void igSetWindowFocus_Nil(void); CIMGUI_API void igSetWindowPos_Str(const char* name,const ImVec2 pos,ImGuiCond cond); CIMGUI_API void igSetWindowSize_Str(const char* name,const ImVec2 size,ImGuiCond cond); CIMGUI_API void igSetWindowCollapsed_Str(const char* name,bool collapsed,ImGuiCond cond); CIMGUI_API void igSetWindowFocus_Str(const char* name); CIMGUI_API float igGetScrollX(void); CIMGUI_API float igGetScrollY(void); CIMGUI_API void igSetScrollX_Float(float scroll_x); CIMGUI_API void igSetScrollY_Float(float scroll_y); CIMGUI_API float igGetScrollMaxX(void); CIMGUI_API float igGetScrollMaxY(void); CIMGUI_API void igSetScrollHereX(float center_x_ratio); CIMGUI_API void igSetScrollHereY(float center_y_ratio); CIMGUI_API void igSetScrollFromPosX_Float(float local_x,float center_x_ratio); CIMGUI_API void igSetScrollFromPosY_Float(float local_y,float center_y_ratio); CIMGUI_API void igPushFont(ImFont* font,float font_size_base_unscaled); CIMGUI_API void igPopFont(void); CIMGUI_API ImFont* igGetFont(void); CIMGUI_API float igGetFontSize(void); CIMGUI_API ImFontBaked* igGetFontBaked(void); CIMGUI_API void igPushStyleColor_U32(ImGuiCol idx,ImU32 col); CIMGUI_API void igPushStyleColor_Vec4(ImGuiCol idx,const ImVec4 col); CIMGUI_API void igPopStyleColor(int count); CIMGUI_API void igPushStyleVar_Float(ImGuiStyleVar idx,float val); CIMGUI_API void igPushStyleVar_Vec2(ImGuiStyleVar idx,const ImVec2 val); CIMGUI_API void igPushStyleVarX(ImGuiStyleVar idx,float val_x); CIMGUI_API void igPushStyleVarY(ImGuiStyleVar idx,float val_y); CIMGUI_API void igPopStyleVar(int count); CIMGUI_API void igPushItemFlag(ImGuiItemFlags option,bool enabled); CIMGUI_API void igPopItemFlag(void); CIMGUI_API void igPushItemWidth(float item_width); CIMGUI_API void igPopItemWidth(void); CIMGUI_API void igSetNextItemWidth(float item_width); CIMGUI_API float igCalcItemWidth(void); CIMGUI_API void igPushTextWrapPos(float wrap_local_pos_x); CIMGUI_API void igPopTextWrapPos(void); CIMGUI_API void igGetFontTexUvWhitePixel(ImVec2 *pOut); CIMGUI_API ImU32 igGetColorU32_Col(ImGuiCol idx,float alpha_mul); CIMGUI_API ImU32 igGetColorU32_Vec4(const ImVec4 col); CIMGUI_API ImU32 igGetColorU32_U32(ImU32 col,float alpha_mul); CIMGUI_API const ImVec4* igGetStyleColorVec4(ImGuiCol idx); CIMGUI_API void igGetCursorScreenPos(ImVec2 *pOut); CIMGUI_API void igSetCursorScreenPos(const ImVec2 pos); CIMGUI_API void igGetContentRegionAvail(ImVec2 *pOut); CIMGUI_API void igGetCursorPos(ImVec2 *pOut); CIMGUI_API float igGetCursorPosX(void); CIMGUI_API float igGetCursorPosY(void); CIMGUI_API void igSetCursorPos(const ImVec2 local_pos); CIMGUI_API void igSetCursorPosX(float local_x); CIMGUI_API void igSetCursorPosY(float local_y); CIMGUI_API void igGetCursorStartPos(ImVec2 *pOut); CIMGUI_API void igSeparator(void); CIMGUI_API void igSameLine(float offset_from_start_x,float spacing); CIMGUI_API void igNewLine(void); CIMGUI_API void igSpacing(void); CIMGUI_API void igDummy(const ImVec2 size); CIMGUI_API void igIndent(float indent_w); CIMGUI_API void igUnindent(float indent_w); CIMGUI_API void igBeginGroup(void); CIMGUI_API void igEndGroup(void); CIMGUI_API void igAlignTextToFramePadding(void); CIMGUI_API float igGetTextLineHeight(void); CIMGUI_API float igGetTextLineHeightWithSpacing(void); CIMGUI_API float igGetFrameHeight(void); CIMGUI_API float igGetFrameHeightWithSpacing(void); CIMGUI_API void igPushID_Str(const char* str_id); CIMGUI_API void igPushID_StrStr(const char* str_id_begin,const char* str_id_end); CIMGUI_API void igPushID_Ptr(const void* ptr_id); CIMGUI_API void igPushID_Int(int int_id); CIMGUI_API void igPopID(void); CIMGUI_API ImGuiID igGetID_Str(const char* str_id); CIMGUI_API ImGuiID igGetID_StrStr(const char* str_id_begin,const char* str_id_end); CIMGUI_API ImGuiID igGetID_Ptr(const void* ptr_id); CIMGUI_API ImGuiID igGetID_Int(int int_id); CIMGUI_API void igTextUnformatted(const char* text,const char* text_end); CIMGUI_API void igText(const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igText0(const char* fmt); #endif CIMGUI_API void igTextV(const char* fmt,va_list args); CIMGUI_API void igTextColored(const ImVec4 col,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igTextColored0(const ImVec4 col,const char* fmt); #endif CIMGUI_API void igTextColoredV(const ImVec4 col,const char* fmt,va_list args); CIMGUI_API void igTextDisabled(const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igTextDisabled0(const char* fmt); #endif CIMGUI_API void igTextDisabledV(const char* fmt,va_list args); CIMGUI_API void igTextWrapped(const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igTextWrapped0(const char* fmt); #endif CIMGUI_API void igTextWrappedV(const char* fmt,va_list args); CIMGUI_API void igLabelText(const char* label,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igLabelText0(const char* label,const char* fmt); #endif CIMGUI_API void igLabelTextV(const char* label,const char* fmt,va_list args); CIMGUI_API void igBulletText(const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igBulletText0(const char* fmt); #endif CIMGUI_API void igBulletTextV(const char* fmt,va_list args); CIMGUI_API void igSeparatorText(const char* label); CIMGUI_API bool igButton(const char* label,const ImVec2 size); CIMGUI_API bool igSmallButton(const char* label); CIMGUI_API bool igInvisibleButton(const char* str_id,const ImVec2 size,ImGuiButtonFlags flags); CIMGUI_API bool igArrowButton(const char* str_id,ImGuiDir dir); CIMGUI_API bool igCheckbox(const char* label,bool* v); CIMGUI_API bool igCheckboxFlags_IntPtr(const char* label,int* flags,int flags_value); CIMGUI_API bool igCheckboxFlags_UintPtr(const char* label,unsigned int* flags,unsigned int flags_value); CIMGUI_API bool igRadioButton_Bool(const char* label,bool active); CIMGUI_API bool igRadioButton_IntPtr(const char* label,int* v,int v_button); CIMGUI_API void igProgressBar(float fraction,const ImVec2 size_arg,const char* overlay); CIMGUI_API void igBullet(void); CIMGUI_API bool igTextLink(const char* label); CIMGUI_API bool igTextLinkOpenURL(const char* label,const char* url); CIMGUI_API void igImage(ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1); CIMGUI_API void igImageWithBg(ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col); CIMGUI_API bool igImageButton(const char* str_id,ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col); CIMGUI_API bool igBeginCombo(const char* label,const char* preview_value,ImGuiComboFlags flags); CIMGUI_API void igEndCombo(void); CIMGUI_API bool igCombo_Str_arr(const char* label,int* current_item,const char* const items[],int items_count,int popup_max_height_in_items); CIMGUI_API bool igCombo_Str(const char* label,int* current_item,const char* items_separated_by_zeros,int popup_max_height_in_items); CIMGUI_API bool igCombo_FnStrPtr(const char* label,int* current_item,const char*(*getter)(void* user_data,int idx),void* user_data,int items_count,int popup_max_height_in_items); CIMGUI_API bool igDragFloat(const char* label,float* v,float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragFloat2(const char* label,float v[2],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragFloat3(const char* label,float v[3],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragFloat4(const char* label,float v[4],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragFloatRange2(const char* label,float* v_current_min,float* v_current_max,float v_speed,float v_min,float v_max,const char* format,const char* format_max,ImGuiSliderFlags flags); CIMGUI_API bool igDragInt(const char* label,int* v,float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragInt2(const char* label,int v[2],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragInt3(const char* label,int v[3],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragInt4(const char* label,int v[4],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragIntRange2(const char* label,int* v_current_min,int* v_current_max,float v_speed,int v_min,int v_max,const char* format,const char* format_max,ImGuiSliderFlags flags); CIMGUI_API bool igDragScalar(const char* label,ImGuiDataType data_type,void* p_data,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igDragScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderFloat(const char* label,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderFloat2(const char* label,float v[2],float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderFloat3(const char* label,float v[3],float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderFloat4(const char* label,float v[4],float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderAngle(const char* label,float* v_rad,float v_degrees_min,float v_degrees_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderInt(const char* label,int* v,int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderInt2(const char* label,int v[2],int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderInt3(const char* label,int v[3],int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderInt4(const char* label,int v[4],int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderScalar(const char* label,ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igVSliderFloat(const char* label,const ImVec2 size,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igVSliderInt(const char* label,const ImVec2 size,int* v,int v_min,int v_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igVSliderScalar(const char* label,const ImVec2 size,ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igInputText(const char* label,char* buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data); CIMGUI_API bool igInputTextMultiline(const char* label,char* buf,size_t buf_size,const ImVec2 size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data); CIMGUI_API bool igInputTextWithHint(const char* label,const char* hint,char* buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data); CIMGUI_API bool igInputFloat(const char* label,float* v,float step,float step_fast,const char* format,ImGuiInputTextFlags flags); CIMGUI_API bool igInputFloat2(const char* label,float v[2],const char* format,ImGuiInputTextFlags flags); CIMGUI_API bool igInputFloat3(const char* label,float v[3],const char* format,ImGuiInputTextFlags flags); CIMGUI_API bool igInputFloat4(const char* label,float v[4],const char* format,ImGuiInputTextFlags flags); CIMGUI_API bool igInputInt(const char* label,int* v,int step,int step_fast,ImGuiInputTextFlags flags); CIMGUI_API bool igInputInt2(const char* label,int v[2],ImGuiInputTextFlags flags); CIMGUI_API bool igInputInt3(const char* label,int v[3],ImGuiInputTextFlags flags); CIMGUI_API bool igInputInt4(const char* label,int v[4],ImGuiInputTextFlags flags); CIMGUI_API bool igInputDouble(const char* label,double* v,double step,double step_fast,const char* format,ImGuiInputTextFlags flags); CIMGUI_API bool igInputScalar(const char* label,ImGuiDataType data_type,void* p_data,const void* p_step,const void* p_step_fast,const char* format,ImGuiInputTextFlags flags); CIMGUI_API bool igInputScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,const void* p_step,const void* p_step_fast,const char* format,ImGuiInputTextFlags flags); CIMGUI_API bool igColorEdit3(const char* label,float col[3],ImGuiColorEditFlags flags); CIMGUI_API bool igColorEdit4(const char* label,float col[4],ImGuiColorEditFlags flags); CIMGUI_API bool igColorPicker3(const char* label,float col[3],ImGuiColorEditFlags flags); CIMGUI_API bool igColorPicker4(const char* label,float col[4],ImGuiColorEditFlags flags,const float* ref_col); CIMGUI_API bool igColorButton(const char* desc_id,const ImVec4 col,ImGuiColorEditFlags flags,const ImVec2 size); CIMGUI_API void igSetColorEditOptions(ImGuiColorEditFlags flags); CIMGUI_API bool igTreeNode_Str(const char* label); CIMGUI_API bool igTreeNode_StrStr(const char* str_id,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API bool igTreeNode_StrStr0(const char* str_id,const char* fmt); #endif CIMGUI_API bool igTreeNode_Ptr(const void* ptr_id,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API bool igTreeNode_Ptr0(const void* ptr_id,const char* fmt); #endif CIMGUI_API bool igTreeNodeV_Str(const char* str_id,const char* fmt,va_list args); CIMGUI_API bool igTreeNodeV_Ptr(const void* ptr_id,const char* fmt,va_list args); CIMGUI_API bool igTreeNodeEx_Str(const char* label,ImGuiTreeNodeFlags flags); CIMGUI_API bool igTreeNodeEx_StrStr(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API bool igTreeNodeEx_StrStr0(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt); #endif CIMGUI_API bool igTreeNodeEx_Ptr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API bool igTreeNodeEx_Ptr0(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt); #endif CIMGUI_API bool igTreeNodeExV_Str(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt,va_list args); CIMGUI_API bool igTreeNodeExV_Ptr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,va_list args); CIMGUI_API void igTreePush_Str(const char* str_id); CIMGUI_API void igTreePush_Ptr(const void* ptr_id); CIMGUI_API void igTreePop(void); CIMGUI_API float igGetTreeNodeToLabelSpacing(void); CIMGUI_API bool igCollapsingHeader_TreeNodeFlags(const char* label,ImGuiTreeNodeFlags flags); CIMGUI_API bool igCollapsingHeader_BoolPtr(const char* label,bool* p_visible,ImGuiTreeNodeFlags flags); CIMGUI_API void igSetNextItemOpen(bool is_open,ImGuiCond cond); CIMGUI_API void igSetNextItemStorageID(ImGuiID storage_id); CIMGUI_API bool igSelectable_Bool(const char* label,bool selected,ImGuiSelectableFlags flags,const ImVec2 size); CIMGUI_API bool igSelectable_BoolPtr(const char* label,bool* p_selected,ImGuiSelectableFlags flags,const ImVec2 size); CIMGUI_API ImGuiMultiSelectIO* igBeginMultiSelect(ImGuiMultiSelectFlags flags,int selection_size,int items_count); CIMGUI_API ImGuiMultiSelectIO* igEndMultiSelect(void); CIMGUI_API void igSetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); CIMGUI_API bool igIsItemToggledSelection(void); CIMGUI_API bool igBeginListBox(const char* label,const ImVec2 size); CIMGUI_API void igEndListBox(void); CIMGUI_API bool igListBox_Str_arr(const char* label,int* current_item,const char* const items[],int items_count,int height_in_items); CIMGUI_API bool igListBox_FnStrPtr(const char* label,int* current_item,const char*(*getter)(void* user_data,int idx),void* user_data,int items_count,int height_in_items); CIMGUI_API void igPlotLines_FloatPtr(const char* label,const float* values,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride); CIMGUI_API void igPlotLines_FnFloatPtr(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); CIMGUI_API void igPlotHistogram_FloatPtr(const char* label,const float* values,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride); CIMGUI_API void igPlotHistogram_FnFloatPtr(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); CIMGUI_API void igValue_Bool(const char* prefix,bool b); CIMGUI_API void igValue_Int(const char* prefix,int v); CIMGUI_API void igValue_Uint(const char* prefix,unsigned int v); CIMGUI_API void igValue_Float(const char* prefix,float v,const char* float_format); CIMGUI_API bool igBeginMenuBar(void); CIMGUI_API void igEndMenuBar(void); CIMGUI_API bool igBeginMainMenuBar(void); CIMGUI_API void igEndMainMenuBar(void); CIMGUI_API bool igBeginMenu(const char* label,bool enabled); CIMGUI_API void igEndMenu(void); CIMGUI_API bool igMenuItem_Bool(const char* label,const char* shortcut,bool selected,bool enabled); CIMGUI_API bool igMenuItem_BoolPtr(const char* label,const char* shortcut,bool* p_selected,bool enabled); CIMGUI_API bool igBeginTooltip(void); CIMGUI_API void igEndTooltip(void); CIMGUI_API void igSetTooltip(const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igSetTooltip0(const char* fmt); #endif CIMGUI_API void igSetTooltipV(const char* fmt,va_list args); CIMGUI_API bool igBeginItemTooltip(void); CIMGUI_API void igSetItemTooltip(const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igSetItemTooltip0(const char* fmt); #endif CIMGUI_API void igSetItemTooltipV(const char* fmt,va_list args); CIMGUI_API bool igBeginPopup(const char* str_id,ImGuiWindowFlags flags); CIMGUI_API bool igBeginPopupModal(const char* name,bool* p_open,ImGuiWindowFlags flags); CIMGUI_API void igEndPopup(void); CIMGUI_API void igOpenPopup_Str(const char* str_id,ImGuiPopupFlags popup_flags); CIMGUI_API void igOpenPopup_ID(ImGuiID id,ImGuiPopupFlags popup_flags); CIMGUI_API void igOpenPopupOnItemClick(const char* str_id,ImGuiPopupFlags popup_flags); CIMGUI_API void igCloseCurrentPopup(void); CIMGUI_API bool igBeginPopupContextItem(const char* str_id,ImGuiPopupFlags popup_flags); CIMGUI_API bool igBeginPopupContextWindow(const char* str_id,ImGuiPopupFlags popup_flags); CIMGUI_API bool igBeginPopupContextVoid(const char* str_id,ImGuiPopupFlags popup_flags); CIMGUI_API bool igIsPopupOpen_Str(const char* str_id,ImGuiPopupFlags flags); CIMGUI_API bool igBeginTable(const char* str_id,int columns,ImGuiTableFlags flags,const ImVec2 outer_size,float inner_width); CIMGUI_API void igEndTable(void); CIMGUI_API void igTableNextRow(ImGuiTableRowFlags row_flags,float min_row_height); CIMGUI_API bool igTableNextColumn(void); CIMGUI_API bool igTableSetColumnIndex(int column_n); CIMGUI_API void igTableSetupColumn(const char* label,ImGuiTableColumnFlags flags,float init_width_or_weight,ImGuiID user_id); CIMGUI_API void igTableSetupScrollFreeze(int cols,int rows); CIMGUI_API void igTableHeader(const char* label); CIMGUI_API void igTableHeadersRow(void); CIMGUI_API void igTableAngledHeadersRow(void); CIMGUI_API ImGuiTableSortSpecs* igTableGetSortSpecs(void); CIMGUI_API int igTableGetColumnCount(void); CIMGUI_API int igTableGetColumnIndex(void); CIMGUI_API int igTableGetRowIndex(void); CIMGUI_API const char* igTableGetColumnName_Int(int column_n); CIMGUI_API ImGuiTableColumnFlags igTableGetColumnFlags(int column_n); CIMGUI_API void igTableSetColumnEnabled(int column_n,bool v); CIMGUI_API int igTableGetHoveredColumn(void); CIMGUI_API void igTableSetBgColor(ImGuiTableBgTarget target,ImU32 color,int column_n); CIMGUI_API void igColumns(int count,const char* id,bool borders); CIMGUI_API void igNextColumn(void); CIMGUI_API int igGetColumnIndex(void); CIMGUI_API float igGetColumnWidth(int column_index); CIMGUI_API void igSetColumnWidth(int column_index,float width); CIMGUI_API float igGetColumnOffset(int column_index); CIMGUI_API void igSetColumnOffset(int column_index,float offset_x); CIMGUI_API int igGetColumnsCount(void); CIMGUI_API bool igBeginTabBar(const char* str_id,ImGuiTabBarFlags flags); CIMGUI_API void igEndTabBar(void); CIMGUI_API bool igBeginTabItem(const char* label,bool* p_open,ImGuiTabItemFlags flags); CIMGUI_API void igEndTabItem(void); CIMGUI_API bool igTabItemButton(const char* label,ImGuiTabItemFlags flags); CIMGUI_API void igSetTabItemClosed(const char* tab_or_docked_window_label); CIMGUI_API ImGuiID igDockSpace(ImGuiID dockspace_id,const ImVec2 size,ImGuiDockNodeFlags flags,const ImGuiWindowClass* window_class); CIMGUI_API ImGuiID igDockSpaceOverViewport(ImGuiID dockspace_id,const ImGuiViewport* viewport,ImGuiDockNodeFlags flags,const ImGuiWindowClass* window_class); CIMGUI_API void igSetNextWindowDockID(ImGuiID dock_id,ImGuiCond cond); CIMGUI_API void igSetNextWindowClass(const ImGuiWindowClass* window_class); CIMGUI_API ImGuiID igGetWindowDockID(void); CIMGUI_API bool igIsWindowDocked(void); CIMGUI_API void igLogToTTY(int auto_open_depth); CIMGUI_API void igLogToFile(int auto_open_depth,const char* filename); CIMGUI_API void igLogToClipboard(int auto_open_depth); CIMGUI_API void igLogFinish(void); CIMGUI_API void igLogButtons(void); CIMGUI_API void igLogText(const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igLogText0(const char* fmt); #endif CIMGUI_API void igLogTextV(const char* fmt,va_list args); CIMGUI_API bool igBeginDragDropSource(ImGuiDragDropFlags flags); CIMGUI_API bool igSetDragDropPayload(const char* type,const void* data,size_t sz,ImGuiCond cond); CIMGUI_API void igEndDragDropSource(void); CIMGUI_API bool igBeginDragDropTarget(void); CIMGUI_API const ImGuiPayload* igAcceptDragDropPayload(const char* type,ImGuiDragDropFlags flags); CIMGUI_API void igEndDragDropTarget(void); CIMGUI_API const ImGuiPayload* igGetDragDropPayload(void); CIMGUI_API void igBeginDisabled(bool disabled); CIMGUI_API void igEndDisabled(void); CIMGUI_API void igPushClipRect(const ImVec2 clip_rect_min,const ImVec2 clip_rect_max,bool intersect_with_current_clip_rect); CIMGUI_API void igPopClipRect(void); CIMGUI_API void igSetItemDefaultFocus(void); CIMGUI_API void igSetKeyboardFocusHere(int offset); CIMGUI_API void igSetNavCursorVisible(bool visible); CIMGUI_API void igSetNextItemAllowOverlap(void); CIMGUI_API bool igIsItemHovered(ImGuiHoveredFlags flags); CIMGUI_API bool igIsItemActive(void); CIMGUI_API bool igIsItemFocused(void); CIMGUI_API bool igIsItemClicked(ImGuiMouseButton mouse_button); CIMGUI_API bool igIsItemVisible(void); CIMGUI_API bool igIsItemEdited(void); CIMGUI_API bool igIsItemActivated(void); CIMGUI_API bool igIsItemDeactivated(void); CIMGUI_API bool igIsItemDeactivatedAfterEdit(void); CIMGUI_API bool igIsItemToggledOpen(void); CIMGUI_API bool igIsAnyItemHovered(void); CIMGUI_API bool igIsAnyItemActive(void); CIMGUI_API bool igIsAnyItemFocused(void); CIMGUI_API ImGuiID igGetItemID(void); CIMGUI_API void igGetItemRectMin(ImVec2 *pOut); CIMGUI_API void igGetItemRectMax(ImVec2 *pOut); CIMGUI_API void igGetItemRectSize(ImVec2 *pOut); CIMGUI_API ImGuiViewport* igGetMainViewport(void); CIMGUI_API ImDrawList* igGetBackgroundDrawList(ImGuiViewport* viewport); CIMGUI_API ImDrawList* igGetForegroundDrawList_ViewportPtr(ImGuiViewport* viewport); CIMGUI_API bool igIsRectVisible_Nil(const ImVec2 size); CIMGUI_API bool igIsRectVisible_Vec2(const ImVec2 rect_min,const ImVec2 rect_max); CIMGUI_API double igGetTime(void); CIMGUI_API int igGetFrameCount(void); CIMGUI_API ImDrawListSharedData* igGetDrawListSharedData(void); CIMGUI_API const char* igGetStyleColorName(ImGuiCol idx); CIMGUI_API void igSetStateStorage(ImGuiStorage* storage); CIMGUI_API ImGuiStorage* igGetStateStorage(void); CIMGUI_API void igCalcTextSize(ImVec2 *pOut,const char* text,const char* text_end,bool hide_text_after_double_hash,float wrap_width); CIMGUI_API void igColorConvertU32ToFloat4(ImVec4 *pOut,ImU32 in); CIMGUI_API ImU32 igColorConvertFloat4ToU32(const ImVec4 in); CIMGUI_API void igColorConvertRGBtoHSV(float r,float g,float b,float* out_h,float* out_s,float* out_v); CIMGUI_API void igColorConvertHSVtoRGB(float h,float s,float v,float* out_r,float* out_g,float* out_b); CIMGUI_API bool igIsKeyDown_Nil(ImGuiKey key); CIMGUI_API bool igIsKeyPressed_Bool(ImGuiKey key,bool repeat); CIMGUI_API bool igIsKeyReleased_Nil(ImGuiKey key); CIMGUI_API bool igIsKeyChordPressed_Nil(ImGuiKeyChord key_chord); CIMGUI_API int igGetKeyPressedAmount(ImGuiKey key,float repeat_delay,float rate); CIMGUI_API const char* igGetKeyName(ImGuiKey key); CIMGUI_API void igSetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); CIMGUI_API bool igShortcut_Nil(ImGuiKeyChord key_chord,ImGuiInputFlags flags); CIMGUI_API void igSetNextItemShortcut(ImGuiKeyChord key_chord,ImGuiInputFlags flags); CIMGUI_API void igSetItemKeyOwner_Nil(ImGuiKey key); CIMGUI_API bool igIsMouseDown_Nil(ImGuiMouseButton button); CIMGUI_API bool igIsMouseClicked_Bool(ImGuiMouseButton button,bool repeat); CIMGUI_API bool igIsMouseReleased_Nil(ImGuiMouseButton button); CIMGUI_API bool igIsMouseDoubleClicked_Nil(ImGuiMouseButton button); CIMGUI_API bool igIsMouseReleasedWithDelay(ImGuiMouseButton button,float delay); CIMGUI_API int igGetMouseClickedCount(ImGuiMouseButton button); CIMGUI_API bool igIsMouseHoveringRect(const ImVec2 r_min,const ImVec2 r_max,bool clip); CIMGUI_API bool igIsMousePosValid(const ImVec2* mouse_pos); CIMGUI_API bool igIsAnyMouseDown(void); CIMGUI_API void igGetMousePos(ImVec2 *pOut); CIMGUI_API void igGetMousePosOnOpeningCurrentPopup(ImVec2 *pOut); CIMGUI_API bool igIsMouseDragging(ImGuiMouseButton button,float lock_threshold); CIMGUI_API void igGetMouseDragDelta(ImVec2 *pOut,ImGuiMouseButton button,float lock_threshold); CIMGUI_API void igResetMouseDragDelta(ImGuiMouseButton button); CIMGUI_API ImGuiMouseCursor igGetMouseCursor(void); CIMGUI_API void igSetMouseCursor(ImGuiMouseCursor cursor_type); CIMGUI_API void igSetNextFrameWantCaptureMouse(bool want_capture_mouse); CIMGUI_API const char* igGetClipboardText(void); CIMGUI_API void igSetClipboardText(const char* text); CIMGUI_API void igLoadIniSettingsFromDisk(const char* ini_filename); CIMGUI_API void igLoadIniSettingsFromMemory(const char* ini_data,size_t ini_size); CIMGUI_API void igSaveIniSettingsToDisk(const char* ini_filename); CIMGUI_API const char* igSaveIniSettingsToMemory(size_t* out_ini_size); CIMGUI_API void igDebugTextEncoding(const char* text); CIMGUI_API void igDebugFlashStyleColor(ImGuiCol idx); CIMGUI_API void igDebugStartItemPicker(void); CIMGUI_API bool igDebugCheckVersionAndDataLayout(const char* version_str,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_drawvert,size_t sz_drawidx); CIMGUI_API void igDebugLog(const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igDebugLog0(const char* fmt); #endif CIMGUI_API void igDebugLogV(const char* fmt,va_list args); CIMGUI_API void igSetAllocatorFunctions(ImGuiMemAllocFunc alloc_func,ImGuiMemFreeFunc free_func,void* user_data); CIMGUI_API void igGetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func,ImGuiMemFreeFunc* p_free_func,void** p_user_data); CIMGUI_API void* igMemAlloc(size_t size); CIMGUI_API void igMemFree(void* ptr); CIMGUI_API void igUpdatePlatformWindows(void); CIMGUI_API void igRenderPlatformWindowsDefault(void* platform_render_arg,void* renderer_render_arg); CIMGUI_API void igDestroyPlatformWindows(void); CIMGUI_API ImGuiViewport* igFindViewportByID(ImGuiID id); CIMGUI_API ImGuiViewport* igFindViewportByPlatformHandle(void* platform_handle); CIMGUI_API ImGuiTableSortSpecs* ImGuiTableSortSpecs_ImGuiTableSortSpecs(void); CIMGUI_API void ImGuiTableSortSpecs_destroy(ImGuiTableSortSpecs* self); CIMGUI_API ImGuiTableColumnSortSpecs* ImGuiTableColumnSortSpecs_ImGuiTableColumnSortSpecs(void); CIMGUI_API void ImGuiTableColumnSortSpecs_destroy(ImGuiTableColumnSortSpecs* self); CIMGUI_API ImGuiStyle* ImGuiStyle_ImGuiStyle(void); CIMGUI_API void ImGuiStyle_destroy(ImGuiStyle* self); CIMGUI_API void ImGuiStyle_ScaleAllSizes(ImGuiStyle* self,float scale_factor); CIMGUI_API void ImGuiIO_AddKeyEvent(ImGuiIO* self,ImGuiKey key,bool down); CIMGUI_API void ImGuiIO_AddKeyAnalogEvent(ImGuiIO* self,ImGuiKey key,bool down,float v); CIMGUI_API void ImGuiIO_AddMousePosEvent(ImGuiIO* self,float x,float y); CIMGUI_API void ImGuiIO_AddMouseButtonEvent(ImGuiIO* self,int button,bool down); CIMGUI_API void ImGuiIO_AddMouseWheelEvent(ImGuiIO* self,float wheel_x,float wheel_y); CIMGUI_API void ImGuiIO_AddMouseSourceEvent(ImGuiIO* self,ImGuiMouseSource source); CIMGUI_API void ImGuiIO_AddMouseViewportEvent(ImGuiIO* self,ImGuiID id); CIMGUI_API void ImGuiIO_AddFocusEvent(ImGuiIO* self,bool focused); CIMGUI_API void ImGuiIO_AddInputCharacter(ImGuiIO* self,unsigned int c); CIMGUI_API void ImGuiIO_AddInputCharacterUTF16(ImGuiIO* self,ImWchar16 c); CIMGUI_API void ImGuiIO_AddInputCharactersUTF8(ImGuiIO* self,const char* str); CIMGUI_API void ImGuiIO_SetKeyEventNativeData(ImGuiIO* self,ImGuiKey key,int native_keycode,int native_scancode,int native_legacy_index); CIMGUI_API void ImGuiIO_SetAppAcceptingEvents(ImGuiIO* self,bool accepting_events); CIMGUI_API void ImGuiIO_ClearEventsQueue(ImGuiIO* self); CIMGUI_API void ImGuiIO_ClearInputKeys(ImGuiIO* self); CIMGUI_API void ImGuiIO_ClearInputMouse(ImGuiIO* self); CIMGUI_API ImGuiIO* ImGuiIO_ImGuiIO(void); CIMGUI_API void ImGuiIO_destroy(ImGuiIO* self); CIMGUI_API ImGuiInputTextCallbackData* ImGuiInputTextCallbackData_ImGuiInputTextCallbackData(void); CIMGUI_API void ImGuiInputTextCallbackData_destroy(ImGuiInputTextCallbackData* self); CIMGUI_API void ImGuiInputTextCallbackData_DeleteChars(ImGuiInputTextCallbackData* self,int pos,int bytes_count); CIMGUI_API void ImGuiInputTextCallbackData_InsertChars(ImGuiInputTextCallbackData* self,int pos,const char* text,const char* text_end); CIMGUI_API void ImGuiInputTextCallbackData_SelectAll(ImGuiInputTextCallbackData* self); CIMGUI_API void ImGuiInputTextCallbackData_ClearSelection(ImGuiInputTextCallbackData* self); CIMGUI_API bool ImGuiInputTextCallbackData_HasSelection(ImGuiInputTextCallbackData* self); CIMGUI_API ImGuiWindowClass* ImGuiWindowClass_ImGuiWindowClass(void); CIMGUI_API void ImGuiWindowClass_destroy(ImGuiWindowClass* self); CIMGUI_API ImGuiPayload* ImGuiPayload_ImGuiPayload(void); CIMGUI_API void ImGuiPayload_destroy(ImGuiPayload* self); CIMGUI_API void ImGuiPayload_Clear(ImGuiPayload* self); CIMGUI_API bool ImGuiPayload_IsDataType(ImGuiPayload* self,const char* type); CIMGUI_API bool ImGuiPayload_IsPreview(ImGuiPayload* self); CIMGUI_API bool ImGuiPayload_IsDelivery(ImGuiPayload* self); CIMGUI_API ImGuiOnceUponAFrame* ImGuiOnceUponAFrame_ImGuiOnceUponAFrame(void); CIMGUI_API void ImGuiOnceUponAFrame_destroy(ImGuiOnceUponAFrame* self); CIMGUI_API ImGuiTextFilter* ImGuiTextFilter_ImGuiTextFilter(const char* default_filter); CIMGUI_API void ImGuiTextFilter_destroy(ImGuiTextFilter* self); CIMGUI_API bool ImGuiTextFilter_Draw(ImGuiTextFilter* self,const char* label,float width); CIMGUI_API bool ImGuiTextFilter_PassFilter(ImGuiTextFilter* self,const char* text,const char* text_end); CIMGUI_API void ImGuiTextFilter_Build(ImGuiTextFilter* self); CIMGUI_API void ImGuiTextFilter_Clear(ImGuiTextFilter* self); CIMGUI_API bool ImGuiTextFilter_IsActive(ImGuiTextFilter* self); CIMGUI_API ImGuiTextRange* ImGuiTextRange_ImGuiTextRange_Nil(void); CIMGUI_API void ImGuiTextRange_destroy(ImGuiTextRange* self); CIMGUI_API ImGuiTextRange* ImGuiTextRange_ImGuiTextRange_Str(const char* _b,const char* _e); CIMGUI_API bool ImGuiTextRange_empty(ImGuiTextRange* self); CIMGUI_API void ImGuiTextRange_split(ImGuiTextRange* self,char separator,ImVector_ImGuiTextRange* out); CIMGUI_API ImGuiTextBuffer* ImGuiTextBuffer_ImGuiTextBuffer(void); CIMGUI_API void ImGuiTextBuffer_destroy(ImGuiTextBuffer* self); CIMGUI_API const char* ImGuiTextBuffer_begin(ImGuiTextBuffer* self); CIMGUI_API const char* ImGuiTextBuffer_end(ImGuiTextBuffer* self); CIMGUI_API int ImGuiTextBuffer_size(ImGuiTextBuffer* self); CIMGUI_API bool ImGuiTextBuffer_empty(ImGuiTextBuffer* self); CIMGUI_API void ImGuiTextBuffer_clear(ImGuiTextBuffer* self); CIMGUI_API void ImGuiTextBuffer_resize(ImGuiTextBuffer* self,int size); CIMGUI_API void ImGuiTextBuffer_reserve(ImGuiTextBuffer* self,int capacity); CIMGUI_API const char* ImGuiTextBuffer_c_str(ImGuiTextBuffer* self); CIMGUI_API void ImGuiTextBuffer_append(ImGuiTextBuffer* self,const char* str,const char* str_end); CIMGUI_API void ImGuiTextBuffer_appendfv(ImGuiTextBuffer* self,const char* fmt,va_list args); CIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Int(ImGuiID _key,int _val); CIMGUI_API void ImGuiStoragePair_destroy(ImGuiStoragePair* self); CIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Float(ImGuiID _key,float _val); CIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Ptr(ImGuiID _key,void* _val); CIMGUI_API void ImGuiStorage_Clear(ImGuiStorage* self); CIMGUI_API int ImGuiStorage_GetInt(ImGuiStorage* self,ImGuiID key,int default_val); CIMGUI_API void ImGuiStorage_SetInt(ImGuiStorage* self,ImGuiID key,int val); CIMGUI_API bool ImGuiStorage_GetBool(ImGuiStorage* self,ImGuiID key,bool default_val); CIMGUI_API void ImGuiStorage_SetBool(ImGuiStorage* self,ImGuiID key,bool val); CIMGUI_API float ImGuiStorage_GetFloat(ImGuiStorage* self,ImGuiID key,float default_val); CIMGUI_API void ImGuiStorage_SetFloat(ImGuiStorage* self,ImGuiID key,float val); CIMGUI_API void* ImGuiStorage_GetVoidPtr(ImGuiStorage* self,ImGuiID key); CIMGUI_API void ImGuiStorage_SetVoidPtr(ImGuiStorage* self,ImGuiID key,void* val); CIMGUI_API int* ImGuiStorage_GetIntRef(ImGuiStorage* self,ImGuiID key,int default_val); CIMGUI_API bool* ImGuiStorage_GetBoolRef(ImGuiStorage* self,ImGuiID key,bool default_val); CIMGUI_API float* ImGuiStorage_GetFloatRef(ImGuiStorage* self,ImGuiID key,float default_val); CIMGUI_API void** ImGuiStorage_GetVoidPtrRef(ImGuiStorage* self,ImGuiID key,void* default_val); CIMGUI_API void ImGuiStorage_BuildSortByKey(ImGuiStorage* self); CIMGUI_API void ImGuiStorage_SetAllInt(ImGuiStorage* self,int val); CIMGUI_API ImGuiListClipper* ImGuiListClipper_ImGuiListClipper(void); CIMGUI_API void ImGuiListClipper_destroy(ImGuiListClipper* self); CIMGUI_API void ImGuiListClipper_Begin(ImGuiListClipper* self,int items_count,float items_height); CIMGUI_API void ImGuiListClipper_End(ImGuiListClipper* self); CIMGUI_API bool ImGuiListClipper_Step(ImGuiListClipper* self); CIMGUI_API void ImGuiListClipper_IncludeItemByIndex(ImGuiListClipper* self,int item_index); CIMGUI_API void ImGuiListClipper_IncludeItemsByIndex(ImGuiListClipper* self,int item_begin,int item_end); CIMGUI_API void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* self,int item_index); CIMGUI_API ImColor* ImColor_ImColor_Nil(void); CIMGUI_API void ImColor_destroy(ImColor* self); CIMGUI_API ImColor* ImColor_ImColor_Float(float r,float g,float b,float a); CIMGUI_API ImColor* ImColor_ImColor_Vec4(const ImVec4 col); CIMGUI_API ImColor* ImColor_ImColor_Int(int r,int g,int b,int a); CIMGUI_API ImColor* ImColor_ImColor_U32(ImU32 rgba); CIMGUI_API void ImColor_SetHSV(ImColor* self,float h,float s,float v,float a); CIMGUI_API void ImColor_HSV(ImColor *pOut,float h,float s,float v,float a); CIMGUI_API ImGuiSelectionBasicStorage* ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage(void); CIMGUI_API void ImGuiSelectionBasicStorage_destroy(ImGuiSelectionBasicStorage* self); CIMGUI_API void ImGuiSelectionBasicStorage_ApplyRequests(ImGuiSelectionBasicStorage* self,ImGuiMultiSelectIO* ms_io); CIMGUI_API bool ImGuiSelectionBasicStorage_Contains(ImGuiSelectionBasicStorage* self,ImGuiID id); CIMGUI_API void ImGuiSelectionBasicStorage_Clear(ImGuiSelectionBasicStorage* self); CIMGUI_API void ImGuiSelectionBasicStorage_Swap(ImGuiSelectionBasicStorage* self,ImGuiSelectionBasicStorage* r); CIMGUI_API void ImGuiSelectionBasicStorage_SetItemSelected(ImGuiSelectionBasicStorage* self,ImGuiID id,bool selected); CIMGUI_API bool ImGuiSelectionBasicStorage_GetNextSelectedItem(ImGuiSelectionBasicStorage* self,void** opaque_it,ImGuiID* out_id); CIMGUI_API ImGuiID ImGuiSelectionBasicStorage_GetStorageIdFromIndex(ImGuiSelectionBasicStorage* self,int idx); CIMGUI_API ImGuiSelectionExternalStorage* ImGuiSelectionExternalStorage_ImGuiSelectionExternalStorage(void); CIMGUI_API void ImGuiSelectionExternalStorage_destroy(ImGuiSelectionExternalStorage* self); CIMGUI_API void ImGuiSelectionExternalStorage_ApplyRequests(ImGuiSelectionExternalStorage* self,ImGuiMultiSelectIO* ms_io); CIMGUI_API ImDrawCmd* ImDrawCmd_ImDrawCmd(void); CIMGUI_API void ImDrawCmd_destroy(ImDrawCmd* self); CIMGUI_API ImTextureID ImDrawCmd_GetTexID(ImDrawCmd* self); CIMGUI_API ImDrawListSplitter* ImDrawListSplitter_ImDrawListSplitter(void); CIMGUI_API void ImDrawListSplitter_destroy(ImDrawListSplitter* self); CIMGUI_API void ImDrawListSplitter_Clear(ImDrawListSplitter* self); CIMGUI_API void ImDrawListSplitter_ClearFreeMemory(ImDrawListSplitter* self); CIMGUI_API void ImDrawListSplitter_Split(ImDrawListSplitter* self,ImDrawList* draw_list,int count); CIMGUI_API void ImDrawListSplitter_Merge(ImDrawListSplitter* self,ImDrawList* draw_list); CIMGUI_API void ImDrawListSplitter_SetCurrentChannel(ImDrawListSplitter* self,ImDrawList* draw_list,int channel_idx); CIMGUI_API ImDrawList* ImDrawList_ImDrawList(ImDrawListSharedData* shared_data); CIMGUI_API void ImDrawList_destroy(ImDrawList* self); CIMGUI_API void ImDrawList_PushClipRect(ImDrawList* self,const ImVec2 clip_rect_min,const ImVec2 clip_rect_max,bool intersect_with_current_clip_rect); CIMGUI_API void ImDrawList_PushClipRectFullScreen(ImDrawList* self); CIMGUI_API void ImDrawList_PopClipRect(ImDrawList* self); CIMGUI_API void ImDrawList_PushTexture(ImDrawList* self,ImTextureRef tex_ref); CIMGUI_API void ImDrawList_PopTexture(ImDrawList* self); CIMGUI_API void ImDrawList_GetClipRectMin(ImVec2 *pOut,ImDrawList* self); CIMGUI_API void ImDrawList_GetClipRectMax(ImVec2 *pOut,ImDrawList* self); CIMGUI_API void ImDrawList_AddLine(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,ImU32 col,float thickness); CIMGUI_API void ImDrawList_AddRect(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col,float rounding,ImDrawFlags flags,float thickness); CIMGUI_API void ImDrawList_AddRectFilled(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col,float rounding,ImDrawFlags flags); CIMGUI_API void ImDrawList_AddRectFilledMultiColor(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col_upr_left,ImU32 col_upr_right,ImU32 col_bot_right,ImU32 col_bot_left); CIMGUI_API void ImDrawList_AddQuad(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col,float thickness); CIMGUI_API void ImDrawList_AddQuadFilled(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col); CIMGUI_API void ImDrawList_AddTriangle(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col,float thickness); CIMGUI_API void ImDrawList_AddTriangleFilled(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col); CIMGUI_API void ImDrawList_AddCircle(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments,float thickness); CIMGUI_API void ImDrawList_AddCircleFilled(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments); CIMGUI_API void ImDrawList_AddNgon(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments,float thickness); CIMGUI_API void ImDrawList_AddNgonFilled(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments); CIMGUI_API void ImDrawList_AddEllipse(ImDrawList* self,const ImVec2 center,const ImVec2 radius,ImU32 col,float rot,int num_segments,float thickness); CIMGUI_API void ImDrawList_AddEllipseFilled(ImDrawList* self,const ImVec2 center,const ImVec2 radius,ImU32 col,float rot,int num_segments); CIMGUI_API void ImDrawList_AddText_Vec2(ImDrawList* self,const ImVec2 pos,ImU32 col,const char* text_begin,const char* text_end); CIMGUI_API void ImDrawList_AddText_FontPtr(ImDrawList* self,ImFont* font,float font_size,const ImVec2 pos,ImU32 col,const char* text_begin,const char* text_end,float wrap_width,const ImVec4* cpu_fine_clip_rect); CIMGUI_API void ImDrawList_AddBezierCubic(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col,float thickness,int num_segments); CIMGUI_API void ImDrawList_AddBezierQuadratic(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col,float thickness,int num_segments); CIMGUI_API void ImDrawList_AddPolyline(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col,ImDrawFlags flags,float thickness); CIMGUI_API void ImDrawList_AddConvexPolyFilled(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col); CIMGUI_API void ImDrawList_AddConcavePolyFilled(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col); CIMGUI_API void ImDrawList_AddImage(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p_min,const ImVec2 p_max,const ImVec2 uv_min,const ImVec2 uv_max,ImU32 col); CIMGUI_API void ImDrawList_AddImageQuad(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 uv1,const ImVec2 uv2,const ImVec2 uv3,const ImVec2 uv4,ImU32 col); CIMGUI_API void ImDrawList_AddImageRounded(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p_min,const ImVec2 p_max,const ImVec2 uv_min,const ImVec2 uv_max,ImU32 col,float rounding,ImDrawFlags flags); CIMGUI_API void ImDrawList_PathClear(ImDrawList* self); CIMGUI_API void ImDrawList_PathLineTo(ImDrawList* self,const ImVec2 pos); CIMGUI_API void ImDrawList_PathLineToMergeDuplicate(ImDrawList* self,const ImVec2 pos); CIMGUI_API void ImDrawList_PathFillConvex(ImDrawList* self,ImU32 col); CIMGUI_API void ImDrawList_PathFillConcave(ImDrawList* self,ImU32 col); CIMGUI_API void ImDrawList_PathStroke(ImDrawList* self,ImU32 col,ImDrawFlags flags,float thickness); CIMGUI_API void ImDrawList_PathArcTo(ImDrawList* self,const ImVec2 center,float radius,float a_min,float a_max,int num_segments); CIMGUI_API void ImDrawList_PathArcToFast(ImDrawList* self,const ImVec2 center,float radius,int a_min_of_12,int a_max_of_12); CIMGUI_API void ImDrawList_PathEllipticalArcTo(ImDrawList* self,const ImVec2 center,const ImVec2 radius,float rot,float a_min,float a_max,int num_segments); CIMGUI_API void ImDrawList_PathBezierCubicCurveTo(ImDrawList* self,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,int num_segments); CIMGUI_API void ImDrawList_PathBezierQuadraticCurveTo(ImDrawList* self,const ImVec2 p2,const ImVec2 p3,int num_segments); CIMGUI_API void ImDrawList_PathRect(ImDrawList* self,const ImVec2 rect_min,const ImVec2 rect_max,float rounding,ImDrawFlags flags); CIMGUI_API void ImDrawList_AddCallback(ImDrawList* self,ImDrawCallback callback,void* userdata,size_t userdata_size); CIMGUI_API void ImDrawList_AddDrawCmd(ImDrawList* self); CIMGUI_API ImDrawList* ImDrawList_CloneOutput(ImDrawList* self); CIMGUI_API void ImDrawList_ChannelsSplit(ImDrawList* self,int count); CIMGUI_API void ImDrawList_ChannelsMerge(ImDrawList* self); CIMGUI_API void ImDrawList_ChannelsSetCurrent(ImDrawList* self,int n); CIMGUI_API void ImDrawList_PrimReserve(ImDrawList* self,int idx_count,int vtx_count); CIMGUI_API void ImDrawList_PrimUnreserve(ImDrawList* self,int idx_count,int vtx_count); CIMGUI_API void ImDrawList_PrimRect(ImDrawList* self,const ImVec2 a,const ImVec2 b,ImU32 col); CIMGUI_API void ImDrawList_PrimRectUV(ImDrawList* self,const ImVec2 a,const ImVec2 b,const ImVec2 uv_a,const ImVec2 uv_b,ImU32 col); CIMGUI_API void ImDrawList_PrimQuadUV(ImDrawList* self,const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 d,const ImVec2 uv_a,const ImVec2 uv_b,const ImVec2 uv_c,const ImVec2 uv_d,ImU32 col); CIMGUI_API void ImDrawList_PrimWriteVtx(ImDrawList* self,const ImVec2 pos,const ImVec2 uv,ImU32 col); CIMGUI_API void ImDrawList_PrimWriteIdx(ImDrawList* self,ImDrawIdx idx); CIMGUI_API void ImDrawList_PrimVtx(ImDrawList* self,const ImVec2 pos,const ImVec2 uv,ImU32 col); CIMGUI_API void ImDrawList__SetDrawListSharedData(ImDrawList* self,ImDrawListSharedData* data); CIMGUI_API void ImDrawList__ResetForNewFrame(ImDrawList* self); CIMGUI_API void ImDrawList__ClearFreeMemory(ImDrawList* self); CIMGUI_API void ImDrawList__PopUnusedDrawCmd(ImDrawList* self); CIMGUI_API void ImDrawList__TryMergeDrawCmds(ImDrawList* self); CIMGUI_API void ImDrawList__OnChangedClipRect(ImDrawList* self); CIMGUI_API void ImDrawList__OnChangedTexture(ImDrawList* self); CIMGUI_API void ImDrawList__OnChangedVtxOffset(ImDrawList* self); CIMGUI_API void ImDrawList__SetTexture(ImDrawList* self,ImTextureRef tex_ref); CIMGUI_API int ImDrawList__CalcCircleAutoSegmentCount(ImDrawList* self,float radius); CIMGUI_API void ImDrawList__PathArcToFastEx(ImDrawList* self,const ImVec2 center,float radius,int a_min_sample,int a_max_sample,int a_step); CIMGUI_API void ImDrawList__PathArcToN(ImDrawList* self,const ImVec2 center,float radius,float a_min,float a_max,int num_segments); CIMGUI_API ImDrawData* ImDrawData_ImDrawData(void); CIMGUI_API void ImDrawData_destroy(ImDrawData* self); CIMGUI_API void ImDrawData_Clear(ImDrawData* self); CIMGUI_API void ImDrawData_AddDrawList(ImDrawData* self,ImDrawList* draw_list); CIMGUI_API void ImDrawData_DeIndexAllBuffers(ImDrawData* self); CIMGUI_API void ImDrawData_ScaleClipRects(ImDrawData* self,const ImVec2 fb_scale); CIMGUI_API ImTextureData* ImTextureData_ImTextureData(void); CIMGUI_API void ImTextureData_destroy(ImTextureData* self); CIMGUI_API void ImTextureData_Create(ImTextureData* self,ImTextureFormat format,int w,int h); CIMGUI_API void ImTextureData_DestroyPixels(ImTextureData* self); CIMGUI_API void* ImTextureData_GetPixels(ImTextureData* self); CIMGUI_API void* ImTextureData_GetPixelsAt(ImTextureData* self,int x,int y); CIMGUI_API int ImTextureData_GetSizeInBytes(ImTextureData* self); CIMGUI_API int ImTextureData_GetPitch(ImTextureData* self); CIMGUI_API void ImTextureData_GetTexRef(ImTextureRef *pOut,ImTextureData* self); CIMGUI_API ImTextureID ImTextureData_GetTexID(ImTextureData* self); CIMGUI_API void ImTextureData_SetTexID(ImTextureData* self,ImTextureID tex_id); CIMGUI_API void ImTextureData_SetStatus(ImTextureData* self,ImTextureStatus status); CIMGUI_API ImFontConfig* ImFontConfig_ImFontConfig(void); CIMGUI_API void ImFontConfig_destroy(ImFontConfig* self); CIMGUI_API ImFontGlyph* ImFontGlyph_ImFontGlyph(void); CIMGUI_API void ImFontGlyph_destroy(ImFontGlyph* self); CIMGUI_API ImFontGlyphRangesBuilder* ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder(void); CIMGUI_API void ImFontGlyphRangesBuilder_destroy(ImFontGlyphRangesBuilder* self); CIMGUI_API void ImFontGlyphRangesBuilder_Clear(ImFontGlyphRangesBuilder* self); CIMGUI_API bool ImFontGlyphRangesBuilder_GetBit(ImFontGlyphRangesBuilder* self,size_t n); CIMGUI_API void ImFontGlyphRangesBuilder_SetBit(ImFontGlyphRangesBuilder* self,size_t n); CIMGUI_API void ImFontGlyphRangesBuilder_AddChar(ImFontGlyphRangesBuilder* self,ImWchar c); CIMGUI_API void ImFontGlyphRangesBuilder_AddText(ImFontGlyphRangesBuilder* self,const char* text,const char* text_end); CIMGUI_API void ImFontGlyphRangesBuilder_AddRanges(ImFontGlyphRangesBuilder* self,const ImWchar* ranges); CIMGUI_API void ImFontGlyphRangesBuilder_BuildRanges(ImFontGlyphRangesBuilder* self,ImVector_ImWchar* out_ranges); CIMGUI_API ImFontAtlasRect* ImFontAtlasRect_ImFontAtlasRect(void); CIMGUI_API void ImFontAtlasRect_destroy(ImFontAtlasRect* self); CIMGUI_API ImFontAtlas* ImFontAtlas_ImFontAtlas(void); CIMGUI_API void ImFontAtlas_destroy(ImFontAtlas* self); CIMGUI_API ImFont* ImFontAtlas_AddFont(ImFontAtlas* self,const ImFontConfig* font_cfg); CIMGUI_API ImFont* ImFontAtlas_AddFontDefault(ImFontAtlas* self,const ImFontConfig* font_cfg); CIMGUI_API ImFont* ImFontAtlas_AddFontFromFileTTF(ImFontAtlas* self,const char* filename,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges); CIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryTTF(ImFontAtlas* self,void* font_data,int font_data_size,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges); CIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryCompressedTTF(ImFontAtlas* self,const void* compressed_font_data,int compressed_font_data_size,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges); CIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryCompressedBase85TTF(ImFontAtlas* self,const char* compressed_font_data_base85,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges); CIMGUI_API void ImFontAtlas_RemoveFont(ImFontAtlas* self,ImFont* font); CIMGUI_API void ImFontAtlas_Clear(ImFontAtlas* self); CIMGUI_API void ImFontAtlas_CompactCache(ImFontAtlas* self); CIMGUI_API void ImFontAtlas_ClearInputData(ImFontAtlas* self); CIMGUI_API void ImFontAtlas_ClearFonts(ImFontAtlas* self); CIMGUI_API void ImFontAtlas_ClearTexData(ImFontAtlas* self); CIMGUI_API const ImWchar* ImFontAtlas_GetGlyphRangesDefault(ImFontAtlas* self); CIMGUI_API ImFontAtlasRectId ImFontAtlas_AddCustomRect(ImFontAtlas* self,int width,int height,ImFontAtlasRect* out_r); CIMGUI_API void ImFontAtlas_RemoveCustomRect(ImFontAtlas* self,ImFontAtlasRectId id); CIMGUI_API bool ImFontAtlas_GetCustomRect(ImFontAtlas* self,ImFontAtlasRectId id,ImFontAtlasRect* out_r); CIMGUI_API ImFontBaked* ImFontBaked_ImFontBaked(void); CIMGUI_API void ImFontBaked_destroy(ImFontBaked* self); CIMGUI_API void ImFontBaked_ClearOutputData(ImFontBaked* self); CIMGUI_API ImFontGlyph* ImFontBaked_FindGlyph(ImFontBaked* self,ImWchar c); CIMGUI_API ImFontGlyph* ImFontBaked_FindGlyphNoFallback(ImFontBaked* self,ImWchar c); CIMGUI_API float ImFontBaked_GetCharAdvance(ImFontBaked* self,ImWchar c); CIMGUI_API bool ImFontBaked_IsGlyphLoaded(ImFontBaked* self,ImWchar c); CIMGUI_API ImFont* ImFont_ImFont(void); CIMGUI_API void ImFont_destroy(ImFont* self); CIMGUI_API bool ImFont_IsGlyphInFont(ImFont* self,ImWchar c); CIMGUI_API bool ImFont_IsLoaded(ImFont* self); CIMGUI_API const char* ImFont_GetDebugName(ImFont* self); CIMGUI_API ImFontBaked* ImFont_GetFontBaked(ImFont* self,float font_size,float density); CIMGUI_API void ImFont_CalcTextSizeA(ImVec2 *pOut,ImFont* self,float size,float max_width,float wrap_width,const char* text_begin,const char* text_end,const char** remaining); CIMGUI_API const char* ImFont_CalcWordWrapPosition(ImFont* self,float size,const char* text,const char* text_end,float wrap_width); CIMGUI_API void ImFont_RenderChar(ImFont* self,ImDrawList* draw_list,float size,const ImVec2 pos,ImU32 col,ImWchar c,const ImVec4* cpu_fine_clip); CIMGUI_API void ImFont_RenderText(ImFont* self,ImDrawList* draw_list,float size,const ImVec2 pos,ImU32 col,const ImVec4 clip_rect,const char* text_begin,const char* text_end,float wrap_width,bool cpu_fine_clip); CIMGUI_API void ImFont_ClearOutputData(ImFont* self); CIMGUI_API void ImFont_AddRemapChar(ImFont* self,ImWchar from_codepoint,ImWchar to_codepoint); CIMGUI_API bool ImFont_IsGlyphRangeUnused(ImFont* self,unsigned int c_begin,unsigned int c_last); CIMGUI_API ImGuiViewport* ImGuiViewport_ImGuiViewport(void); CIMGUI_API void ImGuiViewport_destroy(ImGuiViewport* self); CIMGUI_API void ImGuiViewport_GetCenter(ImVec2 *pOut,ImGuiViewport* self); CIMGUI_API void ImGuiViewport_GetWorkCenter(ImVec2 *pOut,ImGuiViewport* self); CIMGUI_API ImGuiPlatformIO* ImGuiPlatformIO_ImGuiPlatformIO(void); CIMGUI_API void ImGuiPlatformIO_destroy(ImGuiPlatformIO* self); CIMGUI_API ImGuiPlatformMonitor* ImGuiPlatformMonitor_ImGuiPlatformMonitor(void); CIMGUI_API void ImGuiPlatformMonitor_destroy(ImGuiPlatformMonitor* self); CIMGUI_API ImGuiPlatformImeData* ImGuiPlatformImeData_ImGuiPlatformImeData(void); CIMGUI_API void ImGuiPlatformImeData_destroy(ImGuiPlatformImeData* self); CIMGUI_API ImGuiID igImHashData(const void* data,size_t data_size,ImGuiID seed); CIMGUI_API ImGuiID igImHashStr(const char* data,size_t data_size,ImGuiID seed); CIMGUI_API void igImQsort(void* base,size_t count,size_t size_of_element,int(*compare_func)(void const*,void const*)); CIMGUI_API ImU32 igImAlphaBlendColors(ImU32 col_a,ImU32 col_b); CIMGUI_API bool igImIsPowerOfTwo_Int(int v); CIMGUI_API bool igImIsPowerOfTwo_U64(ImU64 v); CIMGUI_API int igImUpperPowerOfTwo(int v); CIMGUI_API unsigned int igImCountSetBits(unsigned int v); CIMGUI_API int igImStricmp(const char* str1,const char* str2); CIMGUI_API int igImStrnicmp(const char* str1,const char* str2,size_t count); CIMGUI_API void igImStrncpy(char* dst,const char* src,size_t count); CIMGUI_API char* igImStrdup(const char* str); CIMGUI_API void* igImMemdup(const void* src,size_t size); CIMGUI_API char* igImStrdupcpy(char* dst,size_t* p_dst_size,const char* str); CIMGUI_API const char* igImStrchrRange(const char* str_begin,const char* str_end,char c); CIMGUI_API const char* igImStreolRange(const char* str,const char* str_end); CIMGUI_API const char* igImStristr(const char* haystack,const char* haystack_end,const char* needle,const char* needle_end); CIMGUI_API void igImStrTrimBlanks(char* str); CIMGUI_API const char* igImStrSkipBlank(const char* str); CIMGUI_API int igImStrlenW(const ImWchar* str); CIMGUI_API const char* igImStrbol(const char* buf_mid_line,const char* buf_begin); CIMGUI_API char igImToUpper(char c); CIMGUI_API bool igImCharIsBlankA(char c); CIMGUI_API bool igImCharIsBlankW(unsigned int c); CIMGUI_API bool igImCharIsXdigitA(char c); CIMGUI_API int igImFormatString(char* buf,size_t buf_size,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API int igImFormatString0(char* buf,size_t buf_size,const char* fmt); #endif CIMGUI_API int igImFormatStringV(char* buf,size_t buf_size,const char* fmt,va_list args); CIMGUI_API void igImFormatStringToTempBuffer(const char** out_buf,const char** out_buf_end,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igImFormatStringToTempBuffer0(const char** out_buf,const char** out_buf_end,const char* fmt); #endif CIMGUI_API void igImFormatStringToTempBufferV(const char** out_buf,const char** out_buf_end,const char* fmt,va_list args); CIMGUI_API const char* igImParseFormatFindStart(const char* format); CIMGUI_API const char* igImParseFormatFindEnd(const char* format); CIMGUI_API const char* igImParseFormatTrimDecorations(const char* format,char* buf,size_t buf_size); CIMGUI_API void igImParseFormatSanitizeForPrinting(const char* fmt_in,char* fmt_out,size_t fmt_out_size); CIMGUI_API const char* igImParseFormatSanitizeForScanning(const char* fmt_in,char* fmt_out,size_t fmt_out_size); CIMGUI_API int igImParseFormatPrecision(const char* format,int default_value); CIMGUI_API int igImTextCharToUtf8(char out_buf[5],unsigned int c); CIMGUI_API int igImTextStrToUtf8(char* out_buf,int out_buf_size,const ImWchar* in_text,const ImWchar* in_text_end); CIMGUI_API int igImTextCharFromUtf8(unsigned int* out_char,const char* in_text,const char* in_text_end); CIMGUI_API int igImTextStrFromUtf8(ImWchar* out_buf,int out_buf_size,const char* in_text,const char* in_text_end,const char** in_remaining); CIMGUI_API int igImTextCountCharsFromUtf8(const char* in_text,const char* in_text_end); CIMGUI_API int igImTextCountUtf8BytesFromChar(const char* in_text,const char* in_text_end); CIMGUI_API int igImTextCountUtf8BytesFromStr(const ImWchar* in_text,const ImWchar* in_text_end); CIMGUI_API const char* igImTextFindPreviousUtf8Codepoint(const char* in_text_start,const char* in_text_curr); CIMGUI_API int igImTextCountLines(const char* in_text,const char* in_text_end); CIMGUI_API ImFileHandle igImFileOpen(const char* filename,const char* mode); CIMGUI_API bool igImFileClose(ImFileHandle file); CIMGUI_API ImU64 igImFileGetSize(ImFileHandle file); CIMGUI_API ImU64 igImFileRead(void* data,ImU64 size,ImU64 count,ImFileHandle file); CIMGUI_API ImU64 igImFileWrite(const void* data,ImU64 size,ImU64 count,ImFileHandle file); CIMGUI_API void* igImFileLoadToMemory(const char* filename,const char* mode,size_t* out_file_size,int padding_bytes); CIMGUI_API float igImPow_Float(float x,float y); CIMGUI_API double igImPow_double(double x,double y); CIMGUI_API float igImLog_Float(float x); CIMGUI_API double igImLog_double(double x); CIMGUI_API int igImAbs_Int(int x); CIMGUI_API float igImAbs_Float(float x); CIMGUI_API double igImAbs_double(double x); CIMGUI_API float igImSign_Float(float x); CIMGUI_API double igImSign_double(double x); CIMGUI_API float igImRsqrt_Float(float x); CIMGUI_API double igImRsqrt_double(double x); CIMGUI_API void igImMin(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs); CIMGUI_API void igImMax(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs); CIMGUI_API void igImClamp(ImVec2 *pOut,const ImVec2 v,const ImVec2 mn,const ImVec2 mx); CIMGUI_API void igImLerp_Vec2Float(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,float t); CIMGUI_API void igImLerp_Vec2Vec2(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 t); CIMGUI_API void igImLerp_Vec4(ImVec4 *pOut,const ImVec4 a,const ImVec4 b,float t); CIMGUI_API float igImSaturate(float f); CIMGUI_API float igImLengthSqr_Vec2(const ImVec2 lhs); CIMGUI_API float igImLengthSqr_Vec4(const ImVec4 lhs); CIMGUI_API float igImInvLength(const ImVec2 lhs,float fail_value); CIMGUI_API float igImTrunc_Float(float f); CIMGUI_API void igImTrunc_Vec2(ImVec2 *pOut,const ImVec2 v); CIMGUI_API float igImFloor_Float(float f); CIMGUI_API void igImFloor_Vec2(ImVec2 *pOut,const ImVec2 v); CIMGUI_API float igImTrunc64(float f); CIMGUI_API float igImRound64(float f); CIMGUI_API int igImModPositive(int a,int b); CIMGUI_API float igImDot(const ImVec2 a,const ImVec2 b); CIMGUI_API void igImRotate(ImVec2 *pOut,const ImVec2 v,float cos_a,float sin_a); CIMGUI_API float igImLinearSweep(float current,float target,float speed); CIMGUI_API float igImLinearRemapClamp(float s0,float s1,float d0,float d1,float x); CIMGUI_API void igImMul(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs); CIMGUI_API bool igImIsFloatAboveGuaranteedIntegerPrecision(float f); CIMGUI_API float igImExponentialMovingAverage(float avg,float sample,int n); CIMGUI_API void igImBezierCubicCalc(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,float t); CIMGUI_API void igImBezierCubicClosestPoint(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 p,int num_segments); CIMGUI_API void igImBezierCubicClosestPointCasteljau(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 p,float tess_tol); CIMGUI_API void igImBezierQuadraticCalc(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,float t); CIMGUI_API void igImLineClosestPoint(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 p); CIMGUI_API bool igImTriangleContainsPoint(const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p); CIMGUI_API void igImTriangleClosestPoint(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p); CIMGUI_API void igImTriangleBarycentricCoords(const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p,float* out_u,float* out_v,float* out_w); CIMGUI_API float igImTriangleArea(const ImVec2 a,const ImVec2 b,const ImVec2 c); CIMGUI_API bool igImTriangleIsClockwise(const ImVec2 a,const ImVec2 b,const ImVec2 c); CIMGUI_API ImVec1* ImVec1_ImVec1_Nil(void); CIMGUI_API void ImVec1_destroy(ImVec1* self); CIMGUI_API ImVec1* ImVec1_ImVec1_Float(float _x); CIMGUI_API ImVec2i* ImVec2i_ImVec2i_Nil(void); CIMGUI_API void ImVec2i_destroy(ImVec2i* self); CIMGUI_API ImVec2i* ImVec2i_ImVec2i_Int(int _x,int _y); CIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_Nil(void); CIMGUI_API void ImVec2ih_destroy(ImVec2ih* self); CIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_short(short _x,short _y); CIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_Vec2(const ImVec2 rhs); CIMGUI_API ImRect* ImRect_ImRect_Nil(void); CIMGUI_API void ImRect_destroy(ImRect* self); CIMGUI_API ImRect* ImRect_ImRect_Vec2(const ImVec2 min,const ImVec2 max); CIMGUI_API ImRect* ImRect_ImRect_Vec4(const ImVec4 v); CIMGUI_API ImRect* ImRect_ImRect_Float(float x1,float y1,float x2,float y2); CIMGUI_API void ImRect_GetCenter(ImVec2 *pOut,ImRect* self); CIMGUI_API void ImRect_GetSize(ImVec2 *pOut,ImRect* self); CIMGUI_API float ImRect_GetWidth(ImRect* self); CIMGUI_API float ImRect_GetHeight(ImRect* self); CIMGUI_API float ImRect_GetArea(ImRect* self); CIMGUI_API void ImRect_GetTL(ImVec2 *pOut,ImRect* self); CIMGUI_API void ImRect_GetTR(ImVec2 *pOut,ImRect* self); CIMGUI_API void ImRect_GetBL(ImVec2 *pOut,ImRect* self); CIMGUI_API void ImRect_GetBR(ImVec2 *pOut,ImRect* self); CIMGUI_API bool ImRect_Contains_Vec2(ImRect* self,const ImVec2 p); CIMGUI_API bool ImRect_Contains_Rect(ImRect* self,const ImRect r); CIMGUI_API bool ImRect_ContainsWithPad(ImRect* self,const ImVec2 p,const ImVec2 pad); CIMGUI_API bool ImRect_Overlaps(ImRect* self,const ImRect r); CIMGUI_API void ImRect_Add_Vec2(ImRect* self,const ImVec2 p); CIMGUI_API void ImRect_Add_Rect(ImRect* self,const ImRect r); CIMGUI_API void ImRect_Expand_Float(ImRect* self,const float amount); CIMGUI_API void ImRect_Expand_Vec2(ImRect* self,const ImVec2 amount); CIMGUI_API void ImRect_Translate(ImRect* self,const ImVec2 d); CIMGUI_API void ImRect_TranslateX(ImRect* self,float dx); CIMGUI_API void ImRect_TranslateY(ImRect* self,float dy); CIMGUI_API void ImRect_ClipWith(ImRect* self,const ImRect r); CIMGUI_API void ImRect_ClipWithFull(ImRect* self,const ImRect r); CIMGUI_API bool ImRect_IsInverted(ImRect* self); CIMGUI_API void ImRect_ToVec4(ImVec4 *pOut,ImRect* self); CIMGUI_API size_t igImBitArrayGetStorageSizeInBytes(int bitcount); CIMGUI_API void igImBitArrayClearAllBits(ImU32* arr,int bitcount); CIMGUI_API bool igImBitArrayTestBit(const ImU32* arr,int n); CIMGUI_API void igImBitArrayClearBit(ImU32* arr,int n); CIMGUI_API void igImBitArraySetBit(ImU32* arr,int n); CIMGUI_API void igImBitArraySetBitRange(ImU32* arr,int n,int n2); CIMGUI_API void ImBitVector_Create(ImBitVector* self,int sz); CIMGUI_API void ImBitVector_Clear(ImBitVector* self); CIMGUI_API bool ImBitVector_TestBit(ImBitVector* self,int n); CIMGUI_API void ImBitVector_SetBit(ImBitVector* self,int n); CIMGUI_API void ImBitVector_ClearBit(ImBitVector* self,int n); CIMGUI_API void ImGuiTextIndex_clear(ImGuiTextIndex* self); CIMGUI_API int ImGuiTextIndex_size(ImGuiTextIndex* self); CIMGUI_API const char* ImGuiTextIndex_get_line_begin(ImGuiTextIndex* self,const char* base,int n); CIMGUI_API const char* ImGuiTextIndex_get_line_end(ImGuiTextIndex* self,const char* base,int n); CIMGUI_API void ImGuiTextIndex_append(ImGuiTextIndex* self,const char* base,int old_size,int new_size); CIMGUI_API ImGuiStoragePair* igImLowerBound(ImGuiStoragePair* in_begin,ImGuiStoragePair* in_end,ImGuiID key); CIMGUI_API ImDrawListSharedData* ImDrawListSharedData_ImDrawListSharedData(void); CIMGUI_API void ImDrawListSharedData_destroy(ImDrawListSharedData* self); CIMGUI_API void ImDrawListSharedData_SetCircleTessellationMaxError(ImDrawListSharedData* self,float max_error); CIMGUI_API ImDrawDataBuilder* ImDrawDataBuilder_ImDrawDataBuilder(void); CIMGUI_API void ImDrawDataBuilder_destroy(ImDrawDataBuilder* self); CIMGUI_API void* ImGuiStyleVarInfo_GetVarPtr(ImGuiStyleVarInfo* self,void* parent); CIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Int(ImGuiStyleVar idx,int v); CIMGUI_API void ImGuiStyleMod_destroy(ImGuiStyleMod* self); CIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Float(ImGuiStyleVar idx,float v); CIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Vec2(ImGuiStyleVar idx,ImVec2 v); CIMGUI_API ImGuiComboPreviewData* ImGuiComboPreviewData_ImGuiComboPreviewData(void); CIMGUI_API void ImGuiComboPreviewData_destroy(ImGuiComboPreviewData* self); CIMGUI_API ImGuiMenuColumns* ImGuiMenuColumns_ImGuiMenuColumns(void); CIMGUI_API void ImGuiMenuColumns_destroy(ImGuiMenuColumns* self); CIMGUI_API void ImGuiMenuColumns_Update(ImGuiMenuColumns* self,float spacing,bool window_reappearing); CIMGUI_API float ImGuiMenuColumns_DeclColumns(ImGuiMenuColumns* self,float w_icon,float w_label,float w_shortcut,float w_mark); CIMGUI_API void ImGuiMenuColumns_CalcNextTotalWidth(ImGuiMenuColumns* self,bool update_offsets); CIMGUI_API ImGuiInputTextDeactivatedState* ImGuiInputTextDeactivatedState_ImGuiInputTextDeactivatedState(void); CIMGUI_API void ImGuiInputTextDeactivatedState_destroy(ImGuiInputTextDeactivatedState* self); CIMGUI_API void ImGuiInputTextDeactivatedState_ClearFreeMemory(ImGuiInputTextDeactivatedState* self); CIMGUI_API ImGuiInputTextState* ImGuiInputTextState_ImGuiInputTextState(void); CIMGUI_API void ImGuiInputTextState_destroy(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_ClearText(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_ClearFreeMemory(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_OnKeyPressed(ImGuiInputTextState* self,int key); CIMGUI_API void ImGuiInputTextState_OnCharPressed(ImGuiInputTextState* self,unsigned int c); CIMGUI_API void ImGuiInputTextState_CursorAnimReset(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_CursorClamp(ImGuiInputTextState* self); CIMGUI_API bool ImGuiInputTextState_HasSelection(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_ClearSelection(ImGuiInputTextState* self); CIMGUI_API int ImGuiInputTextState_GetCursorPos(ImGuiInputTextState* self); CIMGUI_API int ImGuiInputTextState_GetSelectionStart(ImGuiInputTextState* self); CIMGUI_API int ImGuiInputTextState_GetSelectionEnd(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_SelectAll(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_ReloadUserBufAndSelectAll(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_ReloadUserBufAndKeepSelection(ImGuiInputTextState* self); CIMGUI_API void ImGuiInputTextState_ReloadUserBufAndMoveToEnd(ImGuiInputTextState* self); CIMGUI_API ImGuiNextWindowData* ImGuiNextWindowData_ImGuiNextWindowData(void); CIMGUI_API void ImGuiNextWindowData_destroy(ImGuiNextWindowData* self); CIMGUI_API void ImGuiNextWindowData_ClearFlags(ImGuiNextWindowData* self); CIMGUI_API ImGuiNextItemData* ImGuiNextItemData_ImGuiNextItemData(void); CIMGUI_API void ImGuiNextItemData_destroy(ImGuiNextItemData* self); CIMGUI_API void ImGuiNextItemData_ClearFlags(ImGuiNextItemData* self); CIMGUI_API ImGuiLastItemData* ImGuiLastItemData_ImGuiLastItemData(void); CIMGUI_API void ImGuiLastItemData_destroy(ImGuiLastItemData* self); CIMGUI_API ImGuiErrorRecoveryState* ImGuiErrorRecoveryState_ImGuiErrorRecoveryState(void); CIMGUI_API void ImGuiErrorRecoveryState_destroy(ImGuiErrorRecoveryState* self); CIMGUI_API ImGuiPtrOrIndex* ImGuiPtrOrIndex_ImGuiPtrOrIndex_Ptr(void* ptr); CIMGUI_API void ImGuiPtrOrIndex_destroy(ImGuiPtrOrIndex* self); CIMGUI_API ImGuiPtrOrIndex* ImGuiPtrOrIndex_ImGuiPtrOrIndex_Int(int index); CIMGUI_API ImGuiPopupData* ImGuiPopupData_ImGuiPopupData(void); CIMGUI_API void ImGuiPopupData_destroy(ImGuiPopupData* self); CIMGUI_API ImGuiInputEvent* ImGuiInputEvent_ImGuiInputEvent(void); CIMGUI_API void ImGuiInputEvent_destroy(ImGuiInputEvent* self); CIMGUI_API ImGuiKeyRoutingData* ImGuiKeyRoutingData_ImGuiKeyRoutingData(void); CIMGUI_API void ImGuiKeyRoutingData_destroy(ImGuiKeyRoutingData* self); CIMGUI_API ImGuiKeyRoutingTable* ImGuiKeyRoutingTable_ImGuiKeyRoutingTable(void); CIMGUI_API void ImGuiKeyRoutingTable_destroy(ImGuiKeyRoutingTable* self); CIMGUI_API void ImGuiKeyRoutingTable_Clear(ImGuiKeyRoutingTable* self); CIMGUI_API ImGuiKeyOwnerData* ImGuiKeyOwnerData_ImGuiKeyOwnerData(void); CIMGUI_API void ImGuiKeyOwnerData_destroy(ImGuiKeyOwnerData* self); CIMGUI_API ImGuiListClipperRange ImGuiListClipperRange_FromIndices(int min,int max); CIMGUI_API ImGuiListClipperRange ImGuiListClipperRange_FromPositions(float y1,float y2,int off_min,int off_max); CIMGUI_API ImGuiListClipperData* ImGuiListClipperData_ImGuiListClipperData(void); CIMGUI_API void ImGuiListClipperData_destroy(ImGuiListClipperData* self); CIMGUI_API void ImGuiListClipperData_Reset(ImGuiListClipperData* self,ImGuiListClipper* clipper); CIMGUI_API ImGuiNavItemData* ImGuiNavItemData_ImGuiNavItemData(void); CIMGUI_API void ImGuiNavItemData_destroy(ImGuiNavItemData* self); CIMGUI_API void ImGuiNavItemData_Clear(ImGuiNavItemData* self); CIMGUI_API ImGuiTypingSelectState* ImGuiTypingSelectState_ImGuiTypingSelectState(void); CIMGUI_API void ImGuiTypingSelectState_destroy(ImGuiTypingSelectState* self); CIMGUI_API void ImGuiTypingSelectState_Clear(ImGuiTypingSelectState* self); CIMGUI_API ImGuiOldColumnData* ImGuiOldColumnData_ImGuiOldColumnData(void); CIMGUI_API void ImGuiOldColumnData_destroy(ImGuiOldColumnData* self); CIMGUI_API ImGuiOldColumns* ImGuiOldColumns_ImGuiOldColumns(void); CIMGUI_API void ImGuiOldColumns_destroy(ImGuiOldColumns* self); CIMGUI_API ImGuiBoxSelectState* ImGuiBoxSelectState_ImGuiBoxSelectState(void); CIMGUI_API void ImGuiBoxSelectState_destroy(ImGuiBoxSelectState* self); CIMGUI_API ImGuiMultiSelectTempData* ImGuiMultiSelectTempData_ImGuiMultiSelectTempData(void); CIMGUI_API void ImGuiMultiSelectTempData_destroy(ImGuiMultiSelectTempData* self); CIMGUI_API void ImGuiMultiSelectTempData_Clear(ImGuiMultiSelectTempData* self); CIMGUI_API void ImGuiMultiSelectTempData_ClearIO(ImGuiMultiSelectTempData* self); CIMGUI_API ImGuiMultiSelectState* ImGuiMultiSelectState_ImGuiMultiSelectState(void); CIMGUI_API void ImGuiMultiSelectState_destroy(ImGuiMultiSelectState* self); CIMGUI_API ImGuiDockNode* ImGuiDockNode_ImGuiDockNode(ImGuiID id); CIMGUI_API void ImGuiDockNode_destroy(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsRootNode(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsDockSpace(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsFloatingNode(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsCentralNode(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsHiddenTabBar(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsNoTabBar(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsSplitNode(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsLeafNode(ImGuiDockNode* self); CIMGUI_API bool ImGuiDockNode_IsEmpty(ImGuiDockNode* self); CIMGUI_API void ImGuiDockNode_Rect(ImRect *pOut,ImGuiDockNode* self); CIMGUI_API void ImGuiDockNode_SetLocalFlags(ImGuiDockNode* self,ImGuiDockNodeFlags flags); CIMGUI_API void ImGuiDockNode_UpdateMergedFlags(ImGuiDockNode* self); CIMGUI_API ImGuiDockContext* ImGuiDockContext_ImGuiDockContext(void); CIMGUI_API void ImGuiDockContext_destroy(ImGuiDockContext* self); CIMGUI_API ImGuiViewportP* ImGuiViewportP_ImGuiViewportP(void); CIMGUI_API void ImGuiViewportP_destroy(ImGuiViewportP* self); CIMGUI_API void ImGuiViewportP_ClearRequestFlags(ImGuiViewportP* self); CIMGUI_API void ImGuiViewportP_CalcWorkRectPos(ImVec2 *pOut,ImGuiViewportP* self,const ImVec2 inset_min); CIMGUI_API void ImGuiViewportP_CalcWorkRectSize(ImVec2 *pOut,ImGuiViewportP* self,const ImVec2 inset_min,const ImVec2 inset_max); CIMGUI_API void ImGuiViewportP_UpdateWorkRect(ImGuiViewportP* self); CIMGUI_API void ImGuiViewportP_GetMainRect(ImRect *pOut,ImGuiViewportP* self); CIMGUI_API void ImGuiViewportP_GetWorkRect(ImRect *pOut,ImGuiViewportP* self); CIMGUI_API void ImGuiViewportP_GetBuildWorkRect(ImRect *pOut,ImGuiViewportP* self); CIMGUI_API ImGuiWindowSettings* ImGuiWindowSettings_ImGuiWindowSettings(void); CIMGUI_API void ImGuiWindowSettings_destroy(ImGuiWindowSettings* self); CIMGUI_API char* ImGuiWindowSettings_GetName(ImGuiWindowSettings* self); CIMGUI_API ImGuiSettingsHandler* ImGuiSettingsHandler_ImGuiSettingsHandler(void); CIMGUI_API void ImGuiSettingsHandler_destroy(ImGuiSettingsHandler* self); CIMGUI_API ImGuiDebugAllocInfo* ImGuiDebugAllocInfo_ImGuiDebugAllocInfo(void); CIMGUI_API void ImGuiDebugAllocInfo_destroy(ImGuiDebugAllocInfo* self); CIMGUI_API ImGuiStackLevelInfo* ImGuiStackLevelInfo_ImGuiStackLevelInfo(void); CIMGUI_API void ImGuiStackLevelInfo_destroy(ImGuiStackLevelInfo* self); CIMGUI_API ImGuiIDStackTool* ImGuiIDStackTool_ImGuiIDStackTool(void); CIMGUI_API void ImGuiIDStackTool_destroy(ImGuiIDStackTool* self); CIMGUI_API ImGuiContextHook* ImGuiContextHook_ImGuiContextHook(void); CIMGUI_API void ImGuiContextHook_destroy(ImGuiContextHook* self); CIMGUI_API ImGuiContext* ImGuiContext_ImGuiContext(ImFontAtlas* shared_font_atlas); CIMGUI_API void ImGuiContext_destroy(ImGuiContext* self); CIMGUI_API ImGuiWindow* ImGuiWindow_ImGuiWindow(ImGuiContext* context,const char* name); CIMGUI_API void ImGuiWindow_destroy(ImGuiWindow* self); CIMGUI_API ImGuiID ImGuiWindow_GetID_Str(ImGuiWindow* self,const char* str,const char* str_end); CIMGUI_API ImGuiID ImGuiWindow_GetID_Ptr(ImGuiWindow* self,const void* ptr); CIMGUI_API ImGuiID ImGuiWindow_GetID_Int(ImGuiWindow* self,int n); CIMGUI_API ImGuiID ImGuiWindow_GetIDFromPos(ImGuiWindow* self,const ImVec2 p_abs); CIMGUI_API ImGuiID ImGuiWindow_GetIDFromRectangle(ImGuiWindow* self,const ImRect r_abs); CIMGUI_API void ImGuiWindow_Rect(ImRect *pOut,ImGuiWindow* self); CIMGUI_API void ImGuiWindow_TitleBarRect(ImRect *pOut,ImGuiWindow* self); CIMGUI_API void ImGuiWindow_MenuBarRect(ImRect *pOut,ImGuiWindow* self); CIMGUI_API ImGuiTabItem* ImGuiTabItem_ImGuiTabItem(void); CIMGUI_API void ImGuiTabItem_destroy(ImGuiTabItem* self); CIMGUI_API ImGuiTabBar* ImGuiTabBar_ImGuiTabBar(void); CIMGUI_API void ImGuiTabBar_destroy(ImGuiTabBar* self); CIMGUI_API ImGuiTableColumn* ImGuiTableColumn_ImGuiTableColumn(void); CIMGUI_API void ImGuiTableColumn_destroy(ImGuiTableColumn* self); CIMGUI_API ImGuiTableInstanceData* ImGuiTableInstanceData_ImGuiTableInstanceData(void); CIMGUI_API void ImGuiTableInstanceData_destroy(ImGuiTableInstanceData* self); CIMGUI_API ImGuiTable* ImGuiTable_ImGuiTable(void); CIMGUI_API void ImGuiTable_destroy(ImGuiTable* self); CIMGUI_API ImGuiTableTempData* ImGuiTableTempData_ImGuiTableTempData(void); CIMGUI_API void ImGuiTableTempData_destroy(ImGuiTableTempData* self); CIMGUI_API ImGuiTableColumnSettings* ImGuiTableColumnSettings_ImGuiTableColumnSettings(void); CIMGUI_API void ImGuiTableColumnSettings_destroy(ImGuiTableColumnSettings* self); CIMGUI_API ImGuiTableSettings* ImGuiTableSettings_ImGuiTableSettings(void); CIMGUI_API void ImGuiTableSettings_destroy(ImGuiTableSettings* self); CIMGUI_API ImGuiTableColumnSettings* ImGuiTableSettings_GetColumnSettings(ImGuiTableSettings* self); CIMGUI_API ImGuiIO* igGetIO_ContextPtr(ImGuiContext* ctx); CIMGUI_API ImGuiPlatformIO* igGetPlatformIO_ContextPtr(ImGuiContext* ctx); CIMGUI_API ImGuiWindow* igGetCurrentWindowRead(void); CIMGUI_API ImGuiWindow* igGetCurrentWindow(void); CIMGUI_API ImGuiWindow* igFindWindowByID(ImGuiID id); CIMGUI_API ImGuiWindow* igFindWindowByName(const char* name); CIMGUI_API void igUpdateWindowParentAndRootLinks(ImGuiWindow* window,ImGuiWindowFlags flags,ImGuiWindow* parent_window); CIMGUI_API void igUpdateWindowSkipRefresh(ImGuiWindow* window); CIMGUI_API void igCalcWindowNextAutoFitSize(ImVec2 *pOut,ImGuiWindow* window); CIMGUI_API bool igIsWindowChildOf(ImGuiWindow* window,ImGuiWindow* potential_parent,bool popup_hierarchy,bool dock_hierarchy); CIMGUI_API bool igIsWindowWithinBeginStackOf(ImGuiWindow* window,ImGuiWindow* potential_parent); CIMGUI_API bool igIsWindowAbove(ImGuiWindow* potential_above,ImGuiWindow* potential_below); CIMGUI_API bool igIsWindowNavFocusable(ImGuiWindow* window); CIMGUI_API void igSetWindowPos_WindowPtr(ImGuiWindow* window,const ImVec2 pos,ImGuiCond cond); CIMGUI_API void igSetWindowSize_WindowPtr(ImGuiWindow* window,const ImVec2 size,ImGuiCond cond); CIMGUI_API void igSetWindowCollapsed_WindowPtr(ImGuiWindow* window,bool collapsed,ImGuiCond cond); CIMGUI_API void igSetWindowHitTestHole(ImGuiWindow* window,const ImVec2 pos,const ImVec2 size); CIMGUI_API void igSetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window); CIMGUI_API void igSetWindowParentWindowForFocusRoute(ImGuiWindow* window,ImGuiWindow* parent_window); CIMGUI_API void igWindowRectAbsToRel(ImRect *pOut,ImGuiWindow* window,const ImRect r); CIMGUI_API void igWindowRectRelToAbs(ImRect *pOut,ImGuiWindow* window,const ImRect r); CIMGUI_API void igWindowPosAbsToRel(ImVec2 *pOut,ImGuiWindow* window,const ImVec2 p); CIMGUI_API void igWindowPosRelToAbs(ImVec2 *pOut,ImGuiWindow* window,const ImVec2 p); CIMGUI_API void igFocusWindow(ImGuiWindow* window,ImGuiFocusRequestFlags flags); CIMGUI_API void igFocusTopMostWindowUnderOne(ImGuiWindow* under_this_window,ImGuiWindow* ignore_window,ImGuiViewport* filter_viewport,ImGuiFocusRequestFlags flags); CIMGUI_API void igBringWindowToFocusFront(ImGuiWindow* window); CIMGUI_API void igBringWindowToDisplayFront(ImGuiWindow* window); CIMGUI_API void igBringWindowToDisplayBack(ImGuiWindow* window); CIMGUI_API void igBringWindowToDisplayBehind(ImGuiWindow* window,ImGuiWindow* above_window); CIMGUI_API int igFindWindowDisplayIndex(ImGuiWindow* window); CIMGUI_API ImGuiWindow* igFindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); CIMGUI_API void igSetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags); CIMGUI_API void igRegisterUserTexture(ImTextureData* tex); CIMGUI_API void igUnregisterUserTexture(ImTextureData* tex); CIMGUI_API void igRegisterFontAtlas(ImFontAtlas* atlas); CIMGUI_API void igUnregisterFontAtlas(ImFontAtlas* atlas); CIMGUI_API void igSetCurrentFont(ImFont* font,float font_size_before_scaling,float font_size_after_scaling); CIMGUI_API void igUpdateCurrentFontSize(float restore_font_size_after_scaling); CIMGUI_API void igSetFontRasterizerDensity(float rasterizer_density); CIMGUI_API float igGetFontRasterizerDensity(void); CIMGUI_API float igGetRoundedFontSize(float size); CIMGUI_API ImFont* igGetDefaultFont(void); CIMGUI_API void igPushPasswordFont(void); CIMGUI_API void igPopPasswordFont(void); CIMGUI_API ImDrawList* igGetForegroundDrawList_WindowPtr(ImGuiWindow* window); CIMGUI_API void igAddDrawListToDrawDataEx(ImDrawData* draw_data,ImVector_ImDrawListPtr* out_list,ImDrawList* draw_list); CIMGUI_API void igInitialize(void); CIMGUI_API void igShutdown(void); CIMGUI_API void igUpdateInputEvents(bool trickle_fast_inputs); CIMGUI_API void igUpdateHoveredWindowAndCaptureFlags(const ImVec2 mouse_pos); CIMGUI_API void igFindHoveredWindowEx(const ImVec2 pos,bool find_first_and_in_any_viewport,ImGuiWindow** out_hovered_window,ImGuiWindow** out_hovered_window_under_moving_window); CIMGUI_API void igStartMouseMovingWindow(ImGuiWindow* window); CIMGUI_API void igStartMouseMovingWindowOrNode(ImGuiWindow* window,ImGuiDockNode* node,bool undock); CIMGUI_API void igUpdateMouseMovingWindowNewFrame(void); CIMGUI_API void igUpdateMouseMovingWindowEndFrame(void); CIMGUI_API ImGuiID igAddContextHook(ImGuiContext* context,const ImGuiContextHook* hook); CIMGUI_API void igRemoveContextHook(ImGuiContext* context,ImGuiID hook_to_remove); CIMGUI_API void igCallContextHooks(ImGuiContext* context,ImGuiContextHookType type); CIMGUI_API void igTranslateWindowsInViewport(ImGuiViewportP* viewport,const ImVec2 old_pos,const ImVec2 new_pos,const ImVec2 old_size,const ImVec2 new_size); CIMGUI_API void igScaleWindowsInViewport(ImGuiViewportP* viewport,float scale); CIMGUI_API void igDestroyPlatformWindow(ImGuiViewportP* viewport); CIMGUI_API void igSetWindowViewport(ImGuiWindow* window,ImGuiViewportP* viewport); CIMGUI_API void igSetCurrentViewport(ImGuiWindow* window,ImGuiViewportP* viewport); CIMGUI_API const ImGuiPlatformMonitor* igGetViewportPlatformMonitor(ImGuiViewport* viewport); CIMGUI_API ImGuiViewportP* igFindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos); CIMGUI_API void igMarkIniSettingsDirty_Nil(void); CIMGUI_API void igMarkIniSettingsDirty_WindowPtr(ImGuiWindow* window); CIMGUI_API void igClearIniSettings(void); CIMGUI_API void igAddSettingsHandler(const ImGuiSettingsHandler* handler); CIMGUI_API void igRemoveSettingsHandler(const char* type_name); CIMGUI_API ImGuiSettingsHandler* igFindSettingsHandler(const char* type_name); CIMGUI_API ImGuiWindowSettings* igCreateNewWindowSettings(const char* name); CIMGUI_API ImGuiWindowSettings* igFindWindowSettingsByID(ImGuiID id); CIMGUI_API ImGuiWindowSettings* igFindWindowSettingsByWindow(ImGuiWindow* window); CIMGUI_API void igClearWindowSettings(const char* name); CIMGUI_API void igLocalizeRegisterEntries(const ImGuiLocEntry* entries,int count); CIMGUI_API const char* igLocalizeGetMsg(ImGuiLocKey key); CIMGUI_API void igSetScrollX_WindowPtr(ImGuiWindow* window,float scroll_x); CIMGUI_API void igSetScrollY_WindowPtr(ImGuiWindow* window,float scroll_y); CIMGUI_API void igSetScrollFromPosX_WindowPtr(ImGuiWindow* window,float local_x,float center_x_ratio); CIMGUI_API void igSetScrollFromPosY_WindowPtr(ImGuiWindow* window,float local_y,float center_y_ratio); CIMGUI_API void igScrollToItem(ImGuiScrollFlags flags); CIMGUI_API void igScrollToRect(ImGuiWindow* window,const ImRect rect,ImGuiScrollFlags flags); CIMGUI_API void igScrollToRectEx(ImVec2 *pOut,ImGuiWindow* window,const ImRect rect,ImGuiScrollFlags flags); CIMGUI_API void igScrollToBringRectIntoView(ImGuiWindow* window,const ImRect rect); CIMGUI_API ImGuiItemStatusFlags igGetItemStatusFlags(void); CIMGUI_API ImGuiItemFlags igGetItemFlags(void); CIMGUI_API ImGuiID igGetActiveID(void); CIMGUI_API ImGuiID igGetFocusID(void); CIMGUI_API void igSetActiveID(ImGuiID id,ImGuiWindow* window); CIMGUI_API void igSetFocusID(ImGuiID id,ImGuiWindow* window); CIMGUI_API void igClearActiveID(void); CIMGUI_API ImGuiID igGetHoveredID(void); CIMGUI_API void igSetHoveredID(ImGuiID id); CIMGUI_API void igKeepAliveID(ImGuiID id); CIMGUI_API void igMarkItemEdited(ImGuiID id); CIMGUI_API void igPushOverrideID(ImGuiID id); CIMGUI_API ImGuiID igGetIDWithSeed_Str(const char* str_id_begin,const char* str_id_end,ImGuiID seed); CIMGUI_API ImGuiID igGetIDWithSeed_Int(int n,ImGuiID seed); CIMGUI_API void igItemSize_Vec2(const ImVec2 size,float text_baseline_y); CIMGUI_API void igItemSize_Rect(const ImRect bb,float text_baseline_y); CIMGUI_API bool igItemAdd(const ImRect bb,ImGuiID id,const ImRect* nav_bb,ImGuiItemFlags extra_flags); CIMGUI_API bool igItemHoverable(const ImRect bb,ImGuiID id,ImGuiItemFlags item_flags); CIMGUI_API bool igIsWindowContentHoverable(ImGuiWindow* window,ImGuiHoveredFlags flags); CIMGUI_API bool igIsClippedEx(const ImRect bb,ImGuiID id); CIMGUI_API void igSetLastItemData(ImGuiID item_id,ImGuiItemFlags item_flags,ImGuiItemStatusFlags status_flags,const ImRect item_rect); CIMGUI_API void igCalcItemSize(ImVec2 *pOut,ImVec2 size,float default_w,float default_h); CIMGUI_API float igCalcWrapWidthForPos(const ImVec2 pos,float wrap_pos_x); CIMGUI_API void igPushMultiItemsWidths(int components,float width_full); CIMGUI_API void igShrinkWidths(ImGuiShrinkWidthItem* items,int count,float width_excess,float width_min); CIMGUI_API const ImGuiStyleVarInfo* igGetStyleVarInfo(ImGuiStyleVar idx); CIMGUI_API void igBeginDisabledOverrideReenable(void); CIMGUI_API void igEndDisabledOverrideReenable(void); CIMGUI_API void igLogBegin(ImGuiLogFlags flags,int auto_open_depth); CIMGUI_API void igLogToBuffer(int auto_open_depth); CIMGUI_API void igLogRenderedText(const ImVec2* ref_pos,const char* text,const char* text_end); CIMGUI_API void igLogSetNextTextDecoration(const char* prefix,const char* suffix); CIMGUI_API bool igBeginChildEx(const char* name,ImGuiID id,const ImVec2 size_arg,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags); CIMGUI_API bool igBeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_window_flags); CIMGUI_API bool igBeginPopupMenuEx(ImGuiID id,const char* label,ImGuiWindowFlags extra_window_flags); CIMGUI_API void igOpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags); CIMGUI_API void igClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup); CIMGUI_API void igClosePopupsOverWindow(ImGuiWindow* ref_window,bool restore_focus_to_window_under_popup); CIMGUI_API void igClosePopupsExceptModals(void); CIMGUI_API bool igIsPopupOpen_ID(ImGuiID id,ImGuiPopupFlags popup_flags); CIMGUI_API void igGetPopupAllowedExtentRect(ImRect *pOut,ImGuiWindow* window); CIMGUI_API ImGuiWindow* igGetTopMostPopupModal(void); CIMGUI_API ImGuiWindow* igGetTopMostAndVisiblePopupModal(void); CIMGUI_API ImGuiWindow* igFindBlockingModal(ImGuiWindow* window); CIMGUI_API void igFindBestWindowPosForPopup(ImVec2 *pOut,ImGuiWindow* window); CIMGUI_API void igFindBestWindowPosForPopupEx(ImVec2 *pOut,const ImVec2 ref_pos,const ImVec2 size,ImGuiDir* last_dir,const ImRect r_outer,const ImRect r_avoid,ImGuiPopupPositionPolicy policy); CIMGUI_API bool igBeginTooltipEx(ImGuiTooltipFlags tooltip_flags,ImGuiWindowFlags extra_window_flags); CIMGUI_API bool igBeginTooltipHidden(void); CIMGUI_API bool igBeginViewportSideBar(const char* name,ImGuiViewport* viewport,ImGuiDir dir,float size,ImGuiWindowFlags window_flags); CIMGUI_API bool igBeginMenuEx(const char* label,const char* icon,bool enabled); CIMGUI_API bool igMenuItemEx(const char* label,const char* icon,const char* shortcut,bool selected,bool enabled); CIMGUI_API bool igBeginComboPopup(ImGuiID popup_id,const ImRect bb,ImGuiComboFlags flags); CIMGUI_API bool igBeginComboPreview(void); CIMGUI_API void igEndComboPreview(void); CIMGUI_API void igNavInitWindow(ImGuiWindow* window,bool force_reinit); CIMGUI_API void igNavInitRequestApplyResult(void); CIMGUI_API bool igNavMoveRequestButNoResultYet(void); CIMGUI_API void igNavMoveRequestSubmit(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags,ImGuiScrollFlags scroll_flags); CIMGUI_API void igNavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags,ImGuiScrollFlags scroll_flags); CIMGUI_API void igNavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); CIMGUI_API void igNavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result,const ImGuiTreeNodeStackData* tree_node_data); CIMGUI_API void igNavMoveRequestCancel(void); CIMGUI_API void igNavMoveRequestApplyResult(void); CIMGUI_API void igNavMoveRequestTryWrapping(ImGuiWindow* window,ImGuiNavMoveFlags move_flags); CIMGUI_API void igNavHighlightActivated(ImGuiID id); CIMGUI_API void igNavClearPreferredPosForAxis(ImGuiAxis axis); CIMGUI_API void igSetNavCursorVisibleAfterMove(void); CIMGUI_API void igNavUpdateCurrentWindowIsScrollPushableX(void); CIMGUI_API void igSetNavWindow(ImGuiWindow* window); CIMGUI_API void igSetNavID(ImGuiID id,ImGuiNavLayer nav_layer,ImGuiID focus_scope_id,const ImRect rect_rel); CIMGUI_API void igSetNavFocusScope(ImGuiID focus_scope_id); CIMGUI_API void igFocusItem(void); CIMGUI_API void igActivateItemByID(ImGuiID id); CIMGUI_API bool igIsNamedKey(ImGuiKey key); CIMGUI_API bool igIsNamedKeyOrMod(ImGuiKey key); CIMGUI_API bool igIsLegacyKey(ImGuiKey key); CIMGUI_API bool igIsKeyboardKey(ImGuiKey key); CIMGUI_API bool igIsGamepadKey(ImGuiKey key); CIMGUI_API bool igIsMouseKey(ImGuiKey key); CIMGUI_API bool igIsAliasKey(ImGuiKey key); CIMGUI_API bool igIsLRModKey(ImGuiKey key); CIMGUI_API ImGuiKeyChord igFixupKeyChord(ImGuiKeyChord key_chord); CIMGUI_API ImGuiKey igConvertSingleModFlagToKey(ImGuiKey key); CIMGUI_API ImGuiKeyData* igGetKeyData_ContextPtr(ImGuiContext* ctx,ImGuiKey key); CIMGUI_API ImGuiKeyData* igGetKeyData_Key(ImGuiKey key); CIMGUI_API const char* igGetKeyChordName(ImGuiKeyChord key_chord); CIMGUI_API ImGuiKey igMouseButtonToKey(ImGuiMouseButton button); CIMGUI_API bool igIsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold); CIMGUI_API void igGetKeyMagnitude2d(ImVec2 *pOut,ImGuiKey key_left,ImGuiKey key_right,ImGuiKey key_up,ImGuiKey key_down); CIMGUI_API float igGetNavTweakPressedAmount(ImGuiAxis axis); CIMGUI_API int igCalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate); CIMGUI_API void igGetTypematicRepeatRate(ImGuiInputFlags flags,float* repeat_delay,float* repeat_rate); CIMGUI_API void igTeleportMousePos(const ImVec2 pos); CIMGUI_API void igSetActiveIdUsingAllKeyboardKeys(void); CIMGUI_API bool igIsActiveIdUsingNavDir(ImGuiDir dir); CIMGUI_API ImGuiID igGetKeyOwner(ImGuiKey key); CIMGUI_API void igSetKeyOwner(ImGuiKey key,ImGuiID owner_id,ImGuiInputFlags flags); CIMGUI_API void igSetKeyOwnersForKeyChord(ImGuiKeyChord key,ImGuiID owner_id,ImGuiInputFlags flags); CIMGUI_API void igSetItemKeyOwner_InputFlags(ImGuiKey key,ImGuiInputFlags flags); CIMGUI_API bool igTestKeyOwner(ImGuiKey key,ImGuiID owner_id); CIMGUI_API ImGuiKeyOwnerData* igGetKeyOwnerData(ImGuiContext* ctx,ImGuiKey key); CIMGUI_API bool igIsKeyDown_ID(ImGuiKey key,ImGuiID owner_id); CIMGUI_API bool igIsKeyPressed_InputFlags(ImGuiKey key,ImGuiInputFlags flags,ImGuiID owner_id); CIMGUI_API bool igIsKeyReleased_ID(ImGuiKey key,ImGuiID owner_id); CIMGUI_API bool igIsKeyChordPressed_InputFlags(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id); CIMGUI_API bool igIsMouseDown_ID(ImGuiMouseButton button,ImGuiID owner_id); CIMGUI_API bool igIsMouseClicked_InputFlags(ImGuiMouseButton button,ImGuiInputFlags flags,ImGuiID owner_id); CIMGUI_API bool igIsMouseReleased_ID(ImGuiMouseButton button,ImGuiID owner_id); CIMGUI_API bool igIsMouseDoubleClicked_ID(ImGuiMouseButton button,ImGuiID owner_id); CIMGUI_API bool igShortcut_ID(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id); CIMGUI_API bool igSetShortcutRouting(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id); CIMGUI_API bool igTestShortcutRouting(ImGuiKeyChord key_chord,ImGuiID owner_id); CIMGUI_API ImGuiKeyRoutingData* igGetShortcutRoutingData(ImGuiKeyChord key_chord); CIMGUI_API void igDockContextInitialize(ImGuiContext* ctx); CIMGUI_API void igDockContextShutdown(ImGuiContext* ctx); CIMGUI_API void igDockContextClearNodes(ImGuiContext* ctx,ImGuiID root_id,bool clear_settings_refs); CIMGUI_API void igDockContextRebuildNodes(ImGuiContext* ctx); CIMGUI_API void igDockContextNewFrameUpdateUndocking(ImGuiContext* ctx); CIMGUI_API void igDockContextNewFrameUpdateDocking(ImGuiContext* ctx); CIMGUI_API void igDockContextEndFrame(ImGuiContext* ctx); CIMGUI_API ImGuiID igDockContextGenNodeID(ImGuiContext* ctx); CIMGUI_API void igDockContextQueueDock(ImGuiContext* ctx,ImGuiWindow* target,ImGuiDockNode* target_node,ImGuiWindow* payload,ImGuiDir split_dir,float split_ratio,bool split_outer); CIMGUI_API void igDockContextQueueUndockWindow(ImGuiContext* ctx,ImGuiWindow* window); CIMGUI_API void igDockContextQueueUndockNode(ImGuiContext* ctx,ImGuiDockNode* node); CIMGUI_API void igDockContextProcessUndockWindow(ImGuiContext* ctx,ImGuiWindow* window,bool clear_persistent_docking_ref); CIMGUI_API void igDockContextProcessUndockNode(ImGuiContext* ctx,ImGuiDockNode* node); CIMGUI_API bool igDockContextCalcDropPosForDocking(ImGuiWindow* target,ImGuiDockNode* target_node,ImGuiWindow* payload_window,ImGuiDockNode* payload_node,ImGuiDir split_dir,bool split_outer,ImVec2* out_pos); CIMGUI_API ImGuiDockNode* igDockContextFindNodeByID(ImGuiContext* ctx,ImGuiID id); CIMGUI_API void igDockNodeWindowMenuHandler_Default(ImGuiContext* ctx,ImGuiDockNode* node,ImGuiTabBar* tab_bar); CIMGUI_API bool igDockNodeBeginAmendTabBar(ImGuiDockNode* node); CIMGUI_API void igDockNodeEndAmendTabBar(void); CIMGUI_API ImGuiDockNode* igDockNodeGetRootNode(ImGuiDockNode* node); CIMGUI_API bool igDockNodeIsInHierarchyOf(ImGuiDockNode* node,ImGuiDockNode* parent); CIMGUI_API int igDockNodeGetDepth(const ImGuiDockNode* node); CIMGUI_API ImGuiID igDockNodeGetWindowMenuButtonId(const ImGuiDockNode* node); CIMGUI_API ImGuiDockNode* igGetWindowDockNode(void); CIMGUI_API bool igGetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); CIMGUI_API void igBeginDocked(ImGuiWindow* window,bool* p_open); CIMGUI_API void igBeginDockableDragDropSource(ImGuiWindow* window); CIMGUI_API void igBeginDockableDragDropTarget(ImGuiWindow* window); CIMGUI_API void igSetWindowDock(ImGuiWindow* window,ImGuiID dock_id,ImGuiCond cond); CIMGUI_API void igDockBuilderDockWindow(const char* window_name,ImGuiID node_id); CIMGUI_API ImGuiDockNode* igDockBuilderGetNode(ImGuiID node_id); CIMGUI_API ImGuiDockNode* igDockBuilderGetCentralNode(ImGuiID node_id); CIMGUI_API ImGuiID igDockBuilderAddNode(ImGuiID node_id,ImGuiDockNodeFlags flags); CIMGUI_API void igDockBuilderRemoveNode(ImGuiID node_id); CIMGUI_API void igDockBuilderRemoveNodeDockedWindows(ImGuiID node_id,bool clear_settings_refs); CIMGUI_API void igDockBuilderRemoveNodeChildNodes(ImGuiID node_id); CIMGUI_API void igDockBuilderSetNodePos(ImGuiID node_id,ImVec2 pos); CIMGUI_API void igDockBuilderSetNodeSize(ImGuiID node_id,ImVec2 size); CIMGUI_API ImGuiID igDockBuilderSplitNode(ImGuiID node_id,ImGuiDir split_dir,float size_ratio_for_node_at_dir,ImGuiID* out_id_at_dir,ImGuiID* out_id_at_opposite_dir); CIMGUI_API void igDockBuilderCopyDockSpace(ImGuiID src_dockspace_id,ImGuiID dst_dockspace_id,ImVector_const_charPtr* in_window_remap_pairs); CIMGUI_API void igDockBuilderCopyNode(ImGuiID src_node_id,ImGuiID dst_node_id,ImVector_ImGuiID* out_node_remap_pairs); CIMGUI_API void igDockBuilderCopyWindowSettings(const char* src_name,const char* dst_name); CIMGUI_API void igDockBuilderFinish(ImGuiID node_id); CIMGUI_API void igPushFocusScope(ImGuiID id); CIMGUI_API void igPopFocusScope(void); CIMGUI_API ImGuiID igGetCurrentFocusScope(void); CIMGUI_API bool igIsDragDropActive(void); CIMGUI_API bool igBeginDragDropTargetCustom(const ImRect bb,ImGuiID id); CIMGUI_API void igClearDragDrop(void); CIMGUI_API bool igIsDragDropPayloadBeingAccepted(void); CIMGUI_API void igRenderDragDropTargetRectEx(ImDrawList *draw_list, const ImRect bb); CIMGUI_API void igRenderDragDropTargetRectForItem(const ImRect bb); CIMGUI_API ImGuiTypingSelectRequest* igGetTypingSelectRequest(ImGuiTypingSelectFlags flags); CIMGUI_API int igTypingSelectFindMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data,int nav_item_idx); CIMGUI_API int igTypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data,int nav_item_idx); CIMGUI_API int igTypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data); CIMGUI_API bool igBeginBoxSelect(const ImRect scope_rect,ImGuiWindow* window,ImGuiID box_select_id,ImGuiMultiSelectFlags ms_flags); CIMGUI_API void igEndBoxSelect(const ImRect scope_rect,ImGuiMultiSelectFlags ms_flags); CIMGUI_API void igMultiSelectItemHeader(ImGuiID id,bool* p_selected,ImGuiButtonFlags* p_button_flags); CIMGUI_API void igMultiSelectItemFooter(ImGuiID id,bool* p_selected,bool* p_pressed); CIMGUI_API void igMultiSelectAddSetAll(ImGuiMultiSelectTempData* ms,bool selected); CIMGUI_API void igMultiSelectAddSetRange(ImGuiMultiSelectTempData* ms,bool selected,int range_dir,ImGuiSelectionUserData first_item,ImGuiSelectionUserData last_item); CIMGUI_API ImGuiBoxSelectState* igGetBoxSelectState(ImGuiID id); CIMGUI_API ImGuiMultiSelectState* igGetMultiSelectState(ImGuiID id); CIMGUI_API void igSetWindowClipRectBeforeSetChannel(ImGuiWindow* window,const ImRect clip_rect); CIMGUI_API void igBeginColumns(const char* str_id,int count,ImGuiOldColumnFlags flags); CIMGUI_API void igEndColumns(void); CIMGUI_API void igPushColumnClipRect(int column_index); CIMGUI_API void igPushColumnsBackground(void); CIMGUI_API void igPopColumnsBackground(void); CIMGUI_API ImGuiID igGetColumnsID(const char* str_id,int count); CIMGUI_API ImGuiOldColumns* igFindOrCreateColumns(ImGuiWindow* window,ImGuiID id); CIMGUI_API float igGetColumnOffsetFromNorm(const ImGuiOldColumns* columns,float offset_norm); CIMGUI_API float igGetColumnNormFromOffset(const ImGuiOldColumns* columns,float offset); CIMGUI_API void igTableOpenContextMenu(int column_n); CIMGUI_API void igTableSetColumnWidth(int column_n,float width); CIMGUI_API void igTableSetColumnSortDirection(int column_n,ImGuiSortDirection sort_direction,bool append_to_sort_specs); CIMGUI_API int igTableGetHoveredRow(void); CIMGUI_API float igTableGetHeaderRowHeight(void); CIMGUI_API float igTableGetHeaderAngledMaxLabelWidth(void); CIMGUI_API void igTablePushBackgroundChannel(void); CIMGUI_API void igTablePopBackgroundChannel(void); CIMGUI_API void igTablePushColumnChannel(int column_n); CIMGUI_API void igTablePopColumnChannel(void); CIMGUI_API void igTableAngledHeadersRowEx(ImGuiID row_id,float angle,float max_label_width,const ImGuiTableHeaderData* data,int data_count); CIMGUI_API ImGuiTable* igGetCurrentTable(void); CIMGUI_API ImGuiTable* igTableFindByID(ImGuiID id); CIMGUI_API bool igBeginTableEx(const char* name,ImGuiID id,int columns_count,ImGuiTableFlags flags,const ImVec2 outer_size,float inner_width); CIMGUI_API void igTableBeginInitMemory(ImGuiTable* table,int columns_count); CIMGUI_API void igTableBeginApplyRequests(ImGuiTable* table); CIMGUI_API void igTableSetupDrawChannels(ImGuiTable* table); CIMGUI_API void igTableUpdateLayout(ImGuiTable* table); CIMGUI_API void igTableUpdateBorders(ImGuiTable* table); CIMGUI_API void igTableUpdateColumnsWeightFromWidth(ImGuiTable* table); CIMGUI_API void igTableDrawBorders(ImGuiTable* table); CIMGUI_API void igTableDrawDefaultContextMenu(ImGuiTable* table,ImGuiTableFlags flags_for_section_to_display); CIMGUI_API bool igTableBeginContextMenuPopup(ImGuiTable* table); CIMGUI_API void igTableMergeDrawChannels(ImGuiTable* table); CIMGUI_API ImGuiTableInstanceData* igTableGetInstanceData(ImGuiTable* table,int instance_no); CIMGUI_API ImGuiID igTableGetInstanceID(ImGuiTable* table,int instance_no); CIMGUI_API void igTableSortSpecsSanitize(ImGuiTable* table); CIMGUI_API void igTableSortSpecsBuild(ImGuiTable* table); CIMGUI_API ImGuiSortDirection igTableGetColumnNextSortDirection(ImGuiTableColumn* column); CIMGUI_API void igTableFixColumnSortDirection(ImGuiTable* table,ImGuiTableColumn* column); CIMGUI_API float igTableGetColumnWidthAuto(ImGuiTable* table,ImGuiTableColumn* column); CIMGUI_API void igTableBeginRow(ImGuiTable* table); CIMGUI_API void igTableEndRow(ImGuiTable* table); CIMGUI_API void igTableBeginCell(ImGuiTable* table,int column_n); CIMGUI_API void igTableEndCell(ImGuiTable* table); CIMGUI_API void igTableGetCellBgRect(ImRect *pOut,const ImGuiTable* table,int column_n); CIMGUI_API const char* igTableGetColumnName_TablePtr(const ImGuiTable* table,int column_n); CIMGUI_API ImGuiID igTableGetColumnResizeID(ImGuiTable* table,int column_n,int instance_no); CIMGUI_API float igTableCalcMaxColumnWidth(const ImGuiTable* table,int column_n); CIMGUI_API void igTableSetColumnWidthAutoSingle(ImGuiTable* table,int column_n); CIMGUI_API void igTableSetColumnWidthAutoAll(ImGuiTable* table); CIMGUI_API void igTableRemove(ImGuiTable* table); CIMGUI_API void igTableGcCompactTransientBuffers_TablePtr(ImGuiTable* table); CIMGUI_API void igTableGcCompactTransientBuffers_TableTempDataPtr(ImGuiTableTempData* table); CIMGUI_API void igTableGcCompactSettings(void); CIMGUI_API void igTableLoadSettings(ImGuiTable* table); CIMGUI_API void igTableSaveSettings(ImGuiTable* table); CIMGUI_API void igTableResetSettings(ImGuiTable* table); CIMGUI_API ImGuiTableSettings* igTableGetBoundSettings(ImGuiTable* table); CIMGUI_API void igTableSettingsAddSettingsHandler(void); CIMGUI_API ImGuiTableSettings* igTableSettingsCreate(ImGuiID id,int columns_count); CIMGUI_API ImGuiTableSettings* igTableSettingsFindByID(ImGuiID id); CIMGUI_API ImGuiTabBar* igGetCurrentTabBar(void); CIMGUI_API bool igBeginTabBarEx(ImGuiTabBar* tab_bar,const ImRect bb,ImGuiTabBarFlags flags); CIMGUI_API ImGuiTabItem* igTabBarFindTabByID(ImGuiTabBar* tab_bar,ImGuiID tab_id); CIMGUI_API ImGuiTabItem* igTabBarFindTabByOrder(ImGuiTabBar* tab_bar,int order); CIMGUI_API ImGuiTabItem* igTabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar); CIMGUI_API ImGuiTabItem* igTabBarGetCurrentTab(ImGuiTabBar* tab_bar); CIMGUI_API int igTabBarGetTabOrder(ImGuiTabBar* tab_bar,ImGuiTabItem* tab); CIMGUI_API const char* igTabBarGetTabName(ImGuiTabBar* tab_bar,ImGuiTabItem* tab); CIMGUI_API void igTabBarAddTab(ImGuiTabBar* tab_bar,ImGuiTabItemFlags tab_flags,ImGuiWindow* window); CIMGUI_API void igTabBarRemoveTab(ImGuiTabBar* tab_bar,ImGuiID tab_id); CIMGUI_API void igTabBarCloseTab(ImGuiTabBar* tab_bar,ImGuiTabItem* tab); CIMGUI_API void igTabBarQueueFocus_TabItemPtr(ImGuiTabBar* tab_bar,ImGuiTabItem* tab); CIMGUI_API void igTabBarQueueFocus_Str(ImGuiTabBar* tab_bar,const char* tab_name); CIMGUI_API void igTabBarQueueReorder(ImGuiTabBar* tab_bar,ImGuiTabItem* tab,int offset); CIMGUI_API void igTabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar,ImGuiTabItem* tab,ImVec2 mouse_pos); CIMGUI_API bool igTabBarProcessReorder(ImGuiTabBar* tab_bar); CIMGUI_API bool igTabItemEx(ImGuiTabBar* tab_bar,const char* label,bool* p_open,ImGuiTabItemFlags flags,ImGuiWindow* docked_window); CIMGUI_API void igTabItemSpacing(const char* str_id,ImGuiTabItemFlags flags,float width); CIMGUI_API void igTabItemCalcSize_Str(ImVec2 *pOut,const char* label,bool has_close_button_or_unsaved_marker); CIMGUI_API void igTabItemCalcSize_WindowPtr(ImVec2 *pOut,ImGuiWindow* window); CIMGUI_API void igTabItemBackground(ImDrawList* draw_list,const ImRect bb,ImGuiTabItemFlags flags,ImU32 col); CIMGUI_API void igTabItemLabelAndCloseButton(ImDrawList* draw_list,const ImRect bb,ImGuiTabItemFlags flags,ImVec2 frame_padding,const char* label,ImGuiID tab_id,ImGuiID close_button_id,bool is_contents_visible,bool* out_just_closed,bool* out_text_clipped); CIMGUI_API void igRenderText(ImVec2 pos,const char* text,const char* text_end,bool hide_text_after_hash); CIMGUI_API void igRenderTextWrapped(ImVec2 pos,const char* text,const char* text_end,float wrap_width); CIMGUI_API void igRenderTextClipped(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); CIMGUI_API void igRenderTextClippedEx(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,const ImRect* clip_rect); CIMGUI_API void igRenderTextEllipsis(ImDrawList* draw_list,const ImVec2 pos_min,const ImVec2 pos_max,float ellipsis_max_x,const char* text,const char* text_end,const ImVec2* text_size_if_known); CIMGUI_API void igRenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool borders,float rounding); CIMGUI_API void igRenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding); CIMGUI_API void igRenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list,ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,float grid_step,ImVec2 grid_off,float rounding,ImDrawFlags flags); CIMGUI_API void igRenderNavCursor(const ImRect bb,ImGuiID id,ImGuiNavRenderCursorFlags flags); CIMGUI_API const char* igFindRenderedTextEnd(const char* text,const char* text_end); CIMGUI_API void igRenderMouseCursor(ImVec2 pos,float scale,ImGuiMouseCursor mouse_cursor,ImU32 col_fill,ImU32 col_border,ImU32 col_shadow); CIMGUI_API void igRenderArrow(ImDrawList* draw_list,ImVec2 pos,ImU32 col,ImGuiDir dir,float scale); CIMGUI_API void igRenderBullet(ImDrawList* draw_list,ImVec2 pos,ImU32 col); CIMGUI_API void igRenderCheckMark(ImDrawList* draw_list,ImVec2 pos,ImU32 col,float sz); CIMGUI_API void igRenderArrowPointingAt(ImDrawList* draw_list,ImVec2 pos,ImVec2 half_sz,ImGuiDir direction,ImU32 col); CIMGUI_API void igRenderArrowDockMenu(ImDrawList* draw_list,ImVec2 p_min,float sz,ImU32 col); CIMGUI_API void igRenderRectFilledWithHole(ImDrawList* draw_list,const ImRect outer,const ImRect inner,ImU32 col,float rounding); CIMGUI_API ImDrawFlags igCalcRoundingFlagsForRectInRect(const ImRect r_in,const ImRect r_outer,float threshold); CIMGUI_API void igTextEx(const char* text,const char* text_end,ImGuiTextFlags flags); CIMGUI_API void igTextAligned(float align_x,float size_x,const char* fmt,...); #ifdef CIMGUI_VARGS0 CIMGUI_API void igTextAligned0(float align_x,float size_x,const char* fmt); #endif CIMGUI_API void igTextAlignedV(float align_x,float size_x,const char* fmt,va_list args); CIMGUI_API bool igButtonEx(const char* label,const ImVec2 size_arg,ImGuiButtonFlags flags); CIMGUI_API bool igArrowButtonEx(const char* str_id,ImGuiDir dir,ImVec2 size_arg,ImGuiButtonFlags flags); CIMGUI_API bool igImageButtonEx(ImGuiID id,ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col,ImGuiButtonFlags flags); CIMGUI_API void igSeparatorEx(ImGuiSeparatorFlags flags,float thickness); CIMGUI_API void igSeparatorTextEx(ImGuiID id,const char* label,const char* label_end,float extra_width); CIMGUI_API bool igCheckboxFlags_S64Ptr(const char* label,ImS64* flags,ImS64 flags_value); CIMGUI_API bool igCheckboxFlags_U64Ptr(const char* label,ImU64* flags,ImU64 flags_value); CIMGUI_API bool igCloseButton(ImGuiID id,const ImVec2 pos); CIMGUI_API bool igCollapseButton(ImGuiID id,const ImVec2 pos,ImGuiDockNode* dock_node); CIMGUI_API void igScrollbar(ImGuiAxis axis); CIMGUI_API bool igScrollbarEx(const ImRect bb,ImGuiID id,ImGuiAxis axis,ImS64* p_scroll_v,ImS64 avail_v,ImS64 contents_v,ImDrawFlags draw_rounding_flags); CIMGUI_API void igGetWindowScrollbarRect(ImRect *pOut,ImGuiWindow* window,ImGuiAxis axis); CIMGUI_API ImGuiID igGetWindowScrollbarID(ImGuiWindow* window,ImGuiAxis axis); CIMGUI_API ImGuiID igGetWindowResizeCornerID(ImGuiWindow* window,int n); CIMGUI_API ImGuiID igGetWindowResizeBorderID(ImGuiWindow* window,ImGuiDir dir); CIMGUI_API bool igButtonBehavior(const ImRect bb,ImGuiID id,bool* out_hovered,bool* out_held,ImGuiButtonFlags flags); CIMGUI_API bool igDragBehavior(ImGuiID id,ImGuiDataType data_type,void* p_v,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags); CIMGUI_API bool igSliderBehavior(const ImRect bb,ImGuiID id,ImGuiDataType data_type,void* p_v,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags,ImRect* out_grab_bb); CIMGUI_API bool igSplitterBehavior(const ImRect bb,ImGuiID id,ImGuiAxis axis,float* size1,float* size2,float min_size1,float min_size2,float hover_extend,float hover_visibility_delay,ImU32 bg_col); CIMGUI_API bool igTreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char* label,const char* label_end); CIMGUI_API void igTreeNodeDrawLineToChildNode(const ImVec2 target_pos); CIMGUI_API void igTreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data); CIMGUI_API void igTreePushOverrideID(ImGuiID id); CIMGUI_API bool igTreeNodeGetOpen(ImGuiID storage_id); CIMGUI_API void igTreeNodeSetOpen(ImGuiID storage_id,bool open); CIMGUI_API bool igTreeNodeUpdateNextOpen(ImGuiID storage_id,ImGuiTreeNodeFlags flags); CIMGUI_API const ImGuiDataTypeInfo* igDataTypeGetInfo(ImGuiDataType data_type); CIMGUI_API int igDataTypeFormatString(char* buf,int buf_size,ImGuiDataType data_type,const void* p_data,const char* format); CIMGUI_API void igDataTypeApplyOp(ImGuiDataType data_type,int op,void* output,const void* arg_1,const void* arg_2); CIMGUI_API bool igDataTypeApplyFromText(const char* buf,ImGuiDataType data_type,void* p_data,const char* format,void* p_data_when_empty); CIMGUI_API int igDataTypeCompare(ImGuiDataType data_type,const void* arg_1,const void* arg_2); CIMGUI_API bool igDataTypeClamp(ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max); CIMGUI_API bool igDataTypeIsZero(ImGuiDataType data_type,const void* p_data); CIMGUI_API bool igInputTextEx(const char* label,const char* hint,char* buf,int buf_size,const ImVec2 size_arg,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data); CIMGUI_API void igInputTextDeactivateHook(ImGuiID id); CIMGUI_API bool igTempInputText(const ImRect bb,ImGuiID id,const char* label,char* buf,int buf_size,ImGuiInputTextFlags flags); CIMGUI_API bool igTempInputScalar(const ImRect bb,ImGuiID id,const char* label,ImGuiDataType data_type,void* p_data,const char* format,const void* p_clamp_min,const void* p_clamp_max); CIMGUI_API bool igTempInputIsActive(ImGuiID id); CIMGUI_API ImGuiInputTextState* igGetInputTextState(ImGuiID id); CIMGUI_API void igSetNextItemRefVal(ImGuiDataType data_type,void* p_data); CIMGUI_API bool igIsItemActiveAsInputText(void); CIMGUI_API void igColorTooltip(const char* text,const float* col,ImGuiColorEditFlags flags); CIMGUI_API void igColorEditOptionsPopup(const float* col,ImGuiColorEditFlags flags); CIMGUI_API void igColorPickerOptionsPopup(const float* ref_col,ImGuiColorEditFlags flags); CIMGUI_API int igPlotEx(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,const ImVec2 size_arg); CIMGUI_API void igShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,ImVec2 gradient_p0,ImVec2 gradient_p1,ImU32 col0,ImU32 col1); CIMGUI_API void igShadeVertsLinearUV(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,const ImVec2 a,const ImVec2 b,const ImVec2 uv_a,const ImVec2 uv_b,bool clamp); CIMGUI_API void igShadeVertsTransformPos(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,const ImVec2 pivot_in,float cos_a,float sin_a,const ImVec2 pivot_out); CIMGUI_API void igGcCompactTransientMiscBuffers(void); CIMGUI_API void igGcCompactTransientWindowBuffers(ImGuiWindow* window); CIMGUI_API void igGcAwakeTransientWindowBuffers(ImGuiWindow* window); CIMGUI_API bool igErrorLog(const char* msg); CIMGUI_API void igErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out); CIMGUI_API void igErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in); CIMGUI_API void igErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in); CIMGUI_API void igErrorCheckUsingSetCursorPosToExtendParentBoundaries(void); CIMGUI_API void igErrorCheckEndFrameFinalizeErrorTooltip(void); CIMGUI_API bool igBeginErrorTooltip(void); CIMGUI_API void igEndErrorTooltip(void); CIMGUI_API void igDebugAllocHook(ImGuiDebugAllocInfo* info,int frame_count,void* ptr,size_t size); CIMGUI_API void igDebugDrawCursorPos(ImU32 col); CIMGUI_API void igDebugDrawLineExtents(ImU32 col); CIMGUI_API void igDebugDrawItemRect(ImU32 col); CIMGUI_API void igDebugTextUnformattedWithLocateItem(const char* line_begin,const char* line_end); CIMGUI_API void igDebugLocateItem(ImGuiID target_id); CIMGUI_API void igDebugLocateItemOnHover(ImGuiID target_id); CIMGUI_API void igDebugLocateItemResolveWithLastItem(void); CIMGUI_API void igDebugBreakClearData(void); CIMGUI_API bool igDebugBreakButton(const char* label,const char* description_of_location); CIMGUI_API void igDebugBreakButtonTooltip(bool keyboard_only,const char* description_of_location); CIMGUI_API void igShowFontAtlas(ImFontAtlas* atlas); CIMGUI_API void igDebugHookIdInfo(ImGuiID id,ImGuiDataType data_type,const void* data_id,const void* data_id_end); CIMGUI_API void igDebugNodeColumns(ImGuiOldColumns* columns); CIMGUI_API void igDebugNodeDockNode(ImGuiDockNode* node,const char* label); CIMGUI_API void igDebugNodeDrawList(ImGuiWindow* window,ImGuiViewportP* viewport,const ImDrawList* draw_list,const char* label); CIMGUI_API void igDebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list,const ImDrawList* draw_list,const ImDrawCmd* draw_cmd,bool show_mesh,bool show_aabb); CIMGUI_API void igDebugNodeFont(ImFont* font); CIMGUI_API void igDebugNodeFontGlyph(ImFont* font,const ImFontGlyph* glyph); CIMGUI_API void igDebugNodeTexture(ImTextureData* tex,int int_id,const ImFontAtlasRect* highlight_rect); CIMGUI_API void igDebugNodeStorage(ImGuiStorage* storage,const char* label); CIMGUI_API void igDebugNodeTabBar(ImGuiTabBar* tab_bar,const char* label); CIMGUI_API void igDebugNodeTable(ImGuiTable* table); CIMGUI_API void igDebugNodeTableSettings(ImGuiTableSettings* settings); CIMGUI_API void igDebugNodeInputTextState(ImGuiInputTextState* state); CIMGUI_API void igDebugNodeTypingSelectState(ImGuiTypingSelectState* state); CIMGUI_API void igDebugNodeMultiSelectState(ImGuiMultiSelectState* state); CIMGUI_API void igDebugNodeWindow(ImGuiWindow* window,const char* label); CIMGUI_API void igDebugNodeWindowSettings(ImGuiWindowSettings* settings); CIMGUI_API void igDebugNodeWindowsList(ImVector_ImGuiWindowPtr* windows,const char* label); CIMGUI_API void igDebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows,int windows_size,ImGuiWindow* parent_in_begin_stack); CIMGUI_API void igDebugNodeViewport(ImGuiViewportP* viewport); CIMGUI_API void igDebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor,const char* label,int idx); CIMGUI_API void igDebugRenderKeyboardPreview(ImDrawList* draw_list); CIMGUI_API void igDebugRenderViewportThumbnail(ImDrawList* draw_list,ImGuiViewportP* viewport,const ImRect bb); CIMGUI_API ImFontLoader* ImFontLoader_ImFontLoader(void); CIMGUI_API void ImFontLoader_destroy(ImFontLoader* self); CIMGUI_API const ImFontLoader* igImFontAtlasGetFontLoaderForStbTruetype(void); CIMGUI_API int igImFontAtlasRectId_GetIndex(ImFontAtlasRectId id); CIMGUI_API int igImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id); CIMGUI_API ImFontAtlasRectId igImFontAtlasRectId_Make(int index_idx,int gen_idx); CIMGUI_API ImFontAtlasBuilder* ImFontAtlasBuilder_ImFontAtlasBuilder(void); CIMGUI_API void ImFontAtlasBuilder_destroy(ImFontAtlasBuilder* self); CIMGUI_API void igImFontAtlasBuildInit(ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasBuildDestroy(ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasBuildMain(ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas,const ImFontLoader* font_loader); CIMGUI_API void igImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas,int x,int y,int w,int h,const char* in_str,char in_marker_char); CIMGUI_API void igImFontAtlasBuildClear(ImFontAtlas* atlas); CIMGUI_API ImTextureData* igImFontAtlasTextureAdd(ImFontAtlas* atlas,int w,int h); CIMGUI_API void igImFontAtlasTextureMakeSpace(ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasTextureRepack(ImFontAtlas* atlas,int w,int h); CIMGUI_API void igImFontAtlasTextureGrow(ImFontAtlas* atlas,int old_w,int old_h); CIMGUI_API void igImFontAtlasTextureCompact(ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasTextureGetSizeEstimate(ImVec2i *pOut,ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas,ImFont* font,ImFontConfig* src); CIMGUI_API void igImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasBuildGetOversampleFactors(ImFontConfig* src,ImFontBaked* baked,int* out_oversample_h,int* out_oversample_v); CIMGUI_API void igImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas,int unused_frames); CIMGUI_API bool igImFontAtlasFontSourceInit(ImFontAtlas* atlas,ImFontConfig* src); CIMGUI_API void igImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas,ImFont* font,ImFontConfig* src); CIMGUI_API void igImFontAtlasFontDestroySourceData(ImFontAtlas* atlas,ImFontConfig* src); CIMGUI_API bool igImFontAtlasFontInitOutput(ImFontAtlas* atlas,ImFont* font); CIMGUI_API void igImFontAtlasFontDestroyOutput(ImFontAtlas* atlas,ImFont* font); CIMGUI_API void igImFontAtlasFontDiscardBakes(ImFontAtlas* atlas,ImFont* font,int unused_frames); CIMGUI_API ImGuiID igImFontAtlasBakedGetId(ImGuiID font_id,float baked_size,float rasterizer_density); CIMGUI_API ImFontBaked* igImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density); CIMGUI_API ImFontBaked* igImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density); CIMGUI_API ImFontBaked* igImFontAtlasBakedAdd(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density,ImGuiID baked_id); CIMGUI_API void igImFontAtlasBakedDiscard(ImFontAtlas* atlas,ImFont* font,ImFontBaked* baked); CIMGUI_API ImFontGlyph* igImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas,ImFontBaked* baked,ImFontConfig* src,const ImFontGlyph* in_glyph); CIMGUI_API void igImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas,ImFont* font,ImFontBaked* baked,ImFontGlyph* glyph); CIMGUI_API void igImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas,ImFontBaked* baked,ImFontConfig* src,ImFontGlyph* glyph,ImTextureRect* r,const unsigned char* src_pixels,ImTextureFormat src_fmt,int src_pitch); CIMGUI_API void igImFontAtlasPackInit(ImFontAtlas* atlas); CIMGUI_API ImFontAtlasRectId igImFontAtlasPackAddRect(ImFontAtlas* atlas,int w,int h,ImFontAtlasRectEntry* overwrite_entry); CIMGUI_API ImTextureRect* igImFontAtlasPackGetRect(ImFontAtlas* atlas,ImFontAtlasRectId id); CIMGUI_API ImTextureRect* igImFontAtlasPackGetRectSafe(ImFontAtlas* atlas,ImFontAtlasRectId id); CIMGUI_API void igImFontAtlasPackDiscardRect(ImFontAtlas* atlas,ImFontAtlasRectId id); CIMGUI_API void igImFontAtlasUpdateNewFrame(ImFontAtlas* atlas,int frame_count,bool renderer_has_textures); CIMGUI_API void igImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas,ImDrawListSharedData* data); CIMGUI_API void igImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas,ImDrawListSharedData* data); CIMGUI_API void igImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas,ImTextureRef old_tex,ImTextureRef new_tex); CIMGUI_API void igImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas); CIMGUI_API void igImFontAtlasTextureBlockConvert(const unsigned char* src_pixels,ImTextureFormat src_fmt,int src_pitch,unsigned char* dst_pixels,ImTextureFormat dst_fmt,int dst_pitch,int w,int h); CIMGUI_API void igImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data); CIMGUI_API void igImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data,float multiply_factor); CIMGUI_API void igImFontAtlasTextureBlockFill(ImTextureData* dst_tex,int dst_x,int dst_y,int w,int h,ImU32 col); CIMGUI_API void igImFontAtlasTextureBlockCopy(ImTextureData* src_tex,int src_x,int src_y,ImTextureData* dst_tex,int dst_x,int dst_y,int w,int h); CIMGUI_API void igImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas,ImTextureData* tex,int x,int y,int w,int h); CIMGUI_API int igImTextureDataGetFormatBytesPerPixel(ImTextureFormat format); CIMGUI_API const char* igImTextureDataGetStatusName(ImTextureStatus status); CIMGUI_API const char* igImTextureDataGetFormatName(ImTextureFormat format); CIMGUI_API void igImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas); CIMGUI_API bool igImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas,ImGuiMouseCursor cursor_type,ImVec2* out_offset,ImVec2* out_size,ImVec2 out_uv_border[2],ImVec2 out_uv_fill[2]); #ifdef IMGUI_ENABLE_FREETYPE CIMGUI_API const ImFontLoader* ImGuiFreeType_GetFontLoader(void); CIMGUI_API void ImGuiFreeType_SetAllocatorFunctions(void*(*alloc_func)(size_t sz,void* user_data),void(*free_func)(void* ptr,void* user_data),void* user_data); CIMGUI_API bool ImGuiFreeType_DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags); #endif /////////////////////////hand written functions //no appendfV CIMGUI_API void ImGuiTextBuffer_appendf(ImGuiTextBuffer *self, const char *fmt, ...); //for getting FLT_MAX in bindings CIMGUI_API float igGET_FLT_MAX(void); //for getting FLT_MIN in bindings CIMGUI_API float igGET_FLT_MIN(void); CIMGUI_API ImVector_ImWchar* ImVector_ImWchar_create(void); CIMGUI_API void ImVector_ImWchar_destroy(ImVector_ImWchar* self); CIMGUI_API void ImVector_ImWchar_Init(ImVector_ImWchar* p); CIMGUI_API void ImVector_ImWchar_UnInit(ImVector_ImWchar* p); #ifdef IMGUI_HAS_DOCK CIMGUI_API void ImGuiPlatformIO_Set_Platform_GetWindowPos(ImGuiPlatformIO* platform_io, void(*user_callback)(ImGuiViewport* vp, ImVec2* out_pos)); CIMGUI_API void ImGuiPlatformIO_Set_Platform_GetWindowSize(ImGuiPlatformIO* platform_io, void(*user_callback)(ImGuiViewport* vp, ImVec2* out_size)); #endif #endif //CIMGUI_INCLUDED ================================================ FILE: lib/third_party/imgui/cimgui/source/cimgui.cpp ================================================ //This file is automatically generated by generator.lua from https://github.com/cimgui/cimgui //based on imgui.h file version "1.92.0" 19200 from Dear ImGui https://github.com/ocornut/imgui //with imgui_internal.h api //with imgui_freetype.h api //docking branch #include #include #include "cimgui.h" CIMGUI_API ImVec2* ImVec2_ImVec2_Nil(void) { return IM_NEW(ImVec2)(); } CIMGUI_API void ImVec2_destroy(ImVec2* self) { IM_DELETE(self); } CIMGUI_API ImVec2* ImVec2_ImVec2_Float(float _x,float _y) { return IM_NEW(ImVec2)(_x,_y); } CIMGUI_API ImVec4* ImVec4_ImVec4_Nil(void) { return IM_NEW(ImVec4)(); } CIMGUI_API void ImVec4_destroy(ImVec4* self) { IM_DELETE(self); } CIMGUI_API ImVec4* ImVec4_ImVec4_Float(float _x,float _y,float _z,float _w) { return IM_NEW(ImVec4)(_x,_y,_z,_w); } CIMGUI_API ImTextureRef* ImTextureRef_ImTextureRef_Nil(void) { return IM_NEW(ImTextureRef)(); } CIMGUI_API void ImTextureRef_destroy(ImTextureRef* self) { IM_DELETE(self); } CIMGUI_API ImTextureRef* ImTextureRef_ImTextureRef_TextureID(ImTextureID tex_id) { return IM_NEW(ImTextureRef)(tex_id); } CIMGUI_API ImTextureID ImTextureRef_GetTexID(ImTextureRef* self) { return self->GetTexID(); } CIMGUI_API ImGuiContext* igCreateContext(ImFontAtlas* shared_font_atlas) { return ImGui::CreateContext(shared_font_atlas); } CIMGUI_API void igDestroyContext(ImGuiContext* ctx) { return ImGui::DestroyContext(ctx); } CIMGUI_API ImGuiContext* igGetCurrentContext() { return ImGui::GetCurrentContext(); } CIMGUI_API void igSetCurrentContext(ImGuiContext* ctx) { return ImGui::SetCurrentContext(ctx); } CIMGUI_API ImGuiIO* igGetIO_Nil() { return &ImGui::GetIO(); } CIMGUI_API ImGuiPlatformIO* igGetPlatformIO_Nil() { return &ImGui::GetPlatformIO(); } CIMGUI_API ImGuiStyle* igGetStyle() { return &ImGui::GetStyle(); } CIMGUI_API void igNewFrame() { return ImGui::NewFrame(); } CIMGUI_API void igEndFrame() { return ImGui::EndFrame(); } CIMGUI_API void igRender() { return ImGui::Render(); } CIMGUI_API ImDrawData* igGetDrawData() { return ImGui::GetDrawData(); } CIMGUI_API void igShowDemoWindow(bool* p_open) { return ImGui::ShowDemoWindow(p_open); } CIMGUI_API void igShowMetricsWindow(bool* p_open) { return ImGui::ShowMetricsWindow(p_open); } CIMGUI_API void igShowDebugLogWindow(bool* p_open) { return ImGui::ShowDebugLogWindow(p_open); } CIMGUI_API void igShowIDStackToolWindow(bool* p_open) { return ImGui::ShowIDStackToolWindow(p_open); } CIMGUI_API void igShowAboutWindow(bool* p_open) { return ImGui::ShowAboutWindow(p_open); } CIMGUI_API void igShowStyleEditor(ImGuiStyle* ref) { return ImGui::ShowStyleEditor(ref); } CIMGUI_API bool igShowStyleSelector(const char* label) { return ImGui::ShowStyleSelector(label); } CIMGUI_API void igShowFontSelector(const char* label) { return ImGui::ShowFontSelector(label); } CIMGUI_API void igShowUserGuide() { return ImGui::ShowUserGuide(); } CIMGUI_API const char* igGetVersion() { return ImGui::GetVersion(); } CIMGUI_API void igStyleColorsDark(ImGuiStyle* dst) { return ImGui::StyleColorsDark(dst); } CIMGUI_API void igStyleColorsLight(ImGuiStyle* dst) { return ImGui::StyleColorsLight(dst); } CIMGUI_API void igStyleColorsClassic(ImGuiStyle* dst) { return ImGui::StyleColorsClassic(dst); } CIMGUI_API bool igBegin(const char* name,bool* p_open,ImGuiWindowFlags flags) { return ImGui::Begin(name,p_open,flags); } CIMGUI_API void igEnd() { return ImGui::End(); } CIMGUI_API bool igBeginChild_Str(const char* str_id,const ImVec2 size,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags) { return ImGui::BeginChild(str_id,size,child_flags,window_flags); } CIMGUI_API bool igBeginChild_ID(ImGuiID id,const ImVec2 size,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags) { return ImGui::BeginChild(id,size,child_flags,window_flags); } CIMGUI_API void igEndChild() { return ImGui::EndChild(); } CIMGUI_API bool igIsWindowAppearing() { return ImGui::IsWindowAppearing(); } CIMGUI_API bool igIsWindowCollapsed() { return ImGui::IsWindowCollapsed(); } CIMGUI_API bool igIsWindowFocused(ImGuiFocusedFlags flags) { return ImGui::IsWindowFocused(flags); } CIMGUI_API bool igIsWindowHovered(ImGuiHoveredFlags flags) { return ImGui::IsWindowHovered(flags); } CIMGUI_API ImDrawList* igGetWindowDrawList() { return ImGui::GetWindowDrawList(); } CIMGUI_API float igGetWindowDpiScale() { return ImGui::GetWindowDpiScale(); } CIMGUI_API void igGetWindowPos(ImVec2 *pOut) { *pOut = ImGui::GetWindowPos(); } CIMGUI_API void igGetWindowSize(ImVec2 *pOut) { *pOut = ImGui::GetWindowSize(); } CIMGUI_API float igGetWindowWidth() { return ImGui::GetWindowWidth(); } CIMGUI_API float igGetWindowHeight() { return ImGui::GetWindowHeight(); } CIMGUI_API ImGuiViewport* igGetWindowViewport() { return ImGui::GetWindowViewport(); } CIMGUI_API void igSetNextWindowPos(const ImVec2 pos,ImGuiCond cond,const ImVec2 pivot) { return ImGui::SetNextWindowPos(pos,cond,pivot); } CIMGUI_API void igSetNextWindowSize(const ImVec2 size,ImGuiCond cond) { return ImGui::SetNextWindowSize(size,cond); } CIMGUI_API void igSetNextWindowSizeConstraints(const ImVec2 size_min,const ImVec2 size_max,ImGuiSizeCallback custom_callback,void* custom_callback_data) { return ImGui::SetNextWindowSizeConstraints(size_min,size_max,custom_callback,custom_callback_data); } CIMGUI_API void igSetNextWindowContentSize(const ImVec2 size) { return ImGui::SetNextWindowContentSize(size); } CIMGUI_API void igSetNextWindowCollapsed(bool collapsed,ImGuiCond cond) { return ImGui::SetNextWindowCollapsed(collapsed,cond); } CIMGUI_API void igSetNextWindowFocus() { return ImGui::SetNextWindowFocus(); } CIMGUI_API void igSetNextWindowScroll(const ImVec2 scroll) { return ImGui::SetNextWindowScroll(scroll); } CIMGUI_API void igSetNextWindowBgAlpha(float alpha) { return ImGui::SetNextWindowBgAlpha(alpha); } CIMGUI_API void igSetNextWindowViewport(ImGuiID viewport_id) { return ImGui::SetNextWindowViewport(viewport_id); } CIMGUI_API void igSetWindowPos_Vec2(const ImVec2 pos,ImGuiCond cond) { return ImGui::SetWindowPos(pos,cond); } CIMGUI_API void igSetWindowSize_Vec2(const ImVec2 size,ImGuiCond cond) { return ImGui::SetWindowSize(size,cond); } CIMGUI_API void igSetWindowCollapsed_Bool(bool collapsed,ImGuiCond cond) { return ImGui::SetWindowCollapsed(collapsed,cond); } CIMGUI_API void igSetWindowFocus_Nil() { return ImGui::SetWindowFocus(); } CIMGUI_API void igSetWindowPos_Str(const char* name,const ImVec2 pos,ImGuiCond cond) { return ImGui::SetWindowPos(name,pos,cond); } CIMGUI_API void igSetWindowSize_Str(const char* name,const ImVec2 size,ImGuiCond cond) { return ImGui::SetWindowSize(name,size,cond); } CIMGUI_API void igSetWindowCollapsed_Str(const char* name,bool collapsed,ImGuiCond cond) { return ImGui::SetWindowCollapsed(name,collapsed,cond); } CIMGUI_API void igSetWindowFocus_Str(const char* name) { return ImGui::SetWindowFocus(name); } CIMGUI_API float igGetScrollX() { return ImGui::GetScrollX(); } CIMGUI_API float igGetScrollY() { return ImGui::GetScrollY(); } CIMGUI_API void igSetScrollX_Float(float scroll_x) { return ImGui::SetScrollX(scroll_x); } CIMGUI_API void igSetScrollY_Float(float scroll_y) { return ImGui::SetScrollY(scroll_y); } CIMGUI_API float igGetScrollMaxX() { return ImGui::GetScrollMaxX(); } CIMGUI_API float igGetScrollMaxY() { return ImGui::GetScrollMaxY(); } CIMGUI_API void igSetScrollHereX(float center_x_ratio) { return ImGui::SetScrollHereX(center_x_ratio); } CIMGUI_API void igSetScrollHereY(float center_y_ratio) { return ImGui::SetScrollHereY(center_y_ratio); } CIMGUI_API void igSetScrollFromPosX_Float(float local_x,float center_x_ratio) { return ImGui::SetScrollFromPosX(local_x,center_x_ratio); } CIMGUI_API void igSetScrollFromPosY_Float(float local_y,float center_y_ratio) { return ImGui::SetScrollFromPosY(local_y,center_y_ratio); } CIMGUI_API void igPushFont(ImFont* font,float font_size_base_unscaled) { return ImGui::PushFont(font,font_size_base_unscaled); } CIMGUI_API void igPopFont() { return ImGui::PopFont(); } CIMGUI_API ImFont* igGetFont() { return ImGui::GetFont(); } CIMGUI_API float igGetFontSize() { return ImGui::GetFontSize(); } CIMGUI_API ImFontBaked* igGetFontBaked() { return ImGui::GetFontBaked(); } CIMGUI_API void igPushStyleColor_U32(ImGuiCol idx,ImU32 col) { return ImGui::PushStyleColor(idx,col); } CIMGUI_API void igPushStyleColor_Vec4(ImGuiCol idx,const ImVec4 col) { return ImGui::PushStyleColor(idx,col); } CIMGUI_API void igPopStyleColor(int count) { return ImGui::PopStyleColor(count); } CIMGUI_API void igPushStyleVar_Float(ImGuiStyleVar idx,float val) { return ImGui::PushStyleVar(idx,val); } CIMGUI_API void igPushStyleVar_Vec2(ImGuiStyleVar idx,const ImVec2 val) { return ImGui::PushStyleVar(idx,val); } CIMGUI_API void igPushStyleVarX(ImGuiStyleVar idx,float val_x) { return ImGui::PushStyleVarX(idx,val_x); } CIMGUI_API void igPushStyleVarY(ImGuiStyleVar idx,float val_y) { return ImGui::PushStyleVarY(idx,val_y); } CIMGUI_API void igPopStyleVar(int count) { return ImGui::PopStyleVar(count); } CIMGUI_API void igPushItemFlag(ImGuiItemFlags option,bool enabled) { return ImGui::PushItemFlag(option,enabled); } CIMGUI_API void igPopItemFlag() { return ImGui::PopItemFlag(); } CIMGUI_API void igPushItemWidth(float item_width) { return ImGui::PushItemWidth(item_width); } CIMGUI_API void igPopItemWidth() { return ImGui::PopItemWidth(); } CIMGUI_API void igSetNextItemWidth(float item_width) { return ImGui::SetNextItemWidth(item_width); } CIMGUI_API float igCalcItemWidth() { return ImGui::CalcItemWidth(); } CIMGUI_API void igPushTextWrapPos(float wrap_local_pos_x) { return ImGui::PushTextWrapPos(wrap_local_pos_x); } CIMGUI_API void igPopTextWrapPos() { return ImGui::PopTextWrapPos(); } CIMGUI_API void igGetFontTexUvWhitePixel(ImVec2 *pOut) { *pOut = ImGui::GetFontTexUvWhitePixel(); } CIMGUI_API ImU32 igGetColorU32_Col(ImGuiCol idx,float alpha_mul) { return ImGui::GetColorU32(idx,alpha_mul); } CIMGUI_API ImU32 igGetColorU32_Vec4(const ImVec4 col) { return ImGui::GetColorU32(col); } CIMGUI_API ImU32 igGetColorU32_U32(ImU32 col,float alpha_mul) { return ImGui::GetColorU32(col,alpha_mul); } CIMGUI_API const ImVec4* igGetStyleColorVec4(ImGuiCol idx) { return &ImGui::GetStyleColorVec4(idx); } CIMGUI_API void igGetCursorScreenPos(ImVec2 *pOut) { *pOut = ImGui::GetCursorScreenPos(); } CIMGUI_API void igSetCursorScreenPos(const ImVec2 pos) { return ImGui::SetCursorScreenPos(pos); } CIMGUI_API void igGetContentRegionAvail(ImVec2 *pOut) { *pOut = ImGui::GetContentRegionAvail(); } CIMGUI_API void igGetCursorPos(ImVec2 *pOut) { *pOut = ImGui::GetCursorPos(); } CIMGUI_API float igGetCursorPosX() { return ImGui::GetCursorPosX(); } CIMGUI_API float igGetCursorPosY() { return ImGui::GetCursorPosY(); } CIMGUI_API void igSetCursorPos(const ImVec2 local_pos) { return ImGui::SetCursorPos(local_pos); } CIMGUI_API void igSetCursorPosX(float local_x) { return ImGui::SetCursorPosX(local_x); } CIMGUI_API void igSetCursorPosY(float local_y) { return ImGui::SetCursorPosY(local_y); } CIMGUI_API void igGetCursorStartPos(ImVec2 *pOut) { *pOut = ImGui::GetCursorStartPos(); } CIMGUI_API void igSeparator() { return ImGui::Separator(); } CIMGUI_API void igSameLine(float offset_from_start_x,float spacing) { return ImGui::SameLine(offset_from_start_x,spacing); } CIMGUI_API void igNewLine() { return ImGui::NewLine(); } CIMGUI_API void igSpacing() { return ImGui::Spacing(); } CIMGUI_API void igDummy(const ImVec2 size) { return ImGui::Dummy(size); } CIMGUI_API void igIndent(float indent_w) { return ImGui::Indent(indent_w); } CIMGUI_API void igUnindent(float indent_w) { return ImGui::Unindent(indent_w); } CIMGUI_API void igBeginGroup() { return ImGui::BeginGroup(); } CIMGUI_API void igEndGroup() { return ImGui::EndGroup(); } CIMGUI_API void igAlignTextToFramePadding() { return ImGui::AlignTextToFramePadding(); } CIMGUI_API float igGetTextLineHeight() { return ImGui::GetTextLineHeight(); } CIMGUI_API float igGetTextLineHeightWithSpacing() { return ImGui::GetTextLineHeightWithSpacing(); } CIMGUI_API float igGetFrameHeight() { return ImGui::GetFrameHeight(); } CIMGUI_API float igGetFrameHeightWithSpacing() { return ImGui::GetFrameHeightWithSpacing(); } CIMGUI_API void igPushID_Str(const char* str_id) { return ImGui::PushID(str_id); } CIMGUI_API void igPushID_StrStr(const char* str_id_begin,const char* str_id_end) { return ImGui::PushID(str_id_begin,str_id_end); } CIMGUI_API void igPushID_Ptr(const void* ptr_id) { return ImGui::PushID(ptr_id); } CIMGUI_API void igPushID_Int(int int_id) { return ImGui::PushID(int_id); } CIMGUI_API void igPopID() { return ImGui::PopID(); } CIMGUI_API ImGuiID igGetID_Str(const char* str_id) { return ImGui::GetID(str_id); } CIMGUI_API ImGuiID igGetID_StrStr(const char* str_id_begin,const char* str_id_end) { return ImGui::GetID(str_id_begin,str_id_end); } CIMGUI_API ImGuiID igGetID_Ptr(const void* ptr_id) { return ImGui::GetID(ptr_id); } CIMGUI_API ImGuiID igGetID_Int(int int_id) { return ImGui::GetID(int_id); } CIMGUI_API void igTextUnformatted(const char* text,const char* text_end) { return ImGui::TextUnformatted(text,text_end); } CIMGUI_API void igText(const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::TextV(fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igText0(const char* fmt) { return igText(fmt); } #endif CIMGUI_API void igTextV(const char* fmt,va_list args) { return ImGui::TextV(fmt,args); } CIMGUI_API void igTextColored(const ImVec4 col,const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::TextColoredV(col,fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igTextColored0(const ImVec4 col,const char* fmt) { return igTextColored(col,fmt); } #endif CIMGUI_API void igTextColoredV(const ImVec4 col,const char* fmt,va_list args) { return ImGui::TextColoredV(col,fmt,args); } CIMGUI_API void igTextDisabled(const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::TextDisabledV(fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igTextDisabled0(const char* fmt) { return igTextDisabled(fmt); } #endif CIMGUI_API void igTextDisabledV(const char* fmt,va_list args) { return ImGui::TextDisabledV(fmt,args); } CIMGUI_API void igTextWrapped(const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::TextWrappedV(fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igTextWrapped0(const char* fmt) { return igTextWrapped(fmt); } #endif CIMGUI_API void igTextWrappedV(const char* fmt,va_list args) { return ImGui::TextWrappedV(fmt,args); } CIMGUI_API void igLabelText(const char* label,const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::LabelTextV(label,fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igLabelText0(const char* label,const char* fmt) { return igLabelText(label,fmt); } #endif CIMGUI_API void igLabelTextV(const char* label,const char* fmt,va_list args) { return ImGui::LabelTextV(label,fmt,args); } CIMGUI_API void igBulletText(const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::BulletTextV(fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igBulletText0(const char* fmt) { return igBulletText(fmt); } #endif CIMGUI_API void igBulletTextV(const char* fmt,va_list args) { return ImGui::BulletTextV(fmt,args); } CIMGUI_API void igSeparatorText(const char* label) { return ImGui::SeparatorText(label); } CIMGUI_API bool igButton(const char* label,const ImVec2 size) { return ImGui::Button(label,size); } CIMGUI_API bool igSmallButton(const char* label) { return ImGui::SmallButton(label); } CIMGUI_API bool igInvisibleButton(const char* str_id,const ImVec2 size,ImGuiButtonFlags flags) { return ImGui::InvisibleButton(str_id,size,flags); } CIMGUI_API bool igArrowButton(const char* str_id,ImGuiDir dir) { return ImGui::ArrowButton(str_id,dir); } CIMGUI_API bool igCheckbox(const char* label,bool* v) { return ImGui::Checkbox(label,v); } CIMGUI_API bool igCheckboxFlags_IntPtr(const char* label,int* flags,int flags_value) { return ImGui::CheckboxFlags(label,flags,flags_value); } CIMGUI_API bool igCheckboxFlags_UintPtr(const char* label,unsigned int* flags,unsigned int flags_value) { return ImGui::CheckboxFlags(label,flags,flags_value); } CIMGUI_API bool igRadioButton_Bool(const char* label,bool active) { return ImGui::RadioButton(label,active); } CIMGUI_API bool igRadioButton_IntPtr(const char* label,int* v,int v_button) { return ImGui::RadioButton(label,v,v_button); } CIMGUI_API void igProgressBar(float fraction,const ImVec2 size_arg,const char* overlay) { return ImGui::ProgressBar(fraction,size_arg,overlay); } CIMGUI_API void igBullet() { return ImGui::Bullet(); } CIMGUI_API bool igTextLink(const char* label) { return ImGui::TextLink(label); } CIMGUI_API bool igTextLinkOpenURL(const char* label,const char* url) { return ImGui::TextLinkOpenURL(label,url); } CIMGUI_API void igImage(ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1) { return ImGui::Image(tex_ref,image_size,uv0,uv1); } CIMGUI_API void igImageWithBg(ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col) { return ImGui::ImageWithBg(tex_ref,image_size,uv0,uv1,bg_col,tint_col); } CIMGUI_API bool igImageButton(const char* str_id,ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col) { return ImGui::ImageButton(str_id,tex_ref,image_size,uv0,uv1,bg_col,tint_col); } CIMGUI_API bool igBeginCombo(const char* label,const char* preview_value,ImGuiComboFlags flags) { return ImGui::BeginCombo(label,preview_value,flags); } CIMGUI_API void igEndCombo() { return ImGui::EndCombo(); } CIMGUI_API bool igCombo_Str_arr(const char* label,int* current_item,const char* const items[],int items_count,int popup_max_height_in_items) { return ImGui::Combo(label,current_item,items,items_count,popup_max_height_in_items); } CIMGUI_API bool igCombo_Str(const char* label,int* current_item,const char* items_separated_by_zeros,int popup_max_height_in_items) { return ImGui::Combo(label,current_item,items_separated_by_zeros,popup_max_height_in_items); } CIMGUI_API bool igCombo_FnStrPtr(const char* label,int* current_item,const char*(*getter)(void* user_data,int idx),void* user_data,int items_count,int popup_max_height_in_items) { return ImGui::Combo(label,current_item,getter,user_data,items_count,popup_max_height_in_items); } CIMGUI_API bool igDragFloat(const char* label,float* v,float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragFloat(label,v,v_speed,v_min,v_max,format,flags); } CIMGUI_API bool igDragFloat2(const char* label,float v[2],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragFloat2(label,v,v_speed,v_min,v_max,format,flags); } CIMGUI_API bool igDragFloat3(const char* label,float v[3],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragFloat3(label,v,v_speed,v_min,v_max,format,flags); } CIMGUI_API bool igDragFloat4(const char* label,float v[4],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragFloat4(label,v,v_speed,v_min,v_max,format,flags); } CIMGUI_API bool igDragFloatRange2(const char* label,float* v_current_min,float* v_current_max,float v_speed,float v_min,float v_max,const char* format,const char* format_max,ImGuiSliderFlags flags) { return ImGui::DragFloatRange2(label,v_current_min,v_current_max,v_speed,v_min,v_max,format,format_max,flags); } CIMGUI_API bool igDragInt(const char* label,int* v,float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragInt(label,v,v_speed,v_min,v_max,format,flags); } CIMGUI_API bool igDragInt2(const char* label,int v[2],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragInt2(label,v,v_speed,v_min,v_max,format,flags); } CIMGUI_API bool igDragInt3(const char* label,int v[3],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragInt3(label,v,v_speed,v_min,v_max,format,flags); } CIMGUI_API bool igDragInt4(const char* label,int v[4],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragInt4(label,v,v_speed,v_min,v_max,format,flags); } CIMGUI_API bool igDragIntRange2(const char* label,int* v_current_min,int* v_current_max,float v_speed,int v_min,int v_max,const char* format,const char* format_max,ImGuiSliderFlags flags) { return ImGui::DragIntRange2(label,v_current_min,v_current_max,v_speed,v_min,v_max,format,format_max,flags); } CIMGUI_API bool igDragScalar(const char* label,ImGuiDataType data_type,void* p_data,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragScalar(label,data_type,p_data,v_speed,p_min,p_max,format,flags); } CIMGUI_API bool igDragScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragScalarN(label,data_type,p_data,components,v_speed,p_min,p_max,format,flags); } CIMGUI_API bool igSliderFloat(const char* label,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderFloat(label,v,v_min,v_max,format,flags); } CIMGUI_API bool igSliderFloat2(const char* label,float v[2],float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderFloat2(label,v,v_min,v_max,format,flags); } CIMGUI_API bool igSliderFloat3(const char* label,float v[3],float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderFloat3(label,v,v_min,v_max,format,flags); } CIMGUI_API bool igSliderFloat4(const char* label,float v[4],float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderFloat4(label,v,v_min,v_max,format,flags); } CIMGUI_API bool igSliderAngle(const char* label,float* v_rad,float v_degrees_min,float v_degrees_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderAngle(label,v_rad,v_degrees_min,v_degrees_max,format,flags); } CIMGUI_API bool igSliderInt(const char* label,int* v,int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderInt(label,v,v_min,v_max,format,flags); } CIMGUI_API bool igSliderInt2(const char* label,int v[2],int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderInt2(label,v,v_min,v_max,format,flags); } CIMGUI_API bool igSliderInt3(const char* label,int v[3],int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderInt3(label,v,v_min,v_max,format,flags); } CIMGUI_API bool igSliderInt4(const char* label,int v[4],int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderInt4(label,v,v_min,v_max,format,flags); } CIMGUI_API bool igSliderScalar(const char* label,ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderScalar(label,data_type,p_data,p_min,p_max,format,flags); } CIMGUI_API bool igSliderScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags) { return ImGui::SliderScalarN(label,data_type,p_data,components,p_min,p_max,format,flags); } CIMGUI_API bool igVSliderFloat(const char* label,const ImVec2 size,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::VSliderFloat(label,size,v,v_min,v_max,format,flags); } CIMGUI_API bool igVSliderInt(const char* label,const ImVec2 size,int* v,int v_min,int v_max,const char* format,ImGuiSliderFlags flags) { return ImGui::VSliderInt(label,size,v,v_min,v_max,format,flags); } CIMGUI_API bool igVSliderScalar(const char* label,const ImVec2 size,ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags) { return ImGui::VSliderScalar(label,size,data_type,p_data,p_min,p_max,format,flags); } CIMGUI_API bool igInputText(const char* label,char* buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data) { return ImGui::InputText(label,buf,buf_size,flags,callback,user_data); } CIMGUI_API bool igInputTextMultiline(const char* label,char* buf,size_t buf_size,const ImVec2 size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data) { return ImGui::InputTextMultiline(label,buf,buf_size,size,flags,callback,user_data); } CIMGUI_API bool igInputTextWithHint(const char* label,const char* hint,char* buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data) { return ImGui::InputTextWithHint(label,hint,buf,buf_size,flags,callback,user_data); } CIMGUI_API bool igInputFloat(const char* label,float* v,float step,float step_fast,const char* format,ImGuiInputTextFlags flags) { return ImGui::InputFloat(label,v,step,step_fast,format,flags); } CIMGUI_API bool igInputFloat2(const char* label,float v[2],const char* format,ImGuiInputTextFlags flags) { return ImGui::InputFloat2(label,v,format,flags); } CIMGUI_API bool igInputFloat3(const char* label,float v[3],const char* format,ImGuiInputTextFlags flags) { return ImGui::InputFloat3(label,v,format,flags); } CIMGUI_API bool igInputFloat4(const char* label,float v[4],const char* format,ImGuiInputTextFlags flags) { return ImGui::InputFloat4(label,v,format,flags); } CIMGUI_API bool igInputInt(const char* label,int* v,int step,int step_fast,ImGuiInputTextFlags flags) { return ImGui::InputInt(label,v,step,step_fast,flags); } CIMGUI_API bool igInputInt2(const char* label,int v[2],ImGuiInputTextFlags flags) { return ImGui::InputInt2(label,v,flags); } CIMGUI_API bool igInputInt3(const char* label,int v[3],ImGuiInputTextFlags flags) { return ImGui::InputInt3(label,v,flags); } CIMGUI_API bool igInputInt4(const char* label,int v[4],ImGuiInputTextFlags flags) { return ImGui::InputInt4(label,v,flags); } CIMGUI_API bool igInputDouble(const char* label,double* v,double step,double step_fast,const char* format,ImGuiInputTextFlags flags) { return ImGui::InputDouble(label,v,step,step_fast,format,flags); } CIMGUI_API bool igInputScalar(const char* label,ImGuiDataType data_type,void* p_data,const void* p_step,const void* p_step_fast,const char* format,ImGuiInputTextFlags flags) { return ImGui::InputScalar(label,data_type,p_data,p_step,p_step_fast,format,flags); } CIMGUI_API bool igInputScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,const void* p_step,const void* p_step_fast,const char* format,ImGuiInputTextFlags flags) { return ImGui::InputScalarN(label,data_type,p_data,components,p_step,p_step_fast,format,flags); } CIMGUI_API bool igColorEdit3(const char* label,float col[3],ImGuiColorEditFlags flags) { return ImGui::ColorEdit3(label,col,flags); } CIMGUI_API bool igColorEdit4(const char* label,float col[4],ImGuiColorEditFlags flags) { return ImGui::ColorEdit4(label,col,flags); } CIMGUI_API bool igColorPicker3(const char* label,float col[3],ImGuiColorEditFlags flags) { return ImGui::ColorPicker3(label,col,flags); } CIMGUI_API bool igColorPicker4(const char* label,float col[4],ImGuiColorEditFlags flags,const float* ref_col) { return ImGui::ColorPicker4(label,col,flags,ref_col); } CIMGUI_API bool igColorButton(const char* desc_id,const ImVec4 col,ImGuiColorEditFlags flags,const ImVec2 size) { return ImGui::ColorButton(desc_id,col,flags,size); } CIMGUI_API void igSetColorEditOptions(ImGuiColorEditFlags flags) { return ImGui::SetColorEditOptions(flags); } CIMGUI_API bool igTreeNode_Str(const char* label) { return ImGui::TreeNode(label); } CIMGUI_API bool igTreeNode_StrStr(const char* str_id,const char* fmt,...) { va_list args; va_start(args, fmt); bool ret = ImGui::TreeNodeV(str_id,fmt,args); va_end(args); return ret; } #ifdef CIMGUI_VARGS0 CIMGUI_API bool igTreeNode_StrStr0(const char* str_id,const char* fmt) { return igTreeNode_StrStr(str_id,fmt); } #endif CIMGUI_API bool igTreeNode_Ptr(const void* ptr_id,const char* fmt,...) { va_list args; va_start(args, fmt); bool ret = ImGui::TreeNodeV(ptr_id,fmt,args); va_end(args); return ret; } #ifdef CIMGUI_VARGS0 CIMGUI_API bool igTreeNode_Ptr0(const void* ptr_id,const char* fmt) { return igTreeNode_Ptr(ptr_id,fmt); } #endif CIMGUI_API bool igTreeNodeV_Str(const char* str_id,const char* fmt,va_list args) { return ImGui::TreeNodeV(str_id,fmt,args); } CIMGUI_API bool igTreeNodeV_Ptr(const void* ptr_id,const char* fmt,va_list args) { return ImGui::TreeNodeV(ptr_id,fmt,args); } CIMGUI_API bool igTreeNodeEx_Str(const char* label,ImGuiTreeNodeFlags flags) { return ImGui::TreeNodeEx(label,flags); } CIMGUI_API bool igTreeNodeEx_StrStr(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt,...) { va_list args; va_start(args, fmt); bool ret = ImGui::TreeNodeExV(str_id,flags,fmt,args); va_end(args); return ret; } #ifdef CIMGUI_VARGS0 CIMGUI_API bool igTreeNodeEx_StrStr0(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt) { return igTreeNodeEx_StrStr(str_id,flags,fmt); } #endif CIMGUI_API bool igTreeNodeEx_Ptr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,...) { va_list args; va_start(args, fmt); bool ret = ImGui::TreeNodeExV(ptr_id,flags,fmt,args); va_end(args); return ret; } #ifdef CIMGUI_VARGS0 CIMGUI_API bool igTreeNodeEx_Ptr0(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt) { return igTreeNodeEx_Ptr(ptr_id,flags,fmt); } #endif CIMGUI_API bool igTreeNodeExV_Str(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt,va_list args) { return ImGui::TreeNodeExV(str_id,flags,fmt,args); } CIMGUI_API bool igTreeNodeExV_Ptr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,va_list args) { return ImGui::TreeNodeExV(ptr_id,flags,fmt,args); } CIMGUI_API void igTreePush_Str(const char* str_id) { return ImGui::TreePush(str_id); } CIMGUI_API void igTreePush_Ptr(const void* ptr_id) { return ImGui::TreePush(ptr_id); } CIMGUI_API void igTreePop() { return ImGui::TreePop(); } CIMGUI_API float igGetTreeNodeToLabelSpacing() { return ImGui::GetTreeNodeToLabelSpacing(); } CIMGUI_API bool igCollapsingHeader_TreeNodeFlags(const char* label,ImGuiTreeNodeFlags flags) { return ImGui::CollapsingHeader(label,flags); } CIMGUI_API bool igCollapsingHeader_BoolPtr(const char* label,bool* p_visible,ImGuiTreeNodeFlags flags) { return ImGui::CollapsingHeader(label,p_visible,flags); } CIMGUI_API void igSetNextItemOpen(bool is_open,ImGuiCond cond) { return ImGui::SetNextItemOpen(is_open,cond); } CIMGUI_API void igSetNextItemStorageID(ImGuiID storage_id) { return ImGui::SetNextItemStorageID(storage_id); } CIMGUI_API bool igSelectable_Bool(const char* label,bool selected,ImGuiSelectableFlags flags,const ImVec2 size) { return ImGui::Selectable(label,selected,flags,size); } CIMGUI_API bool igSelectable_BoolPtr(const char* label,bool* p_selected,ImGuiSelectableFlags flags,const ImVec2 size) { return ImGui::Selectable(label,p_selected,flags,size); } CIMGUI_API ImGuiMultiSelectIO* igBeginMultiSelect(ImGuiMultiSelectFlags flags,int selection_size,int items_count) { return ImGui::BeginMultiSelect(flags,selection_size,items_count); } CIMGUI_API ImGuiMultiSelectIO* igEndMultiSelect() { return ImGui::EndMultiSelect(); } CIMGUI_API void igSetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) { return ImGui::SetNextItemSelectionUserData(selection_user_data); } CIMGUI_API bool igIsItemToggledSelection() { return ImGui::IsItemToggledSelection(); } CIMGUI_API bool igBeginListBox(const char* label,const ImVec2 size) { return ImGui::BeginListBox(label,size); } CIMGUI_API void igEndListBox() { return ImGui::EndListBox(); } CIMGUI_API bool igListBox_Str_arr(const char* label,int* current_item,const char* const items[],int items_count,int height_in_items) { return ImGui::ListBox(label,current_item,items,items_count,height_in_items); } CIMGUI_API bool igListBox_FnStrPtr(const char* label,int* current_item,const char*(*getter)(void* user_data,int idx),void* user_data,int items_count,int height_in_items) { return ImGui::ListBox(label,current_item,getter,user_data,items_count,height_in_items); } CIMGUI_API void igPlotLines_FloatPtr(const char* label,const float* values,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride) { return ImGui::PlotLines(label,values,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size,stride); } CIMGUI_API void igPlotLines_FnFloatPtr(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) { return ImGui::PlotLines(label,values_getter,data,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size); } CIMGUI_API void igPlotHistogram_FloatPtr(const char* label,const float* values,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride) { return ImGui::PlotHistogram(label,values,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size,stride); } CIMGUI_API void igPlotHistogram_FnFloatPtr(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) { return ImGui::PlotHistogram(label,values_getter,data,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size); } CIMGUI_API void igValue_Bool(const char* prefix,bool b) { return ImGui::Value(prefix,b); } CIMGUI_API void igValue_Int(const char* prefix,int v) { return ImGui::Value(prefix,v); } CIMGUI_API void igValue_Uint(const char* prefix,unsigned int v) { return ImGui::Value(prefix,v); } CIMGUI_API void igValue_Float(const char* prefix,float v,const char* float_format) { return ImGui::Value(prefix,v,float_format); } CIMGUI_API bool igBeginMenuBar() { return ImGui::BeginMenuBar(); } CIMGUI_API void igEndMenuBar() { return ImGui::EndMenuBar(); } CIMGUI_API bool igBeginMainMenuBar() { return ImGui::BeginMainMenuBar(); } CIMGUI_API void igEndMainMenuBar() { return ImGui::EndMainMenuBar(); } CIMGUI_API bool igBeginMenu(const char* label,bool enabled) { return ImGui::BeginMenu(label,enabled); } CIMGUI_API void igEndMenu() { return ImGui::EndMenu(); } CIMGUI_API bool igMenuItem_Bool(const char* label,const char* shortcut,bool selected,bool enabled) { return ImGui::MenuItem(label,shortcut,selected,enabled); } CIMGUI_API bool igMenuItem_BoolPtr(const char* label,const char* shortcut,bool* p_selected,bool enabled) { return ImGui::MenuItem(label,shortcut,p_selected,enabled); } CIMGUI_API bool igBeginTooltip() { return ImGui::BeginTooltip(); } CIMGUI_API void igEndTooltip() { return ImGui::EndTooltip(); } CIMGUI_API void igSetTooltip(const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::SetTooltipV(fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igSetTooltip0(const char* fmt) { return igSetTooltip(fmt); } #endif CIMGUI_API void igSetTooltipV(const char* fmt,va_list args) { return ImGui::SetTooltipV(fmt,args); } CIMGUI_API bool igBeginItemTooltip() { return ImGui::BeginItemTooltip(); } CIMGUI_API void igSetItemTooltip(const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::SetItemTooltipV(fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igSetItemTooltip0(const char* fmt) { return igSetItemTooltip(fmt); } #endif CIMGUI_API void igSetItemTooltipV(const char* fmt,va_list args) { return ImGui::SetItemTooltipV(fmt,args); } CIMGUI_API bool igBeginPopup(const char* str_id,ImGuiWindowFlags flags) { return ImGui::BeginPopup(str_id,flags); } CIMGUI_API bool igBeginPopupModal(const char* name,bool* p_open,ImGuiWindowFlags flags) { return ImGui::BeginPopupModal(name,p_open,flags); } CIMGUI_API void igEndPopup() { return ImGui::EndPopup(); } CIMGUI_API void igOpenPopup_Str(const char* str_id,ImGuiPopupFlags popup_flags) { return ImGui::OpenPopup(str_id,popup_flags); } CIMGUI_API void igOpenPopup_ID(ImGuiID id,ImGuiPopupFlags popup_flags) { return ImGui::OpenPopup(id,popup_flags); } CIMGUI_API void igOpenPopupOnItemClick(const char* str_id,ImGuiPopupFlags popup_flags) { return ImGui::OpenPopupOnItemClick(str_id,popup_flags); } CIMGUI_API void igCloseCurrentPopup() { return ImGui::CloseCurrentPopup(); } CIMGUI_API bool igBeginPopupContextItem(const char* str_id,ImGuiPopupFlags popup_flags) { return ImGui::BeginPopupContextItem(str_id,popup_flags); } CIMGUI_API bool igBeginPopupContextWindow(const char* str_id,ImGuiPopupFlags popup_flags) { return ImGui::BeginPopupContextWindow(str_id,popup_flags); } CIMGUI_API bool igBeginPopupContextVoid(const char* str_id,ImGuiPopupFlags popup_flags) { return ImGui::BeginPopupContextVoid(str_id,popup_flags); } CIMGUI_API bool igIsPopupOpen_Str(const char* str_id,ImGuiPopupFlags flags) { return ImGui::IsPopupOpen(str_id,flags); } CIMGUI_API bool igBeginTable(const char* str_id,int columns,ImGuiTableFlags flags,const ImVec2 outer_size,float inner_width) { return ImGui::BeginTable(str_id,columns,flags,outer_size,inner_width); } CIMGUI_API void igEndTable() { return ImGui::EndTable(); } CIMGUI_API void igTableNextRow(ImGuiTableRowFlags row_flags,float min_row_height) { return ImGui::TableNextRow(row_flags,min_row_height); } CIMGUI_API bool igTableNextColumn() { return ImGui::TableNextColumn(); } CIMGUI_API bool igTableSetColumnIndex(int column_n) { return ImGui::TableSetColumnIndex(column_n); } CIMGUI_API void igTableSetupColumn(const char* label,ImGuiTableColumnFlags flags,float init_width_or_weight,ImGuiID user_id) { return ImGui::TableSetupColumn(label,flags,init_width_or_weight,user_id); } CIMGUI_API void igTableSetupScrollFreeze(int cols,int rows) { return ImGui::TableSetupScrollFreeze(cols,rows); } CIMGUI_API void igTableHeader(const char* label) { return ImGui::TableHeader(label); } CIMGUI_API void igTableHeadersRow() { return ImGui::TableHeadersRow(); } CIMGUI_API void igTableAngledHeadersRow() { return ImGui::TableAngledHeadersRow(); } CIMGUI_API ImGuiTableSortSpecs* igTableGetSortSpecs() { return ImGui::TableGetSortSpecs(); } CIMGUI_API int igTableGetColumnCount() { return ImGui::TableGetColumnCount(); } CIMGUI_API int igTableGetColumnIndex() { return ImGui::TableGetColumnIndex(); } CIMGUI_API int igTableGetRowIndex() { return ImGui::TableGetRowIndex(); } CIMGUI_API const char* igTableGetColumnName_Int(int column_n) { return ImGui::TableGetColumnName(column_n); } CIMGUI_API ImGuiTableColumnFlags igTableGetColumnFlags(int column_n) { return ImGui::TableGetColumnFlags(column_n); } CIMGUI_API void igTableSetColumnEnabled(int column_n,bool v) { return ImGui::TableSetColumnEnabled(column_n,v); } CIMGUI_API int igTableGetHoveredColumn() { return ImGui::TableGetHoveredColumn(); } CIMGUI_API void igTableSetBgColor(ImGuiTableBgTarget target,ImU32 color,int column_n) { return ImGui::TableSetBgColor(target,color,column_n); } CIMGUI_API void igColumns(int count,const char* id,bool borders) { return ImGui::Columns(count,id,borders); } CIMGUI_API void igNextColumn() { return ImGui::NextColumn(); } CIMGUI_API int igGetColumnIndex() { return ImGui::GetColumnIndex(); } CIMGUI_API float igGetColumnWidth(int column_index) { return ImGui::GetColumnWidth(column_index); } CIMGUI_API void igSetColumnWidth(int column_index,float width) { return ImGui::SetColumnWidth(column_index,width); } CIMGUI_API float igGetColumnOffset(int column_index) { return ImGui::GetColumnOffset(column_index); } CIMGUI_API void igSetColumnOffset(int column_index,float offset_x) { return ImGui::SetColumnOffset(column_index,offset_x); } CIMGUI_API int igGetColumnsCount() { return ImGui::GetColumnsCount(); } CIMGUI_API bool igBeginTabBar(const char* str_id,ImGuiTabBarFlags flags) { return ImGui::BeginTabBar(str_id,flags); } CIMGUI_API void igEndTabBar() { return ImGui::EndTabBar(); } CIMGUI_API bool igBeginTabItem(const char* label,bool* p_open,ImGuiTabItemFlags flags) { return ImGui::BeginTabItem(label,p_open,flags); } CIMGUI_API void igEndTabItem() { return ImGui::EndTabItem(); } CIMGUI_API bool igTabItemButton(const char* label,ImGuiTabItemFlags flags) { return ImGui::TabItemButton(label,flags); } CIMGUI_API void igSetTabItemClosed(const char* tab_or_docked_window_label) { return ImGui::SetTabItemClosed(tab_or_docked_window_label); } CIMGUI_API ImGuiID igDockSpace(ImGuiID dockspace_id,const ImVec2 size,ImGuiDockNodeFlags flags,const ImGuiWindowClass* window_class) { return ImGui::DockSpace(dockspace_id,size,flags,window_class); } CIMGUI_API ImGuiID igDockSpaceOverViewport(ImGuiID dockspace_id,const ImGuiViewport* viewport,ImGuiDockNodeFlags flags,const ImGuiWindowClass* window_class) { return ImGui::DockSpaceOverViewport(dockspace_id,viewport,flags,window_class); } CIMGUI_API void igSetNextWindowDockID(ImGuiID dock_id,ImGuiCond cond) { return ImGui::SetNextWindowDockID(dock_id,cond); } CIMGUI_API void igSetNextWindowClass(const ImGuiWindowClass* window_class) { return ImGui::SetNextWindowClass(window_class); } CIMGUI_API ImGuiID igGetWindowDockID() { return ImGui::GetWindowDockID(); } CIMGUI_API bool igIsWindowDocked() { return ImGui::IsWindowDocked(); } CIMGUI_API void igLogToTTY(int auto_open_depth) { return ImGui::LogToTTY(auto_open_depth); } CIMGUI_API void igLogToFile(int auto_open_depth,const char* filename) { return ImGui::LogToFile(auto_open_depth,filename); } CIMGUI_API void igLogToClipboard(int auto_open_depth) { return ImGui::LogToClipboard(auto_open_depth); } CIMGUI_API void igLogFinish() { return ImGui::LogFinish(); } CIMGUI_API void igLogButtons() { return ImGui::LogButtons(); } CIMGUI_API void igLogText(const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::LogTextV(fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igLogText0(const char* fmt) { return igLogText(fmt); } #endif CIMGUI_API void igLogTextV(const char* fmt,va_list args) { return ImGui::LogTextV(fmt,args); } CIMGUI_API bool igBeginDragDropSource(ImGuiDragDropFlags flags) { return ImGui::BeginDragDropSource(flags); } CIMGUI_API bool igSetDragDropPayload(const char* type,const void* data,size_t sz,ImGuiCond cond) { return ImGui::SetDragDropPayload(type,data,sz,cond); } CIMGUI_API void igEndDragDropSource() { return ImGui::EndDragDropSource(); } CIMGUI_API bool igBeginDragDropTarget() { return ImGui::BeginDragDropTarget(); } CIMGUI_API const ImGuiPayload* igAcceptDragDropPayload(const char* type,ImGuiDragDropFlags flags) { return ImGui::AcceptDragDropPayload(type,flags); } CIMGUI_API void igEndDragDropTarget() { return ImGui::EndDragDropTarget(); } CIMGUI_API const ImGuiPayload* igGetDragDropPayload() { return ImGui::GetDragDropPayload(); } CIMGUI_API void igBeginDisabled(bool disabled) { return ImGui::BeginDisabled(disabled); } CIMGUI_API void igEndDisabled() { return ImGui::EndDisabled(); } CIMGUI_API void igPushClipRect(const ImVec2 clip_rect_min,const ImVec2 clip_rect_max,bool intersect_with_current_clip_rect) { return ImGui::PushClipRect(clip_rect_min,clip_rect_max,intersect_with_current_clip_rect); } CIMGUI_API void igPopClipRect() { return ImGui::PopClipRect(); } CIMGUI_API void igSetItemDefaultFocus() { return ImGui::SetItemDefaultFocus(); } CIMGUI_API void igSetKeyboardFocusHere(int offset) { return ImGui::SetKeyboardFocusHere(offset); } CIMGUI_API void igSetNavCursorVisible(bool visible) { return ImGui::SetNavCursorVisible(visible); } CIMGUI_API void igSetNextItemAllowOverlap() { return ImGui::SetNextItemAllowOverlap(); } CIMGUI_API bool igIsItemHovered(ImGuiHoveredFlags flags) { return ImGui::IsItemHovered(flags); } CIMGUI_API bool igIsItemActive() { return ImGui::IsItemActive(); } CIMGUI_API bool igIsItemFocused() { return ImGui::IsItemFocused(); } CIMGUI_API bool igIsItemClicked(ImGuiMouseButton mouse_button) { return ImGui::IsItemClicked(mouse_button); } CIMGUI_API bool igIsItemVisible() { return ImGui::IsItemVisible(); } CIMGUI_API bool igIsItemEdited() { return ImGui::IsItemEdited(); } CIMGUI_API bool igIsItemActivated() { return ImGui::IsItemActivated(); } CIMGUI_API bool igIsItemDeactivated() { return ImGui::IsItemDeactivated(); } CIMGUI_API bool igIsItemDeactivatedAfterEdit() { return ImGui::IsItemDeactivatedAfterEdit(); } CIMGUI_API bool igIsItemToggledOpen() { return ImGui::IsItemToggledOpen(); } CIMGUI_API bool igIsAnyItemHovered() { return ImGui::IsAnyItemHovered(); } CIMGUI_API bool igIsAnyItemActive() { return ImGui::IsAnyItemActive(); } CIMGUI_API bool igIsAnyItemFocused() { return ImGui::IsAnyItemFocused(); } CIMGUI_API ImGuiID igGetItemID() { return ImGui::GetItemID(); } CIMGUI_API void igGetItemRectMin(ImVec2 *pOut) { *pOut = ImGui::GetItemRectMin(); } CIMGUI_API void igGetItemRectMax(ImVec2 *pOut) { *pOut = ImGui::GetItemRectMax(); } CIMGUI_API void igGetItemRectSize(ImVec2 *pOut) { *pOut = ImGui::GetItemRectSize(); } CIMGUI_API ImGuiViewport* igGetMainViewport() { return ImGui::GetMainViewport(); } CIMGUI_API ImDrawList* igGetBackgroundDrawList(ImGuiViewport* viewport) { return ImGui::GetBackgroundDrawList(viewport); } CIMGUI_API ImDrawList* igGetForegroundDrawList_ViewportPtr(ImGuiViewport* viewport) { return ImGui::GetForegroundDrawList(viewport); } CIMGUI_API bool igIsRectVisible_Nil(const ImVec2 size) { return ImGui::IsRectVisible(size); } CIMGUI_API bool igIsRectVisible_Vec2(const ImVec2 rect_min,const ImVec2 rect_max) { return ImGui::IsRectVisible(rect_min,rect_max); } CIMGUI_API double igGetTime() { return ImGui::GetTime(); } CIMGUI_API int igGetFrameCount() { return ImGui::GetFrameCount(); } CIMGUI_API ImDrawListSharedData* igGetDrawListSharedData() { return ImGui::GetDrawListSharedData(); } CIMGUI_API const char* igGetStyleColorName(ImGuiCol idx) { return ImGui::GetStyleColorName(idx); } CIMGUI_API void igSetStateStorage(ImGuiStorage* storage) { return ImGui::SetStateStorage(storage); } CIMGUI_API ImGuiStorage* igGetStateStorage() { return ImGui::GetStateStorage(); } CIMGUI_API void igCalcTextSize(ImVec2 *pOut,const char* text,const char* text_end,bool hide_text_after_double_hash,float wrap_width) { *pOut = ImGui::CalcTextSize(text,text_end,hide_text_after_double_hash,wrap_width); } CIMGUI_API void igColorConvertU32ToFloat4(ImVec4 *pOut,ImU32 in) { *pOut = ImGui::ColorConvertU32ToFloat4(in); } CIMGUI_API ImU32 igColorConvertFloat4ToU32(const ImVec4 in) { return ImGui::ColorConvertFloat4ToU32(in); } CIMGUI_API void igColorConvertRGBtoHSV(float r,float g,float b,float* out_h,float* out_s,float* out_v) { return ImGui::ColorConvertRGBtoHSV(r,g,b,*out_h,*out_s,*out_v); } CIMGUI_API void igColorConvertHSVtoRGB(float h,float s,float v,float* out_r,float* out_g,float* out_b) { return ImGui::ColorConvertHSVtoRGB(h,s,v,*out_r,*out_g,*out_b); } CIMGUI_API bool igIsKeyDown_Nil(ImGuiKey key) { return ImGui::IsKeyDown(key); } CIMGUI_API bool igIsKeyPressed_Bool(ImGuiKey key,bool repeat) { return ImGui::IsKeyPressed(key,repeat); } CIMGUI_API bool igIsKeyReleased_Nil(ImGuiKey key) { return ImGui::IsKeyReleased(key); } CIMGUI_API bool igIsKeyChordPressed_Nil(ImGuiKeyChord key_chord) { return ImGui::IsKeyChordPressed(key_chord); } CIMGUI_API int igGetKeyPressedAmount(ImGuiKey key,float repeat_delay,float rate) { return ImGui::GetKeyPressedAmount(key,repeat_delay,rate); } CIMGUI_API const char* igGetKeyName(ImGuiKey key) { return ImGui::GetKeyName(key); } CIMGUI_API void igSetNextFrameWantCaptureKeyboard(bool want_capture_keyboard) { return ImGui::SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } CIMGUI_API bool igShortcut_Nil(ImGuiKeyChord key_chord,ImGuiInputFlags flags) { return ImGui::Shortcut(key_chord,flags); } CIMGUI_API void igSetNextItemShortcut(ImGuiKeyChord key_chord,ImGuiInputFlags flags) { return ImGui::SetNextItemShortcut(key_chord,flags); } CIMGUI_API void igSetItemKeyOwner_Nil(ImGuiKey key) { return ImGui::SetItemKeyOwner(key); } CIMGUI_API bool igIsMouseDown_Nil(ImGuiMouseButton button) { return ImGui::IsMouseDown(button); } CIMGUI_API bool igIsMouseClicked_Bool(ImGuiMouseButton button,bool repeat) { return ImGui::IsMouseClicked(button,repeat); } CIMGUI_API bool igIsMouseReleased_Nil(ImGuiMouseButton button) { return ImGui::IsMouseReleased(button); } CIMGUI_API bool igIsMouseDoubleClicked_Nil(ImGuiMouseButton button) { return ImGui::IsMouseDoubleClicked(button); } CIMGUI_API bool igIsMouseReleasedWithDelay(ImGuiMouseButton button,float delay) { return ImGui::IsMouseReleasedWithDelay(button,delay); } CIMGUI_API int igGetMouseClickedCount(ImGuiMouseButton button) { return ImGui::GetMouseClickedCount(button); } CIMGUI_API bool igIsMouseHoveringRect(const ImVec2 r_min,const ImVec2 r_max,bool clip) { return ImGui::IsMouseHoveringRect(r_min,r_max,clip); } CIMGUI_API bool igIsMousePosValid(const ImVec2* mouse_pos) { return ImGui::IsMousePosValid(mouse_pos); } CIMGUI_API bool igIsAnyMouseDown() { return ImGui::IsAnyMouseDown(); } CIMGUI_API void igGetMousePos(ImVec2 *pOut) { *pOut = ImGui::GetMousePos(); } CIMGUI_API void igGetMousePosOnOpeningCurrentPopup(ImVec2 *pOut) { *pOut = ImGui::GetMousePosOnOpeningCurrentPopup(); } CIMGUI_API bool igIsMouseDragging(ImGuiMouseButton button,float lock_threshold) { return ImGui::IsMouseDragging(button,lock_threshold); } CIMGUI_API void igGetMouseDragDelta(ImVec2 *pOut,ImGuiMouseButton button,float lock_threshold) { *pOut = ImGui::GetMouseDragDelta(button,lock_threshold); } CIMGUI_API void igResetMouseDragDelta(ImGuiMouseButton button) { return ImGui::ResetMouseDragDelta(button); } CIMGUI_API ImGuiMouseCursor igGetMouseCursor() { return ImGui::GetMouseCursor(); } CIMGUI_API void igSetMouseCursor(ImGuiMouseCursor cursor_type) { return ImGui::SetMouseCursor(cursor_type); } CIMGUI_API void igSetNextFrameWantCaptureMouse(bool want_capture_mouse) { return ImGui::SetNextFrameWantCaptureMouse(want_capture_mouse); } CIMGUI_API const char* igGetClipboardText() { return ImGui::GetClipboardText(); } CIMGUI_API void igSetClipboardText(const char* text) { return ImGui::SetClipboardText(text); } CIMGUI_API void igLoadIniSettingsFromDisk(const char* ini_filename) { return ImGui::LoadIniSettingsFromDisk(ini_filename); } CIMGUI_API void igLoadIniSettingsFromMemory(const char* ini_data,size_t ini_size) { return ImGui::LoadIniSettingsFromMemory(ini_data,ini_size); } CIMGUI_API void igSaveIniSettingsToDisk(const char* ini_filename) { return ImGui::SaveIniSettingsToDisk(ini_filename); } CIMGUI_API const char* igSaveIniSettingsToMemory(size_t* out_ini_size) { return ImGui::SaveIniSettingsToMemory(out_ini_size); } CIMGUI_API void igDebugTextEncoding(const char* text) { return ImGui::DebugTextEncoding(text); } CIMGUI_API void igDebugFlashStyleColor(ImGuiCol idx) { return ImGui::DebugFlashStyleColor(idx); } CIMGUI_API void igDebugStartItemPicker() { return ImGui::DebugStartItemPicker(); } CIMGUI_API bool igDebugCheckVersionAndDataLayout(const char* version_str,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_drawvert,size_t sz_drawidx) { return ImGui::DebugCheckVersionAndDataLayout(version_str,sz_io,sz_style,sz_vec2,sz_vec4,sz_drawvert,sz_drawidx); } CIMGUI_API void igDebugLog(const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::DebugLogV(fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igDebugLog0(const char* fmt) { return igDebugLog(fmt); } #endif CIMGUI_API void igDebugLogV(const char* fmt,va_list args) { return ImGui::DebugLogV(fmt,args); } CIMGUI_API void igSetAllocatorFunctions(ImGuiMemAllocFunc alloc_func,ImGuiMemFreeFunc free_func,void* user_data) { return ImGui::SetAllocatorFunctions(alloc_func,free_func,user_data); } CIMGUI_API void igGetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func,ImGuiMemFreeFunc* p_free_func,void** p_user_data) { return ImGui::GetAllocatorFunctions(p_alloc_func,p_free_func,p_user_data); } CIMGUI_API void* igMemAlloc(size_t size) { return ImGui::MemAlloc(size); } CIMGUI_API void igMemFree(void* ptr) { return ImGui::MemFree(ptr); } CIMGUI_API void igUpdatePlatformWindows() { return ImGui::UpdatePlatformWindows(); } CIMGUI_API void igRenderPlatformWindowsDefault(void* platform_render_arg,void* renderer_render_arg) { return ImGui::RenderPlatformWindowsDefault(platform_render_arg,renderer_render_arg); } CIMGUI_API void igDestroyPlatformWindows() { return ImGui::DestroyPlatformWindows(); } CIMGUI_API ImGuiViewport* igFindViewportByID(ImGuiID id) { return ImGui::FindViewportByID(id); } CIMGUI_API ImGuiViewport* igFindViewportByPlatformHandle(void* platform_handle) { return ImGui::FindViewportByPlatformHandle(platform_handle); } CIMGUI_API ImGuiTableSortSpecs* ImGuiTableSortSpecs_ImGuiTableSortSpecs(void) { return IM_NEW(ImGuiTableSortSpecs)(); } CIMGUI_API void ImGuiTableSortSpecs_destroy(ImGuiTableSortSpecs* self) { IM_DELETE(self); } CIMGUI_API ImGuiTableColumnSortSpecs* ImGuiTableColumnSortSpecs_ImGuiTableColumnSortSpecs(void) { return IM_NEW(ImGuiTableColumnSortSpecs)(); } CIMGUI_API void ImGuiTableColumnSortSpecs_destroy(ImGuiTableColumnSortSpecs* self) { IM_DELETE(self); } CIMGUI_API ImGuiStyle* ImGuiStyle_ImGuiStyle(void) { return IM_NEW(ImGuiStyle)(); } CIMGUI_API void ImGuiStyle_destroy(ImGuiStyle* self) { IM_DELETE(self); } CIMGUI_API void ImGuiStyle_ScaleAllSizes(ImGuiStyle* self,float scale_factor) { return self->ScaleAllSizes(scale_factor); } CIMGUI_API void ImGuiIO_AddKeyEvent(ImGuiIO* self,ImGuiKey key,bool down) { return self->AddKeyEvent(key,down); } CIMGUI_API void ImGuiIO_AddKeyAnalogEvent(ImGuiIO* self,ImGuiKey key,bool down,float v) { return self->AddKeyAnalogEvent(key,down,v); } CIMGUI_API void ImGuiIO_AddMousePosEvent(ImGuiIO* self,float x,float y) { return self->AddMousePosEvent(x,y); } CIMGUI_API void ImGuiIO_AddMouseButtonEvent(ImGuiIO* self,int button,bool down) { return self->AddMouseButtonEvent(button,down); } CIMGUI_API void ImGuiIO_AddMouseWheelEvent(ImGuiIO* self,float wheel_x,float wheel_y) { return self->AddMouseWheelEvent(wheel_x,wheel_y); } CIMGUI_API void ImGuiIO_AddMouseSourceEvent(ImGuiIO* self,ImGuiMouseSource source) { return self->AddMouseSourceEvent(source); } CIMGUI_API void ImGuiIO_AddMouseViewportEvent(ImGuiIO* self,ImGuiID id) { return self->AddMouseViewportEvent(id); } CIMGUI_API void ImGuiIO_AddFocusEvent(ImGuiIO* self,bool focused) { return self->AddFocusEvent(focused); } CIMGUI_API void ImGuiIO_AddInputCharacter(ImGuiIO* self,unsigned int c) { return self->AddInputCharacter(c); } CIMGUI_API void ImGuiIO_AddInputCharacterUTF16(ImGuiIO* self,ImWchar16 c) { return self->AddInputCharacterUTF16(c); } CIMGUI_API void ImGuiIO_AddInputCharactersUTF8(ImGuiIO* self,const char* str) { return self->AddInputCharactersUTF8(str); } CIMGUI_API void ImGuiIO_SetKeyEventNativeData(ImGuiIO* self,ImGuiKey key,int native_keycode,int native_scancode,int native_legacy_index) { return self->SetKeyEventNativeData(key,native_keycode,native_scancode,native_legacy_index); } CIMGUI_API void ImGuiIO_SetAppAcceptingEvents(ImGuiIO* self,bool accepting_events) { return self->SetAppAcceptingEvents(accepting_events); } CIMGUI_API void ImGuiIO_ClearEventsQueue(ImGuiIO* self) { return self->ClearEventsQueue(); } CIMGUI_API void ImGuiIO_ClearInputKeys(ImGuiIO* self) { return self->ClearInputKeys(); } CIMGUI_API void ImGuiIO_ClearInputMouse(ImGuiIO* self) { return self->ClearInputMouse(); } CIMGUI_API ImGuiIO* ImGuiIO_ImGuiIO(void) { return IM_NEW(ImGuiIO)(); } CIMGUI_API void ImGuiIO_destroy(ImGuiIO* self) { IM_DELETE(self); } CIMGUI_API ImGuiInputTextCallbackData* ImGuiInputTextCallbackData_ImGuiInputTextCallbackData(void) { return IM_NEW(ImGuiInputTextCallbackData)(); } CIMGUI_API void ImGuiInputTextCallbackData_destroy(ImGuiInputTextCallbackData* self) { IM_DELETE(self); } CIMGUI_API void ImGuiInputTextCallbackData_DeleteChars(ImGuiInputTextCallbackData* self,int pos,int bytes_count) { return self->DeleteChars(pos,bytes_count); } CIMGUI_API void ImGuiInputTextCallbackData_InsertChars(ImGuiInputTextCallbackData* self,int pos,const char* text,const char* text_end) { return self->InsertChars(pos,text,text_end); } CIMGUI_API void ImGuiInputTextCallbackData_SelectAll(ImGuiInputTextCallbackData* self) { return self->SelectAll(); } CIMGUI_API void ImGuiInputTextCallbackData_ClearSelection(ImGuiInputTextCallbackData* self) { return self->ClearSelection(); } CIMGUI_API bool ImGuiInputTextCallbackData_HasSelection(ImGuiInputTextCallbackData* self) { return self->HasSelection(); } CIMGUI_API ImGuiWindowClass* ImGuiWindowClass_ImGuiWindowClass(void) { return IM_NEW(ImGuiWindowClass)(); } CIMGUI_API void ImGuiWindowClass_destroy(ImGuiWindowClass* self) { IM_DELETE(self); } CIMGUI_API ImGuiPayload* ImGuiPayload_ImGuiPayload(void) { return IM_NEW(ImGuiPayload)(); } CIMGUI_API void ImGuiPayload_destroy(ImGuiPayload* self) { IM_DELETE(self); } CIMGUI_API void ImGuiPayload_Clear(ImGuiPayload* self) { return self->Clear(); } CIMGUI_API bool ImGuiPayload_IsDataType(ImGuiPayload* self,const char* type) { return self->IsDataType(type); } CIMGUI_API bool ImGuiPayload_IsPreview(ImGuiPayload* self) { return self->IsPreview(); } CIMGUI_API bool ImGuiPayload_IsDelivery(ImGuiPayload* self) { return self->IsDelivery(); } CIMGUI_API ImGuiOnceUponAFrame* ImGuiOnceUponAFrame_ImGuiOnceUponAFrame(void) { return IM_NEW(ImGuiOnceUponAFrame)(); } CIMGUI_API void ImGuiOnceUponAFrame_destroy(ImGuiOnceUponAFrame* self) { IM_DELETE(self); } CIMGUI_API ImGuiTextFilter* ImGuiTextFilter_ImGuiTextFilter(const char* default_filter) { return IM_NEW(ImGuiTextFilter)(default_filter); } CIMGUI_API void ImGuiTextFilter_destroy(ImGuiTextFilter* self) { IM_DELETE(self); } CIMGUI_API bool ImGuiTextFilter_Draw(ImGuiTextFilter* self,const char* label,float width) { return self->Draw(label,width); } CIMGUI_API bool ImGuiTextFilter_PassFilter(ImGuiTextFilter* self,const char* text,const char* text_end) { return self->PassFilter(text,text_end); } CIMGUI_API void ImGuiTextFilter_Build(ImGuiTextFilter* self) { return self->Build(); } CIMGUI_API void ImGuiTextFilter_Clear(ImGuiTextFilter* self) { return self->Clear(); } CIMGUI_API bool ImGuiTextFilter_IsActive(ImGuiTextFilter* self) { return self->IsActive(); } CIMGUI_API ImGuiTextRange* ImGuiTextRange_ImGuiTextRange_Nil(void) { return IM_NEW(ImGuiTextRange)(); } CIMGUI_API void ImGuiTextRange_destroy(ImGuiTextRange* self) { IM_DELETE(self); } CIMGUI_API ImGuiTextRange* ImGuiTextRange_ImGuiTextRange_Str(const char* _b,const char* _e) { return IM_NEW(ImGuiTextRange)(_b,_e); } CIMGUI_API bool ImGuiTextRange_empty(ImGuiTextRange* self) { return self->empty(); } CIMGUI_API void ImGuiTextRange_split(ImGuiTextRange* self,char separator,ImVector_ImGuiTextRange* out) { return self->split(separator,out); } CIMGUI_API ImGuiTextBuffer* ImGuiTextBuffer_ImGuiTextBuffer(void) { return IM_NEW(ImGuiTextBuffer)(); } CIMGUI_API void ImGuiTextBuffer_destroy(ImGuiTextBuffer* self) { IM_DELETE(self); } CIMGUI_API const char* ImGuiTextBuffer_begin(ImGuiTextBuffer* self) { return self->begin(); } CIMGUI_API const char* ImGuiTextBuffer_end(ImGuiTextBuffer* self) { return self->end(); } CIMGUI_API int ImGuiTextBuffer_size(ImGuiTextBuffer* self) { return self->size(); } CIMGUI_API bool ImGuiTextBuffer_empty(ImGuiTextBuffer* self) { return self->empty(); } CIMGUI_API void ImGuiTextBuffer_clear(ImGuiTextBuffer* self) { return self->clear(); } CIMGUI_API void ImGuiTextBuffer_resize(ImGuiTextBuffer* self,int size) { return self->resize(size); } CIMGUI_API void ImGuiTextBuffer_reserve(ImGuiTextBuffer* self,int capacity) { return self->reserve(capacity); } CIMGUI_API const char* ImGuiTextBuffer_c_str(ImGuiTextBuffer* self) { return self->c_str(); } CIMGUI_API void ImGuiTextBuffer_append(ImGuiTextBuffer* self,const char* str,const char* str_end) { return self->append(str,str_end); } CIMGUI_API void ImGuiTextBuffer_appendfv(ImGuiTextBuffer* self,const char* fmt,va_list args) { return self->appendfv(fmt,args); } CIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Int(ImGuiID _key,int _val) { return IM_NEW(ImGuiStoragePair)(_key,_val); } CIMGUI_API void ImGuiStoragePair_destroy(ImGuiStoragePair* self) { IM_DELETE(self); } CIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Float(ImGuiID _key,float _val) { return IM_NEW(ImGuiStoragePair)(_key,_val); } CIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Ptr(ImGuiID _key,void* _val) { return IM_NEW(ImGuiStoragePair)(_key,_val); } CIMGUI_API void ImGuiStorage_Clear(ImGuiStorage* self) { return self->Clear(); } CIMGUI_API int ImGuiStorage_GetInt(ImGuiStorage* self,ImGuiID key,int default_val) { return self->GetInt(key,default_val); } CIMGUI_API void ImGuiStorage_SetInt(ImGuiStorage* self,ImGuiID key,int val) { return self->SetInt(key,val); } CIMGUI_API bool ImGuiStorage_GetBool(ImGuiStorage* self,ImGuiID key,bool default_val) { return self->GetBool(key,default_val); } CIMGUI_API void ImGuiStorage_SetBool(ImGuiStorage* self,ImGuiID key,bool val) { return self->SetBool(key,val); } CIMGUI_API float ImGuiStorage_GetFloat(ImGuiStorage* self,ImGuiID key,float default_val) { return self->GetFloat(key,default_val); } CIMGUI_API void ImGuiStorage_SetFloat(ImGuiStorage* self,ImGuiID key,float val) { return self->SetFloat(key,val); } CIMGUI_API void* ImGuiStorage_GetVoidPtr(ImGuiStorage* self,ImGuiID key) { return self->GetVoidPtr(key); } CIMGUI_API void ImGuiStorage_SetVoidPtr(ImGuiStorage* self,ImGuiID key,void* val) { return self->SetVoidPtr(key,val); } CIMGUI_API int* ImGuiStorage_GetIntRef(ImGuiStorage* self,ImGuiID key,int default_val) { return self->GetIntRef(key,default_val); } CIMGUI_API bool* ImGuiStorage_GetBoolRef(ImGuiStorage* self,ImGuiID key,bool default_val) { return self->GetBoolRef(key,default_val); } CIMGUI_API float* ImGuiStorage_GetFloatRef(ImGuiStorage* self,ImGuiID key,float default_val) { return self->GetFloatRef(key,default_val); } CIMGUI_API void** ImGuiStorage_GetVoidPtrRef(ImGuiStorage* self,ImGuiID key,void* default_val) { return self->GetVoidPtrRef(key,default_val); } CIMGUI_API void ImGuiStorage_BuildSortByKey(ImGuiStorage* self) { return self->BuildSortByKey(); } CIMGUI_API void ImGuiStorage_SetAllInt(ImGuiStorage* self,int val) { return self->SetAllInt(val); } CIMGUI_API ImGuiListClipper* ImGuiListClipper_ImGuiListClipper(void) { return IM_NEW(ImGuiListClipper)(); } CIMGUI_API void ImGuiListClipper_destroy(ImGuiListClipper* self) { IM_DELETE(self); } CIMGUI_API void ImGuiListClipper_Begin(ImGuiListClipper* self,int items_count,float items_height) { return self->Begin(items_count,items_height); } CIMGUI_API void ImGuiListClipper_End(ImGuiListClipper* self) { return self->End(); } CIMGUI_API bool ImGuiListClipper_Step(ImGuiListClipper* self) { return self->Step(); } CIMGUI_API void ImGuiListClipper_IncludeItemByIndex(ImGuiListClipper* self,int item_index) { return self->IncludeItemByIndex(item_index); } CIMGUI_API void ImGuiListClipper_IncludeItemsByIndex(ImGuiListClipper* self,int item_begin,int item_end) { return self->IncludeItemsByIndex(item_begin,item_end); } CIMGUI_API void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* self,int item_index) { return self->SeekCursorForItem(item_index); } CIMGUI_API ImColor* ImColor_ImColor_Nil(void) { return IM_NEW(ImColor)(); } CIMGUI_API void ImColor_destroy(ImColor* self) { IM_DELETE(self); } CIMGUI_API ImColor* ImColor_ImColor_Float(float r,float g,float b,float a) { return IM_NEW(ImColor)(r,g,b,a); } CIMGUI_API ImColor* ImColor_ImColor_Vec4(const ImVec4 col) { return IM_NEW(ImColor)(col); } CIMGUI_API ImColor* ImColor_ImColor_Int(int r,int g,int b,int a) { return IM_NEW(ImColor)(r,g,b,a); } CIMGUI_API ImColor* ImColor_ImColor_U32(ImU32 rgba) { return IM_NEW(ImColor)(rgba); } CIMGUI_API void ImColor_SetHSV(ImColor* self,float h,float s,float v,float a) { return self->SetHSV(h,s,v,a); } CIMGUI_API void ImColor_HSV(ImColor *pOut,float h,float s,float v,float a) { *pOut = ImColor::HSV(h,s,v,a); } CIMGUI_API ImGuiSelectionBasicStorage* ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage(void) { return IM_NEW(ImGuiSelectionBasicStorage)(); } CIMGUI_API void ImGuiSelectionBasicStorage_destroy(ImGuiSelectionBasicStorage* self) { IM_DELETE(self); } CIMGUI_API void ImGuiSelectionBasicStorage_ApplyRequests(ImGuiSelectionBasicStorage* self,ImGuiMultiSelectIO* ms_io) { return self->ApplyRequests(ms_io); } CIMGUI_API bool ImGuiSelectionBasicStorage_Contains(ImGuiSelectionBasicStorage* self,ImGuiID id) { return self->Contains(id); } CIMGUI_API void ImGuiSelectionBasicStorage_Clear(ImGuiSelectionBasicStorage* self) { return self->Clear(); } CIMGUI_API void ImGuiSelectionBasicStorage_Swap(ImGuiSelectionBasicStorage* self,ImGuiSelectionBasicStorage* r) { return self->Swap(*r); } CIMGUI_API void ImGuiSelectionBasicStorage_SetItemSelected(ImGuiSelectionBasicStorage* self,ImGuiID id,bool selected) { return self->SetItemSelected(id,selected); } CIMGUI_API bool ImGuiSelectionBasicStorage_GetNextSelectedItem(ImGuiSelectionBasicStorage* self,void** opaque_it,ImGuiID* out_id) { return self->GetNextSelectedItem(opaque_it,out_id); } CIMGUI_API ImGuiID ImGuiSelectionBasicStorage_GetStorageIdFromIndex(ImGuiSelectionBasicStorage* self,int idx) { return self->GetStorageIdFromIndex(idx); } CIMGUI_API ImGuiSelectionExternalStorage* ImGuiSelectionExternalStorage_ImGuiSelectionExternalStorage(void) { return IM_NEW(ImGuiSelectionExternalStorage)(); } CIMGUI_API void ImGuiSelectionExternalStorage_destroy(ImGuiSelectionExternalStorage* self) { IM_DELETE(self); } CIMGUI_API void ImGuiSelectionExternalStorage_ApplyRequests(ImGuiSelectionExternalStorage* self,ImGuiMultiSelectIO* ms_io) { return self->ApplyRequests(ms_io); } CIMGUI_API ImDrawCmd* ImDrawCmd_ImDrawCmd(void) { return IM_NEW(ImDrawCmd)(); } CIMGUI_API void ImDrawCmd_destroy(ImDrawCmd* self) { IM_DELETE(self); } CIMGUI_API ImTextureID ImDrawCmd_GetTexID(ImDrawCmd* self) { return self->GetTexID(); } CIMGUI_API ImDrawListSplitter* ImDrawListSplitter_ImDrawListSplitter(void) { return IM_NEW(ImDrawListSplitter)(); } CIMGUI_API void ImDrawListSplitter_destroy(ImDrawListSplitter* self) { IM_DELETE(self); } CIMGUI_API void ImDrawListSplitter_Clear(ImDrawListSplitter* self) { return self->Clear(); } CIMGUI_API void ImDrawListSplitter_ClearFreeMemory(ImDrawListSplitter* self) { return self->ClearFreeMemory(); } CIMGUI_API void ImDrawListSplitter_Split(ImDrawListSplitter* self,ImDrawList* draw_list,int count) { return self->Split(draw_list,count); } CIMGUI_API void ImDrawListSplitter_Merge(ImDrawListSplitter* self,ImDrawList* draw_list) { return self->Merge(draw_list); } CIMGUI_API void ImDrawListSplitter_SetCurrentChannel(ImDrawListSplitter* self,ImDrawList* draw_list,int channel_idx) { return self->SetCurrentChannel(draw_list,channel_idx); } CIMGUI_API ImDrawList* ImDrawList_ImDrawList(ImDrawListSharedData* shared_data) { return IM_NEW(ImDrawList)(shared_data); } CIMGUI_API void ImDrawList_destroy(ImDrawList* self) { IM_DELETE(self); } CIMGUI_API void ImDrawList_PushClipRect(ImDrawList* self,const ImVec2 clip_rect_min,const ImVec2 clip_rect_max,bool intersect_with_current_clip_rect) { return self->PushClipRect(clip_rect_min,clip_rect_max,intersect_with_current_clip_rect); } CIMGUI_API void ImDrawList_PushClipRectFullScreen(ImDrawList* self) { return self->PushClipRectFullScreen(); } CIMGUI_API void ImDrawList_PopClipRect(ImDrawList* self) { return self->PopClipRect(); } CIMGUI_API void ImDrawList_PushTexture(ImDrawList* self,ImTextureRef tex_ref) { return self->PushTexture(tex_ref); } CIMGUI_API void ImDrawList_PopTexture(ImDrawList* self) { return self->PopTexture(); } CIMGUI_API void ImDrawList_GetClipRectMin(ImVec2 *pOut,ImDrawList* self) { *pOut = self->GetClipRectMin(); } CIMGUI_API void ImDrawList_GetClipRectMax(ImVec2 *pOut,ImDrawList* self) { *pOut = self->GetClipRectMax(); } CIMGUI_API void ImDrawList_AddLine(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,ImU32 col,float thickness) { return self->AddLine(p1,p2,col,thickness); } CIMGUI_API void ImDrawList_AddRect(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col,float rounding,ImDrawFlags flags,float thickness) { return self->AddRect(p_min,p_max,col,rounding,flags,thickness); } CIMGUI_API void ImDrawList_AddRectFilled(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col,float rounding,ImDrawFlags flags) { return self->AddRectFilled(p_min,p_max,col,rounding,flags); } CIMGUI_API void ImDrawList_AddRectFilledMultiColor(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col_upr_left,ImU32 col_upr_right,ImU32 col_bot_right,ImU32 col_bot_left) { return self->AddRectFilledMultiColor(p_min,p_max,col_upr_left,col_upr_right,col_bot_right,col_bot_left); } CIMGUI_API void ImDrawList_AddQuad(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col,float thickness) { return self->AddQuad(p1,p2,p3,p4,col,thickness); } CIMGUI_API void ImDrawList_AddQuadFilled(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col) { return self->AddQuadFilled(p1,p2,p3,p4,col); } CIMGUI_API void ImDrawList_AddTriangle(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col,float thickness) { return self->AddTriangle(p1,p2,p3,col,thickness); } CIMGUI_API void ImDrawList_AddTriangleFilled(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col) { return self->AddTriangleFilled(p1,p2,p3,col); } CIMGUI_API void ImDrawList_AddCircle(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments,float thickness) { return self->AddCircle(center,radius,col,num_segments,thickness); } CIMGUI_API void ImDrawList_AddCircleFilled(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments) { return self->AddCircleFilled(center,radius,col,num_segments); } CIMGUI_API void ImDrawList_AddNgon(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments,float thickness) { return self->AddNgon(center,radius,col,num_segments,thickness); } CIMGUI_API void ImDrawList_AddNgonFilled(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments) { return self->AddNgonFilled(center,radius,col,num_segments); } CIMGUI_API void ImDrawList_AddEllipse(ImDrawList* self,const ImVec2 center,const ImVec2 radius,ImU32 col,float rot,int num_segments,float thickness) { return self->AddEllipse(center,radius,col,rot,num_segments,thickness); } CIMGUI_API void ImDrawList_AddEllipseFilled(ImDrawList* self,const ImVec2 center,const ImVec2 radius,ImU32 col,float rot,int num_segments) { return self->AddEllipseFilled(center,radius,col,rot,num_segments); } CIMGUI_API void ImDrawList_AddText_Vec2(ImDrawList* self,const ImVec2 pos,ImU32 col,const char* text_begin,const char* text_end) { return self->AddText(pos,col,text_begin,text_end); } CIMGUI_API void ImDrawList_AddText_FontPtr(ImDrawList* self,ImFont* font,float font_size,const ImVec2 pos,ImU32 col,const char* text_begin,const char* text_end,float wrap_width,const ImVec4* cpu_fine_clip_rect) { return self->AddText(font,font_size,pos,col,text_begin,text_end,wrap_width,cpu_fine_clip_rect); } CIMGUI_API void ImDrawList_AddBezierCubic(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col,float thickness,int num_segments) { return self->AddBezierCubic(p1,p2,p3,p4,col,thickness,num_segments); } CIMGUI_API void ImDrawList_AddBezierQuadratic(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col,float thickness,int num_segments) { return self->AddBezierQuadratic(p1,p2,p3,col,thickness,num_segments); } CIMGUI_API void ImDrawList_AddPolyline(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col,ImDrawFlags flags,float thickness) { return self->AddPolyline(points,num_points,col,flags,thickness); } CIMGUI_API void ImDrawList_AddConvexPolyFilled(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col) { return self->AddConvexPolyFilled(points,num_points,col); } CIMGUI_API void ImDrawList_AddConcavePolyFilled(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col) { return self->AddConcavePolyFilled(points,num_points,col); } CIMGUI_API void ImDrawList_AddImage(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p_min,const ImVec2 p_max,const ImVec2 uv_min,const ImVec2 uv_max,ImU32 col) { return self->AddImage(tex_ref,p_min,p_max,uv_min,uv_max,col); } CIMGUI_API void ImDrawList_AddImageQuad(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 uv1,const ImVec2 uv2,const ImVec2 uv3,const ImVec2 uv4,ImU32 col) { return self->AddImageQuad(tex_ref,p1,p2,p3,p4,uv1,uv2,uv3,uv4,col); } CIMGUI_API void ImDrawList_AddImageRounded(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p_min,const ImVec2 p_max,const ImVec2 uv_min,const ImVec2 uv_max,ImU32 col,float rounding,ImDrawFlags flags) { return self->AddImageRounded(tex_ref,p_min,p_max,uv_min,uv_max,col,rounding,flags); } CIMGUI_API void ImDrawList_PathClear(ImDrawList* self) { return self->PathClear(); } CIMGUI_API void ImDrawList_PathLineTo(ImDrawList* self,const ImVec2 pos) { return self->PathLineTo(pos); } CIMGUI_API void ImDrawList_PathLineToMergeDuplicate(ImDrawList* self,const ImVec2 pos) { return self->PathLineToMergeDuplicate(pos); } CIMGUI_API void ImDrawList_PathFillConvex(ImDrawList* self,ImU32 col) { return self->PathFillConvex(col); } CIMGUI_API void ImDrawList_PathFillConcave(ImDrawList* self,ImU32 col) { return self->PathFillConcave(col); } CIMGUI_API void ImDrawList_PathStroke(ImDrawList* self,ImU32 col,ImDrawFlags flags,float thickness) { return self->PathStroke(col,flags,thickness); } CIMGUI_API void ImDrawList_PathArcTo(ImDrawList* self,const ImVec2 center,float radius,float a_min,float a_max,int num_segments) { return self->PathArcTo(center,radius,a_min,a_max,num_segments); } CIMGUI_API void ImDrawList_PathArcToFast(ImDrawList* self,const ImVec2 center,float radius,int a_min_of_12,int a_max_of_12) { return self->PathArcToFast(center,radius,a_min_of_12,a_max_of_12); } CIMGUI_API void ImDrawList_PathEllipticalArcTo(ImDrawList* self,const ImVec2 center,const ImVec2 radius,float rot,float a_min,float a_max,int num_segments) { return self->PathEllipticalArcTo(center,radius,rot,a_min,a_max,num_segments); } CIMGUI_API void ImDrawList_PathBezierCubicCurveTo(ImDrawList* self,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,int num_segments) { return self->PathBezierCubicCurveTo(p2,p3,p4,num_segments); } CIMGUI_API void ImDrawList_PathBezierQuadraticCurveTo(ImDrawList* self,const ImVec2 p2,const ImVec2 p3,int num_segments) { return self->PathBezierQuadraticCurveTo(p2,p3,num_segments); } CIMGUI_API void ImDrawList_PathRect(ImDrawList* self,const ImVec2 rect_min,const ImVec2 rect_max,float rounding,ImDrawFlags flags) { return self->PathRect(rect_min,rect_max,rounding,flags); } CIMGUI_API void ImDrawList_AddCallback(ImDrawList* self,ImDrawCallback callback,void* userdata,size_t userdata_size) { return self->AddCallback(callback,userdata,userdata_size); } CIMGUI_API void ImDrawList_AddDrawCmd(ImDrawList* self) { return self->AddDrawCmd(); } CIMGUI_API ImDrawList* ImDrawList_CloneOutput(ImDrawList* self) { return self->CloneOutput(); } CIMGUI_API void ImDrawList_ChannelsSplit(ImDrawList* self,int count) { return self->ChannelsSplit(count); } CIMGUI_API void ImDrawList_ChannelsMerge(ImDrawList* self) { return self->ChannelsMerge(); } CIMGUI_API void ImDrawList_ChannelsSetCurrent(ImDrawList* self,int n) { return self->ChannelsSetCurrent(n); } CIMGUI_API void ImDrawList_PrimReserve(ImDrawList* self,int idx_count,int vtx_count) { return self->PrimReserve(idx_count,vtx_count); } CIMGUI_API void ImDrawList_PrimUnreserve(ImDrawList* self,int idx_count,int vtx_count) { return self->PrimUnreserve(idx_count,vtx_count); } CIMGUI_API void ImDrawList_PrimRect(ImDrawList* self,const ImVec2 a,const ImVec2 b,ImU32 col) { return self->PrimRect(a,b,col); } CIMGUI_API void ImDrawList_PrimRectUV(ImDrawList* self,const ImVec2 a,const ImVec2 b,const ImVec2 uv_a,const ImVec2 uv_b,ImU32 col) { return self->PrimRectUV(a,b,uv_a,uv_b,col); } CIMGUI_API void ImDrawList_PrimQuadUV(ImDrawList* self,const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 d,const ImVec2 uv_a,const ImVec2 uv_b,const ImVec2 uv_c,const ImVec2 uv_d,ImU32 col) { return self->PrimQuadUV(a,b,c,d,uv_a,uv_b,uv_c,uv_d,col); } CIMGUI_API void ImDrawList_PrimWriteVtx(ImDrawList* self,const ImVec2 pos,const ImVec2 uv,ImU32 col) { return self->PrimWriteVtx(pos,uv,col); } CIMGUI_API void ImDrawList_PrimWriteIdx(ImDrawList* self,ImDrawIdx idx) { return self->PrimWriteIdx(idx); } CIMGUI_API void ImDrawList_PrimVtx(ImDrawList* self,const ImVec2 pos,const ImVec2 uv,ImU32 col) { return self->PrimVtx(pos,uv,col); } CIMGUI_API void ImDrawList__SetDrawListSharedData(ImDrawList* self,ImDrawListSharedData* data) { return self->_SetDrawListSharedData(data); } CIMGUI_API void ImDrawList__ResetForNewFrame(ImDrawList* self) { return self->_ResetForNewFrame(); } CIMGUI_API void ImDrawList__ClearFreeMemory(ImDrawList* self) { return self->_ClearFreeMemory(); } CIMGUI_API void ImDrawList__PopUnusedDrawCmd(ImDrawList* self) { return self->_PopUnusedDrawCmd(); } CIMGUI_API void ImDrawList__TryMergeDrawCmds(ImDrawList* self) { return self->_TryMergeDrawCmds(); } CIMGUI_API void ImDrawList__OnChangedClipRect(ImDrawList* self) { return self->_OnChangedClipRect(); } CIMGUI_API void ImDrawList__OnChangedTexture(ImDrawList* self) { return self->_OnChangedTexture(); } CIMGUI_API void ImDrawList__OnChangedVtxOffset(ImDrawList* self) { return self->_OnChangedVtxOffset(); } CIMGUI_API void ImDrawList__SetTexture(ImDrawList* self,ImTextureRef tex_ref) { return self->_SetTexture(tex_ref); } CIMGUI_API int ImDrawList__CalcCircleAutoSegmentCount(ImDrawList* self,float radius) { return self->_CalcCircleAutoSegmentCount(radius); } CIMGUI_API void ImDrawList__PathArcToFastEx(ImDrawList* self,const ImVec2 center,float radius,int a_min_sample,int a_max_sample,int a_step) { return self->_PathArcToFastEx(center,radius,a_min_sample,a_max_sample,a_step); } CIMGUI_API void ImDrawList__PathArcToN(ImDrawList* self,const ImVec2 center,float radius,float a_min,float a_max,int num_segments) { return self->_PathArcToN(center,radius,a_min,a_max,num_segments); } CIMGUI_API ImDrawData* ImDrawData_ImDrawData(void) { return IM_NEW(ImDrawData)(); } CIMGUI_API void ImDrawData_destroy(ImDrawData* self) { IM_DELETE(self); } CIMGUI_API void ImDrawData_Clear(ImDrawData* self) { return self->Clear(); } CIMGUI_API void ImDrawData_AddDrawList(ImDrawData* self,ImDrawList* draw_list) { return self->AddDrawList(draw_list); } CIMGUI_API void ImDrawData_DeIndexAllBuffers(ImDrawData* self) { return self->DeIndexAllBuffers(); } CIMGUI_API void ImDrawData_ScaleClipRects(ImDrawData* self,const ImVec2 fb_scale) { return self->ScaleClipRects(fb_scale); } CIMGUI_API ImTextureData* ImTextureData_ImTextureData(void) { return IM_NEW(ImTextureData)(); } CIMGUI_API void ImTextureData_destroy(ImTextureData* self) { IM_DELETE(self); } CIMGUI_API void ImTextureData_Create(ImTextureData* self,ImTextureFormat format,int w,int h) { return self->Create(format,w,h); } CIMGUI_API void ImTextureData_DestroyPixels(ImTextureData* self) { return self->DestroyPixels(); } CIMGUI_API void* ImTextureData_GetPixels(ImTextureData* self) { return self->GetPixels(); } CIMGUI_API void* ImTextureData_GetPixelsAt(ImTextureData* self,int x,int y) { return self->GetPixelsAt(x,y); } CIMGUI_API int ImTextureData_GetSizeInBytes(ImTextureData* self) { return self->GetSizeInBytes(); } CIMGUI_API int ImTextureData_GetPitch(ImTextureData* self) { return self->GetPitch(); } CIMGUI_API void ImTextureData_GetTexRef(ImTextureRef *pOut,ImTextureData* self) { *pOut = self->GetTexRef(); } CIMGUI_API ImTextureID ImTextureData_GetTexID(ImTextureData* self) { return self->GetTexID(); } CIMGUI_API void ImTextureData_SetTexID(ImTextureData* self,ImTextureID tex_id) { return self->SetTexID(tex_id); } CIMGUI_API void ImTextureData_SetStatus(ImTextureData* self,ImTextureStatus status) { return self->SetStatus(status); } CIMGUI_API ImFontConfig* ImFontConfig_ImFontConfig(void) { return IM_NEW(ImFontConfig)(); } CIMGUI_API void ImFontConfig_destroy(ImFontConfig* self) { IM_DELETE(self); } CIMGUI_API ImFontGlyph* ImFontGlyph_ImFontGlyph(void) { return IM_NEW(ImFontGlyph)(); } CIMGUI_API void ImFontGlyph_destroy(ImFontGlyph* self) { IM_DELETE(self); } CIMGUI_API ImFontGlyphRangesBuilder* ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder(void) { return IM_NEW(ImFontGlyphRangesBuilder)(); } CIMGUI_API void ImFontGlyphRangesBuilder_destroy(ImFontGlyphRangesBuilder* self) { IM_DELETE(self); } CIMGUI_API void ImFontGlyphRangesBuilder_Clear(ImFontGlyphRangesBuilder* self) { return self->Clear(); } CIMGUI_API bool ImFontGlyphRangesBuilder_GetBit(ImFontGlyphRangesBuilder* self,size_t n) { return self->GetBit(n); } CIMGUI_API void ImFontGlyphRangesBuilder_SetBit(ImFontGlyphRangesBuilder* self,size_t n) { return self->SetBit(n); } CIMGUI_API void ImFontGlyphRangesBuilder_AddChar(ImFontGlyphRangesBuilder* self,ImWchar c) { return self->AddChar(c); } CIMGUI_API void ImFontGlyphRangesBuilder_AddText(ImFontGlyphRangesBuilder* self,const char* text,const char* text_end) { return self->AddText(text,text_end); } CIMGUI_API void ImFontGlyphRangesBuilder_AddRanges(ImFontGlyphRangesBuilder* self,const ImWchar* ranges) { return self->AddRanges(ranges); } CIMGUI_API void ImFontGlyphRangesBuilder_BuildRanges(ImFontGlyphRangesBuilder* self,ImVector_ImWchar* out_ranges) { return self->BuildRanges(out_ranges); } CIMGUI_API ImFontAtlasRect* ImFontAtlasRect_ImFontAtlasRect(void) { return IM_NEW(ImFontAtlasRect)(); } CIMGUI_API void ImFontAtlasRect_destroy(ImFontAtlasRect* self) { IM_DELETE(self); } CIMGUI_API ImFontAtlas* ImFontAtlas_ImFontAtlas(void) { return IM_NEW(ImFontAtlas)(); } CIMGUI_API void ImFontAtlas_destroy(ImFontAtlas* self) { IM_DELETE(self); } CIMGUI_API ImFont* ImFontAtlas_AddFont(ImFontAtlas* self,const ImFontConfig* font_cfg) { return self->AddFont(font_cfg); } CIMGUI_API ImFont* ImFontAtlas_AddFontDefault(ImFontAtlas* self,const ImFontConfig* font_cfg) { return self->AddFontDefault(font_cfg); } CIMGUI_API ImFont* ImFontAtlas_AddFontFromFileTTF(ImFontAtlas* self,const char* filename,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges) { return self->AddFontFromFileTTF(filename,size_pixels,font_cfg,glyph_ranges); } CIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryTTF(ImFontAtlas* self,void* font_data,int font_data_size,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges) { return self->AddFontFromMemoryTTF(font_data,font_data_size,size_pixels,font_cfg,glyph_ranges); } CIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryCompressedTTF(ImFontAtlas* self,const void* compressed_font_data,int compressed_font_data_size,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges) { return self->AddFontFromMemoryCompressedTTF(compressed_font_data,compressed_font_data_size,size_pixels,font_cfg,glyph_ranges); } CIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryCompressedBase85TTF(ImFontAtlas* self,const char* compressed_font_data_base85,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges) { return self->AddFontFromMemoryCompressedBase85TTF(compressed_font_data_base85,size_pixels,font_cfg,glyph_ranges); } CIMGUI_API void ImFontAtlas_RemoveFont(ImFontAtlas* self,ImFont* font) { return self->RemoveFont(font); } CIMGUI_API void ImFontAtlas_Clear(ImFontAtlas* self) { return self->Clear(); } CIMGUI_API void ImFontAtlas_CompactCache(ImFontAtlas* self) { return self->CompactCache(); } CIMGUI_API void ImFontAtlas_ClearInputData(ImFontAtlas* self) { return self->ClearInputData(); } CIMGUI_API void ImFontAtlas_ClearFonts(ImFontAtlas* self) { return self->ClearFonts(); } CIMGUI_API void ImFontAtlas_ClearTexData(ImFontAtlas* self) { return self->ClearTexData(); } CIMGUI_API const ImWchar* ImFontAtlas_GetGlyphRangesDefault(ImFontAtlas* self) { return self->GetGlyphRangesDefault(); } CIMGUI_API ImFontAtlasRectId ImFontAtlas_AddCustomRect(ImFontAtlas* self,int width,int height,ImFontAtlasRect* out_r) { return self->AddCustomRect(width,height,out_r); } CIMGUI_API void ImFontAtlas_RemoveCustomRect(ImFontAtlas* self,ImFontAtlasRectId id) { return self->RemoveCustomRect(id); } CIMGUI_API bool ImFontAtlas_GetCustomRect(ImFontAtlas* self,ImFontAtlasRectId id,ImFontAtlasRect* out_r) { return self->GetCustomRect(id,out_r); } CIMGUI_API ImFontBaked* ImFontBaked_ImFontBaked(void) { return IM_NEW(ImFontBaked)(); } CIMGUI_API void ImFontBaked_destroy(ImFontBaked* self) { IM_DELETE(self); } CIMGUI_API void ImFontBaked_ClearOutputData(ImFontBaked* self) { return self->ClearOutputData(); } CIMGUI_API ImFontGlyph* ImFontBaked_FindGlyph(ImFontBaked* self,ImWchar c) { return self->FindGlyph(c); } CIMGUI_API ImFontGlyph* ImFontBaked_FindGlyphNoFallback(ImFontBaked* self,ImWchar c) { return self->FindGlyphNoFallback(c); } CIMGUI_API float ImFontBaked_GetCharAdvance(ImFontBaked* self,ImWchar c) { return self->GetCharAdvance(c); } CIMGUI_API bool ImFontBaked_IsGlyphLoaded(ImFontBaked* self,ImWchar c) { return self->IsGlyphLoaded(c); } CIMGUI_API ImFont* ImFont_ImFont(void) { return IM_NEW(ImFont)(); } CIMGUI_API void ImFont_destroy(ImFont* self) { IM_DELETE(self); } CIMGUI_API bool ImFont_IsGlyphInFont(ImFont* self,ImWchar c) { return self->IsGlyphInFont(c); } CIMGUI_API bool ImFont_IsLoaded(ImFont* self) { return self->IsLoaded(); } CIMGUI_API const char* ImFont_GetDebugName(ImFont* self) { return self->GetDebugName(); } CIMGUI_API ImFontBaked* ImFont_GetFontBaked(ImFont* self,float font_size,float density) { return self->GetFontBaked(font_size,density); } CIMGUI_API void ImFont_CalcTextSizeA(ImVec2 *pOut,ImFont* self,float size,float max_width,float wrap_width,const char* text_begin,const char* text_end,const char** remaining) { *pOut = self->CalcTextSizeA(size,max_width,wrap_width,text_begin,text_end,remaining); } CIMGUI_API const char* ImFont_CalcWordWrapPosition(ImFont* self,float size,const char* text,const char* text_end,float wrap_width) { return self->CalcWordWrapPosition(size,text,text_end,wrap_width); } CIMGUI_API void ImFont_RenderChar(ImFont* self,ImDrawList* draw_list,float size,const ImVec2 pos,ImU32 col,ImWchar c,const ImVec4* cpu_fine_clip) { return self->RenderChar(draw_list,size,pos,col,c,cpu_fine_clip); } CIMGUI_API void ImFont_RenderText(ImFont* self,ImDrawList* draw_list,float size,const ImVec2 pos,ImU32 col,const ImVec4 clip_rect,const char* text_begin,const char* text_end,float wrap_width,bool cpu_fine_clip) { return self->RenderText(draw_list,size,pos,col,clip_rect,text_begin,text_end,wrap_width,cpu_fine_clip); } CIMGUI_API void ImFont_ClearOutputData(ImFont* self) { return self->ClearOutputData(); } CIMGUI_API void ImFont_AddRemapChar(ImFont* self,ImWchar from_codepoint,ImWchar to_codepoint) { return self->AddRemapChar(from_codepoint,to_codepoint); } CIMGUI_API bool ImFont_IsGlyphRangeUnused(ImFont* self,unsigned int c_begin,unsigned int c_last) { return self->IsGlyphRangeUnused(c_begin,c_last); } CIMGUI_API ImGuiViewport* ImGuiViewport_ImGuiViewport(void) { return IM_NEW(ImGuiViewport)(); } CIMGUI_API void ImGuiViewport_destroy(ImGuiViewport* self) { IM_DELETE(self); } CIMGUI_API void ImGuiViewport_GetCenter(ImVec2 *pOut,ImGuiViewport* self) { *pOut = self->GetCenter(); } CIMGUI_API void ImGuiViewport_GetWorkCenter(ImVec2 *pOut,ImGuiViewport* self) { *pOut = self->GetWorkCenter(); } CIMGUI_API ImGuiPlatformIO* ImGuiPlatformIO_ImGuiPlatformIO(void) { return IM_NEW(ImGuiPlatformIO)(); } CIMGUI_API void ImGuiPlatformIO_destroy(ImGuiPlatformIO* self) { IM_DELETE(self); } CIMGUI_API ImGuiPlatformMonitor* ImGuiPlatformMonitor_ImGuiPlatformMonitor(void) { return IM_NEW(ImGuiPlatformMonitor)(); } CIMGUI_API void ImGuiPlatformMonitor_destroy(ImGuiPlatformMonitor* self) { IM_DELETE(self); } CIMGUI_API ImGuiPlatformImeData* ImGuiPlatformImeData_ImGuiPlatformImeData(void) { return IM_NEW(ImGuiPlatformImeData)(); } CIMGUI_API void ImGuiPlatformImeData_destroy(ImGuiPlatformImeData* self) { IM_DELETE(self); } CIMGUI_API ImGuiID igImHashData(const void* data,size_t data_size,ImGuiID seed) { return ImHashData(data,data_size,seed); } CIMGUI_API ImGuiID igImHashStr(const char* data,size_t data_size,ImGuiID seed) { return ImHashStr(data,data_size,seed); } CIMGUI_API void igImQsort(void* base,size_t count,size_t size_of_element,int(*compare_func)(void const*,void const*)) { return ImQsort(base,count,size_of_element,compare_func); } CIMGUI_API ImU32 igImAlphaBlendColors(ImU32 col_a,ImU32 col_b) { return ImAlphaBlendColors(col_a,col_b); } CIMGUI_API bool igImIsPowerOfTwo_Int(int v) { return ImIsPowerOfTwo(v); } CIMGUI_API bool igImIsPowerOfTwo_U64(ImU64 v) { return ImIsPowerOfTwo(v); } CIMGUI_API int igImUpperPowerOfTwo(int v) { return ImUpperPowerOfTwo(v); } CIMGUI_API unsigned int igImCountSetBits(unsigned int v) { return ImCountSetBits(v); } CIMGUI_API int igImStricmp(const char* str1,const char* str2) { return ImStricmp(str1,str2); } CIMGUI_API int igImStrnicmp(const char* str1,const char* str2,size_t count) { return ImStrnicmp(str1,str2,count); } CIMGUI_API void igImStrncpy(char* dst,const char* src,size_t count) { return ImStrncpy(dst,src,count); } CIMGUI_API char* igImStrdup(const char* str) { return ImStrdup(str); } CIMGUI_API void* igImMemdup(const void* src,size_t size) { return ImMemdup(src,size); } CIMGUI_API char* igImStrdupcpy(char* dst,size_t* p_dst_size,const char* str) { return ImStrdupcpy(dst,p_dst_size,str); } CIMGUI_API const char* igImStrchrRange(const char* str_begin,const char* str_end,char c) { return ImStrchrRange(str_begin,str_end,c); } CIMGUI_API const char* igImStreolRange(const char* str,const char* str_end) { return ImStreolRange(str,str_end); } CIMGUI_API const char* igImStristr(const char* haystack,const char* haystack_end,const char* needle,const char* needle_end) { return ImStristr(haystack,haystack_end,needle,needle_end); } CIMGUI_API void igImStrTrimBlanks(char* str) { return ImStrTrimBlanks(str); } CIMGUI_API const char* igImStrSkipBlank(const char* str) { return ImStrSkipBlank(str); } CIMGUI_API int igImStrlenW(const ImWchar* str) { return ImStrlenW(str); } CIMGUI_API const char* igImStrbol(const char* buf_mid_line,const char* buf_begin) { return ImStrbol(buf_mid_line,buf_begin); } CIMGUI_API char igImToUpper(char c) { return ImToUpper(c); } CIMGUI_API bool igImCharIsBlankA(char c) { return ImCharIsBlankA(c); } CIMGUI_API bool igImCharIsBlankW(unsigned int c) { return ImCharIsBlankW(c); } CIMGUI_API bool igImCharIsXdigitA(char c) { return ImCharIsXdigitA(c); } CIMGUI_API int igImFormatString(char* buf,size_t buf_size,const char* fmt,...) { va_list args; va_start(args, fmt); int ret = ImFormatStringV(buf,buf_size,fmt,args); va_end(args); return ret; } #ifdef CIMGUI_VARGS0 CIMGUI_API int igImFormatString0(char* buf,size_t buf_size,const char* fmt) { return igImFormatString(buf,buf_size,fmt); } #endif CIMGUI_API int igImFormatStringV(char* buf,size_t buf_size,const char* fmt,va_list args) { return ImFormatStringV(buf,buf_size,fmt,args); } CIMGUI_API void igImFormatStringToTempBuffer(const char** out_buf,const char** out_buf_end,const char* fmt,...) { va_list args; va_start(args, fmt); ImFormatStringToTempBufferV(out_buf,out_buf_end,fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igImFormatStringToTempBuffer0(const char** out_buf,const char** out_buf_end,const char* fmt) { return igImFormatStringToTempBuffer(out_buf,out_buf_end,fmt); } #endif CIMGUI_API void igImFormatStringToTempBufferV(const char** out_buf,const char** out_buf_end,const char* fmt,va_list args) { return ImFormatStringToTempBufferV(out_buf,out_buf_end,fmt,args); } CIMGUI_API const char* igImParseFormatFindStart(const char* format) { return ImParseFormatFindStart(format); } CIMGUI_API const char* igImParseFormatFindEnd(const char* format) { return ImParseFormatFindEnd(format); } CIMGUI_API const char* igImParseFormatTrimDecorations(const char* format,char* buf,size_t buf_size) { return ImParseFormatTrimDecorations(format,buf,buf_size); } CIMGUI_API void igImParseFormatSanitizeForPrinting(const char* fmt_in,char* fmt_out,size_t fmt_out_size) { return ImParseFormatSanitizeForPrinting(fmt_in,fmt_out,fmt_out_size); } CIMGUI_API const char* igImParseFormatSanitizeForScanning(const char* fmt_in,char* fmt_out,size_t fmt_out_size) { return ImParseFormatSanitizeForScanning(fmt_in,fmt_out,fmt_out_size); } CIMGUI_API int igImParseFormatPrecision(const char* format,int default_value) { return ImParseFormatPrecision(format,default_value); } CIMGUI_API int igImTextCharToUtf8(char out_buf[5],unsigned int c) { return ImTextCharToUtf8(out_buf,c); } CIMGUI_API int igImTextStrToUtf8(char* out_buf,int out_buf_size,const ImWchar* in_text,const ImWchar* in_text_end) { return ImTextStrToUtf8(out_buf,out_buf_size,in_text,in_text_end); } CIMGUI_API int igImTextCharFromUtf8(unsigned int* out_char,const char* in_text,const char* in_text_end) { return ImTextCharFromUtf8(out_char,in_text,in_text_end); } CIMGUI_API int igImTextStrFromUtf8(ImWchar* out_buf,int out_buf_size,const char* in_text,const char* in_text_end,const char** in_remaining) { return ImTextStrFromUtf8(out_buf,out_buf_size,in_text,in_text_end,in_remaining); } CIMGUI_API int igImTextCountCharsFromUtf8(const char* in_text,const char* in_text_end) { return ImTextCountCharsFromUtf8(in_text,in_text_end); } CIMGUI_API int igImTextCountUtf8BytesFromChar(const char* in_text,const char* in_text_end) { return ImTextCountUtf8BytesFromChar(in_text,in_text_end); } CIMGUI_API int igImTextCountUtf8BytesFromStr(const ImWchar* in_text,const ImWchar* in_text_end) { return ImTextCountUtf8BytesFromStr(in_text,in_text_end); } CIMGUI_API const char* igImTextFindPreviousUtf8Codepoint(const char* in_text_start,const char* in_text_curr) { return ImTextFindPreviousUtf8Codepoint(in_text_start,in_text_curr); } CIMGUI_API int igImTextCountLines(const char* in_text,const char* in_text_end) { return ImTextCountLines(in_text,in_text_end); } CIMGUI_API ImFileHandle igImFileOpen(const char* filename,const char* mode) { return ImFileOpen(filename,mode); } CIMGUI_API bool igImFileClose(ImFileHandle file) { return ImFileClose(file); } CIMGUI_API ImU64 igImFileGetSize(ImFileHandle file) { return ImFileGetSize(file); } CIMGUI_API ImU64 igImFileRead(void* data,ImU64 size,ImU64 count,ImFileHandle file) { return ImFileRead(data,size,count,file); } CIMGUI_API ImU64 igImFileWrite(const void* data,ImU64 size,ImU64 count,ImFileHandle file) { return ImFileWrite(data,size,count,file); } CIMGUI_API void* igImFileLoadToMemory(const char* filename,const char* mode,size_t* out_file_size,int padding_bytes) { return ImFileLoadToMemory(filename,mode,out_file_size,padding_bytes); } CIMGUI_API float igImPow_Float(float x,float y) { return ImPow(x,y); } CIMGUI_API double igImPow_double(double x,double y) { return ImPow(x,y); } CIMGUI_API float igImLog_Float(float x) { return ImLog(x); } CIMGUI_API double igImLog_double(double x) { return ImLog(x); } CIMGUI_API int igImAbs_Int(int x) { return ImAbs(x); } CIMGUI_API float igImAbs_Float(float x) { return ImAbs(x); } CIMGUI_API double igImAbs_double(double x) { return ImAbs(x); } CIMGUI_API float igImSign_Float(float x) { return ImSign(x); } CIMGUI_API double igImSign_double(double x) { return ImSign(x); } CIMGUI_API float igImRsqrt_Float(float x) { return ImRsqrt(x); } CIMGUI_API double igImRsqrt_double(double x) { return ImRsqrt(x); } CIMGUI_API void igImMin(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs) { *pOut = ImMin(lhs,rhs); } CIMGUI_API void igImMax(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs) { *pOut = ImMax(lhs,rhs); } CIMGUI_API void igImClamp(ImVec2 *pOut,const ImVec2 v,const ImVec2 mn,const ImVec2 mx) { *pOut = ImClamp(v,mn,mx); } CIMGUI_API void igImLerp_Vec2Float(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,float t) { *pOut = ImLerp(a,b,t); } CIMGUI_API void igImLerp_Vec2Vec2(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 t) { *pOut = ImLerp(a,b,t); } CIMGUI_API void igImLerp_Vec4(ImVec4 *pOut,const ImVec4 a,const ImVec4 b,float t) { *pOut = ImLerp(a,b,t); } CIMGUI_API float igImSaturate(float f) { return ImSaturate(f); } CIMGUI_API float igImLengthSqr_Vec2(const ImVec2 lhs) { return ImLengthSqr(lhs); } CIMGUI_API float igImLengthSqr_Vec4(const ImVec4 lhs) { return ImLengthSqr(lhs); } CIMGUI_API float igImInvLength(const ImVec2 lhs,float fail_value) { return ImInvLength(lhs,fail_value); } CIMGUI_API float igImTrunc_Float(float f) { return ImTrunc(f); } CIMGUI_API void igImTrunc_Vec2(ImVec2 *pOut,const ImVec2 v) { *pOut = ImTrunc(v); } CIMGUI_API float igImFloor_Float(float f) { return ImFloor(f); } CIMGUI_API void igImFloor_Vec2(ImVec2 *pOut,const ImVec2 v) { *pOut = ImFloor(v); } CIMGUI_API float igImTrunc64(float f) { return ImTrunc64(f); } CIMGUI_API float igImRound64(float f) { return ImRound64(f); } CIMGUI_API int igImModPositive(int a,int b) { return ImModPositive(a,b); } CIMGUI_API float igImDot(const ImVec2 a,const ImVec2 b) { return ImDot(a,b); } CIMGUI_API void igImRotate(ImVec2 *pOut,const ImVec2 v,float cos_a,float sin_a) { *pOut = ImRotate(v,cos_a,sin_a); } CIMGUI_API float igImLinearSweep(float current,float target,float speed) { return ImLinearSweep(current,target,speed); } CIMGUI_API float igImLinearRemapClamp(float s0,float s1,float d0,float d1,float x) { return ImLinearRemapClamp(s0,s1,d0,d1,x); } CIMGUI_API void igImMul(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs) { *pOut = ImMul(lhs,rhs); } CIMGUI_API bool igImIsFloatAboveGuaranteedIntegerPrecision(float f) { return ImIsFloatAboveGuaranteedIntegerPrecision(f); } CIMGUI_API float igImExponentialMovingAverage(float avg,float sample,int n) { return ImExponentialMovingAverage(avg,sample,n); } CIMGUI_API void igImBezierCubicCalc(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,float t) { *pOut = ImBezierCubicCalc(p1,p2,p3,p4,t); } CIMGUI_API void igImBezierCubicClosestPoint(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 p,int num_segments) { *pOut = ImBezierCubicClosestPoint(p1,p2,p3,p4,p,num_segments); } CIMGUI_API void igImBezierCubicClosestPointCasteljau(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 p,float tess_tol) { *pOut = ImBezierCubicClosestPointCasteljau(p1,p2,p3,p4,p,tess_tol); } CIMGUI_API void igImBezierQuadraticCalc(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,float t) { *pOut = ImBezierQuadraticCalc(p1,p2,p3,t); } CIMGUI_API void igImLineClosestPoint(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 p) { *pOut = ImLineClosestPoint(a,b,p); } CIMGUI_API bool igImTriangleContainsPoint(const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p) { return ImTriangleContainsPoint(a,b,c,p); } CIMGUI_API void igImTriangleClosestPoint(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p) { *pOut = ImTriangleClosestPoint(a,b,c,p); } CIMGUI_API void igImTriangleBarycentricCoords(const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p,float* out_u,float* out_v,float* out_w) { return ImTriangleBarycentricCoords(a,b,c,p,*out_u,*out_v,*out_w); } CIMGUI_API float igImTriangleArea(const ImVec2 a,const ImVec2 b,const ImVec2 c) { return ImTriangleArea(a,b,c); } CIMGUI_API bool igImTriangleIsClockwise(const ImVec2 a,const ImVec2 b,const ImVec2 c) { return ImTriangleIsClockwise(a,b,c); } CIMGUI_API ImVec1* ImVec1_ImVec1_Nil(void) { return IM_NEW(ImVec1)(); } CIMGUI_API void ImVec1_destroy(ImVec1* self) { IM_DELETE(self); } CIMGUI_API ImVec1* ImVec1_ImVec1_Float(float _x) { return IM_NEW(ImVec1)(_x); } CIMGUI_API ImVec2i* ImVec2i_ImVec2i_Nil(void) { return IM_NEW(ImVec2i)(); } CIMGUI_API void ImVec2i_destroy(ImVec2i* self) { IM_DELETE(self); } CIMGUI_API ImVec2i* ImVec2i_ImVec2i_Int(int _x,int _y) { return IM_NEW(ImVec2i)(_x,_y); } CIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_Nil(void) { return IM_NEW(ImVec2ih)(); } CIMGUI_API void ImVec2ih_destroy(ImVec2ih* self) { IM_DELETE(self); } CIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_short(short _x,short _y) { return IM_NEW(ImVec2ih)(_x,_y); } CIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_Vec2(const ImVec2 rhs) { return IM_NEW(ImVec2ih)(rhs); } CIMGUI_API ImRect* ImRect_ImRect_Nil(void) { return IM_NEW(ImRect)(); } CIMGUI_API void ImRect_destroy(ImRect* self) { IM_DELETE(self); } CIMGUI_API ImRect* ImRect_ImRect_Vec2(const ImVec2 min,const ImVec2 max) { return IM_NEW(ImRect)(min,max); } CIMGUI_API ImRect* ImRect_ImRect_Vec4(const ImVec4 v) { return IM_NEW(ImRect)(v); } CIMGUI_API ImRect* ImRect_ImRect_Float(float x1,float y1,float x2,float y2) { return IM_NEW(ImRect)(x1,y1,x2,y2); } CIMGUI_API void ImRect_GetCenter(ImVec2 *pOut,ImRect* self) { *pOut = self->GetCenter(); } CIMGUI_API void ImRect_GetSize(ImVec2 *pOut,ImRect* self) { *pOut = self->GetSize(); } CIMGUI_API float ImRect_GetWidth(ImRect* self) { return self->GetWidth(); } CIMGUI_API float ImRect_GetHeight(ImRect* self) { return self->GetHeight(); } CIMGUI_API float ImRect_GetArea(ImRect* self) { return self->GetArea(); } CIMGUI_API void ImRect_GetTL(ImVec2 *pOut,ImRect* self) { *pOut = self->GetTL(); } CIMGUI_API void ImRect_GetTR(ImVec2 *pOut,ImRect* self) { *pOut = self->GetTR(); } CIMGUI_API void ImRect_GetBL(ImVec2 *pOut,ImRect* self) { *pOut = self->GetBL(); } CIMGUI_API void ImRect_GetBR(ImVec2 *pOut,ImRect* self) { *pOut = self->GetBR(); } CIMGUI_API bool ImRect_Contains_Vec2(ImRect* self,const ImVec2 p) { return self->Contains(p); } CIMGUI_API bool ImRect_Contains_Rect(ImRect* self,const ImRect r) { return self->Contains(r); } CIMGUI_API bool ImRect_ContainsWithPad(ImRect* self,const ImVec2 p,const ImVec2 pad) { return self->ContainsWithPad(p,pad); } CIMGUI_API bool ImRect_Overlaps(ImRect* self,const ImRect r) { return self->Overlaps(r); } CIMGUI_API void ImRect_Add_Vec2(ImRect* self,const ImVec2 p) { return self->Add(p); } CIMGUI_API void ImRect_Add_Rect(ImRect* self,const ImRect r) { return self->Add(r); } CIMGUI_API void ImRect_Expand_Float(ImRect* self,const float amount) { return self->Expand(amount); } CIMGUI_API void ImRect_Expand_Vec2(ImRect* self,const ImVec2 amount) { return self->Expand(amount); } CIMGUI_API void ImRect_Translate(ImRect* self,const ImVec2 d) { return self->Translate(d); } CIMGUI_API void ImRect_TranslateX(ImRect* self,float dx) { return self->TranslateX(dx); } CIMGUI_API void ImRect_TranslateY(ImRect* self,float dy) { return self->TranslateY(dy); } CIMGUI_API void ImRect_ClipWith(ImRect* self,const ImRect r) { return self->ClipWith(r); } CIMGUI_API void ImRect_ClipWithFull(ImRect* self,const ImRect r) { return self->ClipWithFull(r); } CIMGUI_API bool ImRect_IsInverted(ImRect* self) { return self->IsInverted(); } CIMGUI_API void ImRect_ToVec4(ImVec4 *pOut,ImRect* self) { *pOut = self->ToVec4(); } CIMGUI_API size_t igImBitArrayGetStorageSizeInBytes(int bitcount) { return ImBitArrayGetStorageSizeInBytes(bitcount); } CIMGUI_API void igImBitArrayClearAllBits(ImU32* arr,int bitcount) { return ImBitArrayClearAllBits(arr,bitcount); } CIMGUI_API bool igImBitArrayTestBit(const ImU32* arr,int n) { return ImBitArrayTestBit(arr,n); } CIMGUI_API void igImBitArrayClearBit(ImU32* arr,int n) { return ImBitArrayClearBit(arr,n); } CIMGUI_API void igImBitArraySetBit(ImU32* arr,int n) { return ImBitArraySetBit(arr,n); } CIMGUI_API void igImBitArraySetBitRange(ImU32* arr,int n,int n2) { return ImBitArraySetBitRange(arr,n,n2); } CIMGUI_API void ImBitVector_Create(ImBitVector* self,int sz) { return self->Create(sz); } CIMGUI_API void ImBitVector_Clear(ImBitVector* self) { return self->Clear(); } CIMGUI_API bool ImBitVector_TestBit(ImBitVector* self,int n) { return self->TestBit(n); } CIMGUI_API void ImBitVector_SetBit(ImBitVector* self,int n) { return self->SetBit(n); } CIMGUI_API void ImBitVector_ClearBit(ImBitVector* self,int n) { return self->ClearBit(n); } CIMGUI_API void ImGuiTextIndex_clear(ImGuiTextIndex* self) { return self->clear(); } CIMGUI_API int ImGuiTextIndex_size(ImGuiTextIndex* self) { return self->size(); } CIMGUI_API const char* ImGuiTextIndex_get_line_begin(ImGuiTextIndex* self,const char* base,int n) { return self->get_line_begin(base,n); } CIMGUI_API const char* ImGuiTextIndex_get_line_end(ImGuiTextIndex* self,const char* base,int n) { return self->get_line_end(base,n); } CIMGUI_API void ImGuiTextIndex_append(ImGuiTextIndex* self,const char* base,int old_size,int new_size) { return self->append(base,old_size,new_size); } CIMGUI_API ImGuiStoragePair* igImLowerBound(ImGuiStoragePair* in_begin,ImGuiStoragePair* in_end,ImGuiID key) { return ImLowerBound(in_begin,in_end,key); } CIMGUI_API ImDrawListSharedData* ImDrawListSharedData_ImDrawListSharedData(void) { return IM_NEW(ImDrawListSharedData)(); } CIMGUI_API void ImDrawListSharedData_destroy(ImDrawListSharedData* self) { IM_DELETE(self); } CIMGUI_API void ImDrawListSharedData_SetCircleTessellationMaxError(ImDrawListSharedData* self,float max_error) { return self->SetCircleTessellationMaxError(max_error); } CIMGUI_API ImDrawDataBuilder* ImDrawDataBuilder_ImDrawDataBuilder(void) { return IM_NEW(ImDrawDataBuilder)(); } CIMGUI_API void ImDrawDataBuilder_destroy(ImDrawDataBuilder* self) { IM_DELETE(self); } CIMGUI_API void* ImGuiStyleVarInfo_GetVarPtr(ImGuiStyleVarInfo* self,void* parent) { return self->GetVarPtr(parent); } CIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Int(ImGuiStyleVar idx,int v) { return IM_NEW(ImGuiStyleMod)(idx,v); } CIMGUI_API void ImGuiStyleMod_destroy(ImGuiStyleMod* self) { IM_DELETE(self); } CIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Float(ImGuiStyleVar idx,float v) { return IM_NEW(ImGuiStyleMod)(idx,v); } CIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Vec2(ImGuiStyleVar idx,ImVec2 v) { return IM_NEW(ImGuiStyleMod)(idx,v); } CIMGUI_API ImGuiComboPreviewData* ImGuiComboPreviewData_ImGuiComboPreviewData(void) { return IM_NEW(ImGuiComboPreviewData)(); } CIMGUI_API void ImGuiComboPreviewData_destroy(ImGuiComboPreviewData* self) { IM_DELETE(self); } CIMGUI_API ImGuiMenuColumns* ImGuiMenuColumns_ImGuiMenuColumns(void) { return IM_NEW(ImGuiMenuColumns)(); } CIMGUI_API void ImGuiMenuColumns_destroy(ImGuiMenuColumns* self) { IM_DELETE(self); } CIMGUI_API void ImGuiMenuColumns_Update(ImGuiMenuColumns* self,float spacing,bool window_reappearing) { return self->Update(spacing,window_reappearing); } CIMGUI_API float ImGuiMenuColumns_DeclColumns(ImGuiMenuColumns* self,float w_icon,float w_label,float w_shortcut,float w_mark) { return self->DeclColumns(w_icon,w_label,w_shortcut,w_mark); } CIMGUI_API void ImGuiMenuColumns_CalcNextTotalWidth(ImGuiMenuColumns* self,bool update_offsets) { return self->CalcNextTotalWidth(update_offsets); } CIMGUI_API ImGuiInputTextDeactivatedState* ImGuiInputTextDeactivatedState_ImGuiInputTextDeactivatedState(void) { return IM_NEW(ImGuiInputTextDeactivatedState)(); } CIMGUI_API void ImGuiInputTextDeactivatedState_destroy(ImGuiInputTextDeactivatedState* self) { IM_DELETE(self); } CIMGUI_API void ImGuiInputTextDeactivatedState_ClearFreeMemory(ImGuiInputTextDeactivatedState* self) { return self->ClearFreeMemory(); } CIMGUI_API ImGuiInputTextState* ImGuiInputTextState_ImGuiInputTextState(void) { return IM_NEW(ImGuiInputTextState)(); } CIMGUI_API void ImGuiInputTextState_destroy(ImGuiInputTextState* self) { IM_DELETE(self); } CIMGUI_API void ImGuiInputTextState_ClearText(ImGuiInputTextState* self) { return self->ClearText(); } CIMGUI_API void ImGuiInputTextState_ClearFreeMemory(ImGuiInputTextState* self) { return self->ClearFreeMemory(); } CIMGUI_API void ImGuiInputTextState_OnKeyPressed(ImGuiInputTextState* self,int key) { return self->OnKeyPressed(key); } CIMGUI_API void ImGuiInputTextState_OnCharPressed(ImGuiInputTextState* self,unsigned int c) { return self->OnCharPressed(c); } CIMGUI_API void ImGuiInputTextState_CursorAnimReset(ImGuiInputTextState* self) { return self->CursorAnimReset(); } CIMGUI_API void ImGuiInputTextState_CursorClamp(ImGuiInputTextState* self) { return self->CursorClamp(); } CIMGUI_API bool ImGuiInputTextState_HasSelection(ImGuiInputTextState* self) { return self->HasSelection(); } CIMGUI_API void ImGuiInputTextState_ClearSelection(ImGuiInputTextState* self) { return self->ClearSelection(); } CIMGUI_API int ImGuiInputTextState_GetCursorPos(ImGuiInputTextState* self) { return self->GetCursorPos(); } CIMGUI_API int ImGuiInputTextState_GetSelectionStart(ImGuiInputTextState* self) { return self->GetSelectionStart(); } CIMGUI_API int ImGuiInputTextState_GetSelectionEnd(ImGuiInputTextState* self) { return self->GetSelectionEnd(); } CIMGUI_API void ImGuiInputTextState_SelectAll(ImGuiInputTextState* self) { return self->SelectAll(); } CIMGUI_API void ImGuiInputTextState_ReloadUserBufAndSelectAll(ImGuiInputTextState* self) { return self->ReloadUserBufAndSelectAll(); } CIMGUI_API void ImGuiInputTextState_ReloadUserBufAndKeepSelection(ImGuiInputTextState* self) { return self->ReloadUserBufAndKeepSelection(); } CIMGUI_API void ImGuiInputTextState_ReloadUserBufAndMoveToEnd(ImGuiInputTextState* self) { return self->ReloadUserBufAndMoveToEnd(); } CIMGUI_API ImGuiNextWindowData* ImGuiNextWindowData_ImGuiNextWindowData(void) { return IM_NEW(ImGuiNextWindowData)(); } CIMGUI_API void ImGuiNextWindowData_destroy(ImGuiNextWindowData* self) { IM_DELETE(self); } CIMGUI_API void ImGuiNextWindowData_ClearFlags(ImGuiNextWindowData* self) { return self->ClearFlags(); } CIMGUI_API ImGuiNextItemData* ImGuiNextItemData_ImGuiNextItemData(void) { return IM_NEW(ImGuiNextItemData)(); } CIMGUI_API void ImGuiNextItemData_destroy(ImGuiNextItemData* self) { IM_DELETE(self); } CIMGUI_API void ImGuiNextItemData_ClearFlags(ImGuiNextItemData* self) { return self->ClearFlags(); } CIMGUI_API ImGuiLastItemData* ImGuiLastItemData_ImGuiLastItemData(void) { return IM_NEW(ImGuiLastItemData)(); } CIMGUI_API void ImGuiLastItemData_destroy(ImGuiLastItemData* self) { IM_DELETE(self); } CIMGUI_API ImGuiErrorRecoveryState* ImGuiErrorRecoveryState_ImGuiErrorRecoveryState(void) { return IM_NEW(ImGuiErrorRecoveryState)(); } CIMGUI_API void ImGuiErrorRecoveryState_destroy(ImGuiErrorRecoveryState* self) { IM_DELETE(self); } CIMGUI_API ImGuiPtrOrIndex* ImGuiPtrOrIndex_ImGuiPtrOrIndex_Ptr(void* ptr) { return IM_NEW(ImGuiPtrOrIndex)(ptr); } CIMGUI_API void ImGuiPtrOrIndex_destroy(ImGuiPtrOrIndex* self) { IM_DELETE(self); } CIMGUI_API ImGuiPtrOrIndex* ImGuiPtrOrIndex_ImGuiPtrOrIndex_Int(int index) { return IM_NEW(ImGuiPtrOrIndex)(index); } CIMGUI_API ImGuiPopupData* ImGuiPopupData_ImGuiPopupData(void) { return IM_NEW(ImGuiPopupData)(); } CIMGUI_API void ImGuiPopupData_destroy(ImGuiPopupData* self) { IM_DELETE(self); } CIMGUI_API ImGuiInputEvent* ImGuiInputEvent_ImGuiInputEvent(void) { return IM_NEW(ImGuiInputEvent)(); } CIMGUI_API void ImGuiInputEvent_destroy(ImGuiInputEvent* self) { IM_DELETE(self); } CIMGUI_API ImGuiKeyRoutingData* ImGuiKeyRoutingData_ImGuiKeyRoutingData(void) { return IM_NEW(ImGuiKeyRoutingData)(); } CIMGUI_API void ImGuiKeyRoutingData_destroy(ImGuiKeyRoutingData* self) { IM_DELETE(self); } CIMGUI_API ImGuiKeyRoutingTable* ImGuiKeyRoutingTable_ImGuiKeyRoutingTable(void) { return IM_NEW(ImGuiKeyRoutingTable)(); } CIMGUI_API void ImGuiKeyRoutingTable_destroy(ImGuiKeyRoutingTable* self) { IM_DELETE(self); } CIMGUI_API void ImGuiKeyRoutingTable_Clear(ImGuiKeyRoutingTable* self) { return self->Clear(); } CIMGUI_API ImGuiKeyOwnerData* ImGuiKeyOwnerData_ImGuiKeyOwnerData(void) { return IM_NEW(ImGuiKeyOwnerData)(); } CIMGUI_API void ImGuiKeyOwnerData_destroy(ImGuiKeyOwnerData* self) { IM_DELETE(self); } CIMGUI_API ImGuiListClipperRange ImGuiListClipperRange_FromIndices(int min,int max) { return ImGuiListClipperRange::FromIndices(min,max); } CIMGUI_API ImGuiListClipperRange ImGuiListClipperRange_FromPositions(float y1,float y2,int off_min,int off_max) { return ImGuiListClipperRange::FromPositions(y1,y2,off_min,off_max); } CIMGUI_API ImGuiListClipperData* ImGuiListClipperData_ImGuiListClipperData(void) { return IM_NEW(ImGuiListClipperData)(); } CIMGUI_API void ImGuiListClipperData_destroy(ImGuiListClipperData* self) { IM_DELETE(self); } CIMGUI_API void ImGuiListClipperData_Reset(ImGuiListClipperData* self,ImGuiListClipper* clipper) { return self->Reset(clipper); } CIMGUI_API ImGuiNavItemData* ImGuiNavItemData_ImGuiNavItemData(void) { return IM_NEW(ImGuiNavItemData)(); } CIMGUI_API void ImGuiNavItemData_destroy(ImGuiNavItemData* self) { IM_DELETE(self); } CIMGUI_API void ImGuiNavItemData_Clear(ImGuiNavItemData* self) { return self->Clear(); } CIMGUI_API ImGuiTypingSelectState* ImGuiTypingSelectState_ImGuiTypingSelectState(void) { return IM_NEW(ImGuiTypingSelectState)(); } CIMGUI_API void ImGuiTypingSelectState_destroy(ImGuiTypingSelectState* self) { IM_DELETE(self); } CIMGUI_API void ImGuiTypingSelectState_Clear(ImGuiTypingSelectState* self) { return self->Clear(); } CIMGUI_API ImGuiOldColumnData* ImGuiOldColumnData_ImGuiOldColumnData(void) { return IM_NEW(ImGuiOldColumnData)(); } CIMGUI_API void ImGuiOldColumnData_destroy(ImGuiOldColumnData* self) { IM_DELETE(self); } CIMGUI_API ImGuiOldColumns* ImGuiOldColumns_ImGuiOldColumns(void) { return IM_NEW(ImGuiOldColumns)(); } CIMGUI_API void ImGuiOldColumns_destroy(ImGuiOldColumns* self) { IM_DELETE(self); } CIMGUI_API ImGuiBoxSelectState* ImGuiBoxSelectState_ImGuiBoxSelectState(void) { return IM_NEW(ImGuiBoxSelectState)(); } CIMGUI_API void ImGuiBoxSelectState_destroy(ImGuiBoxSelectState* self) { IM_DELETE(self); } CIMGUI_API ImGuiMultiSelectTempData* ImGuiMultiSelectTempData_ImGuiMultiSelectTempData(void) { return IM_NEW(ImGuiMultiSelectTempData)(); } CIMGUI_API void ImGuiMultiSelectTempData_destroy(ImGuiMultiSelectTempData* self) { IM_DELETE(self); } CIMGUI_API void ImGuiMultiSelectTempData_Clear(ImGuiMultiSelectTempData* self) { return self->Clear(); } CIMGUI_API void ImGuiMultiSelectTempData_ClearIO(ImGuiMultiSelectTempData* self) { return self->ClearIO(); } CIMGUI_API ImGuiMultiSelectState* ImGuiMultiSelectState_ImGuiMultiSelectState(void) { return IM_NEW(ImGuiMultiSelectState)(); } CIMGUI_API void ImGuiMultiSelectState_destroy(ImGuiMultiSelectState* self) { IM_DELETE(self); } CIMGUI_API ImGuiDockNode* ImGuiDockNode_ImGuiDockNode(ImGuiID id) { return IM_NEW(ImGuiDockNode)(id); } CIMGUI_API void ImGuiDockNode_destroy(ImGuiDockNode* self) { IM_DELETE(self); } CIMGUI_API bool ImGuiDockNode_IsRootNode(ImGuiDockNode* self) { return self->IsRootNode(); } CIMGUI_API bool ImGuiDockNode_IsDockSpace(ImGuiDockNode* self) { return self->IsDockSpace(); } CIMGUI_API bool ImGuiDockNode_IsFloatingNode(ImGuiDockNode* self) { return self->IsFloatingNode(); } CIMGUI_API bool ImGuiDockNode_IsCentralNode(ImGuiDockNode* self) { return self->IsCentralNode(); } CIMGUI_API bool ImGuiDockNode_IsHiddenTabBar(ImGuiDockNode* self) { return self->IsHiddenTabBar(); } CIMGUI_API bool ImGuiDockNode_IsNoTabBar(ImGuiDockNode* self) { return self->IsNoTabBar(); } CIMGUI_API bool ImGuiDockNode_IsSplitNode(ImGuiDockNode* self) { return self->IsSplitNode(); } CIMGUI_API bool ImGuiDockNode_IsLeafNode(ImGuiDockNode* self) { return self->IsLeafNode(); } CIMGUI_API bool ImGuiDockNode_IsEmpty(ImGuiDockNode* self) { return self->IsEmpty(); } CIMGUI_API void ImGuiDockNode_Rect(ImRect *pOut,ImGuiDockNode* self) { *pOut = self->Rect(); } CIMGUI_API void ImGuiDockNode_SetLocalFlags(ImGuiDockNode* self,ImGuiDockNodeFlags flags) { return self->SetLocalFlags(flags); } CIMGUI_API void ImGuiDockNode_UpdateMergedFlags(ImGuiDockNode* self) { return self->UpdateMergedFlags(); } CIMGUI_API ImGuiDockContext* ImGuiDockContext_ImGuiDockContext(void) { return IM_NEW(ImGuiDockContext)(); } CIMGUI_API void ImGuiDockContext_destroy(ImGuiDockContext* self) { IM_DELETE(self); } CIMGUI_API ImGuiViewportP* ImGuiViewportP_ImGuiViewportP(void) { return IM_NEW(ImGuiViewportP)(); } CIMGUI_API void ImGuiViewportP_destroy(ImGuiViewportP* self) { IM_DELETE(self); } CIMGUI_API void ImGuiViewportP_ClearRequestFlags(ImGuiViewportP* self) { return self->ClearRequestFlags(); } CIMGUI_API void ImGuiViewportP_CalcWorkRectPos(ImVec2 *pOut,ImGuiViewportP* self,const ImVec2 inset_min) { *pOut = self->CalcWorkRectPos(inset_min); } CIMGUI_API void ImGuiViewportP_CalcWorkRectSize(ImVec2 *pOut,ImGuiViewportP* self,const ImVec2 inset_min,const ImVec2 inset_max) { *pOut = self->CalcWorkRectSize(inset_min,inset_max); } CIMGUI_API void ImGuiViewportP_UpdateWorkRect(ImGuiViewportP* self) { return self->UpdateWorkRect(); } CIMGUI_API void ImGuiViewportP_GetMainRect(ImRect *pOut,ImGuiViewportP* self) { *pOut = self->GetMainRect(); } CIMGUI_API void ImGuiViewportP_GetWorkRect(ImRect *pOut,ImGuiViewportP* self) { *pOut = self->GetWorkRect(); } CIMGUI_API void ImGuiViewportP_GetBuildWorkRect(ImRect *pOut,ImGuiViewportP* self) { *pOut = self->GetBuildWorkRect(); } CIMGUI_API ImGuiWindowSettings* ImGuiWindowSettings_ImGuiWindowSettings(void) { return IM_NEW(ImGuiWindowSettings)(); } CIMGUI_API void ImGuiWindowSettings_destroy(ImGuiWindowSettings* self) { IM_DELETE(self); } CIMGUI_API char* ImGuiWindowSettings_GetName(ImGuiWindowSettings* self) { return self->GetName(); } CIMGUI_API ImGuiSettingsHandler* ImGuiSettingsHandler_ImGuiSettingsHandler(void) { return IM_NEW(ImGuiSettingsHandler)(); } CIMGUI_API void ImGuiSettingsHandler_destroy(ImGuiSettingsHandler* self) { IM_DELETE(self); } CIMGUI_API ImGuiDebugAllocInfo* ImGuiDebugAllocInfo_ImGuiDebugAllocInfo(void) { return IM_NEW(ImGuiDebugAllocInfo)(); } CIMGUI_API void ImGuiDebugAllocInfo_destroy(ImGuiDebugAllocInfo* self) { IM_DELETE(self); } CIMGUI_API ImGuiStackLevelInfo* ImGuiStackLevelInfo_ImGuiStackLevelInfo(void) { return IM_NEW(ImGuiStackLevelInfo)(); } CIMGUI_API void ImGuiStackLevelInfo_destroy(ImGuiStackLevelInfo* self) { IM_DELETE(self); } CIMGUI_API ImGuiIDStackTool* ImGuiIDStackTool_ImGuiIDStackTool(void) { return IM_NEW(ImGuiIDStackTool)(); } CIMGUI_API void ImGuiIDStackTool_destroy(ImGuiIDStackTool* self) { IM_DELETE(self); } CIMGUI_API ImGuiContextHook* ImGuiContextHook_ImGuiContextHook(void) { return IM_NEW(ImGuiContextHook)(); } CIMGUI_API void ImGuiContextHook_destroy(ImGuiContextHook* self) { IM_DELETE(self); } CIMGUI_API ImGuiContext* ImGuiContext_ImGuiContext(ImFontAtlas* shared_font_atlas) { return IM_NEW(ImGuiContext)(shared_font_atlas); } CIMGUI_API void ImGuiContext_destroy(ImGuiContext* self) { IM_DELETE(self); } CIMGUI_API ImGuiWindow* ImGuiWindow_ImGuiWindow(ImGuiContext* context,const char* name) { return IM_NEW(ImGuiWindow)(context,name); } CIMGUI_API void ImGuiWindow_destroy(ImGuiWindow* self) { IM_DELETE(self); } CIMGUI_API ImGuiID ImGuiWindow_GetID_Str(ImGuiWindow* self,const char* str,const char* str_end) { return self->GetID(str,str_end); } CIMGUI_API ImGuiID ImGuiWindow_GetID_Ptr(ImGuiWindow* self,const void* ptr) { return self->GetID(ptr); } CIMGUI_API ImGuiID ImGuiWindow_GetID_Int(ImGuiWindow* self,int n) { return self->GetID(n); } CIMGUI_API ImGuiID ImGuiWindow_GetIDFromPos(ImGuiWindow* self,const ImVec2 p_abs) { return self->GetIDFromPos(p_abs); } CIMGUI_API ImGuiID ImGuiWindow_GetIDFromRectangle(ImGuiWindow* self,const ImRect r_abs) { return self->GetIDFromRectangle(r_abs); } CIMGUI_API void ImGuiWindow_Rect(ImRect *pOut,ImGuiWindow* self) { *pOut = self->Rect(); } CIMGUI_API void ImGuiWindow_TitleBarRect(ImRect *pOut,ImGuiWindow* self) { *pOut = self->TitleBarRect(); } CIMGUI_API void ImGuiWindow_MenuBarRect(ImRect *pOut,ImGuiWindow* self) { *pOut = self->MenuBarRect(); } CIMGUI_API ImGuiTabItem* ImGuiTabItem_ImGuiTabItem(void) { return IM_NEW(ImGuiTabItem)(); } CIMGUI_API void ImGuiTabItem_destroy(ImGuiTabItem* self) { IM_DELETE(self); } CIMGUI_API ImGuiTabBar* ImGuiTabBar_ImGuiTabBar(void) { return IM_NEW(ImGuiTabBar)(); } CIMGUI_API void ImGuiTabBar_destroy(ImGuiTabBar* self) { IM_DELETE(self); } CIMGUI_API ImGuiTableColumn* ImGuiTableColumn_ImGuiTableColumn(void) { return IM_NEW(ImGuiTableColumn)(); } CIMGUI_API void ImGuiTableColumn_destroy(ImGuiTableColumn* self) { IM_DELETE(self); } CIMGUI_API ImGuiTableInstanceData* ImGuiTableInstanceData_ImGuiTableInstanceData(void) { return IM_NEW(ImGuiTableInstanceData)(); } CIMGUI_API void ImGuiTableInstanceData_destroy(ImGuiTableInstanceData* self) { IM_DELETE(self); } CIMGUI_API ImGuiTable* ImGuiTable_ImGuiTable(void) { return IM_NEW(ImGuiTable)(); } CIMGUI_API void ImGuiTable_destroy(ImGuiTable* self) { IM_DELETE(self); } CIMGUI_API ImGuiTableTempData* ImGuiTableTempData_ImGuiTableTempData(void) { return IM_NEW(ImGuiTableTempData)(); } CIMGUI_API void ImGuiTableTempData_destroy(ImGuiTableTempData* self) { IM_DELETE(self); } CIMGUI_API ImGuiTableColumnSettings* ImGuiTableColumnSettings_ImGuiTableColumnSettings(void) { return IM_NEW(ImGuiTableColumnSettings)(); } CIMGUI_API void ImGuiTableColumnSettings_destroy(ImGuiTableColumnSettings* self) { IM_DELETE(self); } CIMGUI_API ImGuiTableSettings* ImGuiTableSettings_ImGuiTableSettings(void) { return IM_NEW(ImGuiTableSettings)(); } CIMGUI_API void ImGuiTableSettings_destroy(ImGuiTableSettings* self) { IM_DELETE(self); } CIMGUI_API ImGuiTableColumnSettings* ImGuiTableSettings_GetColumnSettings(ImGuiTableSettings* self) { return self->GetColumnSettings(); } CIMGUI_API ImGuiIO* igGetIO_ContextPtr(ImGuiContext* ctx) { return &ImGui::GetIO(ctx); } CIMGUI_API ImGuiPlatformIO* igGetPlatformIO_ContextPtr(ImGuiContext* ctx) { return &ImGui::GetPlatformIO(ctx); } CIMGUI_API ImGuiWindow* igGetCurrentWindowRead() { return ImGui::GetCurrentWindowRead(); } CIMGUI_API ImGuiWindow* igGetCurrentWindow() { return ImGui::GetCurrentWindow(); } CIMGUI_API ImGuiWindow* igFindWindowByID(ImGuiID id) { return ImGui::FindWindowByID(id); } CIMGUI_API ImGuiWindow* igFindWindowByName(const char* name) { return ImGui::FindWindowByName(name); } CIMGUI_API void igUpdateWindowParentAndRootLinks(ImGuiWindow* window,ImGuiWindowFlags flags,ImGuiWindow* parent_window) { return ImGui::UpdateWindowParentAndRootLinks(window,flags,parent_window); } CIMGUI_API void igUpdateWindowSkipRefresh(ImGuiWindow* window) { return ImGui::UpdateWindowSkipRefresh(window); } CIMGUI_API void igCalcWindowNextAutoFitSize(ImVec2 *pOut,ImGuiWindow* window) { *pOut = ImGui::CalcWindowNextAutoFitSize(window); } CIMGUI_API bool igIsWindowChildOf(ImGuiWindow* window,ImGuiWindow* potential_parent,bool popup_hierarchy,bool dock_hierarchy) { return ImGui::IsWindowChildOf(window,potential_parent,popup_hierarchy,dock_hierarchy); } CIMGUI_API bool igIsWindowWithinBeginStackOf(ImGuiWindow* window,ImGuiWindow* potential_parent) { return ImGui::IsWindowWithinBeginStackOf(window,potential_parent); } CIMGUI_API bool igIsWindowAbove(ImGuiWindow* potential_above,ImGuiWindow* potential_below) { return ImGui::IsWindowAbove(potential_above,potential_below); } CIMGUI_API bool igIsWindowNavFocusable(ImGuiWindow* window) { return ImGui::IsWindowNavFocusable(window); } CIMGUI_API void igSetWindowPos_WindowPtr(ImGuiWindow* window,const ImVec2 pos,ImGuiCond cond) { return ImGui::SetWindowPos(window,pos,cond); } CIMGUI_API void igSetWindowSize_WindowPtr(ImGuiWindow* window,const ImVec2 size,ImGuiCond cond) { return ImGui::SetWindowSize(window,size,cond); } CIMGUI_API void igSetWindowCollapsed_WindowPtr(ImGuiWindow* window,bool collapsed,ImGuiCond cond) { return ImGui::SetWindowCollapsed(window,collapsed,cond); } CIMGUI_API void igSetWindowHitTestHole(ImGuiWindow* window,const ImVec2 pos,const ImVec2 size) { return ImGui::SetWindowHitTestHole(window,pos,size); } CIMGUI_API void igSetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window) { return ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(window); } CIMGUI_API void igSetWindowParentWindowForFocusRoute(ImGuiWindow* window,ImGuiWindow* parent_window) { return ImGui::SetWindowParentWindowForFocusRoute(window,parent_window); } CIMGUI_API void igWindowRectAbsToRel(ImRect *pOut,ImGuiWindow* window,const ImRect r) { *pOut = ImGui::WindowRectAbsToRel(window,r); } CIMGUI_API void igWindowRectRelToAbs(ImRect *pOut,ImGuiWindow* window,const ImRect r) { *pOut = ImGui::WindowRectRelToAbs(window,r); } CIMGUI_API void igWindowPosAbsToRel(ImVec2 *pOut,ImGuiWindow* window,const ImVec2 p) { *pOut = ImGui::WindowPosAbsToRel(window,p); } CIMGUI_API void igWindowPosRelToAbs(ImVec2 *pOut,ImGuiWindow* window,const ImVec2 p) { *pOut = ImGui::WindowPosRelToAbs(window,p); } CIMGUI_API void igFocusWindow(ImGuiWindow* window,ImGuiFocusRequestFlags flags) { return ImGui::FocusWindow(window,flags); } CIMGUI_API void igFocusTopMostWindowUnderOne(ImGuiWindow* under_this_window,ImGuiWindow* ignore_window,ImGuiViewport* filter_viewport,ImGuiFocusRequestFlags flags) { return ImGui::FocusTopMostWindowUnderOne(under_this_window,ignore_window,filter_viewport,flags); } CIMGUI_API void igBringWindowToFocusFront(ImGuiWindow* window) { return ImGui::BringWindowToFocusFront(window); } CIMGUI_API void igBringWindowToDisplayFront(ImGuiWindow* window) { return ImGui::BringWindowToDisplayFront(window); } CIMGUI_API void igBringWindowToDisplayBack(ImGuiWindow* window) { return ImGui::BringWindowToDisplayBack(window); } CIMGUI_API void igBringWindowToDisplayBehind(ImGuiWindow* window,ImGuiWindow* above_window) { return ImGui::BringWindowToDisplayBehind(window,above_window); } CIMGUI_API int igFindWindowDisplayIndex(ImGuiWindow* window) { return ImGui::FindWindowDisplayIndex(window); } CIMGUI_API ImGuiWindow* igFindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window) { return ImGui::FindBottomMostVisibleWindowWithinBeginStack(window); } CIMGUI_API void igSetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags) { return ImGui::SetNextWindowRefreshPolicy(flags); } CIMGUI_API void igRegisterUserTexture(ImTextureData* tex) { return ImGui::RegisterUserTexture(tex); } CIMGUI_API void igUnregisterUserTexture(ImTextureData* tex) { return ImGui::UnregisterUserTexture(tex); } CIMGUI_API void igRegisterFontAtlas(ImFontAtlas* atlas) { return ImGui::RegisterFontAtlas(atlas); } CIMGUI_API void igUnregisterFontAtlas(ImFontAtlas* atlas) { return ImGui::UnregisterFontAtlas(atlas); } CIMGUI_API void igSetCurrentFont(ImFont* font,float font_size_before_scaling,float font_size_after_scaling) { return ImGui::SetCurrentFont(font,font_size_before_scaling,font_size_after_scaling); } CIMGUI_API void igUpdateCurrentFontSize(float restore_font_size_after_scaling) { return ImGui::UpdateCurrentFontSize(restore_font_size_after_scaling); } CIMGUI_API void igSetFontRasterizerDensity(float rasterizer_density) { return ImGui::SetFontRasterizerDensity(rasterizer_density); } CIMGUI_API float igGetFontRasterizerDensity() { return ImGui::GetFontRasterizerDensity(); } CIMGUI_API float igGetRoundedFontSize(float size) { return ImGui::GetRoundedFontSize(size); } CIMGUI_API ImFont* igGetDefaultFont() { return ImGui::GetDefaultFont(); } CIMGUI_API void igPushPasswordFont() { return ImGui::PushPasswordFont(); } CIMGUI_API void igPopPasswordFont() { return ImGui::PopPasswordFont(); } CIMGUI_API ImDrawList* igGetForegroundDrawList_WindowPtr(ImGuiWindow* window) { return ImGui::GetForegroundDrawList(window); } CIMGUI_API void igAddDrawListToDrawDataEx(ImDrawData* draw_data,ImVector_ImDrawListPtr* out_list,ImDrawList* draw_list) { return ImGui::AddDrawListToDrawDataEx(draw_data,out_list,draw_list); } CIMGUI_API void igInitialize() { return ImGui::Initialize(); } CIMGUI_API void igShutdown() { return ImGui::Shutdown(); } CIMGUI_API void igUpdateInputEvents(bool trickle_fast_inputs) { return ImGui::UpdateInputEvents(trickle_fast_inputs); } CIMGUI_API void igUpdateHoveredWindowAndCaptureFlags(const ImVec2 mouse_pos) { return ImGui::UpdateHoveredWindowAndCaptureFlags(mouse_pos); } CIMGUI_API void igFindHoveredWindowEx(const ImVec2 pos,bool find_first_and_in_any_viewport,ImGuiWindow** out_hovered_window,ImGuiWindow** out_hovered_window_under_moving_window) { return ImGui::FindHoveredWindowEx(pos,find_first_and_in_any_viewport,out_hovered_window,out_hovered_window_under_moving_window); } CIMGUI_API void igStartMouseMovingWindow(ImGuiWindow* window) { return ImGui::StartMouseMovingWindow(window); } CIMGUI_API void igStartMouseMovingWindowOrNode(ImGuiWindow* window,ImGuiDockNode* node,bool undock) { return ImGui::StartMouseMovingWindowOrNode(window,node,undock); } CIMGUI_API void igUpdateMouseMovingWindowNewFrame() { return ImGui::UpdateMouseMovingWindowNewFrame(); } CIMGUI_API void igUpdateMouseMovingWindowEndFrame() { return ImGui::UpdateMouseMovingWindowEndFrame(); } CIMGUI_API ImGuiID igAddContextHook(ImGuiContext* context,const ImGuiContextHook* hook) { return ImGui::AddContextHook(context,hook); } CIMGUI_API void igRemoveContextHook(ImGuiContext* context,ImGuiID hook_to_remove) { return ImGui::RemoveContextHook(context,hook_to_remove); } CIMGUI_API void igCallContextHooks(ImGuiContext* context,ImGuiContextHookType type) { return ImGui::CallContextHooks(context,type); } CIMGUI_API void igTranslateWindowsInViewport(ImGuiViewportP* viewport,const ImVec2 old_pos,const ImVec2 new_pos,const ImVec2 old_size,const ImVec2 new_size) { return ImGui::TranslateWindowsInViewport(viewport,old_pos,new_pos,old_size,new_size); } CIMGUI_API void igScaleWindowsInViewport(ImGuiViewportP* viewport,float scale) { return ImGui::ScaleWindowsInViewport(viewport,scale); } CIMGUI_API void igDestroyPlatformWindow(ImGuiViewportP* viewport) { return ImGui::DestroyPlatformWindow(viewport); } CIMGUI_API void igSetWindowViewport(ImGuiWindow* window,ImGuiViewportP* viewport) { return ImGui::SetWindowViewport(window,viewport); } CIMGUI_API void igSetCurrentViewport(ImGuiWindow* window,ImGuiViewportP* viewport) { return ImGui::SetCurrentViewport(window,viewport); } CIMGUI_API const ImGuiPlatformMonitor* igGetViewportPlatformMonitor(ImGuiViewport* viewport) { return ImGui::GetViewportPlatformMonitor(viewport); } CIMGUI_API ImGuiViewportP* igFindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos) { return ImGui::FindHoveredViewportFromPlatformWindowStack(mouse_platform_pos); } CIMGUI_API void igMarkIniSettingsDirty_Nil() { return ImGui::MarkIniSettingsDirty(); } CIMGUI_API void igMarkIniSettingsDirty_WindowPtr(ImGuiWindow* window) { return ImGui::MarkIniSettingsDirty(window); } CIMGUI_API void igClearIniSettings() { return ImGui::ClearIniSettings(); } CIMGUI_API void igAddSettingsHandler(const ImGuiSettingsHandler* handler) { return ImGui::AddSettingsHandler(handler); } CIMGUI_API void igRemoveSettingsHandler(const char* type_name) { return ImGui::RemoveSettingsHandler(type_name); } CIMGUI_API ImGuiSettingsHandler* igFindSettingsHandler(const char* type_name) { return ImGui::FindSettingsHandler(type_name); } CIMGUI_API ImGuiWindowSettings* igCreateNewWindowSettings(const char* name) { return ImGui::CreateNewWindowSettings(name); } CIMGUI_API ImGuiWindowSettings* igFindWindowSettingsByID(ImGuiID id) { return ImGui::FindWindowSettingsByID(id); } CIMGUI_API ImGuiWindowSettings* igFindWindowSettingsByWindow(ImGuiWindow* window) { return ImGui::FindWindowSettingsByWindow(window); } CIMGUI_API void igClearWindowSettings(const char* name) { return ImGui::ClearWindowSettings(name); } CIMGUI_API void igLocalizeRegisterEntries(const ImGuiLocEntry* entries,int count) { return ImGui::LocalizeRegisterEntries(entries,count); } CIMGUI_API const char* igLocalizeGetMsg(ImGuiLocKey key) { return ImGui::LocalizeGetMsg(key); } CIMGUI_API void igSetScrollX_WindowPtr(ImGuiWindow* window,float scroll_x) { return ImGui::SetScrollX(window,scroll_x); } CIMGUI_API void igSetScrollY_WindowPtr(ImGuiWindow* window,float scroll_y) { return ImGui::SetScrollY(window,scroll_y); } CIMGUI_API void igSetScrollFromPosX_WindowPtr(ImGuiWindow* window,float local_x,float center_x_ratio) { return ImGui::SetScrollFromPosX(window,local_x,center_x_ratio); } CIMGUI_API void igSetScrollFromPosY_WindowPtr(ImGuiWindow* window,float local_y,float center_y_ratio) { return ImGui::SetScrollFromPosY(window,local_y,center_y_ratio); } CIMGUI_API void igScrollToItem(ImGuiScrollFlags flags) { return ImGui::ScrollToItem(flags); } CIMGUI_API void igScrollToRect(ImGuiWindow* window,const ImRect rect,ImGuiScrollFlags flags) { return ImGui::ScrollToRect(window,rect,flags); } CIMGUI_API void igScrollToRectEx(ImVec2 *pOut,ImGuiWindow* window,const ImRect rect,ImGuiScrollFlags flags) { *pOut = ImGui::ScrollToRectEx(window,rect,flags); } CIMGUI_API void igScrollToBringRectIntoView(ImGuiWindow* window,const ImRect rect) { return ImGui::ScrollToBringRectIntoView(window,rect); } CIMGUI_API ImGuiItemStatusFlags igGetItemStatusFlags() { return ImGui::GetItemStatusFlags(); } CIMGUI_API ImGuiItemFlags igGetItemFlags() { return ImGui::GetItemFlags(); } CIMGUI_API ImGuiID igGetActiveID() { return ImGui::GetActiveID(); } CIMGUI_API ImGuiID igGetFocusID() { return ImGui::GetFocusID(); } CIMGUI_API void igSetActiveID(ImGuiID id,ImGuiWindow* window) { return ImGui::SetActiveID(id,window); } CIMGUI_API void igSetFocusID(ImGuiID id,ImGuiWindow* window) { return ImGui::SetFocusID(id,window); } CIMGUI_API void igClearActiveID() { return ImGui::ClearActiveID(); } CIMGUI_API ImGuiID igGetHoveredID() { return ImGui::GetHoveredID(); } CIMGUI_API void igSetHoveredID(ImGuiID id) { return ImGui::SetHoveredID(id); } CIMGUI_API void igKeepAliveID(ImGuiID id) { return ImGui::KeepAliveID(id); } CIMGUI_API void igMarkItemEdited(ImGuiID id) { return ImGui::MarkItemEdited(id); } CIMGUI_API void igPushOverrideID(ImGuiID id) { return ImGui::PushOverrideID(id); } CIMGUI_API ImGuiID igGetIDWithSeed_Str(const char* str_id_begin,const char* str_id_end,ImGuiID seed) { return ImGui::GetIDWithSeed(str_id_begin,str_id_end,seed); } CIMGUI_API ImGuiID igGetIDWithSeed_Int(int n,ImGuiID seed) { return ImGui::GetIDWithSeed(n,seed); } CIMGUI_API void igItemSize_Vec2(const ImVec2 size,float text_baseline_y) { return ImGui::ItemSize(size,text_baseline_y); } CIMGUI_API void igItemSize_Rect(const ImRect bb,float text_baseline_y) { return ImGui::ItemSize(bb,text_baseline_y); } CIMGUI_API bool igItemAdd(const ImRect bb,ImGuiID id,const ImRect* nav_bb,ImGuiItemFlags extra_flags) { return ImGui::ItemAdd(bb,id,nav_bb,extra_flags); } CIMGUI_API bool igItemHoverable(const ImRect bb,ImGuiID id,ImGuiItemFlags item_flags) { return ImGui::ItemHoverable(bb,id,item_flags); } CIMGUI_API bool igIsWindowContentHoverable(ImGuiWindow* window,ImGuiHoveredFlags flags) { return ImGui::IsWindowContentHoverable(window,flags); } CIMGUI_API bool igIsClippedEx(const ImRect bb,ImGuiID id) { return ImGui::IsClippedEx(bb,id); } CIMGUI_API void igSetLastItemData(ImGuiID item_id,ImGuiItemFlags item_flags,ImGuiItemStatusFlags status_flags,const ImRect item_rect) { return ImGui::SetLastItemData(item_id,item_flags,status_flags,item_rect); } CIMGUI_API void igCalcItemSize(ImVec2 *pOut,ImVec2 size,float default_w,float default_h) { *pOut = ImGui::CalcItemSize(size,default_w,default_h); } CIMGUI_API float igCalcWrapWidthForPos(const ImVec2 pos,float wrap_pos_x) { return ImGui::CalcWrapWidthForPos(pos,wrap_pos_x); } CIMGUI_API void igPushMultiItemsWidths(int components,float width_full) { return ImGui::PushMultiItemsWidths(components,width_full); } CIMGUI_API void igShrinkWidths(ImGuiShrinkWidthItem* items,int count,float width_excess,float width_min) { return ImGui::ShrinkWidths(items,count,width_excess,width_min); } CIMGUI_API const ImGuiStyleVarInfo* igGetStyleVarInfo(ImGuiStyleVar idx) { return ImGui::GetStyleVarInfo(idx); } CIMGUI_API void igBeginDisabledOverrideReenable() { return ImGui::BeginDisabledOverrideReenable(); } CIMGUI_API void igEndDisabledOverrideReenable() { return ImGui::EndDisabledOverrideReenable(); } CIMGUI_API void igLogBegin(ImGuiLogFlags flags,int auto_open_depth) { return ImGui::LogBegin(flags,auto_open_depth); } CIMGUI_API void igLogToBuffer(int auto_open_depth) { return ImGui::LogToBuffer(auto_open_depth); } CIMGUI_API void igLogRenderedText(const ImVec2* ref_pos,const char* text,const char* text_end) { return ImGui::LogRenderedText(ref_pos,text,text_end); } CIMGUI_API void igLogSetNextTextDecoration(const char* prefix,const char* suffix) { return ImGui::LogSetNextTextDecoration(prefix,suffix); } CIMGUI_API bool igBeginChildEx(const char* name,ImGuiID id,const ImVec2 size_arg,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags) { return ImGui::BeginChildEx(name,id,size_arg,child_flags,window_flags); } CIMGUI_API bool igBeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_window_flags) { return ImGui::BeginPopupEx(id,extra_window_flags); } CIMGUI_API bool igBeginPopupMenuEx(ImGuiID id,const char* label,ImGuiWindowFlags extra_window_flags) { return ImGui::BeginPopupMenuEx(id,label,extra_window_flags); } CIMGUI_API void igOpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags) { return ImGui::OpenPopupEx(id,popup_flags); } CIMGUI_API void igClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup) { return ImGui::ClosePopupToLevel(remaining,restore_focus_to_window_under_popup); } CIMGUI_API void igClosePopupsOverWindow(ImGuiWindow* ref_window,bool restore_focus_to_window_under_popup) { return ImGui::ClosePopupsOverWindow(ref_window,restore_focus_to_window_under_popup); } CIMGUI_API void igClosePopupsExceptModals() { return ImGui::ClosePopupsExceptModals(); } CIMGUI_API bool igIsPopupOpen_ID(ImGuiID id,ImGuiPopupFlags popup_flags) { return ImGui::IsPopupOpen(id,popup_flags); } CIMGUI_API void igGetPopupAllowedExtentRect(ImRect *pOut,ImGuiWindow* window) { *pOut = ImGui::GetPopupAllowedExtentRect(window); } CIMGUI_API ImGuiWindow* igGetTopMostPopupModal() { return ImGui::GetTopMostPopupModal(); } CIMGUI_API ImGuiWindow* igGetTopMostAndVisiblePopupModal() { return ImGui::GetTopMostAndVisiblePopupModal(); } CIMGUI_API ImGuiWindow* igFindBlockingModal(ImGuiWindow* window) { return ImGui::FindBlockingModal(window); } CIMGUI_API void igFindBestWindowPosForPopup(ImVec2 *pOut,ImGuiWindow* window) { *pOut = ImGui::FindBestWindowPosForPopup(window); } CIMGUI_API void igFindBestWindowPosForPopupEx(ImVec2 *pOut,const ImVec2 ref_pos,const ImVec2 size,ImGuiDir* last_dir,const ImRect r_outer,const ImRect r_avoid,ImGuiPopupPositionPolicy policy) { *pOut = ImGui::FindBestWindowPosForPopupEx(ref_pos,size,last_dir,r_outer,r_avoid,policy); } CIMGUI_API bool igBeginTooltipEx(ImGuiTooltipFlags tooltip_flags,ImGuiWindowFlags extra_window_flags) { return ImGui::BeginTooltipEx(tooltip_flags,extra_window_flags); } CIMGUI_API bool igBeginTooltipHidden() { return ImGui::BeginTooltipHidden(); } CIMGUI_API bool igBeginViewportSideBar(const char* name,ImGuiViewport* viewport,ImGuiDir dir,float size,ImGuiWindowFlags window_flags) { return ImGui::BeginViewportSideBar(name,viewport,dir,size,window_flags); } CIMGUI_API bool igBeginMenuEx(const char* label,const char* icon,bool enabled) { return ImGui::BeginMenuEx(label,icon,enabled); } CIMGUI_API bool igMenuItemEx(const char* label,const char* icon,const char* shortcut,bool selected,bool enabled) { return ImGui::MenuItemEx(label,icon,shortcut,selected,enabled); } CIMGUI_API bool igBeginComboPopup(ImGuiID popup_id,const ImRect bb,ImGuiComboFlags flags) { return ImGui::BeginComboPopup(popup_id,bb,flags); } CIMGUI_API bool igBeginComboPreview() { return ImGui::BeginComboPreview(); } CIMGUI_API void igEndComboPreview() { return ImGui::EndComboPreview(); } CIMGUI_API void igNavInitWindow(ImGuiWindow* window,bool force_reinit) { return ImGui::NavInitWindow(window,force_reinit); } CIMGUI_API void igNavInitRequestApplyResult() { return ImGui::NavInitRequestApplyResult(); } CIMGUI_API bool igNavMoveRequestButNoResultYet() { return ImGui::NavMoveRequestButNoResultYet(); } CIMGUI_API void igNavMoveRequestSubmit(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags,ImGuiScrollFlags scroll_flags) { return ImGui::NavMoveRequestSubmit(move_dir,clip_dir,move_flags,scroll_flags); } CIMGUI_API void igNavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags,ImGuiScrollFlags scroll_flags) { return ImGui::NavMoveRequestForward(move_dir,clip_dir,move_flags,scroll_flags); } CIMGUI_API void igNavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) { return ImGui::NavMoveRequestResolveWithLastItem(result); } CIMGUI_API void igNavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result,const ImGuiTreeNodeStackData* tree_node_data) { return ImGui::NavMoveRequestResolveWithPastTreeNode(result,tree_node_data); } CIMGUI_API void igNavMoveRequestCancel() { return ImGui::NavMoveRequestCancel(); } CIMGUI_API void igNavMoveRequestApplyResult() { return ImGui::NavMoveRequestApplyResult(); } CIMGUI_API void igNavMoveRequestTryWrapping(ImGuiWindow* window,ImGuiNavMoveFlags move_flags) { return ImGui::NavMoveRequestTryWrapping(window,move_flags); } CIMGUI_API void igNavHighlightActivated(ImGuiID id) { return ImGui::NavHighlightActivated(id); } CIMGUI_API void igNavClearPreferredPosForAxis(ImGuiAxis axis) { return ImGui::NavClearPreferredPosForAxis(axis); } CIMGUI_API void igSetNavCursorVisibleAfterMove() { return ImGui::SetNavCursorVisibleAfterMove(); } CIMGUI_API void igNavUpdateCurrentWindowIsScrollPushableX() { return ImGui::NavUpdateCurrentWindowIsScrollPushableX(); } CIMGUI_API void igSetNavWindow(ImGuiWindow* window) { return ImGui::SetNavWindow(window); } CIMGUI_API void igSetNavID(ImGuiID id,ImGuiNavLayer nav_layer,ImGuiID focus_scope_id,const ImRect rect_rel) { return ImGui::SetNavID(id,nav_layer,focus_scope_id,rect_rel); } CIMGUI_API void igSetNavFocusScope(ImGuiID focus_scope_id) { return ImGui::SetNavFocusScope(focus_scope_id); } CIMGUI_API void igFocusItem() { return ImGui::FocusItem(); } CIMGUI_API void igActivateItemByID(ImGuiID id) { return ImGui::ActivateItemByID(id); } CIMGUI_API bool igIsNamedKey(ImGuiKey key) { return ImGui::IsNamedKey(key); } CIMGUI_API bool igIsNamedKeyOrMod(ImGuiKey key) { return ImGui::IsNamedKeyOrMod(key); } CIMGUI_API bool igIsLegacyKey(ImGuiKey key) { return ImGui::IsLegacyKey(key); } CIMGUI_API bool igIsKeyboardKey(ImGuiKey key) { return ImGui::IsKeyboardKey(key); } CIMGUI_API bool igIsGamepadKey(ImGuiKey key) { return ImGui::IsGamepadKey(key); } CIMGUI_API bool igIsMouseKey(ImGuiKey key) { return ImGui::IsMouseKey(key); } CIMGUI_API bool igIsAliasKey(ImGuiKey key) { return ImGui::IsAliasKey(key); } CIMGUI_API bool igIsLRModKey(ImGuiKey key) { return ImGui::IsLRModKey(key); } CIMGUI_API ImGuiKeyChord igFixupKeyChord(ImGuiKeyChord key_chord) { return ImGui::FixupKeyChord(key_chord); } CIMGUI_API ImGuiKey igConvertSingleModFlagToKey(ImGuiKey key) { return ImGui::ConvertSingleModFlagToKey(key); } CIMGUI_API ImGuiKeyData* igGetKeyData_ContextPtr(ImGuiContext* ctx,ImGuiKey key) { return ImGui::GetKeyData(ctx,key); } CIMGUI_API ImGuiKeyData* igGetKeyData_Key(ImGuiKey key) { return ImGui::GetKeyData(key); } CIMGUI_API const char* igGetKeyChordName(ImGuiKeyChord key_chord) { return ImGui::GetKeyChordName(key_chord); } CIMGUI_API ImGuiKey igMouseButtonToKey(ImGuiMouseButton button) { return ImGui::MouseButtonToKey(button); } CIMGUI_API bool igIsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold) { return ImGui::IsMouseDragPastThreshold(button,lock_threshold); } CIMGUI_API void igGetKeyMagnitude2d(ImVec2 *pOut,ImGuiKey key_left,ImGuiKey key_right,ImGuiKey key_up,ImGuiKey key_down) { *pOut = ImGui::GetKeyMagnitude2d(key_left,key_right,key_up,key_down); } CIMGUI_API float igGetNavTweakPressedAmount(ImGuiAxis axis) { return ImGui::GetNavTweakPressedAmount(axis); } CIMGUI_API int igCalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate) { return ImGui::CalcTypematicRepeatAmount(t0,t1,repeat_delay,repeat_rate); } CIMGUI_API void igGetTypematicRepeatRate(ImGuiInputFlags flags,float* repeat_delay,float* repeat_rate) { return ImGui::GetTypematicRepeatRate(flags,repeat_delay,repeat_rate); } CIMGUI_API void igTeleportMousePos(const ImVec2 pos) { return ImGui::TeleportMousePos(pos); } CIMGUI_API void igSetActiveIdUsingAllKeyboardKeys() { return ImGui::SetActiveIdUsingAllKeyboardKeys(); } CIMGUI_API bool igIsActiveIdUsingNavDir(ImGuiDir dir) { return ImGui::IsActiveIdUsingNavDir(dir); } CIMGUI_API ImGuiID igGetKeyOwner(ImGuiKey key) { return ImGui::GetKeyOwner(key); } CIMGUI_API void igSetKeyOwner(ImGuiKey key,ImGuiID owner_id,ImGuiInputFlags flags) { return ImGui::SetKeyOwner(key,owner_id,flags); } CIMGUI_API void igSetKeyOwnersForKeyChord(ImGuiKeyChord key,ImGuiID owner_id,ImGuiInputFlags flags) { return ImGui::SetKeyOwnersForKeyChord(key,owner_id,flags); } CIMGUI_API void igSetItemKeyOwner_InputFlags(ImGuiKey key,ImGuiInputFlags flags) { return ImGui::SetItemKeyOwner(key,flags); } CIMGUI_API bool igTestKeyOwner(ImGuiKey key,ImGuiID owner_id) { return ImGui::TestKeyOwner(key,owner_id); } CIMGUI_API ImGuiKeyOwnerData* igGetKeyOwnerData(ImGuiContext* ctx,ImGuiKey key) { return ImGui::GetKeyOwnerData(ctx,key); } CIMGUI_API bool igIsKeyDown_ID(ImGuiKey key,ImGuiID owner_id) { return ImGui::IsKeyDown(key,owner_id); } CIMGUI_API bool igIsKeyPressed_InputFlags(ImGuiKey key,ImGuiInputFlags flags,ImGuiID owner_id) { return ImGui::IsKeyPressed(key,flags,owner_id); } CIMGUI_API bool igIsKeyReleased_ID(ImGuiKey key,ImGuiID owner_id) { return ImGui::IsKeyReleased(key,owner_id); } CIMGUI_API bool igIsKeyChordPressed_InputFlags(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id) { return ImGui::IsKeyChordPressed(key_chord,flags,owner_id); } CIMGUI_API bool igIsMouseDown_ID(ImGuiMouseButton button,ImGuiID owner_id) { return ImGui::IsMouseDown(button,owner_id); } CIMGUI_API bool igIsMouseClicked_InputFlags(ImGuiMouseButton button,ImGuiInputFlags flags,ImGuiID owner_id) { return ImGui::IsMouseClicked(button,flags,owner_id); } CIMGUI_API bool igIsMouseReleased_ID(ImGuiMouseButton button,ImGuiID owner_id) { return ImGui::IsMouseReleased(button,owner_id); } CIMGUI_API bool igIsMouseDoubleClicked_ID(ImGuiMouseButton button,ImGuiID owner_id) { return ImGui::IsMouseDoubleClicked(button,owner_id); } CIMGUI_API bool igShortcut_ID(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id) { return ImGui::Shortcut(key_chord,flags,owner_id); } CIMGUI_API bool igSetShortcutRouting(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id) { return ImGui::SetShortcutRouting(key_chord,flags,owner_id); } CIMGUI_API bool igTestShortcutRouting(ImGuiKeyChord key_chord,ImGuiID owner_id) { return ImGui::TestShortcutRouting(key_chord,owner_id); } CIMGUI_API ImGuiKeyRoutingData* igGetShortcutRoutingData(ImGuiKeyChord key_chord) { return ImGui::GetShortcutRoutingData(key_chord); } CIMGUI_API void igDockContextInitialize(ImGuiContext* ctx) { return ImGui::DockContextInitialize(ctx); } CIMGUI_API void igDockContextShutdown(ImGuiContext* ctx) { return ImGui::DockContextShutdown(ctx); } CIMGUI_API void igDockContextClearNodes(ImGuiContext* ctx,ImGuiID root_id,bool clear_settings_refs) { return ImGui::DockContextClearNodes(ctx,root_id,clear_settings_refs); } CIMGUI_API void igDockContextRebuildNodes(ImGuiContext* ctx) { return ImGui::DockContextRebuildNodes(ctx); } CIMGUI_API void igDockContextNewFrameUpdateUndocking(ImGuiContext* ctx) { return ImGui::DockContextNewFrameUpdateUndocking(ctx); } CIMGUI_API void igDockContextNewFrameUpdateDocking(ImGuiContext* ctx) { return ImGui::DockContextNewFrameUpdateDocking(ctx); } CIMGUI_API void igDockContextEndFrame(ImGuiContext* ctx) { return ImGui::DockContextEndFrame(ctx); } CIMGUI_API ImGuiID igDockContextGenNodeID(ImGuiContext* ctx) { return ImGui::DockContextGenNodeID(ctx); } CIMGUI_API void igDockContextQueueDock(ImGuiContext* ctx,ImGuiWindow* target,ImGuiDockNode* target_node,ImGuiWindow* payload,ImGuiDir split_dir,float split_ratio,bool split_outer) { return ImGui::DockContextQueueDock(ctx,target,target_node,payload,split_dir,split_ratio,split_outer); } CIMGUI_API void igDockContextQueueUndockWindow(ImGuiContext* ctx,ImGuiWindow* window) { return ImGui::DockContextQueueUndockWindow(ctx,window); } CIMGUI_API void igDockContextQueueUndockNode(ImGuiContext* ctx,ImGuiDockNode* node) { return ImGui::DockContextQueueUndockNode(ctx,node); } CIMGUI_API void igDockContextProcessUndockWindow(ImGuiContext* ctx,ImGuiWindow* window,bool clear_persistent_docking_ref) { return ImGui::DockContextProcessUndockWindow(ctx,window,clear_persistent_docking_ref); } CIMGUI_API void igDockContextProcessUndockNode(ImGuiContext* ctx,ImGuiDockNode* node) { return ImGui::DockContextProcessUndockNode(ctx,node); } CIMGUI_API bool igDockContextCalcDropPosForDocking(ImGuiWindow* target,ImGuiDockNode* target_node,ImGuiWindow* payload_window,ImGuiDockNode* payload_node,ImGuiDir split_dir,bool split_outer,ImVec2* out_pos) { return ImGui::DockContextCalcDropPosForDocking(target,target_node,payload_window,payload_node,split_dir,split_outer,out_pos); } CIMGUI_API ImGuiDockNode* igDockContextFindNodeByID(ImGuiContext* ctx,ImGuiID id) { return ImGui::DockContextFindNodeByID(ctx,id); } CIMGUI_API void igDockNodeWindowMenuHandler_Default(ImGuiContext* ctx,ImGuiDockNode* node,ImGuiTabBar* tab_bar) { return ImGui::DockNodeWindowMenuHandler_Default(ctx,node,tab_bar); } CIMGUI_API bool igDockNodeBeginAmendTabBar(ImGuiDockNode* node) { return ImGui::DockNodeBeginAmendTabBar(node); } CIMGUI_API void igDockNodeEndAmendTabBar() { return ImGui::DockNodeEndAmendTabBar(); } CIMGUI_API ImGuiDockNode* igDockNodeGetRootNode(ImGuiDockNode* node) { return ImGui::DockNodeGetRootNode(node); } CIMGUI_API bool igDockNodeIsInHierarchyOf(ImGuiDockNode* node,ImGuiDockNode* parent) { return ImGui::DockNodeIsInHierarchyOf(node,parent); } CIMGUI_API int igDockNodeGetDepth(const ImGuiDockNode* node) { return ImGui::DockNodeGetDepth(node); } CIMGUI_API ImGuiID igDockNodeGetWindowMenuButtonId(const ImGuiDockNode* node) { return ImGui::DockNodeGetWindowMenuButtonId(node); } CIMGUI_API ImGuiDockNode* igGetWindowDockNode() { return ImGui::GetWindowDockNode(); } CIMGUI_API bool igGetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) { return ImGui::GetWindowAlwaysWantOwnTabBar(window); } CIMGUI_API void igBeginDocked(ImGuiWindow* window,bool* p_open) { return ImGui::BeginDocked(window,p_open); } CIMGUI_API void igBeginDockableDragDropSource(ImGuiWindow* window) { return ImGui::BeginDockableDragDropSource(window); } CIMGUI_API void igBeginDockableDragDropTarget(ImGuiWindow* window) { return ImGui::BeginDockableDragDropTarget(window); } CIMGUI_API void igSetWindowDock(ImGuiWindow* window,ImGuiID dock_id,ImGuiCond cond) { return ImGui::SetWindowDock(window,dock_id,cond); } CIMGUI_API void igDockBuilderDockWindow(const char* window_name,ImGuiID node_id) { return ImGui::DockBuilderDockWindow(window_name,node_id); } CIMGUI_API ImGuiDockNode* igDockBuilderGetNode(ImGuiID node_id) { return ImGui::DockBuilderGetNode(node_id); } CIMGUI_API ImGuiDockNode* igDockBuilderGetCentralNode(ImGuiID node_id) { return ImGui::DockBuilderGetCentralNode(node_id); } CIMGUI_API ImGuiID igDockBuilderAddNode(ImGuiID node_id,ImGuiDockNodeFlags flags) { return ImGui::DockBuilderAddNode(node_id,flags); } CIMGUI_API void igDockBuilderRemoveNode(ImGuiID node_id) { return ImGui::DockBuilderRemoveNode(node_id); } CIMGUI_API void igDockBuilderRemoveNodeDockedWindows(ImGuiID node_id,bool clear_settings_refs) { return ImGui::DockBuilderRemoveNodeDockedWindows(node_id,clear_settings_refs); } CIMGUI_API void igDockBuilderRemoveNodeChildNodes(ImGuiID node_id) { return ImGui::DockBuilderRemoveNodeChildNodes(node_id); } CIMGUI_API void igDockBuilderSetNodePos(ImGuiID node_id,ImVec2 pos) { return ImGui::DockBuilderSetNodePos(node_id,pos); } CIMGUI_API void igDockBuilderSetNodeSize(ImGuiID node_id,ImVec2 size) { return ImGui::DockBuilderSetNodeSize(node_id,size); } CIMGUI_API ImGuiID igDockBuilderSplitNode(ImGuiID node_id,ImGuiDir split_dir,float size_ratio_for_node_at_dir,ImGuiID* out_id_at_dir,ImGuiID* out_id_at_opposite_dir) { return ImGui::DockBuilderSplitNode(node_id,split_dir,size_ratio_for_node_at_dir,out_id_at_dir,out_id_at_opposite_dir); } CIMGUI_API void igDockBuilderCopyDockSpace(ImGuiID src_dockspace_id,ImGuiID dst_dockspace_id,ImVector_const_charPtr* in_window_remap_pairs) { return ImGui::DockBuilderCopyDockSpace(src_dockspace_id,dst_dockspace_id,in_window_remap_pairs); } CIMGUI_API void igDockBuilderCopyNode(ImGuiID src_node_id,ImGuiID dst_node_id,ImVector_ImGuiID* out_node_remap_pairs) { return ImGui::DockBuilderCopyNode(src_node_id,dst_node_id,out_node_remap_pairs); } CIMGUI_API void igDockBuilderCopyWindowSettings(const char* src_name,const char* dst_name) { return ImGui::DockBuilderCopyWindowSettings(src_name,dst_name); } CIMGUI_API void igDockBuilderFinish(ImGuiID node_id) { return ImGui::DockBuilderFinish(node_id); } CIMGUI_API void igPushFocusScope(ImGuiID id) { return ImGui::PushFocusScope(id); } CIMGUI_API void igPopFocusScope() { return ImGui::PopFocusScope(); } CIMGUI_API ImGuiID igGetCurrentFocusScope() { return ImGui::GetCurrentFocusScope(); } CIMGUI_API bool igIsDragDropActive() { return ImGui::IsDragDropActive(); } CIMGUI_API bool igBeginDragDropTargetCustom(const ImRect bb,ImGuiID id) { return ImGui::BeginDragDropTargetCustom(bb,id); } CIMGUI_API void igClearDragDrop() { return ImGui::ClearDragDrop(); } CIMGUI_API bool igIsDragDropPayloadBeingAccepted() { return ImGui::IsDragDropPayloadBeingAccepted(); } CIMGUI_API void igRenderDragDropTargetRectEx(ImDrawList *draw_list, const ImRect bb) { return ImGui::RenderDragDropTargetRectEx(draw_list, bb); } CIMGUI_API void igRenderDragDropTargetRectForItem(const ImRect bb) { return ImGui::RenderDragDropTargetRectForItem(bb); } CIMGUI_API ImGuiTypingSelectRequest* igGetTypingSelectRequest(ImGuiTypingSelectFlags flags) { return ImGui::GetTypingSelectRequest(flags); } CIMGUI_API int igTypingSelectFindMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data,int nav_item_idx) { return ImGui::TypingSelectFindMatch(req,items_count,get_item_name_func,user_data,nav_item_idx); } CIMGUI_API int igTypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data,int nav_item_idx) { return ImGui::TypingSelectFindNextSingleCharMatch(req,items_count,get_item_name_func,user_data,nav_item_idx); } CIMGUI_API int igTypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data) { return ImGui::TypingSelectFindBestLeadingMatch(req,items_count,get_item_name_func,user_data); } CIMGUI_API bool igBeginBoxSelect(const ImRect scope_rect,ImGuiWindow* window,ImGuiID box_select_id,ImGuiMultiSelectFlags ms_flags) { return ImGui::BeginBoxSelect(scope_rect,window,box_select_id,ms_flags); } CIMGUI_API void igEndBoxSelect(const ImRect scope_rect,ImGuiMultiSelectFlags ms_flags) { return ImGui::EndBoxSelect(scope_rect,ms_flags); } CIMGUI_API void igMultiSelectItemHeader(ImGuiID id,bool* p_selected,ImGuiButtonFlags* p_button_flags) { return ImGui::MultiSelectItemHeader(id,p_selected,p_button_flags); } CIMGUI_API void igMultiSelectItemFooter(ImGuiID id,bool* p_selected,bool* p_pressed) { return ImGui::MultiSelectItemFooter(id,p_selected,p_pressed); } CIMGUI_API void igMultiSelectAddSetAll(ImGuiMultiSelectTempData* ms,bool selected) { return ImGui::MultiSelectAddSetAll(ms,selected); } CIMGUI_API void igMultiSelectAddSetRange(ImGuiMultiSelectTempData* ms,bool selected,int range_dir,ImGuiSelectionUserData first_item,ImGuiSelectionUserData last_item) { return ImGui::MultiSelectAddSetRange(ms,selected,range_dir,first_item,last_item); } CIMGUI_API ImGuiBoxSelectState* igGetBoxSelectState(ImGuiID id) { return ImGui::GetBoxSelectState(id); } CIMGUI_API ImGuiMultiSelectState* igGetMultiSelectState(ImGuiID id) { return ImGui::GetMultiSelectState(id); } CIMGUI_API void igSetWindowClipRectBeforeSetChannel(ImGuiWindow* window,const ImRect clip_rect) { return ImGui::SetWindowClipRectBeforeSetChannel(window,clip_rect); } CIMGUI_API void igBeginColumns(const char* str_id,int count,ImGuiOldColumnFlags flags) { return ImGui::BeginColumns(str_id,count,flags); } CIMGUI_API void igEndColumns() { return ImGui::EndColumns(); } CIMGUI_API void igPushColumnClipRect(int column_index) { return ImGui::PushColumnClipRect(column_index); } CIMGUI_API void igPushColumnsBackground() { return ImGui::PushColumnsBackground(); } CIMGUI_API void igPopColumnsBackground() { return ImGui::PopColumnsBackground(); } CIMGUI_API ImGuiID igGetColumnsID(const char* str_id,int count) { return ImGui::GetColumnsID(str_id,count); } CIMGUI_API ImGuiOldColumns* igFindOrCreateColumns(ImGuiWindow* window,ImGuiID id) { return ImGui::FindOrCreateColumns(window,id); } CIMGUI_API float igGetColumnOffsetFromNorm(const ImGuiOldColumns* columns,float offset_norm) { return ImGui::GetColumnOffsetFromNorm(columns,offset_norm); } CIMGUI_API float igGetColumnNormFromOffset(const ImGuiOldColumns* columns,float offset) { return ImGui::GetColumnNormFromOffset(columns,offset); } CIMGUI_API void igTableOpenContextMenu(int column_n) { return ImGui::TableOpenContextMenu(column_n); } CIMGUI_API void igTableSetColumnWidth(int column_n,float width) { return ImGui::TableSetColumnWidth(column_n,width); } CIMGUI_API void igTableSetColumnSortDirection(int column_n,ImGuiSortDirection sort_direction,bool append_to_sort_specs) { return ImGui::TableSetColumnSortDirection(column_n,sort_direction,append_to_sort_specs); } CIMGUI_API int igTableGetHoveredRow() { return ImGui::TableGetHoveredRow(); } CIMGUI_API float igTableGetHeaderRowHeight() { return ImGui::TableGetHeaderRowHeight(); } CIMGUI_API float igTableGetHeaderAngledMaxLabelWidth() { return ImGui::TableGetHeaderAngledMaxLabelWidth(); } CIMGUI_API void igTablePushBackgroundChannel() { return ImGui::TablePushBackgroundChannel(); } CIMGUI_API void igTablePopBackgroundChannel() { return ImGui::TablePopBackgroundChannel(); } CIMGUI_API void igTablePushColumnChannel(int column_n) { return ImGui::TablePushColumnChannel(column_n); } CIMGUI_API void igTablePopColumnChannel() { return ImGui::TablePopColumnChannel(); } CIMGUI_API void igTableAngledHeadersRowEx(ImGuiID row_id,float angle,float max_label_width,const ImGuiTableHeaderData* data,int data_count) { return ImGui::TableAngledHeadersRowEx(row_id,angle,max_label_width,data,data_count); } CIMGUI_API ImGuiTable* igGetCurrentTable() { return ImGui::GetCurrentTable(); } CIMGUI_API ImGuiTable* igTableFindByID(ImGuiID id) { return ImGui::TableFindByID(id); } CIMGUI_API bool igBeginTableEx(const char* name,ImGuiID id,int columns_count,ImGuiTableFlags flags,const ImVec2 outer_size,float inner_width) { return ImGui::BeginTableEx(name,id,columns_count,flags,outer_size,inner_width); } CIMGUI_API void igTableBeginInitMemory(ImGuiTable* table,int columns_count) { return ImGui::TableBeginInitMemory(table,columns_count); } CIMGUI_API void igTableBeginApplyRequests(ImGuiTable* table) { return ImGui::TableBeginApplyRequests(table); } CIMGUI_API void igTableSetupDrawChannels(ImGuiTable* table) { return ImGui::TableSetupDrawChannels(table); } CIMGUI_API void igTableUpdateLayout(ImGuiTable* table) { return ImGui::TableUpdateLayout(table); } CIMGUI_API void igTableUpdateBorders(ImGuiTable* table) { return ImGui::TableUpdateBorders(table); } CIMGUI_API void igTableUpdateColumnsWeightFromWidth(ImGuiTable* table) { return ImGui::TableUpdateColumnsWeightFromWidth(table); } CIMGUI_API void igTableDrawBorders(ImGuiTable* table) { return ImGui::TableDrawBorders(table); } CIMGUI_API void igTableDrawDefaultContextMenu(ImGuiTable* table,ImGuiTableFlags flags_for_section_to_display) { return ImGui::TableDrawDefaultContextMenu(table,flags_for_section_to_display); } CIMGUI_API bool igTableBeginContextMenuPopup(ImGuiTable* table) { return ImGui::TableBeginContextMenuPopup(table); } CIMGUI_API void igTableMergeDrawChannels(ImGuiTable* table) { return ImGui::TableMergeDrawChannels(table); } CIMGUI_API ImGuiTableInstanceData* igTableGetInstanceData(ImGuiTable* table,int instance_no) { return ImGui::TableGetInstanceData(table,instance_no); } CIMGUI_API ImGuiID igTableGetInstanceID(ImGuiTable* table,int instance_no) { return ImGui::TableGetInstanceID(table,instance_no); } CIMGUI_API void igTableSortSpecsSanitize(ImGuiTable* table) { return ImGui::TableSortSpecsSanitize(table); } CIMGUI_API void igTableSortSpecsBuild(ImGuiTable* table) { return ImGui::TableSortSpecsBuild(table); } CIMGUI_API ImGuiSortDirection igTableGetColumnNextSortDirection(ImGuiTableColumn* column) { return ImGui::TableGetColumnNextSortDirection(column); } CIMGUI_API void igTableFixColumnSortDirection(ImGuiTable* table,ImGuiTableColumn* column) { return ImGui::TableFixColumnSortDirection(table,column); } CIMGUI_API float igTableGetColumnWidthAuto(ImGuiTable* table,ImGuiTableColumn* column) { return ImGui::TableGetColumnWidthAuto(table,column); } CIMGUI_API void igTableBeginRow(ImGuiTable* table) { return ImGui::TableBeginRow(table); } CIMGUI_API void igTableEndRow(ImGuiTable* table) { return ImGui::TableEndRow(table); } CIMGUI_API void igTableBeginCell(ImGuiTable* table,int column_n) { return ImGui::TableBeginCell(table,column_n); } CIMGUI_API void igTableEndCell(ImGuiTable* table) { return ImGui::TableEndCell(table); } CIMGUI_API void igTableGetCellBgRect(ImRect *pOut,const ImGuiTable* table,int column_n) { *pOut = ImGui::TableGetCellBgRect(table,column_n); } CIMGUI_API const char* igTableGetColumnName_TablePtr(const ImGuiTable* table,int column_n) { return ImGui::TableGetColumnName(table,column_n); } CIMGUI_API ImGuiID igTableGetColumnResizeID(ImGuiTable* table,int column_n,int instance_no) { return ImGui::TableGetColumnResizeID(table,column_n,instance_no); } CIMGUI_API float igTableCalcMaxColumnWidth(const ImGuiTable* table,int column_n) { return ImGui::TableCalcMaxColumnWidth(table,column_n); } CIMGUI_API void igTableSetColumnWidthAutoSingle(ImGuiTable* table,int column_n) { return ImGui::TableSetColumnWidthAutoSingle(table,column_n); } CIMGUI_API void igTableSetColumnWidthAutoAll(ImGuiTable* table) { return ImGui::TableSetColumnWidthAutoAll(table); } CIMGUI_API void igTableRemove(ImGuiTable* table) { return ImGui::TableRemove(table); } CIMGUI_API void igTableGcCompactTransientBuffers_TablePtr(ImGuiTable* table) { return ImGui::TableGcCompactTransientBuffers(table); } CIMGUI_API void igTableGcCompactTransientBuffers_TableTempDataPtr(ImGuiTableTempData* table) { return ImGui::TableGcCompactTransientBuffers(table); } CIMGUI_API void igTableGcCompactSettings() { return ImGui::TableGcCompactSettings(); } CIMGUI_API void igTableLoadSettings(ImGuiTable* table) { return ImGui::TableLoadSettings(table); } CIMGUI_API void igTableSaveSettings(ImGuiTable* table) { return ImGui::TableSaveSettings(table); } CIMGUI_API void igTableResetSettings(ImGuiTable* table) { return ImGui::TableResetSettings(table); } CIMGUI_API ImGuiTableSettings* igTableGetBoundSettings(ImGuiTable* table) { return ImGui::TableGetBoundSettings(table); } CIMGUI_API void igTableSettingsAddSettingsHandler() { return ImGui::TableSettingsAddSettingsHandler(); } CIMGUI_API ImGuiTableSettings* igTableSettingsCreate(ImGuiID id,int columns_count) { return ImGui::TableSettingsCreate(id,columns_count); } CIMGUI_API ImGuiTableSettings* igTableSettingsFindByID(ImGuiID id) { return ImGui::TableSettingsFindByID(id); } CIMGUI_API ImGuiTabBar* igGetCurrentTabBar() { return ImGui::GetCurrentTabBar(); } CIMGUI_API bool igBeginTabBarEx(ImGuiTabBar* tab_bar,const ImRect bb,ImGuiTabBarFlags flags) { return ImGui::BeginTabBarEx(tab_bar,bb,flags); } CIMGUI_API ImGuiTabItem* igTabBarFindTabByID(ImGuiTabBar* tab_bar,ImGuiID tab_id) { return ImGui::TabBarFindTabByID(tab_bar,tab_id); } CIMGUI_API ImGuiTabItem* igTabBarFindTabByOrder(ImGuiTabBar* tab_bar,int order) { return ImGui::TabBarFindTabByOrder(tab_bar,order); } CIMGUI_API ImGuiTabItem* igTabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar) { return ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(tab_bar); } CIMGUI_API ImGuiTabItem* igTabBarGetCurrentTab(ImGuiTabBar* tab_bar) { return ImGui::TabBarGetCurrentTab(tab_bar); } CIMGUI_API int igTabBarGetTabOrder(ImGuiTabBar* tab_bar,ImGuiTabItem* tab) { return ImGui::TabBarGetTabOrder(tab_bar,tab); } CIMGUI_API const char* igTabBarGetTabName(ImGuiTabBar* tab_bar,ImGuiTabItem* tab) { return ImGui::TabBarGetTabName(tab_bar,tab); } CIMGUI_API void igTabBarAddTab(ImGuiTabBar* tab_bar,ImGuiTabItemFlags tab_flags,ImGuiWindow* window) { return ImGui::TabBarAddTab(tab_bar,tab_flags,window); } CIMGUI_API void igTabBarRemoveTab(ImGuiTabBar* tab_bar,ImGuiID tab_id) { return ImGui::TabBarRemoveTab(tab_bar,tab_id); } CIMGUI_API void igTabBarCloseTab(ImGuiTabBar* tab_bar,ImGuiTabItem* tab) { return ImGui::TabBarCloseTab(tab_bar,tab); } CIMGUI_API void igTabBarQueueFocus_TabItemPtr(ImGuiTabBar* tab_bar,ImGuiTabItem* tab) { return ImGui::TabBarQueueFocus(tab_bar,tab); } CIMGUI_API void igTabBarQueueFocus_Str(ImGuiTabBar* tab_bar,const char* tab_name) { return ImGui::TabBarQueueFocus(tab_bar,tab_name); } CIMGUI_API void igTabBarQueueReorder(ImGuiTabBar* tab_bar,ImGuiTabItem* tab,int offset) { return ImGui::TabBarQueueReorder(tab_bar,tab,offset); } CIMGUI_API void igTabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar,ImGuiTabItem* tab,ImVec2 mouse_pos) { return ImGui::TabBarQueueReorderFromMousePos(tab_bar,tab,mouse_pos); } CIMGUI_API bool igTabBarProcessReorder(ImGuiTabBar* tab_bar) { return ImGui::TabBarProcessReorder(tab_bar); } CIMGUI_API bool igTabItemEx(ImGuiTabBar* tab_bar,const char* label,bool* p_open,ImGuiTabItemFlags flags,ImGuiWindow* docked_window) { return ImGui::TabItemEx(tab_bar,label,p_open,flags,docked_window); } CIMGUI_API void igTabItemSpacing(const char* str_id,ImGuiTabItemFlags flags,float width) { return ImGui::TabItemSpacing(str_id,flags,width); } CIMGUI_API void igTabItemCalcSize_Str(ImVec2 *pOut,const char* label,bool has_close_button_or_unsaved_marker) { *pOut = ImGui::TabItemCalcSize(label,has_close_button_or_unsaved_marker); } CIMGUI_API void igTabItemCalcSize_WindowPtr(ImVec2 *pOut,ImGuiWindow* window) { *pOut = ImGui::TabItemCalcSize(window); } CIMGUI_API void igTabItemBackground(ImDrawList* draw_list,const ImRect bb,ImGuiTabItemFlags flags,ImU32 col) { return ImGui::TabItemBackground(draw_list,bb,flags,col); } CIMGUI_API void igTabItemLabelAndCloseButton(ImDrawList* draw_list,const ImRect bb,ImGuiTabItemFlags flags,ImVec2 frame_padding,const char* label,ImGuiID tab_id,ImGuiID close_button_id,bool is_contents_visible,bool* out_just_closed,bool* out_text_clipped) { return ImGui::TabItemLabelAndCloseButton(draw_list,bb,flags,frame_padding,label,tab_id,close_button_id,is_contents_visible,out_just_closed,out_text_clipped); } CIMGUI_API void igRenderText(ImVec2 pos,const char* text,const char* text_end,bool hide_text_after_hash) { return ImGui::RenderText(pos,text,text_end,hide_text_after_hash); } CIMGUI_API void igRenderTextWrapped(ImVec2 pos,const char* text,const char* text_end,float wrap_width) { return ImGui::RenderTextWrapped(pos,text,text_end,wrap_width); } CIMGUI_API void igRenderTextClipped(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) { return ImGui::RenderTextClipped(pos_min,pos_max,text,text_end,text_size_if_known,align,clip_rect); } CIMGUI_API void igRenderTextClippedEx(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,const ImRect* clip_rect) { return ImGui::RenderTextClippedEx(draw_list,pos_min,pos_max,text,text_end,text_size_if_known,align,clip_rect); } CIMGUI_API void igRenderTextEllipsis(ImDrawList* draw_list,const ImVec2 pos_min,const ImVec2 pos_max,float ellipsis_max_x,const char* text,const char* text_end,const ImVec2* text_size_if_known) { return ImGui::RenderTextEllipsis(draw_list,pos_min,pos_max,ellipsis_max_x,text,text_end,text_size_if_known); } CIMGUI_API void igRenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool borders,float rounding) { return ImGui::RenderFrame(p_min,p_max,fill_col,borders,rounding); } CIMGUI_API void igRenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding) { return ImGui::RenderFrameBorder(p_min,p_max,rounding); } CIMGUI_API void igRenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list,ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,float grid_step,ImVec2 grid_off,float rounding,ImDrawFlags flags) { return ImGui::RenderColorRectWithAlphaCheckerboard(draw_list,p_min,p_max,fill_col,grid_step,grid_off,rounding,flags); } CIMGUI_API void igRenderNavCursor(const ImRect bb,ImGuiID id,ImGuiNavRenderCursorFlags flags) { return ImGui::RenderNavCursor(bb,id,flags); } CIMGUI_API const char* igFindRenderedTextEnd(const char* text,const char* text_end) { return ImGui::FindRenderedTextEnd(text,text_end); } CIMGUI_API void igRenderMouseCursor(ImVec2 pos,float scale,ImGuiMouseCursor mouse_cursor,ImU32 col_fill,ImU32 col_border,ImU32 col_shadow) { return ImGui::RenderMouseCursor(pos,scale,mouse_cursor,col_fill,col_border,col_shadow); } CIMGUI_API void igRenderArrow(ImDrawList* draw_list,ImVec2 pos,ImU32 col,ImGuiDir dir,float scale) { return ImGui::RenderArrow(draw_list,pos,col,dir,scale); } CIMGUI_API void igRenderBullet(ImDrawList* draw_list,ImVec2 pos,ImU32 col) { return ImGui::RenderBullet(draw_list,pos,col); } CIMGUI_API void igRenderCheckMark(ImDrawList* draw_list,ImVec2 pos,ImU32 col,float sz) { return ImGui::RenderCheckMark(draw_list,pos,col,sz); } CIMGUI_API void igRenderArrowPointingAt(ImDrawList* draw_list,ImVec2 pos,ImVec2 half_sz,ImGuiDir direction,ImU32 col) { return ImGui::RenderArrowPointingAt(draw_list,pos,half_sz,direction,col); } CIMGUI_API void igRenderArrowDockMenu(ImDrawList* draw_list,ImVec2 p_min,float sz,ImU32 col) { return ImGui::RenderArrowDockMenu(draw_list,p_min,sz,col); } CIMGUI_API void igRenderRectFilledWithHole(ImDrawList* draw_list,const ImRect outer,const ImRect inner,ImU32 col,float rounding) { return ImGui::RenderRectFilledWithHole(draw_list,outer,inner,col,rounding); } CIMGUI_API ImDrawFlags igCalcRoundingFlagsForRectInRect(const ImRect r_in,const ImRect r_outer,float threshold) { return ImGui::CalcRoundingFlagsForRectInRect(r_in,r_outer,threshold); } CIMGUI_API void igTextEx(const char* text,const char* text_end,ImGuiTextFlags flags) { return ImGui::TextEx(text,text_end,flags); } CIMGUI_API void igTextAligned(float align_x,float size_x,const char* fmt,...) { va_list args; va_start(args, fmt); ImGui::TextAlignedV(align_x,size_x,fmt,args); va_end(args); } #ifdef CIMGUI_VARGS0 CIMGUI_API void igTextAligned0(float align_x,float size_x,const char* fmt) { return igTextAligned(align_x,size_x,fmt); } #endif CIMGUI_API void igTextAlignedV(float align_x,float size_x,const char* fmt,va_list args) { return ImGui::TextAlignedV(align_x,size_x,fmt,args); } CIMGUI_API bool igButtonEx(const char* label,const ImVec2 size_arg,ImGuiButtonFlags flags) { return ImGui::ButtonEx(label,size_arg,flags); } CIMGUI_API bool igArrowButtonEx(const char* str_id,ImGuiDir dir,ImVec2 size_arg,ImGuiButtonFlags flags) { return ImGui::ArrowButtonEx(str_id,dir,size_arg,flags); } CIMGUI_API bool igImageButtonEx(ImGuiID id,ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col,ImGuiButtonFlags flags) { return ImGui::ImageButtonEx(id,tex_ref,image_size,uv0,uv1,bg_col,tint_col,flags); } CIMGUI_API void igSeparatorEx(ImGuiSeparatorFlags flags,float thickness) { return ImGui::SeparatorEx(flags,thickness); } CIMGUI_API void igSeparatorTextEx(ImGuiID id,const char* label,const char* label_end,float extra_width) { return ImGui::SeparatorTextEx(id,label,label_end,extra_width); } CIMGUI_API bool igCheckboxFlags_S64Ptr(const char* label,ImS64* flags,ImS64 flags_value) { return ImGui::CheckboxFlags(label,flags,flags_value); } CIMGUI_API bool igCheckboxFlags_U64Ptr(const char* label,ImU64* flags,ImU64 flags_value) { return ImGui::CheckboxFlags(label,flags,flags_value); } CIMGUI_API bool igCloseButton(ImGuiID id,const ImVec2 pos) { return ImGui::CloseButton(id,pos); } CIMGUI_API bool igCollapseButton(ImGuiID id,const ImVec2 pos,ImGuiDockNode* dock_node) { return ImGui::CollapseButton(id,pos,dock_node); } CIMGUI_API void igScrollbar(ImGuiAxis axis) { return ImGui::Scrollbar(axis); } CIMGUI_API bool igScrollbarEx(const ImRect bb,ImGuiID id,ImGuiAxis axis,ImS64* p_scroll_v,ImS64 avail_v,ImS64 contents_v,ImDrawFlags draw_rounding_flags) { return ImGui::ScrollbarEx(bb,id,axis,p_scroll_v,avail_v,contents_v,draw_rounding_flags); } CIMGUI_API void igGetWindowScrollbarRect(ImRect *pOut,ImGuiWindow* window,ImGuiAxis axis) { *pOut = ImGui::GetWindowScrollbarRect(window,axis); } CIMGUI_API ImGuiID igGetWindowScrollbarID(ImGuiWindow* window,ImGuiAxis axis) { return ImGui::GetWindowScrollbarID(window,axis); } CIMGUI_API ImGuiID igGetWindowResizeCornerID(ImGuiWindow* window,int n) { return ImGui::GetWindowResizeCornerID(window,n); } CIMGUI_API ImGuiID igGetWindowResizeBorderID(ImGuiWindow* window,ImGuiDir dir) { return ImGui::GetWindowResizeBorderID(window,dir); } CIMGUI_API bool igButtonBehavior(const ImRect bb,ImGuiID id,bool* out_hovered,bool* out_held,ImGuiButtonFlags flags) { return ImGui::ButtonBehavior(bb,id,out_hovered,out_held,flags); } CIMGUI_API bool igDragBehavior(ImGuiID id,ImGuiDataType data_type,void* p_v,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags) { return ImGui::DragBehavior(id,data_type,p_v,v_speed,p_min,p_max,format,flags); } CIMGUI_API bool igSliderBehavior(const ImRect bb,ImGuiID id,ImGuiDataType data_type,void* p_v,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags,ImRect* out_grab_bb) { return ImGui::SliderBehavior(bb,id,data_type,p_v,p_min,p_max,format,flags,out_grab_bb); } CIMGUI_API bool igSplitterBehavior(const ImRect bb,ImGuiID id,ImGuiAxis axis,float* size1,float* size2,float min_size1,float min_size2,float hover_extend,float hover_visibility_delay,ImU32 bg_col) { return ImGui::SplitterBehavior(bb,id,axis,size1,size2,min_size1,min_size2,hover_extend,hover_visibility_delay,bg_col); } CIMGUI_API bool igTreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char* label,const char* label_end) { return ImGui::TreeNodeBehavior(id,flags,label,label_end); } CIMGUI_API void igTreeNodeDrawLineToChildNode(const ImVec2 target_pos) { return ImGui::TreeNodeDrawLineToChildNode(target_pos); } CIMGUI_API void igTreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data) { return ImGui::TreeNodeDrawLineToTreePop(data); } CIMGUI_API void igTreePushOverrideID(ImGuiID id) { return ImGui::TreePushOverrideID(id); } CIMGUI_API bool igTreeNodeGetOpen(ImGuiID storage_id) { return ImGui::TreeNodeGetOpen(storage_id); } CIMGUI_API void igTreeNodeSetOpen(ImGuiID storage_id,bool open) { return ImGui::TreeNodeSetOpen(storage_id,open); } CIMGUI_API bool igTreeNodeUpdateNextOpen(ImGuiID storage_id,ImGuiTreeNodeFlags flags) { return ImGui::TreeNodeUpdateNextOpen(storage_id,flags); } CIMGUI_API const ImGuiDataTypeInfo* igDataTypeGetInfo(ImGuiDataType data_type) { return ImGui::DataTypeGetInfo(data_type); } CIMGUI_API int igDataTypeFormatString(char* buf,int buf_size,ImGuiDataType data_type,const void* p_data,const char* format) { return ImGui::DataTypeFormatString(buf,buf_size,data_type,p_data,format); } CIMGUI_API void igDataTypeApplyOp(ImGuiDataType data_type,int op,void* output,const void* arg_1,const void* arg_2) { return ImGui::DataTypeApplyOp(data_type,op,output,arg_1,arg_2); } CIMGUI_API bool igDataTypeApplyFromText(const char* buf,ImGuiDataType data_type,void* p_data,const char* format,void* p_data_when_empty) { return ImGui::DataTypeApplyFromText(buf,data_type,p_data,format,p_data_when_empty); } CIMGUI_API int igDataTypeCompare(ImGuiDataType data_type,const void* arg_1,const void* arg_2) { return ImGui::DataTypeCompare(data_type,arg_1,arg_2); } CIMGUI_API bool igDataTypeClamp(ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max) { return ImGui::DataTypeClamp(data_type,p_data,p_min,p_max); } CIMGUI_API bool igDataTypeIsZero(ImGuiDataType data_type,const void* p_data) { return ImGui::DataTypeIsZero(data_type,p_data); } CIMGUI_API bool igInputTextEx(const char* label,const char* hint,char* buf,int buf_size,const ImVec2 size_arg,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data) { return ImGui::InputTextEx(label,hint,buf,buf_size,size_arg,flags,callback,user_data); } CIMGUI_API void igInputTextDeactivateHook(ImGuiID id) { return ImGui::InputTextDeactivateHook(id); } CIMGUI_API bool igTempInputText(const ImRect bb,ImGuiID id,const char* label,char* buf,int buf_size,ImGuiInputTextFlags flags) { return ImGui::TempInputText(bb,id,label,buf,buf_size,flags); } CIMGUI_API bool igTempInputScalar(const ImRect bb,ImGuiID id,const char* label,ImGuiDataType data_type,void* p_data,const char* format,const void* p_clamp_min,const void* p_clamp_max) { return ImGui::TempInputScalar(bb,id,label,data_type,p_data,format,p_clamp_min,p_clamp_max); } CIMGUI_API bool igTempInputIsActive(ImGuiID id) { return ImGui::TempInputIsActive(id); } CIMGUI_API ImGuiInputTextState* igGetInputTextState(ImGuiID id) { return ImGui::GetInputTextState(id); } CIMGUI_API void igSetNextItemRefVal(ImGuiDataType data_type,void* p_data) { return ImGui::SetNextItemRefVal(data_type,p_data); } CIMGUI_API bool igIsItemActiveAsInputText() { return ImGui::IsItemActiveAsInputText(); } CIMGUI_API void igColorTooltip(const char* text,const float* col,ImGuiColorEditFlags flags) { return ImGui::ColorTooltip(text,col,flags); } CIMGUI_API void igColorEditOptionsPopup(const float* col,ImGuiColorEditFlags flags) { return ImGui::ColorEditOptionsPopup(col,flags); } CIMGUI_API void igColorPickerOptionsPopup(const float* ref_col,ImGuiColorEditFlags flags) { return ImGui::ColorPickerOptionsPopup(ref_col,flags); } CIMGUI_API int igPlotEx(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,const ImVec2 size_arg) { return ImGui::PlotEx(plot_type,label,values_getter,data,values_count,values_offset,overlay_text,scale_min,scale_max,size_arg); } CIMGUI_API void igShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,ImVec2 gradient_p0,ImVec2 gradient_p1,ImU32 col0,ImU32 col1) { return ImGui::ShadeVertsLinearColorGradientKeepAlpha(draw_list,vert_start_idx,vert_end_idx,gradient_p0,gradient_p1,col0,col1); } CIMGUI_API void igShadeVertsLinearUV(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,const ImVec2 a,const ImVec2 b,const ImVec2 uv_a,const ImVec2 uv_b,bool clamp) { return ImGui::ShadeVertsLinearUV(draw_list,vert_start_idx,vert_end_idx,a,b,uv_a,uv_b,clamp); } CIMGUI_API void igShadeVertsTransformPos(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,const ImVec2 pivot_in,float cos_a,float sin_a,const ImVec2 pivot_out) { return ImGui::ShadeVertsTransformPos(draw_list,vert_start_idx,vert_end_idx,pivot_in,cos_a,sin_a,pivot_out); } CIMGUI_API void igGcCompactTransientMiscBuffers() { return ImGui::GcCompactTransientMiscBuffers(); } CIMGUI_API void igGcCompactTransientWindowBuffers(ImGuiWindow* window) { return ImGui::GcCompactTransientWindowBuffers(window); } CIMGUI_API void igGcAwakeTransientWindowBuffers(ImGuiWindow* window) { return ImGui::GcAwakeTransientWindowBuffers(window); } CIMGUI_API bool igErrorLog(const char* msg) { return ImGui::ErrorLog(msg); } CIMGUI_API void igErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out) { return ImGui::ErrorRecoveryStoreState(state_out); } CIMGUI_API void igErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in) { return ImGui::ErrorRecoveryTryToRecoverState(state_in); } CIMGUI_API void igErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in) { return ImGui::ErrorRecoveryTryToRecoverWindowState(state_in); } CIMGUI_API void igErrorCheckUsingSetCursorPosToExtendParentBoundaries() { return ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); } CIMGUI_API void igErrorCheckEndFrameFinalizeErrorTooltip() { return ImGui::ErrorCheckEndFrameFinalizeErrorTooltip(); } CIMGUI_API bool igBeginErrorTooltip() { return ImGui::BeginErrorTooltip(); } CIMGUI_API void igEndErrorTooltip() { return ImGui::EndErrorTooltip(); } CIMGUI_API void igDebugAllocHook(ImGuiDebugAllocInfo* info,int frame_count,void* ptr,size_t size) { return ImGui::DebugAllocHook(info,frame_count,ptr,size); } CIMGUI_API void igDebugDrawCursorPos(ImU32 col) { return ImGui::DebugDrawCursorPos(col); } CIMGUI_API void igDebugDrawLineExtents(ImU32 col) { return ImGui::DebugDrawLineExtents(col); } CIMGUI_API void igDebugDrawItemRect(ImU32 col) { return ImGui::DebugDrawItemRect(col); } CIMGUI_API void igDebugTextUnformattedWithLocateItem(const char* line_begin,const char* line_end) { return ImGui::DebugTextUnformattedWithLocateItem(line_begin,line_end); } CIMGUI_API void igDebugLocateItem(ImGuiID target_id) { return ImGui::DebugLocateItem(target_id); } CIMGUI_API void igDebugLocateItemOnHover(ImGuiID target_id) { return ImGui::DebugLocateItemOnHover(target_id); } CIMGUI_API void igDebugLocateItemResolveWithLastItem() { return ImGui::DebugLocateItemResolveWithLastItem(); } CIMGUI_API void igDebugBreakClearData() { return ImGui::DebugBreakClearData(); } CIMGUI_API bool igDebugBreakButton(const char* label,const char* description_of_location) { return ImGui::DebugBreakButton(label,description_of_location); } CIMGUI_API void igDebugBreakButtonTooltip(bool keyboard_only,const char* description_of_location) { return ImGui::DebugBreakButtonTooltip(keyboard_only,description_of_location); } CIMGUI_API void igShowFontAtlas(ImFontAtlas* atlas) { return ImGui::ShowFontAtlas(atlas); } CIMGUI_API void igDebugHookIdInfo(ImGuiID id,ImGuiDataType data_type,const void* data_id,const void* data_id_end) { return ImGui::DebugHookIdInfo(id,data_type,data_id,data_id_end); } CIMGUI_API void igDebugNodeColumns(ImGuiOldColumns* columns) { return ImGui::DebugNodeColumns(columns); } CIMGUI_API void igDebugNodeDockNode(ImGuiDockNode* node,const char* label) { return ImGui::DebugNodeDockNode(node,label); } CIMGUI_API void igDebugNodeDrawList(ImGuiWindow* window,ImGuiViewportP* viewport,const ImDrawList* draw_list,const char* label) { return ImGui::DebugNodeDrawList(window,viewport,draw_list,label); } CIMGUI_API void igDebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list,const ImDrawList* draw_list,const ImDrawCmd* draw_cmd,bool show_mesh,bool show_aabb) { return ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(out_draw_list,draw_list,draw_cmd,show_mesh,show_aabb); } CIMGUI_API void igDebugNodeFont(ImFont* font) { return ImGui::DebugNodeFont(font); } CIMGUI_API void igDebugNodeFontGlyph(ImFont* font,const ImFontGlyph* glyph) { return ImGui::DebugNodeFontGlyph(font,glyph); } CIMGUI_API void igDebugNodeTexture(ImTextureData* tex,int int_id,const ImFontAtlasRect* highlight_rect) { return ImGui::DebugNodeTexture(tex,int_id,highlight_rect); } CIMGUI_API void igDebugNodeStorage(ImGuiStorage* storage,const char* label) { return ImGui::DebugNodeStorage(storage,label); } CIMGUI_API void igDebugNodeTabBar(ImGuiTabBar* tab_bar,const char* label) { return ImGui::DebugNodeTabBar(tab_bar,label); } CIMGUI_API void igDebugNodeTable(ImGuiTable* table) { return ImGui::DebugNodeTable(table); } CIMGUI_API void igDebugNodeTableSettings(ImGuiTableSettings* settings) { return ImGui::DebugNodeTableSettings(settings); } CIMGUI_API void igDebugNodeInputTextState(ImGuiInputTextState* state) { return ImGui::DebugNodeInputTextState(state); } CIMGUI_API void igDebugNodeTypingSelectState(ImGuiTypingSelectState* state) { return ImGui::DebugNodeTypingSelectState(state); } CIMGUI_API void igDebugNodeMultiSelectState(ImGuiMultiSelectState* state) { return ImGui::DebugNodeMultiSelectState(state); } CIMGUI_API void igDebugNodeWindow(ImGuiWindow* window,const char* label) { return ImGui::DebugNodeWindow(window,label); } CIMGUI_API void igDebugNodeWindowSettings(ImGuiWindowSettings* settings) { return ImGui::DebugNodeWindowSettings(settings); } CIMGUI_API void igDebugNodeWindowsList(ImVector_ImGuiWindowPtr* windows,const char* label) { return ImGui::DebugNodeWindowsList(windows,label); } CIMGUI_API void igDebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows,int windows_size,ImGuiWindow* parent_in_begin_stack) { return ImGui::DebugNodeWindowsListByBeginStackParent(windows,windows_size,parent_in_begin_stack); } CIMGUI_API void igDebugNodeViewport(ImGuiViewportP* viewport) { return ImGui::DebugNodeViewport(viewport); } CIMGUI_API void igDebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor,const char* label,int idx) { return ImGui::DebugNodePlatformMonitor(monitor,label,idx); } CIMGUI_API void igDebugRenderKeyboardPreview(ImDrawList* draw_list) { return ImGui::DebugRenderKeyboardPreview(draw_list); } CIMGUI_API void igDebugRenderViewportThumbnail(ImDrawList* draw_list,ImGuiViewportP* viewport,const ImRect bb) { return ImGui::DebugRenderViewportThumbnail(draw_list,viewport,bb); } CIMGUI_API ImFontLoader* ImFontLoader_ImFontLoader(void) { return IM_NEW(ImFontLoader)(); } CIMGUI_API void ImFontLoader_destroy(ImFontLoader* self) { IM_DELETE(self); } CIMGUI_API int igImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return ImFontAtlasRectId_GetIndex(id); } CIMGUI_API int igImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return ImFontAtlasRectId_GetGeneration(id); } CIMGUI_API ImFontAtlasRectId igImFontAtlasRectId_Make(int index_idx,int gen_idx) { return ImFontAtlasRectId_Make(index_idx,gen_idx); } CIMGUI_API ImFontAtlasBuilder* ImFontAtlasBuilder_ImFontAtlasBuilder(void) { return IM_NEW(ImFontAtlasBuilder)(); } CIMGUI_API void ImFontAtlasBuilder_destroy(ImFontAtlasBuilder* self) { IM_DELETE(self); } CIMGUI_API void igImFontAtlasBuildInit(ImFontAtlas* atlas) { return ImFontAtlasBuildInit(atlas); } CIMGUI_API void igImFontAtlasBuildDestroy(ImFontAtlas* atlas) { return ImFontAtlasBuildDestroy(atlas); } CIMGUI_API void igImFontAtlasBuildMain(ImFontAtlas* atlas) { return ImFontAtlasBuildMain(atlas); } CIMGUI_API void igImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas,const ImFontLoader* font_loader) { return ImFontAtlasBuildSetupFontLoader(atlas,font_loader); } CIMGUI_API void igImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas) { return ImFontAtlasBuildUpdatePointers(atlas); } CIMGUI_API void igImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas,int x,int y,int w,int h,const char* in_str,char in_marker_char) { return ImFontAtlasBuildRenderBitmapFromString(atlas,x,y,w,h,in_str,in_marker_char); } CIMGUI_API void igImFontAtlasBuildClear(ImFontAtlas* atlas) { return ImFontAtlasBuildClear(atlas); } CIMGUI_API ImTextureData* igImFontAtlasTextureAdd(ImFontAtlas* atlas,int w,int h) { return ImFontAtlasTextureAdd(atlas,w,h); } CIMGUI_API void igImFontAtlasTextureMakeSpace(ImFontAtlas* atlas) { return ImFontAtlasTextureMakeSpace(atlas); } CIMGUI_API void igImFontAtlasTextureRepack(ImFontAtlas* atlas,int w,int h) { return ImFontAtlasTextureRepack(atlas,w,h); } CIMGUI_API void igImFontAtlasTextureGrow(ImFontAtlas* atlas,int old_w,int old_h) { return ImFontAtlasTextureGrow(atlas,old_w,old_h); } CIMGUI_API void igImFontAtlasTextureCompact(ImFontAtlas* atlas) { return ImFontAtlasTextureCompact(atlas); } CIMGUI_API void igImFontAtlasTextureGetSizeEstimate(ImVec2i *pOut,ImFontAtlas* atlas) { *pOut = ImFontAtlasTextureGetSizeEstimate(atlas); } CIMGUI_API void igImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas,ImFont* font,ImFontConfig* src) { return ImFontAtlasBuildSetupFontSpecialGlyphs(atlas,font,src); } CIMGUI_API void igImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas) { return ImFontAtlasBuildLegacyPreloadAllGlyphRanges(atlas); } CIMGUI_API void igImFontAtlasBuildGetOversampleFactors(ImFontConfig* src,ImFontBaked* baked,int* out_oversample_h,int* out_oversample_v) { return ImFontAtlasBuildGetOversampleFactors(src,baked,out_oversample_h,out_oversample_v); } CIMGUI_API void igImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas,int unused_frames) { return ImFontAtlasBuildDiscardBakes(atlas,unused_frames); } CIMGUI_API bool igImFontAtlasFontSourceInit(ImFontAtlas* atlas,ImFontConfig* src) { return ImFontAtlasFontSourceInit(atlas,src); } CIMGUI_API void igImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas,ImFont* font,ImFontConfig* src) { return ImFontAtlasFontSourceAddToFont(atlas,font,src); } CIMGUI_API void igImFontAtlasFontDestroySourceData(ImFontAtlas* atlas,ImFontConfig* src) { return ImFontAtlasFontDestroySourceData(atlas,src); } CIMGUI_API bool igImFontAtlasFontInitOutput(ImFontAtlas* atlas,ImFont* font) { return ImFontAtlasFontInitOutput(atlas,font); } CIMGUI_API void igImFontAtlasFontDestroyOutput(ImFontAtlas* atlas,ImFont* font) { return ImFontAtlasFontDestroyOutput(atlas,font); } CIMGUI_API void igImFontAtlasFontDiscardBakes(ImFontAtlas* atlas,ImFont* font,int unused_frames) { return ImFontAtlasFontDiscardBakes(atlas,font,unused_frames); } CIMGUI_API ImGuiID igImFontAtlasBakedGetId(ImGuiID font_id,float baked_size,float rasterizer_density) { return ImFontAtlasBakedGetId(font_id,baked_size,rasterizer_density); } CIMGUI_API ImFontBaked* igImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density) { return ImFontAtlasBakedGetOrAdd(atlas,font,font_size,font_rasterizer_density); } CIMGUI_API ImFontBaked* igImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density) { return ImFontAtlasBakedGetClosestMatch(atlas,font,font_size,font_rasterizer_density); } CIMGUI_API ImFontBaked* igImFontAtlasBakedAdd(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density,ImGuiID baked_id) { return ImFontAtlasBakedAdd(atlas,font,font_size,font_rasterizer_density,baked_id); } CIMGUI_API void igImFontAtlasBakedDiscard(ImFontAtlas* atlas,ImFont* font,ImFontBaked* baked) { return ImFontAtlasBakedDiscard(atlas,font,baked); } CIMGUI_API ImFontGlyph* igImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas,ImFontBaked* baked,ImFontConfig* src,const ImFontGlyph* in_glyph) { return ImFontAtlasBakedAddFontGlyph(atlas,baked,src,in_glyph); } CIMGUI_API void igImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas,ImFont* font,ImFontBaked* baked,ImFontGlyph* glyph) { return ImFontAtlasBakedDiscardFontGlyph(atlas,font,baked,glyph); } CIMGUI_API void igImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas,ImFontBaked* baked,ImFontConfig* src,ImFontGlyph* glyph,ImTextureRect* r,const unsigned char* src_pixels,ImTextureFormat src_fmt,int src_pitch) { return ImFontAtlasBakedSetFontGlyphBitmap(atlas,baked,src,glyph,r,src_pixels,src_fmt,src_pitch); } CIMGUI_API void igImFontAtlasPackInit(ImFontAtlas* atlas) { return ImFontAtlasPackInit(atlas); } CIMGUI_API ImFontAtlasRectId igImFontAtlasPackAddRect(ImFontAtlas* atlas,int w,int h,ImFontAtlasRectEntry* overwrite_entry) { return ImFontAtlasPackAddRect(atlas,w,h,overwrite_entry); } CIMGUI_API ImTextureRect* igImFontAtlasPackGetRect(ImFontAtlas* atlas,ImFontAtlasRectId id) { return ImFontAtlasPackGetRect(atlas,id); } CIMGUI_API ImTextureRect* igImFontAtlasPackGetRectSafe(ImFontAtlas* atlas,ImFontAtlasRectId id) { return ImFontAtlasPackGetRectSafe(atlas,id); } CIMGUI_API void igImFontAtlasPackDiscardRect(ImFontAtlas* atlas,ImFontAtlasRectId id) { return ImFontAtlasPackDiscardRect(atlas,id); } CIMGUI_API void igImFontAtlasUpdateNewFrame(ImFontAtlas* atlas,int frame_count,bool renderer_has_textures) { return ImFontAtlasUpdateNewFrame(atlas,frame_count,renderer_has_textures); } CIMGUI_API void igImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas,ImDrawListSharedData* data) { return ImFontAtlasAddDrawListSharedData(atlas,data); } CIMGUI_API void igImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas,ImDrawListSharedData* data) { return ImFontAtlasRemoveDrawListSharedData(atlas,data); } CIMGUI_API void igImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas,ImTextureRef old_tex,ImTextureRef new_tex) { return ImFontAtlasUpdateDrawListsTextures(atlas,old_tex,new_tex); } CIMGUI_API void igImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas) { return ImFontAtlasUpdateDrawListsSharedData(atlas); } CIMGUI_API void igImFontAtlasTextureBlockConvert(const unsigned char* src_pixels,ImTextureFormat src_fmt,int src_pitch,unsigned char* dst_pixels,ImTextureFormat dst_fmt,int dst_pitch,int w,int h) { return ImFontAtlasTextureBlockConvert(src_pixels,src_fmt,src_pitch,dst_pixels,dst_fmt,dst_pitch,w,h); } CIMGUI_API void igImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data) { return ImFontAtlasTextureBlockPostProcess(data); } CIMGUI_API void igImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data,float multiply_factor) { return ImFontAtlasTextureBlockPostProcessMultiply(data,multiply_factor); } CIMGUI_API void igImFontAtlasTextureBlockFill(ImTextureData* dst_tex,int dst_x,int dst_y,int w,int h,ImU32 col) { return ImFontAtlasTextureBlockFill(dst_tex,dst_x,dst_y,w,h,col); } CIMGUI_API void igImFontAtlasTextureBlockCopy(ImTextureData* src_tex,int src_x,int src_y,ImTextureData* dst_tex,int dst_x,int dst_y,int w,int h) { return ImFontAtlasTextureBlockCopy(src_tex,src_x,src_y,dst_tex,dst_x,dst_y,w,h); } CIMGUI_API void igImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas,ImTextureData* tex,int x,int y,int w,int h) { return ImFontAtlasTextureBlockQueueUpload(atlas,tex,x,y,w,h); } CIMGUI_API int igImTextureDataGetFormatBytesPerPixel(ImTextureFormat format) { return ImTextureDataGetFormatBytesPerPixel(format); } CIMGUI_API const char* igImTextureDataGetStatusName(ImTextureStatus status) { return ImTextureDataGetStatusName(status); } CIMGUI_API const char* igImTextureDataGetFormatName(ImTextureFormat format) { return ImTextureDataGetFormatName(format); } CIMGUI_API void igImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas) { return ImFontAtlasDebugLogTextureRequests(atlas); } CIMGUI_API bool igImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas,ImGuiMouseCursor cursor_type,ImVec2* out_offset,ImVec2* out_size,ImVec2 out_uv_border[2],ImVec2 out_uv_fill[2]) { return ImFontAtlasGetMouseCursorTexData(atlas,cursor_type,out_offset,out_size,out_uv_border,out_uv_fill); } #ifdef IMGUI_ENABLE_FREETYPE CIMGUI_API const ImFontLoader* ImGuiFreeType_GetFontLoader() { return ImGuiFreeType::GetFontLoader(); } CIMGUI_API void ImGuiFreeType_SetAllocatorFunctions(void*(*alloc_func)(size_t sz,void* user_data),void(*free_func)(void* ptr,void* user_data),void* user_data) { return ImGuiFreeType::SetAllocatorFunctions(alloc_func,free_func,user_data); } CIMGUI_API bool ImGuiFreeType_DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags) { return ImGuiFreeType::DebugEditFontLoaderFlags(p_font_loader_flags); } #endif /////////////////////////////manual written functions CIMGUI_API void ImGuiTextBuffer_appendf(ImGuiTextBuffer *self, const char *fmt, ...) { va_list args; va_start(args, fmt); self->appendfv(fmt, args); va_end(args); } CIMGUI_API float igGET_FLT_MAX() { return FLT_MAX; } CIMGUI_API float igGET_FLT_MIN() { return FLT_MIN; } CIMGUI_API ImVector_ImWchar* ImVector_ImWchar_create() { return IM_NEW(ImVector) (); } CIMGUI_API void ImVector_ImWchar_destroy(ImVector_ImWchar* self) { IM_DELETE(self); } CIMGUI_API void ImVector_ImWchar_Init(ImVector_ImWchar* p) { IM_PLACEMENT_NEW(p) ImVector(); } CIMGUI_API void ImVector_ImWchar_UnInit(ImVector_ImWchar* p) { p->~ImVector(); } #ifdef IMGUI_HAS_DOCK // NOTE: Some function pointers in the ImGuiPlatformIO structure are not C-compatible because of their // use of a complex return type. To work around this, we store a custom CimguiStorage object inside // ImGuiIO::BackendLanguageUserData, which contains C-compatible function pointer variants for these // functions. When a user function pointer is provided, we hook up the underlying ImGuiPlatformIO // function pointer to a thunk which accesses the user function pointer through CimguiStorage. struct CimguiStorage { void(*Platform_GetWindowPos)(ImGuiViewport* vp, ImVec2* out_pos); void(*Platform_GetWindowSize)(ImGuiViewport* vp, ImVec2* out_pos); }; // Gets a reference to the CimguiStorage object stored in the current ImGui context's BackendLanguageUserData. CimguiStorage& GetCimguiStorage() { ImGuiIO& io = ImGui::GetIO(); if (io.BackendLanguageUserData == NULL) { io.BackendLanguageUserData = IM_NEW(CimguiStorage)(); } return *(CimguiStorage*)io.BackendLanguageUserData; } // Thunk satisfying the signature of ImGuiPlatformIO::Platform_GetWindowPos. ImVec2 Platform_GetWindowPos_hook(ImGuiViewport* vp) { ImVec2 pos; GetCimguiStorage().Platform_GetWindowPos(vp, &pos); return pos; }; // Fully C-compatible function pointer setter for ImGuiPlatformIO::Platform_GetWindowPos. CIMGUI_API void ImGuiPlatformIO_Set_Platform_GetWindowPos(ImGuiPlatformIO* platform_io, void(*user_callback)(ImGuiViewport* vp, ImVec2* out_pos)) { CimguiStorage& storage = GetCimguiStorage(); storage.Platform_GetWindowPos = user_callback; platform_io->Platform_GetWindowPos = &Platform_GetWindowPos_hook; } // Thunk satisfying the signature of ImGuiPlatformIO::Platform_GetWindowSize. ImVec2 Platform_GetWindowSize_hook(ImGuiViewport* vp) { ImVec2 size; GetCimguiStorage().Platform_GetWindowSize(vp, &size); return size; }; // Fully C-compatible function pointer setter for ImGuiPlatformIO::Platform_GetWindowSize. CIMGUI_API void ImGuiPlatformIO_Set_Platform_GetWindowSize(ImGuiPlatformIO* platform_io, void(*user_callback)(ImGuiViewport* vp, ImVec2* out_size)) { CimguiStorage& storage = GetCimguiStorage(); storage.Platform_GetWindowSize = user_callback; platform_io->Platform_GetWindowSize = &Platform_GetWindowSize_hook; } #endif ================================================ FILE: lib/third_party/imgui/imgui/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) # https://github.com/ocornut/imgui project(imgui_imgui) set(CMAKE_CXX_STANDARD 23) if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD) add_library(imgui_imgui OBJECT source/imgui.cpp source/imgui_demo.cpp source/imgui_draw.cpp source/imgui_tables.cpp source/imgui_widgets.cpp source/misc/freetype/imgui_freetype.cpp ) target_include_directories(imgui_imgui PUBLIC include include/misc/freetype ) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_compile_options(imgui_imgui PRIVATE -Wno-unknown-warning-option) endif() find_package(Freetype REQUIRED) target_include_directories(imgui_imgui PUBLIC ${FREETYPE_INCLUDE_DIRS}) target_link_directories(imgui_imgui PUBLIC ${FREETYPE_LIBRARY_DIRS}) target_link_libraries(imgui_imgui PUBLIC ${FREETYPE_LIBRARIES} ${LUNASVG_LIBRARIES}) target_compile_definitions(imgui_imgui PRIVATE EXPORT_SYMBOLS=1) endif() add_library(imgui_includes INTERFACE) target_include_directories(imgui_includes INTERFACE include) target_include_directories(imgui_all_includes INTERFACE include include/misc/freetype) ================================================ FILE: lib/third_party/imgui/imgui/LICENSE.txt ================================================ The MIT License (MIT) Copyright (c) 2014-2023 Omar Cornut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: lib/third_party/imgui/imgui/README.md ================================================ Dear ImGui =====
"Give someone state and they'll have a bug one day, but teach them how to represent state in two separate locations that have to be kept in sync and they'll have bugs for a lifetime."
-ryg ---- [![Build Status](https://github.com/ocornut/imgui/workflows/build/badge.svg)](https://github.com/ocornut/imgui/actions?workflow=build) [![Static Analysis Status](https://github.com/ocornut/imgui/workflows/static-analysis/badge.svg)](https://github.com/ocornut/imgui/actions?workflow=static-analysis) [![Tests Status](https://github.com/ocornut/imgui_test_engine/workflows/tests/badge.svg)](https://github.com/ocornut/imgui_test_engine/actions?workflow=tests) (This library is available under a free and permissive license, but needs financial support to sustain its continued improvements. In addition to maintenance and stability there are many desirable features yet to be added. If your company is using Dear ImGui, please consider reaching out.) Businesses: support continued development and maintenance via invoiced sponsoring/support contracts:
  _E-mail: contact @ dearimgui dot com_
Individuals: support continued development and maintenance [here](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WGHNC6MBFLZ2S). Also see [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors) page. | [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Integration](#integration) | :----------------------------------------------------------: | | [Gallery](#gallery) - [Support, FAQ](#support-frequently-asked-questions-faq) - [How to help](#how-to-help) - [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors) - [Credits](#credits) - [License](#license) | | [Wiki](https://github.com/ocornut/imgui/wiki) - [Languages & frameworks backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) | ### The Pitch Dear ImGui is a **bloat-free graphical user interface library for C++**. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline-enabled application. It is fast, portable, renderer agnostic, and self-contained (no external dependencies). Dear ImGui is designed to **enable fast iterations** and to **empower programmers** to create **content creation tools and visualization / debug tools** (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal and lacks certain features commonly found in more high-level libraries. Dear ImGui is particularly suited to integration in game engines (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any applications on console platforms where operating system features are non-standard. - Minimize state synchronization. - Minimize UI-related state storage on user side. - Minimize setup and maintenance. - Easy to use to create dynamic UI which are the reflection of a dynamic data set. - 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. - Portable, minimize dependencies, run on target (consoles, phones, etc.). - Efficient runtime and memory consumption. - Battle-tested, used by [many major actors in the game industry](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui). ### Usage **The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (imgui*.cpp, imgui*.h). **No specific build process is required**. You can add the .cpp files into your existing project. **Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui. See the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide and [Integration](#integration) section of this document for more details. After Dear ImGui is set up in your application, you can use it from \_anywhere\_ in your program loop: ```cpp ImGui::Text("Hello, world %d", 123); if (ImGui::Button("Save")) MySaveFunction(); ImGui::InputText("string", buf, IM_ARRAYSIZE(buf)); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); ``` ![sample code output (dark, segoeui font, freetype)](https://user-images.githubusercontent.com/8225057/191050833-b7ecf528-bfae-4a9f-ac1b-f3d83437a2f4.png) ![sample code output (light, segoeui font, freetype)](https://user-images.githubusercontent.com/8225057/191050838-8742efd4-504d-4334-a9a2-e756d15bc2ab.png) ```cpp // Create a window called "My First Tool", with a menu bar. ImGui::Begin("My First Tool", &my_tool_active, ImGuiWindowFlags_MenuBar); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Open..", "Ctrl+O")) { /* Do stuff */ } if (ImGui::MenuItem("Save", "Ctrl+S")) { /* Do stuff */ } if (ImGui::MenuItem("Close", "Ctrl+W")) { my_tool_active = false; } ImGui::EndMenu(); } ImGui::EndMenuBar(); } // Edit a color stored as 4 floats ImGui::ColorEdit4("Color", my_color); // Generate samples and plot them float samples[100]; for (int n = 0; n < 100; n++) samples[n] = sinf(n * 0.2f + ImGui::GetTime() * 1.5f); ImGui::PlotLines("Samples", samples, 100); // Display contents in a scrolling region ImGui::TextColored(ImVec4(1,1,0,1), "Important Stuff"); ImGui::BeginChild("Scrolling"); for (int n = 0; n < 50; n++) ImGui::Text("%04d: Some text", n); ImGui::EndChild(); ImGui::End(); ``` ![my_first_tool_v188](https://user-images.githubusercontent.com/8225057/191055698-690a5651-458f-4856-b5a9-e8cc95c543e2.gif) Dear ImGui allows you to **create elaborate tools** as well as very short-lived ones. On the extreme side of short-livedness: using the Edit&Continue (hot code reload) feature of modern compilers you can add a few widgets to tweak variables while your application is running, and remove the code a minute later! Dear ImGui is not just for tweaking values. You can use it to trace a running algorithm by just emitting text commands. You can use it along with your own reflection data to browse your dataset live. You can use it to expose the internals of a subsystem in your engine, to create a logger, an inspection tool, a profiler, a debugger, an entire game-making editor/framework, etc. ### How it works The IMGUI paradigm through its API tries to minimize superfluous state duplication, state synchronization, and state retention from the user's point of view. It is less error-prone (less code and fewer bugs) than traditional retained-mode interfaces, and lends itself to creating dynamic user interfaces. Check out the Wiki's [About the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) section for more details. Dear ImGui outputs vertex buffers and command lists that you can easily render in your application. The number of draw calls and state changes required to render them is fairly small. Because Dear ImGui doesn't know or touch graphics state directly, you can call its functions anywhere in your code (e.g. in the middle of a running algorithm, or in the middle of your own rendering process). Refer to the sample applications in the examples/ folder for instructions on how to integrate Dear ImGui with your existing codebase. _A common misunderstanding is to mistake immediate mode GUI for immediate mode rendering, which usually implies hammering your driver/GPU with a bunch of inefficient draw calls and state changes as the GUI functions are called. This is NOT what Dear ImGui does. Dear ImGui outputs vertex buffers and a small list of draw calls batches. It never touches your GPU directly. The draw call batches are decently optimal and you can render them later, in your app or even remotely._ ### Releases & Changelogs See [Releases](https://github.com/ocornut/imgui/releases) page for decorated Changelogs. Reading the changelogs is a good way to keep up to date with the things Dear ImGui has to offer, and maybe will give you ideas of some features that you've been ignoring until now! ### Demo Calling the `ImGui::ShowDemoWindow()` function will create a demo window showcasing a variety of features and examples. The code is always available for reference in `imgui_demo.cpp`. [Here's how the demo looks](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v167/v167-misc.png). You should be able to build the examples from sources. If you don't, let us know! If you want to have a quick look at some Dear ImGui features, you can download Windows binaries of the demo app here: - [imgui-demo-binaries-20230704.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20230704.zip) (Windows, 1.89.7, built 2023/07/04, master) or [older binaries](https://www.dearimgui.com/binaries). The demo applications are not DPI aware so expect some blurriness on a 4K screen. For DPI awareness in your application, you can load/reload your font at a different scale and scale your style with `style.ScaleAllSizes()` (see [FAQ](https://www.dearimgui.com/faq)). ### Integration See the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide for details. On most platforms and when using C++, **you should be able to use a combination of the [imgui_impl_xxxx](https://github.com/ocornut/imgui/tree/master/backends) backends without modification** (e.g. `imgui_impl_win32.cpp` + `imgui_impl_dx11.cpp`). If your engine supports multiple platforms, consider using more imgui_impl_xxxx files instead of rewriting them: this will be less work for you, and you can get Dear ImGui running immediately. You can _later_ decide to rewrite a custom backend using your custom engine functions if you wish so. Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading a texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles, which is essentially what Backends are doing. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that: setting up a window and using backends. If you follow the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide it should in theory takes you less than an hour to integrate Dear ImGui. **Make sure to spend time reading the [FAQ](https://www.dearimgui.com/faq), comments, and the examples applications!** Officially maintained backends/bindings (in repository): - Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_Renderer, Vulkan, WebGPU. - Platforms: GLFW, SDL2/SDL3, Win32, Glut, OSX, Android. - Frameworks: Allegro5, Emscripten. [Third-party backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) wiki page: - Languages: C, C# and: Beef, ChaiScript, CovScript, Crystal, D, Go, Haskell, Haxe/hxcpp, Java, JavaScript, Julia, Kotlin, Lobster, Lua, Nim, Odin, Pascal, PureBasic, Python, ReaScript, Ruby, Rust, Swift, Zig... - Frameworks: AGS/Adventure Game Studio, Amethyst, Blender, bsf, Cinder, Cocos2d-x, Defold, Diligent Engine, Ebiten, Flexium, GML/Game Maker Studio, GLEQ, Godot, GTK3, Irrlicht Engine, JUCE, LÖVE+LUA, Mach Engine, Magnum, Marmalade, Monogame, NanoRT, nCine, Nim Game Lib, Nintendo 3DS/Switch/WiiU (homebrew), Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, raylib, SFML, Sokol, Unity, Unreal Engine 4/5, UWP, vtk, VulkanHpp, VulkanSceneGraph, Win32 GDI, WxWidgets. - Many bindings are auto-generated (by good old [cimgui](https://github.com/cimgui/cimgui) or newer/experimental [dear_bindings](https://github.com/dearimgui/dear_bindings)), you can use their metadata output to generate bindings for other languages. [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page: - Automation/testing, Text editors, node editors, timeline editors, plotting, software renderers, remote network access, memory editors, gizmos, etc. Notable and well supported extensions include [ImPlot](https://github.com/epezent/implot) and [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine). Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas. ### Gallery For more user-submitted screenshots of projects using Dear ImGui, check out the [Gallery Threads](https://github.com/ocornut/imgui/issues/6897)! For a list of third-party widgets and extensions, check out the [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page. | | | |--|--| | Custom engine [erhe](https://github.com/tksuoran/erhe) (docking branch)
[![erhe](https://user-images.githubusercontent.com/8225057/190203358-6988b846-0686-480e-8663-1311fbd18abd.jpg)](https://user-images.githubusercontent.com/994606/147875067-a848991e-2ad2-4fd3-bf71-4aeb8a547bcf.png) | Custom engine for [Wonder Boy: The Dragon's Trap](http://www.TheDragonsTrap.com) (2017)
[![the dragon's trap](https://user-images.githubusercontent.com/8225057/190203379-57fcb80e-4aec-4fec-959e-17ddd3cd71e5.jpg)](https://cloud.githubusercontent.com/assets/8225057/20628927/33e14cac-b329-11e6-80f6-9524e93b048a.png) | | Custom engine (untitled)
[![editor white](https://user-images.githubusercontent.com/8225057/190203393-c5ac9f22-b900-4d1e-bfeb-6027c63e3d92.jpg)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v160/editor_white.png) | Tracy Profiler ([github](https://github.com/wolfpld/tracy))
[![tracy profiler](https://user-images.githubusercontent.com/8225057/190203401-7b595f6e-607c-44d3-97ea-4c2673244dfb.jpg)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v176/tracy_profiler.png) | ### Support, Frequently Asked Questions (FAQ) See: [Frequently Asked Questions (FAQ)](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md) where common questions are answered. See: [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) and [Wiki](https://github.com/ocornut/imgui/wiki) for many links, references, articles. See: [Articles about the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) to read/learn about the Immediate Mode GUI paradigm. See: [Upcoming Changes](https://github.com/ocornut/imgui/wiki/Upcoming-Changes). See: [Dear ImGui Test Engine + Test Suite](https://github.com/ocornut/imgui_test_engine) for Automation & Testing. Getting started? For first-time users having issues compiling/linking/running or issues loading fonts, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions). For ANY other questions, bug reports, requests, feedback, please post on [GitHub Issues](https://github.com/ocornut/imgui/issues). Please read and fill the New Issue template carefully. Private support is available for paying business customers (E-mail: _contact @ dearimgui dot com_). **Which version should I get?** We occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to latest `master` or `docking` branch. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/issues/1542) and [Docking](https://github.com/ocornut/imgui/issues/2109) features. This branch is kept in sync with master regularly. **Who uses Dear ImGui?** See the [Quotes](https://github.com/ocornut/imgui/wiki/Quotes), [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors), and [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) Wiki pages for an idea of who is using Dear ImGui. Please add your game/software if you can! Also, see the [Gallery Threads](https://github.com/ocornut/imgui/issues/6897)! How to help ----------- **How can I help?** - See [GitHub Forum/Issues](https://github.com/ocornut/imgui/issues). - You may help with development and submit pull requests! Please understand that by submitting a PR you are also submitting a request for the maintainer to review your code and then take over its maintenance forever. PR should be crafted both in the interest of the end-users and also to ease the maintainer into understanding and accepting it. - See [Help wanted](https://github.com/ocornut/imgui/wiki/Help-Wanted) on the [Wiki](https://github.com/ocornut/imgui/wiki/) for some more ideas. - Be a [sponsor](https://github.com/ocornut/imgui/wiki/Sponsors)! Have your company financially support this project via invoiced sponsors/maintenance or by buying a license for [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine) (please reach out: omar AT dearimgui DOT com). Sponsors -------- Ongoing Dear ImGui development is and has been financially supported by users and private sponsors.
Please see the **[detailed list of current and past Dear ImGui supporters](https://github.com/ocornut/imgui/wiki/Sponsors)** for details.
From November 2014 to December 2019, ongoing development has also been financially supported by its users on Patreon and through individual donations. **THANK YOU to all past and present supporters for helping to keep this project alive and thriving!** Dear ImGui is using software and services provided free of charge for open source projects: - [PVS-Studio](https://www.viva64.com/en/b/0570/) for static analysis. - [GitHub actions](https://github.com/features/actions) for continuous integration systems. - [OpenCppCoverage](https://github.com/OpenCppCoverage/OpenCppCoverage) for code coverage analysis. Credits ------- Developed by [Omar Cornut](https://www.miracleworld.net) and every direct or indirect [contributors](https://github.com/ocornut/imgui/graphs/contributors) to the GitHub. The early version of this library was developed with the support of [Media Molecule](https://www.mediamolecule.com) and first used internally on the game [Tearaway](https://tearaway.mediamolecule.com) (PS Vita). Recurring contributors include Rokas Kupstys [@rokups](https://github.com/rokups) (2020-2022): a good portion of work on automation system and regression tests now available in [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine). Sponsoring, maintenance/support contracts and other B2B transactions are hosted and handled by [Disco Hello](https://www.discohello.com). Omar: "I first discovered the IMGUI paradigm at [Q-Games](https://www.q-games.com) where Atman Binstock had dropped his own simple implementation in the codebase, which I spent quite some time improving and thinking about. It turned out that Atman was exposed to the concept directly by working with Casey. When I moved to Media Molecule I rewrote a new library trying to overcome the flaws and limitations of the first one I've worked with. It became this library and since then I have spent an unreasonable amount of time iterating and improving it." Embeds [ProggyClean.ttf](http://upperbounds.net) font by Tristan Grimmer (MIT license).
Embeds [stb_textedit.h, stb_truetype.h, stb_rect_pack.h](https://github.com/nothings/stb/) by Sean Barrett (public domain). Inspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. Also thank you to everyone posting feedback, questions and patches on GitHub. License ------- Dear ImGui is licensed under the MIT License, see [LICENSE.txt](https://github.com/ocornut/imgui/blob/master/LICENSE.txt) for more information. ================================================ FILE: lib/third_party/imgui/imgui/include/imconfig.h ================================================ //----------------------------------------------------------------------------- // COMPILE-TIME OPTIONS FOR DEAR IMGUI // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. //----------------------------------------------------------------------------- // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. //----------------------------------------------------------------------------- // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. //----------------------------------------------------------------------------- #pragma once #include // For alloc() on ARM64 Windows #if __has_include() #include #endif //---- Define assertion handler. Defaults to calling assert(). // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts namespace hex::dbg { [[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString); } #if defined(__PRETTY_FUNCTION__) #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __PRETTY_FUNCTION__, #_EXPR); } } while(0) #elif defined(__FUNCSIG__) #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __FUNCSIG__, #_EXPR); } } while(0) #else #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __FUNCTION__, #_EXPR); } } while(0) #endif //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. // DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. //#define IMGUI_API __declspec( dllexport ) //#define IMGUI_API __declspec( dllimport ) #if defined(_MSC_VER) #if EXPORT_SYMBOLS == 1 #define IMGUI_API __declspec(dllexport) #define IMGUI_IMPL_API __declspec(dllexport) #define IMPLOT_API __declspec(dllexport) #define IMPLOT_IMPL_API __declspec(dllexport) #define IMPLOT3D_API __declspec(dllexport) #define IMPLOT3D_IMPL_API __declspec(dllexport) #else #define IMGUI_API __declspec(dllimport) #define IMGUI_IMPL_API __declspec(dllimport) #define IMPLOT_API __declspec(dllimport) #define IMPLOT_IMPL_API __declspec(dllimport) #define IMPLOT3D_API __declspec(dllimport) #define IMPLOT3D_IMPL_API __declspec(dllimport) #endif #endif //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. //---- Disable all of Dear ImGui or don't implement standard windows/tools. // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp. //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. //#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88). //---- 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. (user32.lib/.a, kernel32.lib/.a) //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#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(). //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //---- Include imgui_user.h at the end of imgui.h as a convenience //#define IMGUI_INCLUDE_IMGUI_USER_H //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) //#define IMGUI_USE_BGRA_PACKED_COLOR //---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) //#define IMGUI_USE_WCHAR32 //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" //#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION //---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) // Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h. //#define IMGUI_USE_STB_SPRINTF //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. //#define IMGUI_ENABLE_FREETYPE //---- Use stb_truetype to build and rasterize the font atlas (default) // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. //#define IMGUI_ENABLE_STB_TRUETYPE //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. // This will be inlined as part of ImVec2 and ImVec4 class declarations. /* #define IM_VEC2_CLASS_EXTRA \ constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \ operator MyVec2() const { return MyVec2(x,y); } #define IM_VEC4_CLASS_EXTRA \ constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \ operator MyVec4() const { return MyVec4(x,y,z,w); } */ //---- ...Or use Dear ImGui's own very basic math operators. //#define IMGUI_DEFINE_MATH_OPERATORS //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. //#define ImDrawIdx unsigned int //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) //struct ImDrawList; //struct ImDrawCmd; //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); //#define ImDrawCallback MyImDrawCallback //---- Debug Tools: Macro to break in Debugger // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) //#define IM_DEBUG_BREAK IM_ASSERT(0) //#define IM_DEBUG_BREAK __debugbreak() //---- Debug Tools: Enable slower asserts //#define IMGUI_DEBUG_PARANOID //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. /* namespace ImGui { void MyFunction(const char* name, const MyMatrix44& v); } */ #define IMGUI_ENABLE_TEST_ENGINE // IMPLOT CONFIG #define IMPLOT_CUSTOM_NUMERIC_TYPES (ImS8)(ImU8)(ImS16)(ImU16)(ImS32)(ImU32)(ImS64)(ImU64)(float)(double)(long double) #define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) #define IM_FLOOR IM_TRUNC #define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DISABLE_OBSOLETE_FUNCTIONS #define IMGUI_DISABLE_OBSOLETE_KEYIO #define IMGUI_ENABLE_FREETYPE #define ImDrawIdx unsigned int #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX #define IMGUI_USE_WCHAR32 #define IMGUI_USE_LEGACY_CRC32_ADLER 1 #if defined(IMGUI_TEST_ENGINE) #include "imgui_te_imconfig.h" #endif ================================================ FILE: lib/third_party/imgui/imgui/include/imgui.h ================================================ // dear imgui, v1.92.7 WIP // (headers) // Help: // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. // - Read top of imgui.cpp for more details, links and comments. // - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including imgui.h (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. // Resources: // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) // - Homepage ................... https://github.com/ocornut/imgui // - Releases & Changelog ....... https://github.com/ocornut/imgui/releases // - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!) // - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) // - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) // - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines) // - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui // - Issues & support ........... https://github.com/ocornut/imgui/issues // - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps) // - Web version of the Demo .... https://pthom.github.io/imgui_manual (w/ source code browser) // For FIRST-TIME users having issues compiling/linking/running: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. // EVERYTHING ELSE should be asked in 'Issues'! We are building a database of cross-linked knowledge there. // Since 1.92, we encourage font loading questions to also be posted in 'Issues'. // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.7 WIP" #define IMGUI_VERSION_NUM 19265 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 #define IMGUI_HAS_VIEWPORT // In 'docking' WIP branch. #define IMGUI_HAS_DOCK // In 'docking' WIP branch. /* Index of this file: // [SECTION] Header mess // [SECTION] Forward declarations and basic types // [SECTION] Texture identifiers (ImTextureID, ImTextureRef) // [SECTION] Dear ImGui end-user API functions // [SECTION] Flags & Enumerations // [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) // [SECTION] Helpers: Debug log, Memory allocations macros, ImVector<> // [SECTION] ImGuiStyle // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiWindowClass, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) // [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage, ImGuiSelectionExternalStorage) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFontBaked, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) // [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformMonitor, ImGuiPlatformImeData) // [SECTION] Obsolete functions and types */ #pragma once // Configuration file with compile-time options // (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system) #ifdef IMGUI_USER_CONFIG #include IMGUI_USER_CONFIG #endif #include "imconfig.h" #ifndef IMGUI_DISABLE //----------------------------------------------------------------------------- // [SECTION] Header mess //----------------------------------------------------------------------------- // Includes #include // FLT_MIN, FLT_MAX #include // va_list, va_start, va_end #include // ptrdiff_t, NULL #include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp // Define attributes of all API symbols declarations (e.g. for DLL under Windows) // IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h) // Using dear imgui via a shared library is not recommended: we don't guarantee backward nor forward ABI compatibility + this is a call-heavy library and function call overhead adds up. #ifndef IMGUI_API #define IMGUI_API #endif #ifndef IMGUI_IMPL_API #define IMGUI_IMPL_API IMGUI_API #endif // Helper Macros // (note: compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.) #ifndef IM_ASSERT #include #define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h #endif #define IM_COUNTOF(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! #define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. #define IM_STRINGIFY_HELPER(_EXPR) #_EXPR #define IM_STRINGIFY(_EXPR) IM_STRINGIFY_HELPER(_EXPR) // Preprocessor idiom to stringify e.g. an integer or a macro. // Check that version and structures layouts are matching between compiled imgui code and caller. Read comments above DebugCheckVersionAndDataLayout() for details. #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. // (MSVC provides an equivalent mechanism via SAL Annotations but it requires the macros in a different // location. e.g. #include + void myprintf(_Printf_format_string_ const char* format, ...), // and only works when using Code Analysis, rather than just normal compiling). // (see https://github.com/ocornut/imgui/issues/8871 for a patch to enable this for MSVC's Code Analysis) #if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) #elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) #define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) #else #define IM_FMTARGS(FMT) #define IM_FMTLIST(FMT) #endif // Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions) #if defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(IMGUI_DEBUG_PARANOID) #define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off)) #define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop)) #else #define IM_MSVC_RUNTIME_CHECKS_OFF #define IM_MSVC_RUNTIME_CHECKS_RESTORE #endif // Warnings #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). #endif #if defined(__clang__) #pragma clang diagnostic push #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast #pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant #pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif //----------------------------------------------------------------------------- // [SECTION] Forward declarations and basic types //----------------------------------------------------------------------------- // Scalar data types typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string) typedef signed char ImS8; // 8-bit signed integer 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) typedef signed long long ImS64; // 64-bit signed integer typedef unsigned long long ImU64; // 64-bit unsigned integer // Forward declarations: ImDrawList, ImFontAtlas layer struct ImDrawChannel; // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and 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 ImDrawListSplitter; // Helper to split a draw list into different layers which can be drawn into out of order, then flattened back. 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 ImFontAtlasBuilder; // Opaque storage for building a ImFontAtlas struct ImFontAtlasRect; // Output of ImFontAtlas::GetCustomRect() when using custom rectangles. struct ImFontBaked; // Baked data for a ImFont at a given size. struct ImFontConfig; // Configuration data when adding a font or merging fonts 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 ImFontLoader; // Opaque interface to a font loading backend (stb_truetype, FreeType etc.). struct ImTextureData; // Specs and pixel storage for a texture used by Dear ImGui. struct ImTextureRect; // Coordinates of a rectangle within a texture. struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using) // Forward declarations: ImGui layer struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) struct ImGuiIO; // Main configuration and I/O between your application and ImGui (also see: ImGuiPlatformIO) struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions. struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiMultiSelectIO; // Structure to interact with a BeginMultiSelect()/EndMultiSelect() block struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPlatformIO; // Interface between platform/renderer backends and ImGui (e.g. Clipboard, IME, Multi-Viewport support). Extends ImGuiIO. struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. struct ImGuiPlatformMonitor; // Multi-viewport support: user-provided bounds for each connected monitor/display. Used when positioning popups and tooltips to avoid them straddling monitors struct ImGuiSelectionBasicStorage; // Optional helper to store multi-selection state + apply multi-selection requests. struct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage. struct ImGuiSelectionRequest; // A selection request (stored in ImGuiMultiSelectIO) struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage (container sorted by key) struct ImGuiStoragePair; // Helper for key->value storage (pair) struct ImGuiStyle; // Runtime data for styling/colors struct ImGuiTableSortSpecs; // Sorting specifications for a table (often handling sort specs for a single column, occasionally more) struct ImGuiTableColumnSortSpecs; // Sorting specification for one column of a table 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[,bbbbb][,ccccc]") struct ImGuiViewport; // A Platform Window (always 1 unless multi-viewport are enabled. One per platform window to output to). In the future may represent Platform Monitor struct ImGuiWindowClass; // Window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info) // Enumerations // - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! // - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. // - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. // - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. enum ImGuiDir : int; // -> enum ImGuiDir // Enum: A cardinal direction (Left, Right, Up, Down) enum ImGuiKey : int; // -> enum ImGuiKey // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value) enum ImGuiMouseSource : int; // -> enum ImGuiMouseSource // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen) enum ImGuiSortDirection : ImU8; // -> enum ImGuiSortDirection // Enum: A sorting direction (ascending or descending) typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle) typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor shape typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor() // Flags (declared as int to allow using as flags without overhead, and to not pollute the top of this file) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! // - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. // - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. // - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Internal, do not use! typedef int ImFontFlags; // -> enum ImFontFlags_ // Flags: for ImFont typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for InvisibleButton() typedef int ImGuiChildFlags; // -> enum ImGuiChildFlags_ // Flags: for BeginChild() typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc. typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() typedef int ImGuiDockNodeFlags; // -> enum ImGuiDockNodeFlags_ // Flags: for DockSpace() typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for BeginDragDropSource(), AcceptDragDropPayload() typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for Shortcut(), SetNextItemShortcut() typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), shared by all items typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values. typedef int ImGuiListClipperFlags; // -> enum ImGuiListClipperFlags_// Flags: for ImGuiListClipper typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() typedef int ImGuiMultiSelectFlags; // -> enum ImGuiMultiSelectFlags_// Flags: for BeginMultiSelect() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar() typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem() typedef int ImGuiTableFlags; // -> enum ImGuiTableFlags_ // Flags: For BeginTable() typedef int ImGuiTableColumnFlags; // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn() typedef int ImGuiTableRowFlags; // -> enum ImGuiTableRowFlags_ // Flags: For TableNextRow() typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader() typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild() // Character types // (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. #ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] typedef ImWchar32 ImWchar; #else typedef ImWchar16 ImWchar; #endif // Multi-Selection item index or identifier when using BeginMultiSelect() // - Used by SetNextItemSelectionUserData() + and inside ImGuiMultiSelectIO structure. // - Most users are likely to use this store an item INDEX but this may be used to store a POINTER/ID as well. Read comments near ImGuiMultiSelectIO for details. typedef ImS64 ImGuiSelectionUserData; // Callback and functions types typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() // ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type] // - This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. // - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec2 { float x, y; constexpr ImVec2() : x(0.0f), y(0.0f) { } constexpr ImVec2(float _x, float _y) : x(_x), y(_y) { } float& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return ((float*)(void*)(char*)this)[idx]; } // We very rarely use this [] operator, so the assert overhead is fine. float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return ((const float*)(const void*)(const char*)this)[idx]; } #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 }; // ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type] struct ImVec4 { float x, y, z, w; constexpr ImVec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) { } constexpr ImVec4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) { } #ifdef IM_VEC4_CLASS_EXTRA IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. #endif }; IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] Texture identifiers (ImTextureID, ImTextureRef) //----------------------------------------------------------------------------- // ImTextureID = backend specific, low-level identifier for a texture uploaded in GPU/graphics system. // [Compile-time configurable type] // - When a Rendered Backend creates a texture, it store its native identifier into a ImTextureID value. // (e.g. Used by DX11 backend to a `ID3D11ShaderResourceView*`; Used by OpenGL backends to store `GLuint`; // Used by SDLGPU backend to store a `SDL_GPUTextureSamplerBinding*`, etc.). // - User may submit their own textures to e.g. ImGui::Image() function by passing this value. // - During the rendering loop, the Renderer Backend retrieve the ImTextureID, which stored inside a // ImTextureRef, which is stored inside a ImDrawCmd. // - Compile-time type configuration: // - To use something other than a 64-bit value: add '#define ImTextureID MyTextureType*' in your imconfig.h file. // - This can be whatever to you want it to be! read the FAQ entry about textures for details. // - You may decide to store a higher-level structure containing texture, sampler, shader etc. with various // constructors if you like. You will need to implement ==/!= operators. // History: // - In v1.91.4 (2024/10/08): the default type for ImTextureID was changed from 'void*' to 'ImU64'. This allowed backends requiring 64-bit worth of data to build on 32-bit architectures. Use intermediary intptr_t cast and read FAQ if you have casting warnings. // - In v1.92.0 (2025/06/11): added ImTextureRef which carry either a ImTextureID either a pointer to internal texture atlas. All user facing functions taking ImTextureID changed to ImTextureRef #ifndef ImTextureID typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or integer). A majority of backends are ok with that. #endif // Define this if you need to change the invalid value for your backend. // - in v1.92.7 (2025/03/12): we changed default value from 0 to -1 as it is a better default, which supports storing offsets/indices. // - If this is causing problem with your custom ImTextureID definition, you can add '#define ImTextureID_Invalid' to your imconfig + please report this to GitHub. #ifndef ImTextureID_Invalid #define ImTextureID_Invalid ((ImTextureID)-1) #endif // ImTextureRef = higher-level identifier for a texture. Store a ImTextureID _or_ a ImTextureData*. // The identifier is valid even before the texture has been uploaded to the GPU/graphics system. // This is what gets passed to functions such as `ImGui::Image()`, `ImDrawList::AddImage()`. // This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering. // - When a texture is created by user code (e.g. custom images), we directly store the low-level ImTextureID. // Because of this, when displaying your own texture you are likely to ever only manage ImTextureID values on your side. // - When a texture is created by the backend, we stores a ImTextureData* which becomes an indirection // to extract the ImTextureID value during rendering, after texture upload has happened. // - To create a ImTextureRef from a ImTextureData you can use ImTextureData::GetTexRef(). // We intentionally do not provide an ImTextureRef constructor for this: we don't expect this // to be frequently useful to the end-user, and it would be erroneously called by many legacy code. // - If you want to bind the current atlas when using custom rectangle, you can use io.Fonts->TexRef. // - Binding generators for languages such as C (which don't have constructors), should provide a helper, e.g. // inline ImTextureRef ImTextureRefFromID(ImTextureID tex_id) { ImTextureRef tex_ref = { ._TexData = NULL, .TexID = tex_id }; return tex_ref; } // In 1.92 we changed most drawing functions using ImTextureID to use ImTextureRef. // We intentionally do not provide an implicit ImTextureRef -> ImTextureID cast operator because it is technically lossy to convert ImTextureRef to ImTextureID before rendering. IM_MSVC_RUNTIME_CHECKS_OFF struct ImTextureRef { ImTextureRef() { _TexData = NULL; _TexID = ImTextureID_Invalid; } ImTextureRef(ImTextureID tex_id) { _TexData = NULL; _TexID = tex_id; } #if !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(ImTextureID) ImTextureRef(void* tex_id) { _TexData = NULL; _TexID = (ImTextureID)(size_t)tex_id; } // For legacy backends casting to ImTextureID #endif inline ImTextureID GetTexID() const; // == (_TexData ? _TexData->TexID : _TexID) // Implemented below in the file. // Members (either are set, never both!) ImTextureData* _TexData; // A texture, generally owned by a ImFontAtlas. Will convert to ImTextureID during render loop, after texture has been uploaded. ImTextureID _TexID; // _OR_ Low-level backend texture identifier, if already uploaded or created by user/app. Generally provided to e.g. ImGui::Image() calls. }; IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] Dear ImGui end-user API functions // (Note that ImGui:: being a namespace, you can add extra ImGui:: functions in your own separate file. Please don't modify imgui source files!) //----------------------------------------------------------------------------- namespace ImGui { // Context creation and access // - Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between contexts. // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for details. IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL); IMGUI_API void DestroyContext(ImGuiContext* ctx = NULL); // NULL = destroy current context IMGUI_API ImGuiContext* GetCurrentContext(); IMGUI_API void SetCurrentContext(ImGuiContext* ctx); // Main IMGUI_API ImGuiIO& GetIO(); // access the ImGuiIO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags) IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // access the ImGuiPlatformIO structure (mostly hooks/functions to connect to platform/renderer and OS Clipboard, IME etc.) IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleColor(), PushStyleVar() to modify style mid-frame! IMGUI_API void NewFrame(); // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame(). IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all! IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData(). IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). Call ImGui_ImplXXXX_RenderDrawData() function in your Renderer Backend to render. // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. 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/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. IMGUI_API void ShowDebugLogWindow(bool* p_open = NULL); // create Debug Log window. display a simplified log of important dear imgui events. IMGUI_API void ShowIDStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. 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 an end-user (mouse/keyboard controls). IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.80 WIP" (essentially the value for IMGUI_VERSION from the compiled version of imgui.cpp) // Styles IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style // Windows // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack. // - 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. // - You may append multiple times to the same window during the same frame by calling Begin()/End() pairs multiple times. // Some information such as 'flags' or 'p_open' will only be considered by the first call to Begin(). // - 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! // [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions // such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding // BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] // - 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(); // 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. // - Before 1.90 (November 2023), the "ImGuiChildFlags child_flags = 0" parameter was "bool border = false". // This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Borders == true. // Consider updating your old code: // BeginChild("Name", size, false) -> Begin("Name", size, 0); or Begin("Name", size, ImGuiChildFlags_None); // BeginChild("Name", size, true) -> Begin("Name", size, ImGuiChildFlags_Borders); // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)): // == 0.0f: use remaining parent window size for this axis. // > 0.0f: use specified size for this axis. // < 0.0f: right/bottom-align to specified distance from available content boundaries. // - Specifying ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY makes the sizing automatic based on child contents. // Combining both ImGuiChildFlags_AutoResizeX _and_ ImGuiChildFlags_AutoResizeY defeats purpose of a scrolling region and is NOT recommended. // - 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. // [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions // such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding // BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0); IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0); 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 hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives IMGUI_API float GetWindowDpiScale(); // get DPI scale currently associated to the current window's viewport. IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead) IMGUI_API ImVec2 GetWindowSize(); // get current window size (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead) IMGUI_API float GetWindowWidth(); // get current window width (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().x. IMGUI_API float GetWindowHeight(); // get current window height (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().y. IMGUI_API ImGuiViewport*GetWindowViewport(); // get viewport currently associated to the current window. // Window manipulation // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). 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. 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() 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 0.0f or FLT_MAX if you don't want limits. Use -1 for both min and max of same axis to preserve current size (which itself is a constraint). Use callback to apply non-trivial programmatic constraints. IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. 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 / top-most. call before Begin() IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // set next window scrolling value (use < 0.0f to not affect a given axis). IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. IMGUI_API void SetNextWindowViewport(ImGuiID viewport_id); // set next window viewport 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(). IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus(). IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. 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. IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / top-most. use NULL to remove focus. // Windows Scrolling // - Any change of Scroll will be applied at the beginning of next frame in the first call to Begin(). // - You may instead use SetNextWindowScroll() prior to calling Begin() to avoid this delay, as an alternative to using SetScrollX()/SetScrollY(). IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()] IMGUI_API float GetScrollY(); // get scrolling amount [0 .. GetScrollMaxY()] 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 float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.x - WindowSize.x - DecorationsSize.x IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.y - WindowSize.y - DecorationsSize.y IMGUI_API void SetScrollHereX(float center_x_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_x_ratio=0.0: left, 0.5: center, 1.0: right. 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 SetScrollFromPosX(float local_x, float center_x_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. 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 (font) // - PushFont(font, 0.0f) // Change font and keep current size // - PushFont(NULL, 20.0f) // Keep font and change current size // - PushFont(font, 20.0f) // Change font and set size to 20.0f // - PushFont(font, style.FontSizeBase * 2.0f) // Change font and set size to be twice bigger than current size. // - PushFont(font, font->LegacySize) // Change font and set size to size passed to AddFontXXX() function. Same as pre-1.92 behavior. // *IMPORTANT* before 1.92, fonts had a single size. They can now be dynamically be adjusted. // - In 1.92 we have REMOVED the single parameter version of PushFont() because it seems like the easiest way to provide an error-proof transition. // - PushFont(font) before 1.92 = PushFont(font, font->LegacySize) after 1.92 // Use default font size as passed to AddFontXXX() function. // *IMPORTANT* global scale factors are applied over the provided size. // - Global scale factors are: 'style.FontScaleMain', 'style.FontScaleDpi' and maybe more. // - If you want to apply a factor to the _current_ font size: // - CORRECT: PushFont(NULL, style.FontSizeBase) // use current unscaled size == does nothing // - CORRECT: PushFont(NULL, style.FontSizeBase * 2.0f) // use current unscaled size x2 == make text twice bigger // - INCORRECT: PushFont(NULL, GetFontSize()) // INCORRECT! using size after global factors already applied == GLOBAL SCALING FACTORS WILL APPLY TWICE! // - INCORRECT: PushFont(NULL, GetFontSize() * 2.0f) // INCORRECT! using size after global factors already applied == GLOBAL SCALING FACTORS WILL APPLY TWICE! IMGUI_API void PushFont(ImFont* font, float font_size_base_unscaled); // Use NULL as a shortcut to keep current font. Use 0.0f to keep current size. IMGUI_API void PopFont(); IMGUI_API ImFont* GetFont(); // get current font IMGUI_API float GetFontSize(); // get current scaled font size (= height in pixels). AFTER global scale factors applied. *IMPORTANT* DO NOT PASS THIS VALUE TO PushFont()! Use ImGui::GetStyle().FontSizeBase to get value before global scale factors. IMGUI_API ImFontBaked* GetFontBaked(); // get current font bound at current size // == GetFont()->GetFontBaked(GetFontSize()) // Parameters stacks (shared) IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); // modify a style color. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); IMGUI_API void PopStyleColor(int count = 1); IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame()! IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. " IMGUI_API void PushStyleVarX(ImGuiStyleVar idx, float val_x); // modify X component of a style ImVec2 variable. " IMGUI_API void PushStyleVarY(ImGuiStyleVar idx, float val_y); // modify Y component of a style ImVec2 variable. " IMGUI_API void PopStyleVar(int count = 1); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true) IMGUI_API void PopItemFlag(); // Parameters stacks (current window) IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). IMGUI_API void PopItemWidth(); IMGUI_API void SetNextItemWidth(float item_width); // set width of the _next_ common large "item+label" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side) IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions. IMGUI_API void PushTextWrapPos(float wrap_local_pos_x = 0.0f); // push word-wrapping position 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(); // Style read access // - Use the ShowStyleEditor() function to interactively see/edit the colors. IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a white pixel, useful to draw custom shapes via the ImDrawList API IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList IMGUI_API ImU32 GetColorU32(ImU32 col, float alpha_mul = 1.0f); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList 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. // Layout cursor positioning // - 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. // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget. // - YOU CAN DO 99% OF WHAT YOU NEED WITH ONLY GetCursorScreenPos() and GetContentRegionAvail(). // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API: // - Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward. // - Window-local coordinates: SameLine(offset), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), PushTextWrapPos() // - Window-local coordinates: GetContentRegionMax(), GetWindowContentRegionMin(), GetWindowContentRegionMax() --> all obsoleted. YOU DON'T NEED THEM. // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates. Try not to use it. IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position, absolute coordinates. THIS IS YOUR BEST FRIEND (prefer using this rather than GetCursorPos(), also more useful to work with ImDrawList API). IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position, absolute coordinates. THIS IS YOUR BEST FRIEND. IMGUI_API ImVec2 GetContentRegionAvail(); // available space from current position. THIS IS YOUR BEST FRIEND. IMGUI_API ImVec2 GetCursorPos(); // [window-local] cursor position in window-local coordinates. This is not your best friend. IMGUI_API float GetCursorPosX(); // [window-local] " IMGUI_API float GetCursorPosY(); // [window-local] " IMGUI_API void SetCursorPos(const ImVec2& local_pos); // [window-local] " IMGUI_API void SetCursorPosX(float local_x); // [window-local] " IMGUI_API void SetCursorPosY(float local_y); // [window-local] " IMGUI_API ImVec2 GetCursorStartPos(); // [window-local] initial cursor position, in window-local coordinates. Call GetCursorScreenPos() after Begin() to get the absolute coordinates version. // Other layout functions 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 offset_from_start_x=0.0f, float spacing=-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 a 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 indent_w, or style.IndentSpacing if indent_w <= 0 IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0 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 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) IMGUI_API float GetFrameHeight(); // ~ FontSize + style.FramePadding.y * 2 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 (docs/FAQ.md or http://dearimgui.com/faq) for more details about how ID are handled in dear imgui. // - Those questions are answered and impacted by understanding of the ID stack system: // - "Q: Why is my widget not reacting when I click on it?" // - "Q: How can I have widgets with an empty label?" // - "Q: How can I have multiple widgets with the same label?" // - Short version: ID are hashes of the entire ID stack. 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 "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 + 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); IMGUI_API ImGuiID GetID(int int_id); // Widgets: Text 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_API void Text(const char* fmt, ...) IM_FMTARGS(1); // formatted text IMGUI_API void TextV(const char* fmt, 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(); IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, 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(); IMGUI_API void TextDisabledV(const char* fmt, 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(). IMGUI_API void TextWrappedV(const char* fmt, 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 IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args) IM_FMTLIST(2); IMGUI_API void BulletText(const char* fmt, ...) IM_FMTARGS(1); // shortcut for Bullet()+Text() IMGUI_API void BulletTextV(const char* fmt, va_list args) IM_FMTLIST(1); IMGUI_API void SeparatorText(const char* label); // currently: formatted text with a horizontal line // Widgets: Main // - Most widgets return true when the value has been changed or when pressed/selected // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state. IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0, 0)); // button IMGUI_API bool SmallButton(const char* label); // button with (FramePadding.y == 0) to easily embed within text IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible 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 bool Checkbox(const char* label, bool* v); IMGUI_API bool CheckboxFlags(const char* label, int* flags, int flags_value); IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; } IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL); IMGUI_API void Bullet(); // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses IMGUI_API bool TextLink(const char* label); // hyperlink text button, return true when clicked IMGUI_API bool TextLinkOpenURL(const char* label, const char* url = NULL); // hyperlink text button, automatically open file/url when clicked // Widgets: Images // - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. // - Image() pads adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side. // - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. // - An obsolete version of Image(), before 1.91.9 (March 2025), had a 'tint_col' parameter which is now supported by the ImageWithBg() function. IMGUI_API void Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1)); IMGUI_API void ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); IMGUI_API bool ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Widgets: Combo Box (Dropdown) // - The 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. This is analogous to how ListBox are created. 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, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1); // Widgets: Drag Sliders // - Ctrl+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every function, 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. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For keyboard/gamepad navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). // - Use v_min < v_max to clamp edits to given limits. Note that Ctrl+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // - Legacy: Pre-1.78 there are DragXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. // If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361 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", ImGuiSliderFlags flags = 0); // 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", ImGuiSliderFlags flags = 0); 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", ImGuiSliderFlags flags = 0); 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", ImGuiSliderFlags flags = 0); 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, ImGuiSliderFlags flags = 0); 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", ImGuiSliderFlags flags = 0); // If v_min >= v_max we have no bound 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", ImGuiSliderFlags flags = 0); 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", ImGuiSliderFlags flags = 0); 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", ImGuiSliderFlags flags = 0); 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, ImGuiSliderFlags flags = 0); IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Regular Sliders // - Ctrl+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - 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. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Legacy: Pre-1.78 there are SliderXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. // If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361 IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); 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", ImGuiSliderFlags flags = 0); IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0); IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0); IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Input with Keyboard // - If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp! // - 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 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* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little color square that can be left-clicked to open a picker, and right-clicked to open an option menu.) // - 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); IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed. 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. IMGUI_API bool TreeNode(const char* label); IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorrelate 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); IMGUI_API bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0); IMGUI_API bool TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, 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); 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_API void TreePush(const void* ptr_id); // " IMGUI_API void TreePop(); // ~ Unindent()+PopID() 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_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(). IMGUI_API bool CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags = 0); // when 'p_visible != NULL': if '*p_visible==true' display an additional small close button on upper right of the header which will set the bool to false when clicked, if '*p_visible==false' don't display the header. IMGUI_API void SetNextItemOpen(bool is_open, ImGuiCond cond = 0); // set next TreeNode/CollapsingHeader open state. IMGUI_API void SetNextItemStorageID(ImGuiID storage_id); // set id to use for open/close storage (default to same as item id). IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id); // retrieve tree node open/close state. // 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. This is so a series of selected Selectable appear contiguous. 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. // Multi-selection system for Selectable(), Checkbox(), TreeNode() functions [BETA] // - This enables standard multi-selection/range-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. // - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else). // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo. // - TreeNode() is technically supported but... using this correctly is more complicated. You need some sort of linear/random access to your tree, // which is suited to advanced trees setups already implementing filters and clipper. We will work simplifying the current demo. // - 'selection_size' and 'items_count' parameters are optional and used by a few features. If they are costly for you to compute, you may avoid them. IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size = -1, int items_count = -1); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); IMGUI_API bool IsItemToggledSelection(); // Was the last item selection state toggled? Useful if you need the per-item information _before_ reaching EndMultiSelect(). We only returns toggle _event_ in order to handle clipping correctly. // Widgets: List Boxes // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. // - If you don't need a label you can probably simply use BeginChild() with the ImGuiChildFlags_FrameStyle flag for the same result. // - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items. // - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analogous to how Combos are created. // - Choose frame width: size.x > 0.0f: custom / size.x < 0.0f or -FLT_MIN: right-align / size.x = 0.0f (default): use current ItemWidth // - Choose frame height: size.y > 0.0f: custom / size.y < 0.0f or -FLT_MIN: bottom-align / size.y = 0.0f (default): arbitrary default height which can fit ~7 items IMGUI_API bool BeginListBox(const char* label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region IMGUI_API void EndListBox(); // only call EndListBox() if BeginListBox() returned true! 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, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1); // Widgets: Data Plotting // - Consider using ImPlot (https://github.com/epezent/implot) which is much better! 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)); 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)); 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. // - 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); IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); // Widgets: Menus // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar. // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it. // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it. // - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment. IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true! IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. 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 // Tooltips // - Tooltips are windows following the mouse. They do not take focus away. // - A tooltip window can contain items of any types. // - SetTooltip() is more or less a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom (with a subtlety that it discard any previously submitted tooltip) IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true! IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip. Often used after a ImGui::IsItemHovered() check. Override any previous call to SetTooltip(). IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); // Tooltips: helpers for showing a tooltip when hovering an item // - BeginItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip) && BeginTooltip())' idiom. // - SetItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) { SetTooltip(...); }' idiom. // - Where 'ImGuiHoveredFlags_ForTooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on active input type. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. IMGUI_API bool BeginItemTooltip(); // begin/append a tooltip window if preceding item was hovered. IMGUI_API void SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip if preceding item was hovered. override any previous call to SetTooltip(). IMGUI_API void SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); // Popups, Modals // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. // - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls. // - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time. // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). // - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack. // This is sometimes leading to confusing mistakes. May rework this in the future. // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards if returned true. ImGuiWindowFlags are forwarded to the window. // - BeginPopupModal(): block every interaction behind the window, cannot be closed by user, add a dimming background, has a title bar. 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. IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it. IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! // Popups: open/close functions // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. // - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually. // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options). // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup(). // - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened. IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. // Popups: Open+Begin popup combined functions helpers to create context menus. // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. // - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future. // - IMPORTANT: If you ever used the left mouse button with BeginPopupContextXXX() helpers before 1.92.6: // - Before this version, OpenPopupOnItemClick(), BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid() had 'a ImGuiPopupFlags popup_flags = 1' default value in their function signature. // - Before: Explicitly passing a literal 0 meant ImGuiPopupFlags_MouseButtonLeft. The default = 1 meant ImGuiPopupFlags_MouseButtonRight. // - After: The default = 0 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 1 also means ImGuiPopupFlags_MouseButtonRight (if legacy behavior are enabled) or will assert (if legacy behavior are disabled). // - TL;DR: if you don't want to use right mouse button for popups, always specify it explicitly using a named ImGuiPopupFlags_MouseButtonXXXX value. // - Read "API BREAKING CHANGES" 2026/01/07 (1.92.6) entry in imgui.cpp or GitHub topic #9157 for all details. IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. 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_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0);// open+begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // open+begin popup when clicked in void (where there are no windows). // Popups: query functions // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open. IMGUI_API bool IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0); // return true if the popup is open. // Tables // - Full-featured replacement for old Columns API. // - See Demo->Tables for demo code. See top of imgui_tables.cpp for general commentary. // - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags. // The typical call flow is: // - 1. Call BeginTable(), early out if returning false. // - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults. // - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows. // - 4. Optionally call TableHeadersRow() to submit a header row. Names are pulled from TableSetupColumn() data. // - 5. Populate contents: // - In most situations you can use TableNextRow() + TableSetColumnIndex(N) to start appending into a column. // - If you are using tables as a sort of grid, where every column is holding the same type of contents, // you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex(). // TableNextColumn() will automatically wrap-around into the next row if needed. // - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column! // - Summary of possible call flow: // - TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1") // OK // - TableNextRow() -> TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK // - TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK: TableNextColumn() automatically gets to next row! // - TableNextRow() -> Text("Hello 0") // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear! // - 5. Call EndTable() IMGUI_API bool BeginTable(const char* str_id, int columns, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f); IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true! IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. 'min_row_height' include the minimum top and bottom padding aka CellPadding.y * 2.0f. IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. // Tables: Headers & Columns declaration // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc. // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column. // Headers are required to perform: reordering, sorting, and opening the context menu. // The context menu can also be made available in columns body using ImGuiTableFlags_ContextMenuInBody. // - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in // some advanced use cases (e.g. adding custom widgets in header row). // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) IMGUI_API void TableHeadersRow(); // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu IMGUI_API void TableAngledHeadersRow(); // submit a row with angled headers for every column with the ImGuiTableColumnFlags_AngledHeader flag. MUST BE FIRST ROW. // Tables: Sorting & Miscellaneous functions // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. // When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have // changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, // else you may wastefully sort your data every frame! // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index. IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable(). IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable) IMGUI_API int TableGetColumnIndex(); // return current column index. IMGUI_API int TableGetRowIndex(); // return current row index (header rows are accounted for) IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column. IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column. IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) IMGUI_API int TableGetHoveredColumn(); // return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. Can also use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. IMGUI_API void TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details. // Legacy Columns API (prefer using Tables!) // - You can also use SameLine(pos_x) to mimic simplified columns. IMGUI_API void Columns(int count = 1, const char* id = NULL, bool borders = 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 IMGUI_API float GetColumnWidth(int column_index = -1); // get column width (in pixels). pass -1 to use current column IMGUI_API void SetColumnWidth(int column_index, float width); // set column width (in pixels). pass -1 to use current column 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_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(); // Tab Bars, Tabs // - Note: Tabs are automatically created by the docking system (when in 'docking' branch). Use this to create tab bars/tabs yourself. 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 bool TabItemButton(const char* label, ImGuiTabItemFlags flags = 0); // create a Tab behaving like a button. return true when clicked. cannot be selected in the tab bar. 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. // Docking // - Read https://github.com/ocornut/imgui/wiki/Docking for details. // - Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // - You can use many Docking facilities without calling any API. // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. // - Drag from window menu button (upper-left button) to undock an entire node (all windows). // - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to enable docking. // - DockSpaceOverViewport: // - This is a helper to create an invisible window covering a viewport, then submit a DockSpace() into it. // - Most applications can simply call DockSpaceOverViewport() once to allow docking windows into e.g. the edge of your screen. // e.g. ImGui::NewFrame(); ImGui::DockSpaceOverViewport(); // Create a dockspace in main viewport. // or: ImGui::NewFrame(); ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); // Create a dockspace in main viewport, central node is transparent. // - Dockspaces: // - A dockspace is an explicit dock node within an existing window. // - IMPORTANT: Dockspaces need to be submitted _before_ any window they can host. Submit them early in your frame! // - IMPORTANT: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked. // If you have e.g. multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly. // - See 'Demo->Examples->Dockspace' or 'Demo->Examples->Documents' for more detailed demos. // - Programmatic docking: // - There is no public API yet other than the very limited SetNextWindowDockID() function. Sorry for that! // - Read https://github.com/ocornut/imgui/wiki/Docking for examples of how to use current internal API. IMGUI_API ImGuiID DockSpace(ImGuiID dockspace_id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (control docking compatibility + provide hints to platform backend via custom viewport flags and platform parent/child relationship) IMGUI_API ImGuiID GetWindowDockID(); // get dock id of current window, or 0 if not associated to any docking node. IMGUI_API bool IsWindowDocked(); // is current window docked into another window? // 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) IMGUI_API void LogTextV(const char* fmt, va_list args) IM_FMTLIST(1); // Drag and Drop // - On source items, call BeginDragDropSource(), if it returns true also call SetDragDropPayload() + EndDragDropSource(). // - On target candidates, call BeginDragDropTarget(), if it returns true also call AcceptDragDropPayload() + EndDragDropTarget(). // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip, see #1725) // - An item can be both drag source and drop target. IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource() 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. Return true when payload has been accepted. 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. returns NULL when drag and drop is finished or inactive. use ImGuiPayload::IsDataType() to test for the payload type. // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) // - Tooltips windows are automatically opted out of disabling. Note that IsItemHovered() by default returns false on disabled items, unless using ImGuiHoveredFlags_AllowWhenDisabled. // - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls) IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); // Clipping // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only. 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 IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a newly appearing 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. // Keyboard/Gamepad Navigation IMGUI_API void SetNavCursorVisible(bool visible); // alter visibility of keyboard/gamepad cursor. by default: show when using an arrow key, hide when clicking with mouse. // Overlapping mode IMGUI_API void SetNextItemAllowOverlap(); // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this. // Item/Widgets Utilities and Query Functions // - Most of the functions are referring to the previous Item that has been 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(ImGuiMouseButton mouse_button = 0); // is the last item hovered and mouse clicked on? (**) == IsMouseClicked(mouse_button) && IsItemHovered()Important. (**) this is NOT equivalent to the behavior of e.g. Button(). Read comments in function definition. 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 require 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 require 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 IsItemToggledOpen(); // was the last item open state toggled? set by TreeNode(). 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 ImGuiID GetItemID(); // get ID of last item (~~ often same ImGui::GetID(label) beforehand) 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 ImGuiItemFlags GetItemFlags(); // get generic flags of last item // Viewports // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. // - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. This can never be NULL. // Background/Foreground Draw Lists IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport = NULL); // get background draw list for the given viewport or viewport associated to the current window. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport = NULL); // get foreground draw list for the given viewport or viewport associated to the current window. this draw list will be the top-most rendered one. Useful to quickly draw shapes/text over dear imgui contents. // 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(); // 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 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(); // Text Utilities IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); // 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 Utilities: Raw Keyboard/Mouse/Gamepad Access // - Consider using the Shortcut() function instead of IsKeyPressed()/IsKeyChordPressed()! Shortcut() is easier to use and better featured (can do focus routing check). // - the ImGuiKey enum contains all possible keyboard, mouse and gamepad inputs (e.g. ImGuiKey_A, ImGuiKey_MouseLeft, ImGuiKey_GamepadDpadUp...). // - (legacy: before v1.87 (2022-02), we used ImGuiKey < 512 values to carry native/user indices as defined by each backends. This was obsoleted in 1.87 (2022-02) and completely removed in 1.91.5 (2024-11). See https://github.com/ocornut/imgui/issues/4921) IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? Repeat rate uses io.KeyRepeatDelay / KeyRepeatRate. IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)? IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord); // was key chord (mods + key) pressed, e.g. you can pass 'ImGuiMod_Ctrl | ImGuiKey_S' as a key-chord. This doesn't do any routing or focus check, please consider using Shortcut() function instead. IMGUI_API int GetKeyPressedAmount(ImGuiKey key, 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 IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names are provided for debugging purpose and are not meant to be saved persistently nor compared. IMGUI_API void SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call. // Inputs Utilities: Shortcut Testing & Routing // - Typical use is e.g.: 'if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S)) { ... }'. // - Flags: Default route use ImGuiInputFlags_RouteFocused, but see ImGuiInputFlags_RouteGlobal and other options in ImGuiInputFlags_! // - Flags: Use ImGuiInputFlags_Repeat to support repeat. // - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super. // ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments // ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments // only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values. // - The general idea is that several callers may register interest in a shortcut, and only one owner gets it. // Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut. // Child1 -> call Shortcut(Ctrl+S) // When Child1 is focused, Child1 gets the shortcut (Child1 overrides Parent shortcuts) // Child2 -> no call // When Child2 is focused, Parent gets the shortcut. // The whole system is order independent, so if Child1 makes its calls before Parent, results will be identical. // This is an important property as it facilitate working with foreign code or larger codebase. // - To understand the difference: // - IsKeyChordPressed() compares mods and call IsKeyPressed() // -> the function has no side-effect. // - Shortcut() submits a route, routes are resolved, if it currently can be routed it calls IsKeyChordPressed() // -> the function has (desirable) side-effects as it can prevents another call from getting the route. // - Visualize registered routes in 'Metrics/Debugger->Inputs'. IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0); IMGUI_API void SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0); // Inputs Utilities: Key/Input Ownership [BETA] // - One common use case would be to allow your items to disable standard inputs behaviors such // as Tab or Alt key handling, Mouse Wheel scrolling, etc. // e.g. Button(...); SetItemKeyOwner(ImGuiKey_MouseWheelY); to make hovering/activating a button disable wheel for scrolling. // - Reminder ImGuiKey enum include access to mouse buttons and gamepad, so key ownership can apply to them. // - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version. IMGUI_API void SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. // Inputs Utilities: Mouse // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold') IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held? IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) IMGUI_API bool IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay); // delayed mouse release (use very sparingly!). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test. This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename. IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). 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 available IMGUI_API bool IsAnyMouseDown(); // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid. IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves) IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (uses io.MouseDraggingThreshold if lock_threshold < 0.0f) IMGUI_API ImVec2 GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (uses io.MouseDraggingThreshold if lock_threshold < 0.0f) IMGUI_API void ResetMouseDragDelta(ImGuiMouseButton button = 0); // IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired mouse cursor shape. Important: 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 cursor_type); // set desired mouse cursor shape IMGUI_API void SetNextFrameWantCaptureMouse(bool want_capture_mouse); // Override io.WantCaptureMouse flag next frame (said flag is left for your application to handle, typical when true it instructs your app to ignore inputs). This is equivalent to setting "io.WantCaptureMouse = want_capture_mouse;" after the next NewFrame() call. // Clipboard Utilities // - Also see the LogToClipboard() function to capture GUI into clipboard, or easily 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. // - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables). 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); // 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. // Debug Utilities // - Your main debugging friend is the ShowMetricsWindow() function. // - Interactive tools are all accessible from the 'Dear ImGui Demo->Tools' menu. // - Read https://github.com/ocornut/imgui/wiki/Debug-Tools for a description of all available debug tools. IMGUI_API void DebugTextEncoding(const char* text); IMGUI_API void DebugFlashStyleColor(ImGuiCol idx); IMGUI_API void DebugStartItemPicker(); IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro. #ifndef IMGUI_DISABLE_DEBUG_TOOLS IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1); // Call via IMGUI_DEBUG_LOG() for maximum stripping in caller code! IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1); #endif // Memory Allocators // - Those functions are not reliant on the current context. // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. IMGUI_API void SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data = NULL); IMGUI_API void GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data); IMGUI_API void* MemAlloc(size_t size); IMGUI_API void MemFree(void* ptr); // (Optional) Platform/OS interface for multi-viewport support // Read comments around the ImGuiPlatformIO structure for more details. // Note: You may use GetWindowViewport() to get the current viewport of the current window. IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from backend Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID viewport_id); // this is a helper for backends. IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for backends. the type platform_handle is decided by the backend (e.g. HWND, MyWindow*, GLFWwindow* etc.) } // namespace ImGui //----------------------------------------------------------------------------- // [SECTION] Flags & Enumerations //----------------------------------------------------------------------------- // Flags for ImGui::Begin() // (Those are per-window flags. There are shared flags in ImGuiIO: io.ConfigWindowsResizeFromEdges and io.ConfigWindowsMoveFromTitleBarOnly) enum ImGuiWindowFlags_ { ImGuiWindowFlags_None = 0, 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 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. Also referred to as Window Menu Button (e.g. within a docking node). 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_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 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_NoNavInputs = 1 << 16, // No keyboard/gamepad navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by Ctrl+Tab) ImGuiWindowFlags_UnsavedDocument = 1 << 18, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiWindowFlags_NoDocking = 1 << 19, // Disable docking of this window 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_DockNodeHost = 1 << 23, // Don't use! For internal use by Begin()/NewFrame() ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() ImGuiWindowFlags_ChildMenu = 1 << 28, // Don't use! For internal use by BeginMenu() // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //ImGuiWindowFlags_NavFlattened = 1 << 29, // Obsoleted in 1.90.9: moved to ImGuiChildFlags. BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0) //ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90.0: moved to ImGuiChildFlags. BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0) #endif }; // Flags for ImGui::BeginChild() // (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.) // About using AutoResizeX/AutoResizeY flags: // - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints"). // - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing. // - This allows BeginChild() to return false when not within boundaries (e.g. when scrolling), which is more optimal. BUT it won't update its auto-size while clipped. // While not perfect, it is a better default behavior as the always-on performance gain is more valuable than the occasional "resizing after becoming visible again" glitch. // - You may also use ImGuiChildFlags_AlwaysAutoResize to force an update even when child window is not in view. // HOWEVER PLEASE UNDERSTAND THAT DOING SO WILL PREVENT BeginChild() FROM EVER RETURNING FALSE, disabling benefits of coarse clipping. enum ImGuiChildFlags_ { ImGuiChildFlags_None = 0, ImGuiChildFlags_Borders = 1 << 0, // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason) ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1, // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense) ImGuiChildFlags_ResizeX = 1 << 2, // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags) ImGuiChildFlags_ResizeY = 1 << 3, // Allow resize from bottom border (layout direction). " ImGuiChildFlags_AutoResizeX = 1 << 4, // Enable auto-resizing width. Read "IMPORTANT: Size measurement" details above. ImGuiChildFlags_AutoResizeY = 1 << 5, // Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above. ImGuiChildFlags_AlwaysAutoResize = 1 << 6, // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED. ImGuiChildFlags_FrameStyle = 1 << 7, // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding. ImGuiChildFlags_NavFlattened = 1 << 8, // [BETA] Share focus scope, allow keyboard/gamepad navigation to cross over parent border to this child or between sibling child windows. // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //ImGuiChildFlags_Border = ImGuiChildFlags_Borders, // Renamed in 1.91.1 (August 2024) for consistency. #endif }; // Flags for ImGui::PushItemFlag() // (Those are shared by all submitted items) enum ImGuiItemFlags_ { ImGuiItemFlags_None = 0, // (Default) ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags_NoNav. ImGuiItemFlags_NoNav = 1 << 1, // false // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls). ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, // false // Disable item being a candidate for default focus (e.g. used by title bar items). ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held. ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window. ImGuiItemFlags_AllowDuplicateId = 1 << 5, // false // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set. ImGuiItemFlags_Disabled = 1 << 6, // false // [Internal] Disable interactions. DOES NOT affect visuals. This is used by BeginDisabled()/EndDisabled() and only provided here so you can read back via GetItemFlags(). }; // Flags for ImGui::InputText() // (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive) enum ImGuiInputTextFlags_ { // Basic filters (also see ImGuiInputTextFlags_CallbackCharFilter) ImGuiInputTextFlags_None = 0, ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef ImGuiInputTextFlags_CharsScientific = 1 << 2, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CharsUppercase = 1 << 3, // Turn a..z into A..Z ImGuiInputTextFlags_CharsNoBlank = 1 << 4, // Filter out spaces, tabs // Inputs ImGuiInputTextFlags_AllowTabInput = 1 << 5, // Pressing TAB input a '\t' character into the text field ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider using IsItemDeactivatedAfterEdit() instead! ImGuiInputTextFlags_EscapeClearsAll = 1 << 7, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8, // In multi-line mode: validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). Note that Shift+Enter always enter a new line either way. // Other options ImGuiInputTextFlags_ReadOnly = 1 << 9, // Read-only mode ImGuiInputTextFlags_Password = 1 << 10, // Password mode, display all characters as '*', disable copy ImGuiInputTextFlags_AlwaysOverwrite = 1 << 11, // Overwrite mode ImGuiInputTextFlags_AutoSelectAll = 1 << 12, // Select entire text when first taking mouse focus ImGuiInputTextFlags_ParseEmptyRefVal = 1 << 13, // InputFloat(), InputInt(), InputScalar() etc. only: parse empty string as zero value. ImGuiInputTextFlags_DisplayEmptyRefVal = 1 << 14, // InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal. ImGuiInputTextFlags_NoHorizontalScroll = 1 << 15, // Disable following the cursor horizontally 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(). // Elide display / Alignment ImGuiInputTextFlags_ElideLeft = 1 << 17, // When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only! // Callback features ImGuiInputTextFlags_CallbackCompletion = 1 << 18, // Callback on pressing TAB (for completion handling) ImGuiInputTextFlags_CallbackHistory = 1 << 19, // Callback on pressing Up/Down arrows (for history handling) ImGuiInputTextFlags_CallbackAlways = 1 << 20, // Callback on each iteration. User code may query cursor position, modify text buffer. ImGuiInputTextFlags_CallbackCharFilter = 1 << 21, // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. ImGuiInputTextFlags_CallbackResize = 1 << 22, // 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) ImGuiInputTextFlags_CallbackEdit = 1 << 23, // Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active. // Multi-line Word-Wrapping [BETA] // - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237. // - Wrapping style is not ideal. Wrapping of long words/sections (e.g. words larger than total available width) may be particularly unpleasing. // - Wrapping width needs to always account for the possibility of a vertical scrollbar. // - It is much slower than regular text fields. // Ballpark estimate of cost on my 2019 desktop PC: for a 100 KB text buffer: +~0.3 ms (Optimized) / +~1.0 ms (Debug build). // The CPU cost is very roughly proportional to text length, so a 10 KB buffer should cost about ten times less. ImGuiInputTextFlags_WordWrap = 1 << 24, // InputTextMultiline(): word-wrap lines that are too long. // Obsolete names //ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior }; // Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*() enum ImGuiTreeNodeFlags_ { ImGuiTreeNodeFlags_None = 0, ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected ImGuiTreeNodeFlags_Framed = 1 << 1, // Draw frame with background (e.g. for CollapsingHeader) ImGuiTreeNodeFlags_AllowOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Open on double-click instead of simple click (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Open when clicking on the arrow part (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). Note: will always open a tree/id scope and return true. If you never use that scope, add ImGuiTreeNodeFlags_NoTreePushOnOpen. ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag! ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node. ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line without using AllowOverlap mode. ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (cover the indent area). ImGuiTreeNodeFlags_SpanLabelWidth = 1 << 13, // Narrow hit box + narrow hovering highlight, will only cover the label text. ImGuiTreeNodeFlags_SpanAllColumns = 1 << 14, // Frame will span all columns of its container table (label will still fit in current column) ImGuiTreeNodeFlags_LabelSpanAllColumns = 1 << 15, // Label will span all columns of its container table //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 16, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17, // Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfulfilled Left nav request remaining. ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog, // [EXPERIMENTAL] Draw lines connecting TreeNode hierarchy. Discuss in GitHub issue #2920. // Default value is pulled from style.TreeLinesFlags. May be overridden in TreeNode calls. ImGuiTreeNodeFlags_DrawLinesNone = 1 << 18, // No lines drawn ImGuiTreeNodeFlags_DrawLinesFull = 1 << 19, // Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents. Faster (for large trees). ImGuiTreeNodeFlags_DrawLinesToNodes = 1 << 20, // Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node. Slower (for large trees). #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiTreeNodeFlags_NavLeftJumpsBackHere = ImGuiTreeNodeFlags_NavLeftJumpsToParent, // Renamed in 1.92.0 ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth, // Renamed in 1.90.7 //ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7 #endif }; // Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions. // - IMPORTANT: If you ever used the left mouse button with BeginPopupContextXXX() helpers before 1.92.6: Read "API BREAKING CHANGES" 2026/01/07 (1.92.6) entry in imgui.cpp or GitHub topic #9157. // - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later). enum ImGuiPopupFlags_ { ImGuiPopupFlags_None = 0, ImGuiPopupFlags_MouseButtonLeft = 1 << 2, // For BeginPopupContext*(): open on Left Mouse release. Only one button allowed! ImGuiPopupFlags_MouseButtonRight = 2 << 2, // For BeginPopupContext*(): open on Right Mouse release. Only one button allowed! (default) ImGuiPopupFlags_MouseButtonMiddle = 3 << 2, // For BeginPopupContext*(): open on Middle Mouse release. Only one button allowed! ImGuiPopupFlags_NoReopen = 1 << 5, // For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation) //ImGuiPopupFlags_NoReopenAlwaysNavInit = 1 << 6, // For OpenPopup*(), BeginPopupContext*(): focus and initialize navigation even when not reopening. ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 7, // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack ImGuiPopupFlags_NoOpenOverItems = 1 << 8, // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space ImGuiPopupFlags_AnyPopupId = 1 << 10, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup. ImGuiPopupFlags_AnyPopupLevel = 1 << 11, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level) ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel, ImGuiPopupFlags_MouseButtonShift_ = 2, // [Internal] ImGuiPopupFlags_MouseButtonMask_ = 0x0C, // [Internal] ImGuiPopupFlags_InvalidMask_ = 0x03, // [Internal] Reserve legacy bits 0-1 to detect incorrectly passing 1 or 2 to the function. }; // Flags for ImGui::Selectable() enum ImGuiSelectableFlags_ { ImGuiSelectableFlags_None = 0, ImGuiSelectableFlags_NoAutoClosePopups = 1 << 0, // Clicking this doesn't close parent popup window (overrides ImGuiItemFlags_AutoClosePopups) ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Frame will span all columns of its container table (text will still fit in current column) ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one ImGuiSelectableFlags_Highlight = 1 << 5, // Make the item be displayed as if it is hovered ImGuiSelectableFlags_SelectOnNav = 1 << 6, // Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiSelectableFlags_DontClosePopups = ImGuiSelectableFlags_NoAutoClosePopups, // Renamed in 1.91.0 //ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 #endif }; // Flags for ImGui::BeginCombo() enum ImGuiComboFlags_ { ImGuiComboFlags_None = 0, ImGuiComboFlags_PopupAlignLeft = 1 << 0, // Align the popup toward the left by default ImGuiComboFlags_HeightSmall = 1 << 1, // Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo() ImGuiComboFlags_HeightRegular = 1 << 2, // Max ~8 items visible (default) ImGuiComboFlags_HeightLarge = 1 << 3, // Max ~20 items visible ImGuiComboFlags_HeightLargest = 1 << 4, // As many fitting items as possible ImGuiComboFlags_NoArrowButton = 1 << 5, // Display on the preview box without the square arrow button ImGuiComboFlags_NoPreview = 1 << 6, // Display only a square arrow button ImGuiComboFlags_WidthFitPreview = 1 << 7, // Width dynamically calculated from preview contents 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, // Disable buttons to open the tab list popup ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll) ImGuiTabBarFlags_NoTooltip = 1 << 5, // Disable tooltips when hovering a tab ImGuiTabBarFlags_DrawSelectedOverline = 1 << 6, // Draw selected overline markers over selected tab // Fitting/Resize policy ImGuiTabBarFlags_FittingPolicyMixed = 1 << 7, // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons. ImGuiTabBarFlags_FittingPolicyShrink = 1 << 8, // Shrink down tabs when they don't fit ImGuiTabBarFlags_FittingPolicyScroll = 1 << 9, // Enable scrolling buttons when tabs don't fit ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyMixed | ImGuiTabBarFlags_FittingPolicyShrink | ImGuiTabBarFlags_FittingPolicyScroll, ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyMixed, #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiTabBarFlags_FittingPolicyResizeDown = ImGuiTabBarFlags_FittingPolicyShrink, // Renamed in 1.92.2 #endif }; // Flags for ImGui::BeginTabItem() enum ImGuiTabItemFlags_ { ImGuiTabItemFlags_None = 0, ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + set ImGuiTabItemFlags_NoAssumedClosure. 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 may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID()/PopID() on BeginTabItem()/EndTabItem() ImGuiTabItemFlags_NoTooltip = 1 << 4, // Disable tooltip for the given tab ImGuiTabItemFlags_NoReorder = 1 << 5, // Disable reordering this tab or having another tab cross over this tab ImGuiTabItemFlags_Leading = 1 << 6, // Enforce the tab position to the left of the tab bar (after the tab list popup button) ImGuiTabItemFlags_Trailing = 1 << 7, // Enforce the tab position to the right of the tab bar (before the scrolling buttons) ImGuiTabItemFlags_NoAssumedClosure = 1 << 8, // Tab is selected when trying to close + closure is not immediately assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. }; // Flags for ImGui::IsWindowFocused() enum ImGuiFocusedFlags_ { ImGuiFocusedFlags_None = 0, ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) ImGuiFocusedFlags_AnyWindow = 1 << 2, // 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 'io.WantCaptureMouse' instead! Please read the FAQ! ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows, }; // Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered() // Note: if you are trying to check whether your mouse should be dispatched to Dear ImGui or to your app, you should use 'io.WantCaptureMouse' instead! Please read the FAQ! // Note: windows with the ImGuiWindowFlags_NoInputs flag are ignored by IsWindowHovered() calls. enum ImGuiHoveredFlags_ { ImGuiHoveredFlags_None = 0, // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them. ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. ImGuiHoveredFlags_AllowWhenOverlappedByItem = 1 << 8, // IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item. ImGuiHoveredFlags_AllowWhenOverlappedByWindow = 1 << 9, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window. ImGuiHoveredFlags_AllowWhenDisabled = 1 << 10, // IsItemHovered() only: Return true even if the item is disabled ImGuiHoveredFlags_NoNavOverride = 1 << 11, // IsItemHovered() only: Disable using keyboard/gamepad navigation state when active, always query mouse ImGuiHoveredFlags_AllowWhenOverlapped = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow, ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, // Tooltips mode // - typically used in IsItemHovered() + SetTooltip() sequence. // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior. // e.g. 'HoverFlagsForTooltipMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled'. // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often. // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay. ImGuiHoveredFlags_ForTooltip = 1 << 12, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence. // (Advanced) Mouse Hovering delays. // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags. // - use those if you need specific overrides. ImGuiHoveredFlags_Stationary = 1 << 13, // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay. ImGuiHoveredFlags_DelayNone = 1 << 14, // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this. ImGuiHoveredFlags_DelayShort = 1 << 15, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). ImGuiHoveredFlags_DelayNormal = 1 << 16, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). ImGuiHoveredFlags_NoSharedDelay = 1 << 17, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; // Flags for ImGui::DockSpace(), shared/inherited by child nodes. // (Some flags can be applied to individual nodes directly) // FIXME-DOCK: Also see ImGuiDockNodeFlagsPrivate_ which may involve using the WIP and internal DockBuilder api. enum ImGuiDockNodeFlags_ { ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. //ImGuiDockNodeFlags_NoCentralNode = 1 << 1, // // Disable Central Node (the node which can stay empty) ImGuiDockNodeFlags_NoDockingOverCentralNode = 1 << 2, // // Disable docking over the Central Node, which will be always kept empty. ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. ImGuiDockNodeFlags_NoDockingSplit = 1 << 4, // // Disable other windows/nodes from splitting this node. ImGuiDockNodeFlags_NoResize = 1 << 5, // Saved // Disable resizing node using the splitter/separators. Useful with programmatically setup dockspaces. ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6, // // Tab bar will automatically hide when there is a single window in the dock node. ImGuiDockNodeFlags_NoUndocking = 1 << 7, // // Disable undocking this node. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiDockNodeFlags_NoSplit = ImGuiDockNodeFlags_NoDockingSplit, // Renamed in 1.90 ImGuiDockNodeFlags_NoDockingInCentralNode = ImGuiDockNodeFlags_NoDockingOverCentralNode, // Renamed in 1.90 #endif }; // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() enum ImGuiDragDropFlags_ { ImGuiDragDropFlags_None = 0, // BeginDragDropSource() flags ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // Disable preview tooltip. By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disables this behavior. ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disables this behavior so you can still call IsItemHovered() on the source item. ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2, // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item. ImGuiDragDropFlags_SourceAllowNullID = 1 << 3, // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit. ImGuiDragDropFlags_SourceExtern = 1 << 4, // External source (from outside of dear imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously. ImGuiDragDropFlags_PayloadAutoExpire = 1 << 5, // Automatically expire the payload if the source cease to be submitted (otherwise payloads are persisting while being dragged) ImGuiDragDropFlags_PayloadNoCrossContext = 1 << 6, // Hint to specify that the payload may not be copied outside current dear imgui context. ImGuiDragDropFlags_PayloadNoCrossProcess = 1 << 7, // Hint to specify that the payload may not be copied outside current process. // AcceptDragDropPayload() flags ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site. ImGuiDragDropFlags_AcceptDrawAsHovered = 1 << 13, // Accepting item will render as if hovered. Useful for e.g. a Button() used as a drop target. ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiDragDropFlags_SourceAutoExpirePayload = ImGuiDragDropFlags_PayloadAutoExpire, // Renamed in 1.90.9 #endif }; // Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui. #define IMGUI_PAYLOAD_TYPE_COLOR_3F "_COL3F" // float[3]: Standard type for colors, without alpha. User code may use this type. #define IMGUI_PAYLOAD_TYPE_COLOR_4F "_COL4F" // float[4]: Standard type for colors. User code may use this type. // A primary data type enum ImGuiDataType_ { ImGuiDataType_S8, // signed char / char (with sensible compilers) 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_Float, // float ImGuiDataType_Double, // double ImGuiDataType_Bool, // bool (provided for user convenience, not supported by scalar widgets) ImGuiDataType_String, // char* (provided for user convenience, not supported by scalar widgets) ImGuiDataType_COUNT }; // A cardinal direction enum ImGuiDir : int { ImGuiDir_None = -1, ImGuiDir_Left = 0, ImGuiDir_Right = 1, ImGuiDir_Up = 2, ImGuiDir_Down = 3, ImGuiDir_COUNT }; // A sorting direction enum ImGuiSortDirection : ImU8 { ImGuiSortDirection_None = 0, ImGuiSortDirection_Ascending = 1, // Ascending = 0->9, A->Z etc. ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc. }; // A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values. // All our named keys are >= 512. Keys value 0 to 511 are left unused and were legacy native/opaque key values (< 1.87). // Support for legacy keys was completely removed in 1.91.5. // Read details about the 1.87+ transition : https://github.com/ocornut/imgui/issues/4921 // Note that "Keys" related to physical keys and are not the same concept as input "Characters", the latter are submitted via io.AddInputCharacter(). // The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps. enum ImGuiKey : int { // Keyboard ImGuiKey_None = 0, ImGuiKey_NamedKey_BEGIN = 512, // First valid key value (other than 0) ImGuiKey_Tab = 512, // == ImGuiKey_NamedKey_BEGIN ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow, ImGuiKey_PageUp, ImGuiKey_PageDown, ImGuiKey_Home, ImGuiKey_End, ImGuiKey_Insert, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper, // Also see ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiMod_Alt, ImGuiMod_Super below! ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper, ImGuiKey_Menu, ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9, ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J, ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T, ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z, ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6, ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12, ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18, ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24, ImGuiKey_Apostrophe, // ' ImGuiKey_Comma, // , ImGuiKey_Minus, // - ImGuiKey_Period, // . ImGuiKey_Slash, // / ImGuiKey_Semicolon, // ; ImGuiKey_Equal, // = ImGuiKey_LeftBracket, // [ ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backslash) ImGuiKey_RightBracket, // ] ImGuiKey_GraveAccent, // ` ImGuiKey_CapsLock, ImGuiKey_ScrollLock, ImGuiKey_NumLock, ImGuiKey_PrintScreen, ImGuiKey_Pause, ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4, ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9, ImGuiKey_KeypadDecimal, ImGuiKey_KeypadDivide, ImGuiKey_KeypadMultiply, ImGuiKey_KeypadSubtract, ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, ImGuiKey_AppBack, // Available on some keyboard/mouses. Often referred as "Browser Back" ImGuiKey_AppForward, ImGuiKey_Oem102, // Non-US backslash. // Gamepad // (analog values are 0.0f to 1.0f) // (download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets) // // XBOX | SWITCH | PLAYSTA. | -> ACTION ImGuiKey_GamepadStart, // Menu | + | Options | ImGuiKey_GamepadBack, // View | - | Share | ImGuiKey_GamepadFaceLeft, // X | Y | Square | Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows) ImGuiKey_GamepadFaceRight, // B | A | Circle | Cancel / Close / Exit ImGuiKey_GamepadFaceUp, // Y | X | Triangle | Open Context Menu ImGuiKey_GamepadFaceDown, // A | B | Cross | Activate / Open / Toggle. Hold for 0.60f to Activate in Text Input mode (e.g. wired to an on-screen keyboard). ImGuiKey_GamepadDpadLeft, // D-pad Left | " | " | Move / Tweak / Resize Window (in Windowing mode) ImGuiKey_GamepadDpadRight, // D-pad Right | " | " | Move / Tweak / Resize Window (in Windowing mode) ImGuiKey_GamepadDpadUp, // D-pad Up | " | " | Move / Tweak / Resize Window (in Windowing mode) ImGuiKey_GamepadDpadDown, // D-pad Down | " | " | Move / Tweak / Resize Window (in Windowing mode) ImGuiKey_GamepadL1, // L Bumper | L | L1 | Tweak Slower / Focus Previous (in Windowing mode) ImGuiKey_GamepadR1, // R Bumper | R | R1 | Tweak Faster / Focus Next (in Windowing mode) ImGuiKey_GamepadL2, // L Trigger | ZL | L2 | [Analog] ImGuiKey_GamepadR2, // R Trigger | ZR | R2 | [Analog] ImGuiKey_GamepadL3, // L Stick | L3 | L3 | ImGuiKey_GamepadR3, // R Stick | R3 | R3 | ImGuiKey_GamepadLStickLeft, // | | | [Analog] Move Window (in Windowing mode) ImGuiKey_GamepadLStickRight, // | | | [Analog] Move Window (in Windowing mode) ImGuiKey_GamepadLStickUp, // | | | [Analog] Move Window (in Windowing mode) ImGuiKey_GamepadLStickDown, // | | | [Analog] Move Window (in Windowing mode) ImGuiKey_GamepadRStickLeft, // | | | [Analog] ImGuiKey_GamepadRStickRight, // | | | [Analog] ImGuiKey_GamepadRStickUp, // | | | [Analog] ImGuiKey_GamepadRStickDown, // | | | [Analog] // Aliases: Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls) // - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API. ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY, // [Internal] Reserved for mod storage ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper, // [Internal] If you need to iterate all keys (for e.g. an input mapper) you may use ImGuiKey_NamedKey_BEGIN..ImGuiKey_NamedKey_END. ImGuiKey_NamedKey_END, ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls) // - Any functions taking a ImGuiKeyChord parameter can binary-or those with regular keys, e.g. Shortcut(ImGuiMod_Ctrl | ImGuiKey_S). // - Those are written back into io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper for convenience, // but may be accessed via standard key API such as IsKeyPressed(), IsKeyReleased(), querying duration etc. // - Code polling every key (e.g. an interface to detect a key press for input mapping) might want to ignore those // and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiMod_Ctrl). // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys. // In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and // backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user... // - On macOS, we swap Cmd(Super) and Ctrl keys at the time of the io.AddKeyEvent() call. ImGuiMod_None = 0, ImGuiMod_Ctrl = 1 << 12, // Ctrl (non-macOS), Cmd (macOS) ImGuiMod_Shift = 1 << 13, // Shift ImGuiMod_Alt = 1 << 14, // Option/Menu ImGuiMod_Super = 1 << 15, // Windows/Super (non-macOS), Ctrl (macOS) ImGuiMod_Mask_ = 0xF000, // 4-bits #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiKey_COUNT = ImGuiKey_NamedKey_END, // Obsoleted in 1.91.5 because it was misleading (since named keys don't start at 0 anymore) ImGuiMod_Shortcut = ImGuiMod_Ctrl, // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl //ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89 //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87 #endif }; // Flags for Shortcut(), SetNextItemShortcut(), // (and for upcoming extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() that are still in imgui_internal.h) // Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function) enum ImGuiInputFlags_ { ImGuiInputFlags_None = 0, ImGuiInputFlags_Repeat = 1 << 0, // Enable repeat. Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1. // Flags for Shortcut(), SetNextItemShortcut() // - Routing policies: RouteGlobal+OverActive >> RouteActive or RouteFocused (if owner is active item) >> RouteGlobal+OverFocused >> RouteFocused (if in focused window stack) >> RouteGlobal. // - Default policy is RouteFocused. Can select only 1 policy among all available. ImGuiInputFlags_RouteActive = 1 << 10, // Route to active item only. ImGuiInputFlags_RouteFocused = 1 << 11, // Route to windows in the focus stack (DEFAULT). Deep-most focused window takes inputs. Active item takes inputs over deep-most focused window. ImGuiInputFlags_RouteGlobal = 1 << 12, // Global route (unless a focused window or active item registered the route). ImGuiInputFlags_RouteAlways = 1 << 13, // Do not register route, poll keys directly. // - Routing options ImGuiInputFlags_RouteOverFocused = 1 << 14, // Option: global route: higher priority than focused route (unless active item in focused route). ImGuiInputFlags_RouteOverActive = 1 << 15, // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. Ctrl+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active. ImGuiInputFlags_RouteUnlessBgFocused = 1 << 16, // Option: global route: will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications. ImGuiInputFlags_RouteFromRootWindow = 1 << 17, // Option: route evaluated from the point of view of root window rather than current window. // Flags for SetNextItemShortcut() ImGuiInputFlags_Tooltip = 1 << 18, // Automatically display a tooltip when hovering item [BETA] Unsure of right api (opt-in/opt-out) }; // Configuration flags stored in io.ConfigFlags. Set by user/application. enum ImGuiConfigFlags_ { ImGuiConfigFlags_None = 0, ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. Enable full Tabbing + directional arrows + Space/Enter to activate. Note: some features such as basic Tabbing and CtrL+Tab are enabled by regardless of this flag (and may be disabled via other means, see #4828, #9218). ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. Backend also needs to set ImGuiBackendFlags_HasGamepad. ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct dear imgui to disable mouse inputs and interactions. ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. ImGuiConfigFlags_NoKeyboard = 1 << 6, // Instruct dear imgui to disable keyboard inputs and interactions. This is done by ignoring keyboard events and clearing existing states. // [BETA] Docking ImGuiConfigFlags_DockingEnable = 1 << 7, // Docking enable flags. // [BETA] Viewports // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiBackendFlags_PlatformHasViewports + ImGuiBackendFlags_RendererHasViewports set by the respective backends) // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. ImGuiConfigFlags_IsTouchScreen = 1 << 21, // Application is using a touch screen instead of a mouse. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // [moved/renamed in 1.91.4] -> use bool io.ConfigNavMoveSetMousePos ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // [moved/renamed in 1.91.4] -> use bool io.ConfigNavCaptureKeyboard ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 14, // [moved/renamed in 1.92.0] -> use bool io.ConfigDpiScaleFonts ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 15, // [moved/renamed in 1.92.0] -> use bool io.ConfigDpiScaleViewports #endif }; // Backend capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom backend. enum ImGuiBackendFlags_ { ImGuiBackendFlags_None = 0, ImGuiBackendFlags_HasGamepad = 1 << 0, // Backend Platform supports gamepad and currently has one connected. ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape. ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if io.ConfigNavMoveSetMousePos is set). ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3, // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. ImGuiBackendFlags_RendererHasTextures = 1 << 4, // Backend Renderer supports ImTextureData requests to create/update/destroy textures. This enables incremental texture updates and texture reloads. See https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for instructions on how to upgrade your custom backend. // [BETA] Multi-Viewports ImGuiBackendFlags_RendererHasViewports = 1 << 10, // Backend Renderer supports multiple viewports. ImGuiBackendFlags_PlatformHasViewports = 1 << 11, // Backend Platform supports multiple viewports. ImGuiBackendFlags_HasMouseHoveredViewport=1 << 12, // Backend Platform supports calling io.AddMouseViewportEvent() with the viewport under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under. ImGuiBackendFlags_HasParentViewport = 1 << 13, // Backend Platform supports honoring viewport->ParentViewport/ParentViewportId value, by applying the corresponding parent/child relation at the Platform level. }; // Enumeration for PushStyleColor() / PopStyleColor() enum ImGuiCol_ { ImGuiCol_Text, ImGuiCol_TextDisabled, ImGuiCol_WindowBg, // Background of normal windows ImGuiCol_ChildBg, // Background of child windows ImGuiCol_PopupBg, // Background of popups, menus, tooltips windows ImGuiCol_Border, ImGuiCol_BorderShadow, ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive, ImGuiCol_TitleBg, // Title bar ImGuiCol_TitleBgActive, // Title bar when focused ImGuiCol_TitleBgCollapsed, // Title bar when collapsed ImGuiCol_MenuBarBg, ImGuiCol_ScrollbarBg, ImGuiCol_ScrollbarGrab, ImGuiCol_ScrollbarGrabHovered, ImGuiCol_ScrollbarGrabActive, ImGuiCol_CheckMark, // Checkbox tick and RadioButton circle ImGuiCol_SliderGrab, ImGuiCol_SliderGrabActive, ImGuiCol_Button, ImGuiCol_ButtonHovered, ImGuiCol_ButtonActive, ImGuiCol_Header, // Header* colors are used for CollapsingHeader, TreeNode, Selectable, MenuItem ImGuiCol_HeaderHovered, ImGuiCol_HeaderActive, ImGuiCol_Separator, ImGuiCol_SeparatorHovered, ImGuiCol_SeparatorActive, ImGuiCol_ResizeGrip, // Resize grip in lower-right and lower-left corners of windows. ImGuiCol_ResizeGripHovered, ImGuiCol_ResizeGripActive, ImGuiCol_InputTextCursor, // InputText cursor/caret ImGuiCol_TabHovered, // Tab background, when hovered ImGuiCol_Tab, // Tab background, when tab-bar is focused & tab is unselected ImGuiCol_TabSelected, // Tab background, when tab-bar is focused & tab is selected ImGuiCol_TabSelectedOverline, // Tab horizontal overline, when tab-bar is focused & tab is selected ImGuiCol_TabDimmed, // Tab background, when tab-bar is unfocused & tab is unselected ImGuiCol_TabDimmedSelected, // Tab background, when tab-bar is unfocused & tab is selected ImGuiCol_TabDimmedSelectedOverline,//..horizontal overline, when tab-bar is unfocused & tab is selected ImGuiCol_DockingPreview, // Preview overlay color when about to docking something ImGuiCol_DockingEmptyBg, // Background color for empty node (e.g. CentralNode with no window docked into it) ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, ImGuiCol_PlotHistogramHovered, ImGuiCol_TableHeaderBg, // Table header background ImGuiCol_TableBorderStrong, // Table outer and header borders (prefer using Alpha=1.0 here) ImGuiCol_TableBorderLight, // Table inner borders (prefer using Alpha=1.0 here) ImGuiCol_TableRowBg, // Table row background (even rows) ImGuiCol_TableRowBgAlt, // Table row background (odd rows) ImGuiCol_TextLink, // Hyperlink color ImGuiCol_TextSelectedBg, // Selected text inside an InputText ImGuiCol_TreeLines, // Tree node hierarchy outlines when using ImGuiTreeNodeFlags_DrawLines ImGuiCol_DragDropTarget, // Rectangle border highlighting a drop target ImGuiCol_DragDropTargetBg, // Rectangle background highlighting a drop target ImGuiCol_UnsavedMarker, // Unsaved Document marker (in window title and tabs) ImGuiCol_NavCursor, // Color of keyboard/gamepad navigation cursor/rectangle, when visible ImGuiCol_NavWindowingHighlight, // Highlight window when using Ctrl+Tab ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the Ctrl+Tab window list, when active ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active ImGuiCol_WindowShadow, // Window shadows ImGuiCol_COUNT, #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiCol_TabActive = ImGuiCol_TabSelected, // [renamed in 1.90.9] ImGuiCol_TabUnfocused = ImGuiCol_TabDimmed, // [renamed in 1.90.9] ImGuiCol_TabUnfocusedActive = ImGuiCol_TabDimmedSelected, // [renamed in 1.90.9] ImGuiCol_NavHighlight = ImGuiCol_NavCursor, // [renamed in 1.91.4] #endif }; // Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure. // - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code. // During initialization or between frames, feel free to just poke into ImGuiStyle directly. // - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description. // - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. // - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. // - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. // - When 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) ImGuiStyleVar_Alpha, // float Alpha ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding ImGuiStyleVar_WindowRounding, // float WindowRounding ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize ImGuiStyleVar_WindowMinSize, // ImVec2 WindowMinSize ImGuiStyleVar_WindowTitleAlign, // ImVec2 WindowTitleAlign ImGuiStyleVar_ChildRounding, // float ChildRounding ImGuiStyleVar_ChildBorderSize, // float ChildBorderSize ImGuiStyleVar_PopupRounding, // float PopupRounding ImGuiStyleVar_PopupBorderSize, // float PopupBorderSize ImGuiStyleVar_FramePadding, // ImVec2 FramePadding ImGuiStyleVar_FrameRounding, // float FrameRounding ImGuiStyleVar_FrameBorderSize, // float FrameBorderSize ImGuiStyleVar_ItemSpacing, // ImVec2 ItemSpacing ImGuiStyleVar_ItemInnerSpacing, // ImVec2 ItemInnerSpacing ImGuiStyleVar_IndentSpacing, // float IndentSpacing ImGuiStyleVar_CellPadding, // ImVec2 CellPadding ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding ImGuiStyleVar_ScrollbarPadding, // float ScrollbarPadding ImGuiStyleVar_GrabMinSize, // float GrabMinSize ImGuiStyleVar_GrabRounding, // float GrabRounding ImGuiStyleVar_ImageRounding, // float ImageRounding ImGuiStyleVar_ImageBorderSize, // float ImageBorderSize ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_TabBorderSize, // float TabBorderSize ImGuiStyleVar_TabMinWidthBase, // float TabMinWidthBase ImGuiStyleVar_TabMinWidthShrink, // float TabMinWidthShrink ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize ImGuiStyleVar_TabBarOverlineSize, // float TabBarOverlineSize ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign ImGuiStyleVar_TreeLinesSize, // float TreeLinesSize ImGuiStyleVar_TreeLinesRounding, // float TreeLinesRounding ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign ImGuiStyleVar_SeparatorSize, // float SeparatorSize ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign ImGuiStyleVar_SeparatorTextPadding, // ImVec2 SeparatorTextPadding ImGuiStyleVar_DockingSeparatorSize, // float DockingSeparatorSize ImGuiStyleVar_COUNT }; // Flags for InvisibleButton() [extended in imgui_internal.h] enum ImGuiButtonFlags_ { ImGuiButtonFlags_None = 0, ImGuiButtonFlags_MouseButtonLeft = 1 << 0, // React on left mouse button (default) ImGuiButtonFlags_MouseButtonRight = 1 << 1, // React on right mouse button ImGuiButtonFlags_MouseButtonMiddle = 1 << 2, // React on center mouse button ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, // [Internal] ImGuiButtonFlags_EnableNav = 1 << 3, // InvisibleButton(): do not disable navigation/tabbing. Otherwise disabled by default. }; // Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() enum ImGuiColorEditFlags_ { ImGuiColorEditFlags_None = 0, 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 color square. ImGuiColorEditFlags_NoOptions = 1 << 3, // // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. ImGuiColorEditFlags_NoSmallPreview = 1 << 4, // // ColorEdit, ColorPicker: disable color square preview next to the inputs. (e.g. to show only the inputs) ImGuiColorEditFlags_NoInputs = 1 << 5, // // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview color square). ImGuiColorEditFlags_NoTooltip = 1 << 6, // // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview. ImGuiColorEditFlags_NoLabel = 1 << 7, // // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker). ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead. ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target/source. ColorButton: disable drag and drop source. ImGuiColorEditFlags_NoBorder = 1 << 10, // // ColorButton: disable border (which is enforced by default) ImGuiColorEditFlags_NoColorMarkers = 1 << 11, // // ColorEdit: disable rendering R/G/B/A color marker. May also be disabled globally by setting style.ColorMarkerSize = 0. // Alpha preview // - Prior to 1.91.8 (2025/01/21): alpha was made opaque in the preview by default using old name ImGuiColorEditFlags_AlphaPreview. // - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. // - The new flags may be combined better and allow finer controls. ImGuiColorEditFlags_AlphaOpaque = 1 << 12, // // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. ImGuiColorEditFlags_AlphaNoBg = 1 << 13, // // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 14, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. // User Options (right-click on widget to change some of them). ImGuiColorEditFlags_AlphaBar = 1 << 18, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. 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_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, // [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_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks ImGuiColorEditFlags_AlphaMask_ = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque | ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf, ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV, // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiColorEditFlags_AlphaPreview = 0, // Removed in 1.91.8. This is the default now. Will display a checkerboard unless ImGuiColorEditFlags_AlphaNoBg is set. #endif //ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] }; // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. // We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // (Those are per-item flags. There is shared behavior flag too: ImGuiIO: io.ConfigDragClickToInputText) enum ImGuiSliderFlags_ { ImGuiSliderFlags_None = 0, ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits. ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits). ImGuiSliderFlags_NoInput = 1 << 7, // Disable Ctrl+Click or Enter key allowing to input text directly into the widget. ImGuiSliderFlags_WrapAround = 1 << 8, // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now. ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds. ImGuiSliderFlags_ClampZeroRange = 1 << 10, // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it. ImGuiSliderFlags_NoSpeedTweaks = 1 << 11, // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. ImGuiSliderFlags_ColorMarkers = 1 << 12, // DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component. ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange, ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from legacy API (obsoleted 2020-08) that has got miscast to this enum, and will trigger an assert if needed. }; // Identify a mouse button. // Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience. enum ImGuiMouseButton_ { ImGuiMouseButton_Left = 0, ImGuiMouseButton_Right = 1, ImGuiMouseButton_Middle = 2, ImGuiMouseButton_COUNT = 5 }; // Enumeration for GetMouseCursor() // User code may request backend to display given cursor by calling SetMouseCursor(), which is why we have some cursors that are marked unused here enum ImGuiMouseCursor_ { ImGuiMouseCursor_None = -1, ImGuiMouseCursor_Arrow = 0, ImGuiMouseCursor_TextInput, // When hovering over InputText, etc. ImGuiMouseCursor_ResizeAll, // (Unused by Dear ImGui functions) ImGuiMouseCursor_ResizeNS, // When hovering over a horizontal border ImGuiMouseCursor_ResizeEW, // When hovering over a vertical border or a column ImGuiMouseCursor_ResizeNESW, // When hovering over the bottom-left corner of a window ImGuiMouseCursor_ResizeNWSE, // When hovering over the bottom-right corner of a window ImGuiMouseCursor_Hand, // (Unused by Dear ImGui functions. Use for e.g. hyperlinks) ImGuiMouseCursor_Wait, // When waiting for something to process/load. ImGuiMouseCursor_Progress, // When waiting for something to process/load, but application is still interactive. ImGuiMouseCursor_NotAllowed, // When hovering something with disallowed interaction. Usually a crossed circle. ImGuiMouseCursor_COUNT }; // Enumeration for AddMouseSourceEvent() actual source of Mouse Input data. // Historically we use "Mouse" terminology everywhere to indicate pointer data, e.g. MousePos, IsMousePressed(), io.AddMousePosEvent() // But that "Mouse" data can come from different source which occasionally may be useful for application to know about. // You can submit a change of pointer type using io.AddMouseSourceEvent(). enum ImGuiMouseSource : int { ImGuiMouseSource_Mouse = 0, // Input is coming from an actual mouse. ImGuiMouseSource_TouchScreen, // Input is coming from a touch screen (no hovering prior to initial press, less precise initial press aiming, dual-axis wheeling possible). ImGuiMouseSource_Pen, // Input is coming from a pressure/magnetic pen (often used in conjunction with high-sampling rates). ImGuiMouseSource_COUNT }; // Enumeration for ImGui::SetNextWindow***(), SetWindow***(), SetNextItem***() 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_ { ImGuiCond_None = 0, // No condition (always set the variable), same as _Always ImGuiCond_Always = 1 << 0, // No condition (always set the variable), same as _None ImGuiCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call will succeed) ImGuiCond_FirstUseEver = 1 << 2, // Set the variable if the object/window has no persistently saved data (no entry in .ini file) ImGuiCond_Appearing = 1 << 3, // Set the variable if the object/window is appearing after being hidden/inactive (or the first time) }; //----------------------------------------------------------------------------- // [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) //----------------------------------------------------------------------------- // Flags for ImGui::BeginTable() // - Important! Sizing policies have complex and subtle side effects, much more so than you would expect. // Read comments/demos carefully + experiment with live demos to get acquainted with them. // - The DEFAULT sizing policies are: // - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize. // - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off. // - When ScrollX is off: // - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight. // - Columns sizing policy allowed: Stretch (default), Fixed/Auto. // - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all). // - Stretch Columns will share the remaining width according to their respective weight. // - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors. // The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. // (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing). // - When ScrollX is on: // - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed // - Columns sizing policy allowed: Fixed/Auto mostly. // - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed. // - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop. // - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable(). // If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again. // - Read on documentation at the top of imgui_tables.cpp for details. enum ImGuiTableFlags_ { // Features ImGuiTableFlags_None = 0, ImGuiTableFlags_Resizable = 1 << 0, // Enable resizing columns. ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu. ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width, visibility and sort settings in the .ini file. ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). // Decorations ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) ImGuiTableFlags_BordersInnerH = 1 << 7, // Draw horizontal borders between rows. ImGuiTableFlags_BordersOuterH = 1 << 8, // Draw horizontal borders at the top and bottom. ImGuiTableFlags_BordersInnerV = 1 << 9, // Draw vertical borders between columns. ImGuiTableFlags_BordersOuterV = 1 << 10, // Draw vertical borders on the left and right sides. ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders. ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders. ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders. ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders. ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders. ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style // Sizing Policy (read above for defaults) ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. ImGuiTableFlags_SizingStretchProp = 3 << 13, // Columns default to _WidthStretch with default weights proportional to each columns contents widths. ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn(). // Sizing Extra Options ImGuiTableFlags_NoHostExtendX = 1 << 16, // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. ImGuiTableFlags_NoHostExtendY = 1 << 17, // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable. ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. // Clipping ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). // Padding ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers. ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outermost padding. ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). // Scrolling ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX. ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. // Sorting ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). // Miscellaneous ImGuiTableFlags_HighlightHoveredColumn = 1 << 28, // Highlight column headers when hovered (may evolve into a fuller highlight) // [Internal] Combinations and masks ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame, }; // Flags for ImGui::TableSetupColumn() enum ImGuiTableColumnFlags_ { // Input configuration flags ImGuiTableColumnFlags_None = 0, ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column. ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column. ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column. ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command). ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will submit an empty label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. You may append into this cell by calling TableSetColumnIndex() right after the TableHeadersRow() call. ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. ImGuiTableColumnFlags_AngledHeader = 1 << 18, // TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row. // Output status flags, read-only via TableGetColumnFlags() ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling. ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse // [Internal] Combinations and masks ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable, ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered, ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30, // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge) }; // Flags for ImGui::TableNextRow() enum ImGuiTableRowFlags_ { ImGuiTableRowFlags_None = 0, ImGuiTableRowFlags_Headers = 1 << 0, // Identify header row (set default background color + width of its contents accounted differently for auto column width) }; // Enum for ImGui::TableSetBgColor() // Background colors are rendering in 3 layers: // - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set. // - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set. // - Layer 2: draw with CellBg color if set. // The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color. // When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows. // If you set the color of RowBg0 target, your color will override the existing RowBg0 color. // If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color. enum ImGuiTableBgTarget_ { ImGuiTableBgTarget_None = 0, ImGuiTableBgTarget_RowBg0 = 1, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used) ImGuiTableBgTarget_RowBg1 = 2, // Set row background color 1 (generally used for selection marking) ImGuiTableBgTarget_CellBg = 3, // Set cell background color (top-most color) }; // Sorting specifications for a table (often handling sort specs for a single column, occasionally more) // Obtained by calling TableGetSortSpecs(). // When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time. // Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame! struct ImGuiTableSortSpecs { const ImGuiTableColumnSortSpecs* Specs; // Pointer to sort spec array. int SpecsCount; // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled. bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag. ImGuiTableSortSpecs() { memset((void*)this, 0, sizeof(*this)); } }; // Sorting specification for one column of a table (sizeof == 12 bytes) struct ImGuiTableColumnSortSpecs { ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call) ImS16 ColumnIndex; // Index of the column ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here) ImGuiSortDirection SortDirection; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending ImGuiTableColumnSortSpecs() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- // [SECTION] Helpers: Debug log, memory allocations macros, ImVector<> //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Debug Logging into ShowDebugLogWindow(), tty and more. //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_DEBUG_TOOLS #define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__) #else #define IMGUI_DEBUG_LOG(...) ((void)0) #endif //----------------------------------------------------------------------------- // IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE() // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. // Defining a custom placement new() with a custom parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. //----------------------------------------------------------------------------- struct ImNewWrapper {}; inline void* operator new(size_t, ImNewWrapper, void* ptr) { return ptr; } inline void operator delete(void*, ImNewWrapper, void*) {} // This is only required so we can use the symmetrical new() #define IM_ALLOC(_SIZE) ImGui::MemAlloc(_SIZE) #define IM_FREE(_PTR) ImGui::MemFree(_PTR) #define IM_PLACEMENT_NEW(_PTR) new(ImNewWrapper(), _PTR) #define IM_NEW(_TYPE) new(ImNewWrapper(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } } //----------------------------------------------------------------------------- // 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 public structures are relying on it. // - We use std-like naming convention here, which is a little unusual for this codebase. // - 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. //----------------------------------------------------------------------------- IM_MSVC_RUNTIME_CHECKS_OFF 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); if (Data && src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything inline void clear_delete() { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); } // Important: never called automatically! always explicit. inline void clear_destruct() { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); } // Important: never called automatically! always explicit. 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 max_size() const { return 0x7FFFFFFF / (int)sizeof(T); } inline int capacity() const { return Capacity; } inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } 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 shrink(int new_size) { IM_ASSERT(new_size <= Size); Size = new_size; } // Resize a vector to a smaller size, guaranteed not to cause a reallocation inline void reserve(int new_capacity) { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; } inline void reserve_discard(int new_capacity) { if (new_capacity <= Capacity) return; if (Data) IM_FREE(Data); Data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); 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 - (size_t)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 T* find(const T& v) { T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; } inline const T* find(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; } inline int find_index(const T& v) const { const T* data_end = Data + Size; const T* it = find(v); if (it == data_end) return -1; const ptrdiff_t off = it - Data; return (int)off; } inline bool find_erase(const T& v) { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; } inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); 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; } }; IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] 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. //----------------------------------------------------------------------------- struct ImGuiStyle { // Font scaling // - recap: ImGui::GetFontSize() == FontSizeBase * (FontScaleMain * FontScaleDpi * other_scaling_factors) float FontSizeBase; // Current base font size before external global factors are applied. Use PushFont(NULL, size) to modify. Use ImGui::GetFontSize() to obtain scaled value. float FontScaleMain; // Main global scale factor. May be set by application once, or exposed to end-user. float FontScaleDpi; // Additional global scale factor from viewport/monitor contents scale. In docking branch: when io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI. float Alpha; // Global alpha applies to everything in Dear ImGui. float DisabledAlpha; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. ImVec2 WindowPadding; // Padding within a window. float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). float WindowBorderHoverPadding; // Hit-testing extent outside/inside resizing border. Also extend determination of hovered window. Generally meaningfully larger than WindowBorderSize to make it easy to reach borders. ImVec2 WindowMinSize; // Minimum window size. This is a global setting. If you want to constrain individual windows, use SetNextWindowSizeConstraints(). ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered. ImGuiDir WindowMenuButtonPosition; // Side of the collapsing/docking button in the title bar (None/Left/Right). Defaults to ImGuiDir_Left. float ChildRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows. float ChildBorderSize; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). float PopupRounding; // Radius of popup window corners rounding. (Note that tooltip windows use WindowRounding) float PopupBorderSize; // Thickness of border around popup/tooltip windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets). float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines. ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label). ImVec2 CellPadding; // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows. ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar. float ScrollbarRounding; // Radius of grab corners for scrollbar. float ScrollbarPadding; // Padding of scrollbar grab within its frame (same for both axes). 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. float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. float ImageRounding; // Rounding of Image() calls. float ImageBorderSize; // Thickness of border around Image() calls. float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. float TabBorderSize; // Thickness of border around tabs. float TabMinWidthBase; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected. float TabMinWidthShrink; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy. float TabCloseButtonMinWidthSelected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. float TabCloseButtonMinWidthUnselected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected. float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. float TabBarOverlineSize; // Thickness of tab-bar overline, which highlights the selected tab-bar. float TableAngledHeadersAngle; // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees). ImVec2 TableAngledHeadersTextAlign;// Alignment of angled headers within the cell ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes. float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines. float TreeLinesRounding; // Radius of lines connecting child nodes to the vertical line. float DragDropTargetRounding; // Radius of the drag and drop target frame. float DragDropTargetBorderSize; // Thickness of the drag and drop target border. float DragDropTargetPadding; // Size to expand the drag and drop target from actual target item size. float ColorMarkerSize; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers. ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. 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. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. float SeparatorSize; // Thickness of border in Separator() float SeparatorTextBorderSize; // Thickness of border in SeparatorText() ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). ImVec2 SeparatorTextPadding; // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. ImVec2 DisplayWindowPadding; // Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen. ImVec2 DisplaySafeAreaPadding; // Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured). bool DockingNodeHasCloseButton; // Docking node has their own CloseButton() to close all docked windows. float DockingSeparatorSize; // Thickness of resizing border between docked windows float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later. bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. float WindowShadowSize; // Size (in pixels) of window shadows. Set this to zero to disable shadows. float WindowShadowOffsetDist; // Offset distance (in pixels) of window shadows from casting window. float WindowShadowOffsetAngle; // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top). // Colors ImVec4 Colors[ImGuiCol_COUNT]; // Behaviors // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO) float HoverStationaryDelay; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. float HoverDelayShort; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. float HoverDelayNormal; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. ImGuiHoveredFlags HoverFlagsForTooltipNav; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. // [Internal] float _MainScale; // FIXME-WIP: Reference scale, as applied by ScaleAllSizes(). PLEASE DO NOT USE THIS FOR NOW. float _NextFrameFontSizeBase; // FIXME: Temporary hack until we finish remaining work. // Functions IMGUI_API ImGuiStyle(); IMGUI_API void ScaleAllSizes(float scale_factor); // Scale all spacing/padding/thickness values. Do not scale fonts. // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // TabMinWidthForCloseButton = TabCloseButtonMinWidthUnselected // Renamed in 1.91.9. #endif }; //----------------------------------------------------------------------------- // [SECTION] 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. // It is generally expected that: // - initialization: backends and user code writes to ImGuiIO. // - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO. //----------------------------------------------------------------------------- // Also see ImGui::GetPlatformIO() and ImGuiPlatformIO struct for OS/platform related functions: clipboard, IME etc. //----------------------------------------------------------------------------- // [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions. // If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration. struct ImGuiKeyData { bool Down; // True for if key is down float DownDuration; // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held) float DownDurationPrev; // Last frame duration the key has been down float AnalogValue; // 0.0f..1.0f for gamepad values }; struct ImGuiIO { //------------------------------------------------------------------ // Configuration // Default value //------------------------------------------------------------------ ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Keyboard/Gamepad navigation options, etc. ImGuiBackendFlags BackendFlags; // = 0 // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend. ImVec2 DisplaySize; // // Main display size, in pixels (== GetMainViewport()->Size). May change every frame. ImVec2 DisplayFramebufferScale; // = (1, 1) // Main display density. For retina display where window coordinates are different from framebuffer coordinates. This will affect font density + will end up in ImDrawData::FramebufferScale. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. May change every frame. float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions. const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). void* UserData; // = NULL // Store your own data. // Font system ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with Ctrl+Wheel. // Keyboard/Gamepad Navigation options bool ConfigNavSwapGamepadButtons; // = false // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout. bool ConfigNavMoveSetMousePos; // = false // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true. bool ConfigNavCaptureKeyboard; // = true // Sets io.WantCaptureKeyboard when io.NavActive is set. bool ConfigNavEscapeClearFocusItem; // = true // Pressing Escape can clear focused item + navigation id/highlight. Set to false if you want to always keep highlight on. bool ConfigNavEscapeClearFocusWindow;// = false // Pressing Escape can clear focused window as well (super set of io.ConfigNavEscapeClearFocusItem). bool ConfigNavCursorVisibleAuto; // = true // Using directional navigation key makes the cursor visible. Mouse click hides the cursor. bool ConfigNavCursorVisibleAlways; // = false // Navigation cursor is always visible. // Docking options (when ImGuiConfigFlags_DockingEnable is set) bool ConfigDockingNoSplit; // = false // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars. bool ConfigDockingNoDockingOver; // = false // Simplified docking mode: disable window merging into a same tab-bar, so docking is limited to splitting windows. bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigDockingAlwaysTabBar; // = false // [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node. bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge. // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. May also set ImGuiViewportFlags_NoAutoMerge on individual viewport. bool ConfigViewportsNoTaskBarIcon; // = false // Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it. bool ConfigViewportsNoDecoration; // = true // Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). bool ConfigViewportsNoDefaultParent; // = true // When false: set secondary viewports' ParentViewportId to main viewport ID by default. Expects the platform backend to setup a parent/child relationship between the OS windows based on this value. Some backend may ignore this. Set to true if you want viewports to automatically be parent of main viewport, otherwise all viewports will be top-level OS windows. bool ConfigViewportsPlatformFocusSetsImGuiFocus;//= true // When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change). // DPI/Scaling options // This may keep evolving during 1.92.x releases. Expect some turbulence. bool ConfigDpiScaleFonts; // = false // [EXPERIMENTAL] Automatically overwrite style.FontScaleDpi when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now. bool ConfigDpiScaleViewports; // = false // [EXPERIMENTAL] Scale Dear ImGui and Platform Windows when Monitor DPI changes. // Miscellaneous options // (you can visualize and interact with all options in 'Demo->Configuration') 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 backend implementations. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + 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. bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates. bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting). bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will reactivate item and select all text (single-line only). bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. bool ConfigWindowsCopyContentsWithCtrlC; // = false // [EXPERIMENTAL] Ctrl+C copy the contents of focused window into the clipboard. Experimental because: (1) has known issues with nested Begin/End pairs (2) text output quality varies (3) text output is in submission order rather than spatial order. bool ConfigScrollbarScrollByPage; // = true // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. // Inputs Behaviors // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle) 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. float KeyRepeatDelay; // = 0.275f // 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. //------------------------------------------------------------------ // Debug options //------------------------------------------------------------------ // Options to configure Error Handling and how we handle recoverable errors [EXPERIMENTAL] // - Error recovery is provided as a way to facilitate: // - Recovery after a programming error (native code or scripting language - the latter tends to facilitate iterating on code while running). // - Recovery after running an exception handler or any error processing which may skip code after an error has been detected. // - Error recovery is not perfect nor guaranteed! It is a feature to ease development. // You not are not supposed to rely on it in the course of a normal application run. // - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT(). // - By design, we do NOT allow error recovery to be 100% silent. One of the three options needs to be checked! // - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled when making direct imgui API calls! // Otherwise it would severely hinder your ability to catch and correct mistakes! // Read https://github.com/ocornut/imgui/wiki/Error-Handling for details. // - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!) // - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (tooltips, visible log entries, use callback etc.) // - Recovery after error/exception: record stack sizes with ErrorRecoveryStoreState(), disable assert, set log callback (to e.g. trigger high-level breakpoint), recover with ErrorRecoveryTryToRecoverState(), restore settings. bool ConfigErrorRecovery; // = true // Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled. bool ConfigErrorRecoveryEnableAssert; // = true // Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR() bool ConfigErrorRecoveryEnableDebugLog; // = true // Enable debug log output on recoverable errors. bool ConfigErrorRecoveryEnableTooltip; // = true // Enable tooltip on recoverable errors. The tooltip include a way to enable asserts if they were disabled. // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro. // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability. // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application. // e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version. bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK(). // Tools to detect code submitting items with conflicting/duplicate IDs // - Code should use PushID()/PopID() in loops, or append "##xx" to same-label identifiers. // - Empty label e.g. Button("") == same ID as parent widget/node. Use Button("##xx") instead! // - See FAQ https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-about-the-id-stack-system bool ConfigDebugHighlightIdConflicts;// = true // Highlight and show an error message popup when multiple items have conflicting identifiers. bool ConfigDebugHighlightIdConflictsShowItemPicker;//=true // Show "Item Picker" button in aforementioned popup. // Tools to test correct Begin/End and BeginChild/EndChild behaviors. // - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX() // - This is inconsistent with other BeginXXX functions and create confusion for many users. // - We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior. bool ConfigDebugBeginReturnValueOnce;// = false // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows. bool ConfigDebugBeginReturnValueLoop;// = false // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running. // Option to deactivate io.AddFocusEvent(false) handling. // - May facilitate interactions with a debugger when focus loss leads to clearing inputs data. // - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them. bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys()/io.ClearInputMouse() in input processing. // Option to audit .ini data bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) //------------------------------------------------------------------ // Platform Identifiers // (the imgui_impl_xxxx backend files are setting those up for you) //------------------------------------------------------------------ // Nowadays those would be stored in ImGuiPlatformIO but we are leaving them here for legacy reasons. // Optional: Platform/Renderer backend name (informational only! will be displayed in About Window) + User data for backend/wrappers to store their own stuff. const char* BackendPlatformName; // = NULL const char* BackendRendererName; // = NULL void* BackendPlatformUserData; // = NULL // User data for platform backend void* BackendRendererUserData; // = NULL // User data for renderer backend void* BackendLanguageUserData; // = NULL // User data for non C++ programming language backend //------------------------------------------------------------------ // Input - Call before calling NewFrame() //------------------------------------------------------------------ // Input Functions IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend. IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change IMGUI_API void AddMouseWheelEvent(float wheel_x, float wheel_y); // Queue a mouse wheel update. wheel_y<0: scroll down, wheel_y>0: scroll up, wheel_x<0: scroll right, wheel_x>0: scroll left. IMGUI_API void AddMouseSourceEvent(ImGuiMouseSource source); // Queue a mouse source change (Mouse/TouchScreen/Pen) IMGUI_API void AddMouseViewportEvent(ImGuiID id); // Queue a mouse hovered viewport. Requires backend to set ImGuiBackendFlags_HasMouseHoveredViewport to call this (for multi-viewport support). IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from a UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from a UTF-8 string IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode. IMGUI_API void SetAppAcceptingEvents(bool accepting_events); // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen. IMGUI_API void ClearEventsQueue(); // Clear all incoming events. IMGUI_API void ClearInputKeys(); // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons. IMGUI_API void ClearInputMouse(); // Clear current mouse state. //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() // (when reading from the io.WantCaptureMouse, io.WantCaptureKeyboard flags to dispatch your inputs, it is // generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!) //------------------------------------------------------------------ bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, 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; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, 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 set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when io.ConfigNavMoveSetMousePos 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: clear io.WantSaveIniSettings yourself after saving! bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavVisible; // Keyboard/Gamepad navigation highlight is visible and allowed (will handle ImGuiKey_NavXXX events). float Framerate; // Estimate of application framerate (rolling average over 60 frames, based on io.DeltaTime), in frame per second. Solely for convenience. Slow applications may not want to use a moving average or may want to reset underlying buffers occasionally. 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 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] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ ImGuiContext* Ctx; // Parent UI context (needs to be set explicitly by parent). // Main Input State // (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead) // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere) ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.) bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow 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. >0 scrolls Up, <0 scrolls Down. Hold Shift to turn vertical scroll into horizontal scroll. float MouseWheelH; // Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends. ImGuiMouseSource MouseSource; // Mouse actual input peripheral (Mouse/TouchScreen/Pen). ImGuiID MouseHoveredViewport; // (Optional) Modify using io.AddMouseViewportEvent(). With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). bool KeyCtrl; // Keyboard modifier down: Ctrl (non-macOS), Cmd (macOS) bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt bool KeySuper; // Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS) // Other state maintained from data above + IO function calls ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame() ImGuiKeyData KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. MUST use 'key - ImGuiKey_NamedKey_BEGIN' as index. Use IsKeyXXX() functions to access this. bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. 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 (same as MouseClickedCount[x] != 0) bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. bool MouseReleased[5]; // Mouse button went from Down to !Down double MouseReleasedTime[5]; // Time of last released (rarely used! but useful to handle delayed single-click when trying to disambiguate them from double-click). bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding Shift requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system. bool MouseCtrlLeftAsRightClick; // (OSX) Set to true when the current click was a Ctrl+Click that spawned a simulated right click 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 (used for moving thresholds) float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. bool AppFocusLost; // Only modify via AddFocusEvent() bool AppAcceptingEvents; // Only modify via SetAppAcceptingEvents() ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent(). // Old (<1.87): ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space) // Old (<1.87): ImGui::IsKeyPressed(MYPLATFORM_KEY_SPACE) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space) // Read https://github.com/ocornut/imgui/issues/4921 for details. //int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512. //bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: 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). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow. //float NavInputs[ImGuiNavInput_COUNT]; // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums. //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS float FontGlobalScale; // Moved io.FontGlobalScale to style.FontScaleMain in 1.92 (June 2025) // Legacy: before 1.91.1, clipboard functions were stored in ImGuiIO instead of ImGuiPlatformIO. // As this is will affect all users of custom engines/backends, we are providing proper legacy redirection (will obsolete). const char* (*GetClipboardTextFn)(void* user_data); void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; //void ClearInputCharacters() { InputQueueCharacters.resize(0); } // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys(). Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue. #endif IMGUI_API ImGuiIO(); }; //----------------------------------------------------------------------------- // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) //----------------------------------------------------------------------------- // 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_CallbackEdit: Callback on buffer edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active. // - ImGuiInputTextFlags_CallbackAlways: Callback on each iteration // - ImGuiInputTextFlags_CallbackCompletion: Callback on pressing TAB // - ImGuiInputTextFlags_CallbackHistory: Callback on pressing Up/Down arrows // - 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 { ImGuiContext* Ctx; // Parent UI context 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 ImGuiID ID; // Widget ID // Read-only // Arguments for the different callback events // - During Resize callback, Buf will be same as your input buffer. // - However, during Completion/History/Always callback, Buf always points to our own internal data (it is not the same as your buffer)! Changes to it will be reflected into your own buffer shortly after the callback. // - 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. ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] 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; bool EventActivated; // Input field just got activated // Read-only // [Always] bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] 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 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); void SelectAll() { SelectionStart = 0; CursorPos = SelectionEnd = BufTextLen; } void SetSelection(int s, int e) { IM_ASSERT(s >= 0 && s <= BufTextLen); IM_ASSERT(e >= 0 && e <= BufTextLen); SelectionStart = s; CursorPos = SelectionEnd = e; } void ClearSelection() { SelectionStart = SelectionEnd = BufTextLen; } 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(). Generally store an integer or float in here (need reinterpret_cast<>). 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. }; // [ALPHA] Rarely used / very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions. // Important: the content of this class is still highly WIP and likely to change and be refactored // before we stabilize Docking features. Please be mindful if using this. // Provide hints: // - To the platform backend via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) // - To the platform backend for OS level parent/child relationships of viewport. // - To the docking system for various options and filtering. struct ImGuiWindowClass { ImGuiID ClassId; // User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others. ImGuiID ParentViewportId; // Hint for the platform backend. -1: use default. 0: request platform backend to not parent the platform. != 0: request platform backend to create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not. ImGuiID FocusRouteParentWindowId; // ID of parent window for shortcut focus route evaluation, e.g. Shortcut() call from Parent Window will succeed when this window is focused. ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiTabItemFlags TabItemFlagsOverrideSet; // [EXPERIMENTAL] TabItem flags to set when a window of this class gets submitted into a dock node tab bar. May use with ImGuiTabItemFlags_Leading or ImGuiTabItemFlags_Trailing. ImGuiDockNodeFlags DockNodeFlagsOverrideSet; // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!) bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override? ImGuiWindowClass() { memset((void*)this, 0, sizeof(*this)); ParentViewportId = (ImGuiID)-1; DockingAllowUnclassed = true; } }; // 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; } }; //----------------------------------------------------------------------------- // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) //----------------------------------------------------------------------------- // Helper: Unicode defines #define IM_UNICODE_CODEPOINT_INVALID 0xFFFD // Invalid Unicode code point (standard value). #ifdef IMGUI_USE_WCHAR32 #define IM_UNICODE_CODEPOINT_MAX 0x10FFFF // Maximum Unicode code point supported by this build. #else #define IM_UNICODE_CODEPOINT_MAX 0xFFFF // Maximum Unicode code point supported by this build. #endif // Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create a UI within deep-nested code that runs multiple times every frame. // Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text("This will be called only once per frame"); struct ImGuiOnceUponAFrame { ImGuiOnceUponAFrame() { RefFrame = -1; } mutable int RefFrame; operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } }; // 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 PassFilter(const char* text, const char* text_end = NULL) const; IMGUI_API void Build(); void Clear() { InputBuf[0] = 0; Build(); } bool IsActive() const { return !Filters.empty(); } // [Internal] struct ImGuiTextRange { const char* b; const char* e; ImGuiTextRange() { b = e = NULL; } ImGuiTextRange(const char* _b, const char* _e) { b = _b; e = _e; } bool empty() const { return b == e; } IMGUI_API void split(char separator, ImVector* out) const; }; char InputBuf[256]; ImVectorFilters; int CountGrep; }; // Helper: Growable text buffer for logging/accumulating text // (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder') struct ImGuiTextBuffer { ImVector Buf; IMGUI_API static char EmptyString[1]; ImGuiTextBuffer() { } inline char operator[](int i) const { 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.Size ? Buf.Size - 1 : 0; } bool empty() const { return Buf.Size <= 1; } void clear() { Buf.clear(); } void resize(int size) { if (Buf.Size > size) Buf.Data[size] = 0; Buf.resize(size ? size + 1 : 0, 0); } // Similar to resize(0) on ImVector: empty string but don't free buffer. 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); }; // [Internal] Key+Value for ImGuiStorage struct ImGuiStoragePair { ImGuiID key; union { int val_i; float val_f; void* val_p; }; ImGuiStoragePair(ImGuiID _key, int _val) { key = _key; val_i = _val; } ImGuiStoragePair(ImGuiID _key, float _val) { key = _key; val_f = _val; } ImGuiStoragePair(ImGuiID _key, void* _val) { key = _key; val_p = _val; } }; // 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) // You can use it as custom user storage for temporary values. Declare your own storage if, for example: // - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state). // - You want to store custom debug data easily without adding or editing structures in your code (probably not efficient, but convenient) // Types are NOT stored, so it is up to you to make sure your Key don't collide with different types. struct ImGuiStorage { // [Internal] ImVector Data; // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N) // - Set***() functions find pair, insertion on demand if missing. // - Sorted insertion is costly, paid once. A typical frame shouldn't need to insert any new pair. void Clear() { Data.clear(); } IMGUI_API int GetInt(ImGuiID key, int default_val = 0) const; IMGUI_API void SetInt(ImGuiID key, int val); IMGUI_API bool GetBool(ImGuiID key, bool default_val = false) const; IMGUI_API void SetBool(ImGuiID key, bool val); IMGUI_API float GetFloat(ImGuiID key, float default_val = 0.0f) const; IMGUI_API void SetFloat(ImGuiID key, float val); IMGUI_API void* GetVoidPtr(ImGuiID key) const; // default_val is NULL IMGUI_API void SetVoidPtr(ImGuiID key, void* val); // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set. // - 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. // - A typical use case where this is convenient for quick hacking (e.g. add storage during a live Edit&Continue session if you can't modify existing struct) // float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat("var", pvar, 0, 100.0f); some_var += *pvar; IMGUI_API int* GetIntRef(ImGuiID key, int default_val = 0); IMGUI_API bool* GetBoolRef(ImGuiID key, bool default_val = false); IMGUI_API float* GetFloatRef(ImGuiID key, float default_val = 0.0f); IMGUI_API void** GetVoidPtrRef(ImGuiID key, void* default_val = NULL); // Advanced: for quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. IMGUI_API void BuildSortByKey(); // Obsolete: use on your own storage if you know only integer are being stored (open/close all tree nodes) IMGUI_API void SetAllInt(int val); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //typedef ::ImGuiStoragePair ImGuiStoragePair; // 1.90.8: moved type outside struct #endif }; // Flags for ImGuiListClipper (currently not fully exposed in function calls: a future refactor will likely add this to ImGuiListClipper::Begin function equivalent) enum ImGuiListClipperFlags_ { ImGuiListClipperFlags_None = 0, ImGuiListClipperFlags_NoSetTableRowCounters = 1 << 0, // [Internal] Disabled modifying table row counters. Avoid assumption that 1 clipper item == 1 table row. }; // Helper: Manually clip large list of items. // If you have lots evenly spaced items and you have random access to the list, you can perform coarse // clipping based on visibility to only submit items that are in view. // The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. // (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally // fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily // scale using lists with tens of thousands of items without a problem) // Usage: // ImGuiListClipper clipper; // clipper.Begin(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); // Generally what happens is: // - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not. // - User code submit that one element. // - Clipper can measure the height of the first element // - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element. // - User code submit visible elements. // - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc. struct ImGuiListClipper { ImGuiContext* Ctx; // Parent UI context int DisplayStart; // First item to display, updated by each call to Step() int DisplayEnd; // End of items to display (exclusive) int UserIndex; // Helper storage for user convenience/code. Optional, and otherwise unused if you don't use it. int ItemsCount; // [Internal] Number of items float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it double StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed double StartSeekOffsetY; // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows. void* TempData; // [Internal] Internal data ImGuiListClipperFlags Flags; // [Internal] Flags, currently not yet well exposed. // items_count: 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, and you can call SeekCursorForItem() manually if you need) // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). IMGUI_API ImGuiListClipper(); IMGUI_API ~ImGuiListClipper(); IMGUI_API void Begin(int items_count, float items_height = -1.0f); IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. // Call IncludeItemByIndex() or IncludeItemsByIndex() *BEFORE* first call to Step() if you need a range of items to not be clipped, regardless of their visibility. // (Due to alignment / padding of certain items it is possible that an extra item may be included on either end of the display range). inline void IncludeItemByIndex(int item_index) { IncludeItemsByIndex(item_index, item_index + 1); } IMGUI_API void IncludeItemsByIndex(int item_begin, int item_end); // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped. // Seek cursor toward given item. This is automatically called while stepping. // - The only reason to call this is: you can use ImGuiListClipper::Begin(INT_MAX) if you don't know item count ahead of time. // - In this case, after all steps are done, you'll want to call SeekCursorForItem(item_count). IMGUI_API void SeekCursorForItem(int item_index); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9] //inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6] //inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset((void*)this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] #endif }; // Helpers: ImVec2/ImVec4 operators // - It is important that we are keeping those disabled by default so they don't leak in user space. // - This is in order to allow user enabling implicit cast operators between ImVec2/ImVec4 and their own types (using IM_VEC2_CLASS_EXTRA in imconfig.h) // - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. // - We intentionally provide ImVec2*float but not float*ImVec2: this is rare enough and we want to reduce the surface for possible user mistake. #ifdef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED IM_MSVC_RUNTIME_CHECKS_OFF // ImVec2 operators inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); } inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); } inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); } inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); } inline ImVec2 operator-(const ImVec2& lhs) { return ImVec2(-lhs.x, -lhs.y); } inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; } inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } inline ImVec2& operator*=(ImVec2& lhs, const ImVec2& rhs) { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; } inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; } inline bool operator==(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; } inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y; } // ImVec4 operators inline ImVec4 operator*(const ImVec4& lhs, const float rhs) { return ImVec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs); } inline ImVec4 operator/(const ImVec4& lhs, const float rhs) { return ImVec4(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs); } inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); } inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); } inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); } inline ImVec4 operator/(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z, lhs.w / rhs.w); } inline ImVec4 operator-(const ImVec4& lhs) { return ImVec4(-lhs.x, -lhs.y, -lhs.z, -lhs.w); } inline bool operator==(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w; } inline bool operator!=(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z || lhs.w != rhs.w; } IM_MSVC_RUNTIME_CHECKS_RESTORE #endif // Helpers macros to generate 32-bit encoded colors // - User can declare their own format by #defining the 5 _SHIFT/_MASK macros in their imconfig file. // - Any setting other than the default will need custom backend support. The only standard backend that supports anything else than the default is DirectX9. #ifndef IM_COL32_R_SHIFT #ifdef IMGUI_USE_BGRA_PACKED_COLOR #define IM_COL32_R_SHIFT 16 #define IM_COL32_G_SHIFT 8 #define IM_COL32_B_SHIFT 0 #define IM_COL32_A_SHIFT 24 #define IM_COL32_A_MASK 0xFF000000 #else #define IM_COL32_R_SHIFT 0 #define IM_COL32_G_SHIFT 8 #define IM_COL32_B_SHIFT 16 #define IM_COL32_A_SHIFT 24 #define IM_COL32_A_MASK 0xFF000000 #endif #endif #define IM_COL32(R,G,B,A) (((ImU32)(A)<> IM_COL32_R_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * (1.0f / 255.0f)) {} inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } inline operator ImVec4() const { return Value; } // FIXME-OBSOLETE: May need to obsolete/cleanup those helpers. inline void SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; } 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); } }; //----------------------------------------------------------------------------- // [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage) //----------------------------------------------------------------------------- // Multi-selection system // Documentation at: https://github.com/ocornut/imgui/wiki/Multi-Select // - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this. // - This system implements standard multi-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc) // with support for clipper (skipping non-visible items), box-select and many other details. // - Selectable(), Checkbox() are supported but custom widgets may use it as well. // - TreeNode() is technically supported but... using this correctly is more complicated: you need some sort of linear/random access to your tree, // which is suited to advanced trees setups also implementing filters and clipper. We will work toward simplifying and demoing it. // - In the spirit of Dear ImGui design, your code owns actual selection data. // This is designed to allow all kinds of selection storage you may use in your application e.g. set/map/hash. // About ImGuiSelectionBasicStorage: // - This is an optional helper to store a selection state and apply selection requests. // - It is used by our demos and provided as a convenience to quickly implement multi-selection. // Usage: // - Identify submitted items with SetNextItemSelectionUserData(), most likely using an index into your current data-set. // - Store and maintain actual selection data using persistent object identifiers. // - Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (2) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 6. // - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper.IncludeItemByIndex(ms_io->RangeSrcItem) call will work. // LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (6) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 2. // If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is fine to always honor those steps. // About ImGuiSelectionUserData: // - This can store an application-defined identifier (e.g. index or pointer) submitted via SetNextItemSelectionUserData(). // - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO. // - Most applications will store an object INDEX, hence the chosen name and type. Storing an index is natural, because // SetRange requests will give you two end-points and you will need to iterate/interpolate between them to update your selection. // - However it is perfectly possible to store a POINTER or another IDENTIFIER inside ImGuiSelectionUserData. // Our system never assume that you identify items by indices, it never attempts to interpolate between two values. // - If you enable ImGuiMultiSelectFlags_NoRangeSelect then it is guaranteed that you will never have to interpolate // between two ImGuiSelectionUserData, which may be a convenient way to use part of the feature with less code work. // - As most users will want to store an index, for convenience and to reduce confusion we use ImS64 instead of void*, // being syntactically easier to downcast. Feel free to reinterpret_cast and store a pointer inside. // Flags for BeginMultiSelect() enum ImGuiMultiSelectFlags_ { ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable Ctrl+A shortcut to select all. ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). With BoxSelect is also ensure contiguous SetRange requests are not combined into one. This allows not handling interpolation in SetRange requests. ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes). ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes). ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5, // Disable clearing selection when clicking/selecting an already selected item. ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. full row Selectable()). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. ImGuiMultiSelectFlags_BoxSelect2d = 1 << 7, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This is slower: alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 8, // Disable scrolling when box-selecting near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 9, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 10, // Clear selection when clicking on empty location within scope. ImGuiMultiSelectFlags_ScopeWindow = 1 << 11, // Scope for _BoxSelect and _ClearOnClickVoid is whole window (Default). Use if BeginMultiSelect() covers a whole window or used a single time in same window. ImGuiMultiSelectFlags_ScopeRect = 1 << 12, // Scope for _BoxSelect and _ClearOnClickVoid is rectangle encompassing BeginMultiSelect()/EndMultiSelect(). Use if BeginMultiSelect() is called multiple times in same window. ImGuiMultiSelectFlags_SelectOnClick = 1 << 13, // Apply selection on mouse down when clicking on unselected item. (Default) ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. //ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does. ImGuiMultiSelectFlags_NavWrapX = 1 << 16, // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one. ImGuiMultiSelectFlags_NoSelectOnRightClick = 1 << 17, // Disable default right-click processing, which selects item on mouse down, and is designed for context-menus. }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). // This mainly contains a list of selection requests. // - Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen. // - Some fields are only useful if your list is dynamic and allows deletion (getting post-deletion focus/state right is shown in the demo) // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code. struct ImGuiMultiSelectIO { //------------------------------------------// BeginMultiSelect / EndMultiSelect ImVector Requests; // ms:w, app:r / ms:w app:r // Requests to apply to your selection data. ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (often the first selected item) must never be clipped: use clipper.IncludeItemByIndex() to ensure it is submitted. ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). int ItemsCount; // ms:w, app:r / app:r // 'int items_count' parameter to BeginMultiSelect() is copied here for convenience, allowing simpler calls to your ApplyRequests handler. Not used internally. }; // Selection request type enum ImGuiSelectionRequestType { ImGuiSelectionRequestType_None = 0, ImGuiSelectionRequestType_SetAll, // Request app to clear selection (if Selected==false) or select all items (if Selected==true). We cannot set RangeFirstItem/RangeLastItem as its contents is entirely up to user (not necessarily an index) ImGuiSelectionRequestType_SetRange, // Request app to select/unselect [RangeFirstItem..RangeLastItem] items (inclusive) based on value of Selected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. }; // Selection request item struct ImGuiSelectionRequest { //------------------------------------------// BeginMultiSelect / EndMultiSelect ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range. bool Selected; // ms:w, app:r / ms:w, app:r // Parameter for SetAll/SetRange requests (true = select, false = unselect) ImS8 RangeDirection; // / ms:w app:r // Parameter for SetRange request: +1 when RangeFirstItem comes before RangeLastItem, -1 otherwise. Useful if you want to preserve selection order on a backward Shift+Click. ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom). ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top). Inclusive! }; // Optional helper to store multi-selection state + apply multi-selection requests. // - Used by our demos and provided as a convenience to easily implement basic multi-selection. // - Iterate selection with 'void* it = NULL; ImGuiID id; while (selection.GetNextSelectedItem(&it, &id)) { ... }' // Or you can check 'if (Contains(id)) { ... }' for each possible object if their number is not too high to iterate. // - USING THIS IS NOT MANDATORY. This is only a helper and not a required API. // To store a multi-selection, in your application you could: // - Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. // - Use your own external storage: e.g. std::set, std::vector, interval trees, intrusively stored selection etc. // In ImGuiSelectionBasicStorage we: // - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) // - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index. // - use decently optimized logic to allow queries and insertion of very large selection sets. // - do not preserve selection order. // Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. // Large applications are likely to eventually want to get rid of this indirection layer and do their own thing. // See https://github.com/ocornut/imgui/wiki/Multi-Select for details and pseudo-code using this helper. struct ImGuiSelectionBasicStorage { // Members int Size; // // Number of selected items, maintained by this helper. bool PreserveOrder; // = false // GetNextSelectedItem() will return ordered selection (currently implemented by two additional sorts of selection. Could be improved) void* UserData; // = NULL // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; int _SelectionOrder;// [Internal] Increasing counter to store selection order ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set. Prefer not accessing directly: iterate with GetNextSelectedItem(). // Methods IMGUI_API ImGuiSelectionBasicStorage(); IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect() IMGUI_API bool Contains(ImGuiID id) const; // Query if an item id is in selection. IMGUI_API void Clear(); // Clear selection IMGUI_API void Swap(ImGuiSelectionBasicStorage& r); // Swap two selections IMGUI_API void SetItemSelected(ImGuiID id, bool selected); // Add/remove an item from selection (generally done by ApplyRequests() function) IMGUI_API bool GetNextSelectedItem(void** opaque_it, ImGuiID* out_id); // Iterate selection with 'void* it = NULL; ImGuiID id; while (selection.GetNextSelectedItem(&it, &id)) { ... }' inline ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } // Convert index to item id based on provided adapter. }; // Optional helper to apply multi-selection requests to existing randomly accessible storage. // Convenient if you want to quickly wire multi-select API on e.g. an array of bool or items storing their own selection state. struct ImGuiSelectionExternalStorage { // Members void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; } // Methods IMGUI_API ImGuiSelectionExternalStorage(); IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Apply selection requests by using AdapterSetItemSelected() calls }; //----------------------------------------------------------------------------- // [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. //----------------------------------------------------------------------------- // The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking. #ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX #define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (32) #endif // ImDrawIdx: vertex index. [Compile-time configurable type] // - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended). // - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file. #ifndef ImDrawIdx typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibility with renderer backends) #endif // ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h] // 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() }' // If you want to override the signature of ImDrawCallback, you can simply use e.g. '#define ImDrawCallback MyDrawCallback' (in imconfig.h) + update rendering backend accordingly. #ifndef ImDrawCallback typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); #endif // Special Draw callback value to request renderer backend to reset the graphics/render state. // The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address. // This is useful, for example, if you submitted callbacks which you know have altered the render state and you want it to be restored. // Render state is not reset by default because they are many perfectly useful way of altering render state (e.g. changing shader/blending settings before an Image call). #define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8) // Typically, 1 command = 1 GPU draw call (unless command is a callback) // - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled, // this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices. // Backends made for <1.71. will typically ignore the VtxOffset fields. // - The ClipRect/TexRef/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for). struct ImDrawCmd { ImVec4 ClipRect; // 4*4 // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates ImTextureRef TexRef; // 16 // Reference to a font/texture atlas (where backend called ImTextureData::SetTexID()) or to a user-provided texture ID (via e.g. ImGui::Image() calls). Both will lead to a ImTextureID value. unsigned int VtxOffset; // 4 // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices. unsigned int IdxOffset; // 4 // Start offset in index buffer. unsigned int ElemCount; // 4 // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. ImDrawCallback UserCallback; // 4-8 // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. void* UserCallbackData; // 4-8 // Callback user data (when UserCallback != NULL). If called AddCallback() with size == 0, this is a copy of the AddCallback() argument. If called AddCallback() with size > 0, this is pointing to a buffer where data is stored. int UserCallbackDataSize; // 4 // Size of callback user data when using storage, otherwise 0. int UserCallbackDataOffset;// 4 // [Internal] Offset of callback user data when using storage, otherwise -1. ImDrawCmd() { memset((void*)this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) // Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used! inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID) }; // Vertex layout #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; ImVec2 uv; ImU32 col; }; #else // You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h // The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine. // The type has to be described within the macro (you can either declare the struct or use a typedef). This is because ImVec2/ImU32 are likely not declared at the time you'd want to set your type up. // NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM. IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; #endif // [Internal] For use by ImDrawList struct ImDrawCmdHeader { ImVec4 ClipRect; ImTextureRef TexRef; unsigned int VtxOffset; }; // [Internal] For use by ImDrawListSplitter struct ImDrawChannel { ImVector _CmdBuffer; ImVector _IdxBuffer; }; // Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order. // This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call. struct ImDrawListSplitter { int _Current; // Current channel number (0) int _Count; // Number of active channels (1+) ImVector _Channels; // Draw channels (not resized down so _Count might be < Channels.Size) inline ImDrawListSplitter() { memset((void*)this, 0, sizeof(*this)); } inline ~ImDrawListSplitter() { ClearFreeMemory(); } inline void Clear() { _Current = 0; _Count = 1; } // Do not clear Channels[] so our allocations are reused next frame IMGUI_API void ClearFreeMemory(); IMGUI_API void Split(ImDrawList* draw_list, int count); IMGUI_API void Merge(ImDrawList* draw_list); IMGUI_API void SetCurrentChannel(ImDrawList* draw_list, int channel_idx); }; // Flags for ImDrawList functions // (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused) enum ImDrawFlags_ { ImDrawFlags_None = 0, ImDrawFlags_Closed = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason) ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01. ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02. ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04. ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08. ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag! ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft, ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, ImDrawFlags_ShadowCutOutShapeBackground = 1 << 9, // Do not render the shadow shape under the objects to be shadowed to save on fill-rate or facilitate blending. Slower on CPU. }; // Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. // It is however possible to temporarily alter flags between calls to ImDrawList:: functions. enum ImDrawListFlags_ { ImDrawListFlags_None = 0, ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AllowVtxOffset = 1 << 3, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. }; // Draw command list // This is the low-level list of polygons that ImGui:: functions are filling. At the end of the frame, // all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. // Each dear imgui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to // access the current window draw list and draw custom primitives. // You can interleave normal ImGui:: calls and adding primitives to the current draw list. // In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize). // You are totally free to apply whatever transformation matrix you want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!) // Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects. struct ImDrawList { // This is what you have to render ImVector CmdBuffer; // Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback. ImVector IdxBuffer; // Index buffer. Each command consume ImDrawCmd::ElemCount of those ImVector VtxBuffer; // Vertex buffer. ImDrawListFlags Flags; // Flags, you may poke into these to adjust anti-aliasing settings per-primitive. // [Internal, used while building lists] unsigned int _VtxCurrentIdx; // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0. ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context) ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) ImVector _Path; // [Internal] current path building ImDrawCmdHeader _CmdHeader; // [Internal] template of active commands. Fields should match those of CmdBuffer.back(). ImDrawListSplitter _Splitter; // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!) ImVector _ClipRectStack; // [Internal] ImVector _TextureStack; // [Internal] ImVector _CallbacksDataBuf; // [Internal] float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content const char* _OwnerName; // Pointer to owner window's name for debugging // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData(). // (advanced: you may create and use your own ImDrawListSharedData so you can use ImDrawList without ImGui, but that's more involved) IMGUI_API ImDrawList(ImDrawListSharedData* shared_data); IMGUI_API ~ImDrawList(); IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) IMGUI_API void PushClipRectFullScreen(); IMGUI_API void PopClipRect(); IMGUI_API void PushTexture(ImTextureRef tex_ref); IMGUI_API void PopTexture(); inline ImVec2 GetClipRectMin() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.x, cr.y); } inline ImVec2 GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); } // Primitives // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. // - For rectangular primitives, "p_min" and "p_max" represent the upper-left and lower-right corners. // - For circle primitives, use "num_segments == 0" to automatically calculate tessellation (preferred). // In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12. // In future versions we will use textures to provide cheaper and higher-quality circles. // Use AddNgon() and AddNgonFilled() functions if you need to guarantee a specific number of sides. IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f); IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); IMGUI_API void AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness = 1.0f); IMGUI_API void AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col); IMGUI_API void AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness = 1.0f); IMGUI_API void AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col); IMGUI_API void AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 0, float thickness = 1.0f); IMGUI_API void AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0); IMGUI_API void AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f); IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments); IMGUI_API void AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f); IMGUI_API void AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0); IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); IMGUI_API void AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) IMGUI_API void AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points) // General polygon // - Only simple polygons are supported by filling functions (no self-intersections, no holes). // - Concave polygon fill is more expensive than convex one: it has O(N^2) complexity. Provided as a convenience for the user but not used by the main library. IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); IMGUI_API void AddConcavePolyFilled(const ImVec2* points, int num_points, ImU32 col); // Image primitives // - Read FAQ to understand what ImTextureID/ImTextureRef are. // - "p_min" and "p_max" represent the upper-left and lower-right corners of the rectangle. // - "uv_min" and "uv_max" represent the normalized texture coordinates to use for those corners. Using (0,0)->(1,1) texture coordinates will generally display the entire texture. IMGUI_API void AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min = ImVec2(0, 0), const ImVec2& uv_max = ImVec2(1, 1), ImU32 col = IM_COL32_WHITE); IMGUI_API void AddImageQuad(ImTextureRef tex_ref, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE); IMGUI_API void AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0); // Shadows primitives // [BETA] API // - Add shadow for a object, with min/max or center/radius describing the object extents, and offset shifting the shadow. // - Rounding parameters refer to the object itself, not the shadow! // - By default, the area under the object is filled, because this is simpler to process. // Using the ImDrawFlags_ShadowCutOutShapeBackground flag makes the function not render this area and leave a hole under the object. // - Shadows w/ fill under the object: a bit faster for CPU, more pixels rendered, visible/darkening if used behind a transparent shape. // Typically used by: small, frequent objects, opaque objects, transparent objects if shadow darkening isn't an issue. // - Shadows w/ hole under the object: a bit slower for CPU, less pixels rendered, no difference if used behind a transparent shape. // Typically used by: large, infrequent objects, transparent objects if exact blending/color matter. // - FIXME-SHADOWS: 'offset' + ImDrawFlags_ShadowCutOutShapeBackground are not currently supported together with AddShadowCircle(), AddShadowConvexPoly(), AddShadowNGon(). #define IMGUI_HAS_SHADOWS 1 IMGUI_API void AddShadowRect(const ImVec2& obj_min, const ImVec2& obj_max, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0, float obj_rounding = 0.0f); IMGUI_API void AddShadowCircle(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0, int obj_num_segments = 12); IMGUI_API void AddShadowConvexPoly(const ImVec2* points, int points_count, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0); IMGUI_API void AddShadowNGon(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, int obj_num_segments); // Stateful path API, add points then finish with PathFillConvex() or PathStroke() // - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. // so e.g. 'PathArcTo(center, radius, PI * -0.5f, PI)' is ok, whereas 'PathArcTo(center, radius, PI, PI * -0.5f)' won't have correct anti-aliasing when followed by PathFillConvex(). 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.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; } inline void PathFillConcave(ImU32 col) { AddConcavePolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0); IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle IMGUI_API void PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0); // Advanced: Draw Callbacks // - May be used to alter render state (change sampler, blending, current shader). May be used to emit custom rendering commands (difficult to do correctly, but possible). // - Use special ImDrawCallback_ResetRenderState callback to instruct backend to reset its render state to the default. // - Your rendering loop must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. All standard backends are honoring this. // - For some backends, the callback may access selected render-states exposed by the backend in a ImGui_ImplXXXX_RenderState structure pointed to by platform_io.Renderer_RenderState. // - IMPORTANT: please be mindful of the different level of indirection between using size==0 (copying argument) and using size>0 (copying pointed data into a buffer). // - If userdata_size == 0: we copy/store the 'userdata' argument as-is. It will be available unmodified in ImDrawCmd::UserCallbackData during render. // - If userdata_size > 0, we copy/store 'userdata_size' bytes pointed to by 'userdata'. We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData will point inside that buffer so you have to retrieve data from there. Your callback may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data. // - Support for userdata_size > 0 was added in v1.91.4, October 2024. So earlier code always only allowed to copy/store a simple void*. IMGUI_API void AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size = 0); // Advanced: Miscellaneous IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club instead. // Advanced: Channels // - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives) // - Use to minimize draw calls (e.g. if going back-and-forth between multiple clipping rectangles, prefer to append into separate channels then merge at the end) // - This API shouldn't have been in ImDrawList in the first place! // Prefer using your own persistent instance of ImDrawListSplitter as you can stack them. // Using the ImDrawList::ChannelsXXXX you cannot stack a split over another. inline void ChannelsSplit(int count) { _Splitter.Split(this, count); } inline void ChannelsMerge() { _Splitter.Merge(this); } inline void ChannelsSetCurrent(int n) { _Splitter.SetCurrentChannel(this, n); } // Advanced: Primitives allocations // - We render triangles (three vertices) // - All primitives needs to be reserved via PrimReserve() beforehand. IMGUI_API void PrimReserve(int idx_count, int vtx_count); IMGUI_API void PrimUnreserve(int idx_count, int vtx_count); IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); // Axis aligned rectangle (composed of two triangles) IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); IMGUI_API void PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col); inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; } inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; } inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.0 inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.0 #endif //inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024) //inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024) //inline void PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0) { PathEllipticalArcTo(center, ImVec2(radius_x, radius_y), rot, a_min, a_max, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024) //inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021) //inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021) // [Internal helpers] IMGUI_API void _SetDrawListSharedData(ImDrawListSharedData* data); IMGUI_API void _ResetForNewFrame(); IMGUI_API void _ClearFreeMemory(); IMGUI_API void _PopUnusedDrawCmd(); IMGUI_API void _TryMergeDrawCmds(); IMGUI_API void _OnChangedClipRect(); IMGUI_API void _OnChangedTexture(); IMGUI_API void _OnChangedVtxOffset(); IMGUI_API void _SetTexture(ImTextureRef tex_ref); IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const; IMGUI_API void _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step); IMGUI_API void _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments); }; // All draw data to render a Dear ImGui frame // (NB: the style and the naming convention here is a little inconsistent, we currently preserve them for backward compatibility purpose, // as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList) struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. int CmdListsCount; // == CmdLists.Size. (OBSOLETE: exists for legacy reasons). Number of ImDrawList* to render. int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Copied from viewport->FramebufferScale (== io.DisplayFramebufferScale for main viewport). Generally (1,1) on normal display, (2,2) on OSX with Retina display. ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not). ImVector* Textures; // List of textures to update. Most of the times the list is shared by all ImDrawData, has only 1 texture and it doesn't need any update. This almost always points to ImGui::GetPlatformIO().Textures[]. May be overridden or set to NULL if you want to manually update textures. // Functions ImDrawData() { Clear(); } IMGUI_API void Clear(); IMGUI_API void AddDrawList(ImDrawList* draw_list); // Helper to add an external draw list into an existing ImDrawData. 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 Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; //----------------------------------------------------------------------------- // [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData) //----------------------------------------------------------------------------- // In principle, the only data types that user/application code should care about are 'ImTextureRef' and 'ImTextureID'. // They are defined above in this header file. Read their description to the difference between ImTextureRef and ImTextureID. // FOR ALL OTHER ImTextureXXXX TYPES: ONLY CORE LIBRARY AND RENDERER BACKENDS NEED TO KNOW AND CARE ABOUT THEM. //----------------------------------------------------------------------------- #undef Status // X11 headers are leaking this. // We intentionally support a limited amount of texture formats to limit burden on CPU-side code and extension. // Most standard backends only support RGBA32 but we provide a single channel option for low-resource/embedded systems. enum ImTextureFormat { ImTextureFormat_RGBA32, // 4 components per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 ImTextureFormat_Alpha8, // 1 component per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight }; // Status of a texture to communicate with Renderer Backend. enum ImTextureStatus { ImTextureStatus_OK, ImTextureStatus_Destroyed, // Backend destroyed the texture. ImTextureStatus_WantCreate, // Requesting backend to create the texture. Set status OK when done. ImTextureStatus_WantUpdates, // Requesting backend to update specific blocks of pixels (write to texture portions which have never been used before). Set status OK when done. ImTextureStatus_WantDestroy, // Requesting backend to destroy the texture. Set status to Destroyed when done. }; // Coordinates of a rectangle within a texture. // When a texture is in ImTextureStatus_WantUpdates state, we provide a list of individual rectangles to copy to the graphics system. // You may use ImTextureData::Updates[] for the list, or ImTextureData::UpdateBox for a single bounding box. struct ImTextureRect { unsigned short x, y; // Upper-left coordinates of rectangle to update unsigned short w, h; // Size of rectangle to update (in pixels) }; // Specs and pixel storage for a texture used by Dear ImGui. // This is only useful for (1) core library and (2) backends. End-user/applications do not need to care about this. // Renderer Backends will create a GPU-side version of this. // Why does we store two identifiers: TexID and BackendUserData? // - ImTextureID TexID = lower-level identifier stored in ImDrawCmd. ImDrawCmd can refer to textures not created by the backend, and for which there's no ImTextureData. // - void* BackendUserData = higher-level opaque storage for backend own book-keeping. Some backends may have enough with TexID and not need both. // In columns below: who reads/writes each fields? 'r'=read, 'w'=write, 'core'=main library, 'backend'=renderer backend struct ImTextureData { //------------------------------------------ core / backend --------------------------------------- int UniqueID; // w - // [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas. ImTextureStatus Status; // rw rw // ImTextureStatus_OK/_WantCreate/_WantUpdates/_WantDestroy. Always use SetStatus() to modify! void* BackendUserData; // - rw // Convenience storage for backend. Some backends may have enough with TexID. ImTextureID TexID; // r w // Backend-specific texture identifier. Always use SetTexID() to modify! The identifier will stored in ImDrawCmd::GetTexID() and passed to backend's RenderDrawData function. ImTextureFormat Format; // w r // ImTextureFormat_RGBA32 (default) or ImTextureFormat_Alpha8 int Width; // w r // Texture width int Height; // w r // Texture height int BytesPerPixel; // w r // 4 or 1 unsigned char* Pixels; // w r // Pointer to buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes. ImTextureRect UsedRect; // w r // Bounding box encompassing all past and queued Updates[]. ImTextureRect UpdateRect; // w r // Bounding box encompassing all queued Updates[]. ImVector Updates; // w r // Array of individual updates. int UnusedFrames; // w r // In order to facilitate handling Status==WantDestroy in some backend: this is a count successive frames where the texture was not used. Always >0 when Status==WantDestroy. unsigned short RefCount; // w r // Number of contexts using this texture. Used during backend shutdown. bool UseColors; // w r // Tell whether our texture data is known to use colors (rather than just white + alpha). bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame. // Functions ImTextureData() { memset((void*)this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; } ~ImTextureData() { DestroyPixels(); } IMGUI_API void Create(ImTextureFormat format, int w, int h); IMGUI_API void DestroyPixels(); void* GetPixels() { IM_ASSERT(Pixels != NULL); return Pixels; } void* GetPixelsAt(int x, int y) { IM_ASSERT(Pixels != NULL); return Pixels + (x + y * Width) * BytesPerPixel; } int GetSizeInBytes() const { return Width * Height * BytesPerPixel; } int GetPitch() const { return Width * BytesPerPixel; } ImTextureRef GetTexRef() { ImTextureRef tex_ref; tex_ref._TexData = this; tex_ref._TexID = ImTextureID_Invalid; return tex_ref; } ImTextureID GetTexID() const { return TexID; } // Called by Renderer backend // - Call SetTexID() and SetStatus() after honoring texture requests. Never modify TexID and Status directly! // - A backend may decide to destroy a texture that we did not request to destroy, which is fine (e.g. freeing resources), but we immediately set the texture back in _WantCreate mode. void SetTexID(ImTextureID tex_id) { TexID = tex_id; } void SetStatus(ImTextureStatus status) { Status = status; if (status == ImTextureStatus_Destroyed && !WantDestroyNextFrame && Pixels != nullptr) Status = ImTextureStatus_WantCreate; } }; //----------------------------------------------------------------------------- // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) //----------------------------------------------------------------------------- // [Internal] Shadow texture baking config struct ImFontAtlasShadowTexConfig { int TexCornerSize; // Size of the corner areas. int TexEdgeSize; // Size of the edge areas (and by extension the center). Changing this is normally unnecessary. float TexFalloffPower; // The power factor for the shadow falloff curve. float TexDistanceFieldOffset; // How much to offset the distance field by (allows over/under-shadowing, potentially useful for accommodating rounded corners on the "casting" shape). bool TexBlur; // Do we want to Gaussian blur the shadow texture? inline ImFontAtlasShadowTexConfig() { memset(this, 0, sizeof(*this)); } IMGUI_API void SetupDefaults(); int GetRectTexPadding() const { return 2; } // Number of pixels of padding to add to the rectangular texture to avoid sampling artifacts at the edges. int CalcRectTexSize() const { return TexCornerSize + TexEdgeSize + GetRectTexPadding(); } // The size of the texture area required for the actual 2x2 rectangle shadow texture (after the redundant corners have been removed). Padding is required here to avoid sampling artifacts at the edge adjoining the removed corners. int CalcConvexTexWidth() const; // The width of the texture area required for the convex shape shadow texture. int GetConvexTexPadding() const { return 8; } // Number of pixels of padding to add to the convex shape texture to avoid sampling artifacts at the edges. This also acts as padding for the expanded corner triangles. int CalcConvexTexWidth() const; // The width of the texture area required for the convex shape shadow texture. int CalcConvexTexHeight() const; // The height of the texture area required for the convex shape shadow texture. }; // A font input/source (we may rename this to ImFontSource in the future) struct ImFontConfig { // Data Source char Name[40]; // // Name (strictly to ease debugging, hence limited size buffer) void* FontData; // // TTF/OTF data int FontDataSize; // // TTF/OTF data size bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself). SINCE 1.92, THE DATA NEEDS TO PERSIST FOR WHOLE DURATION OF ATLAS. // Options bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Prevents fractional font size from working correctly! Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, OversampleH/V will default to 1. ImS8 OversampleH; // 0 (2) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. ImS8 OversampleV; // 0 (1) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis. ImWchar EllipsisChar; // 0 // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. float SizePixels; // // Output size in pixels for rasterizer (more or less maps to the resulting font height). const ImWchar* GlyphRanges; // NULL // *LEGACY* THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). const ImWchar* GlyphExcludeRanges; // NULL // Pointer to a small user-provided list of Unicode ranges (2 value per range, values are inclusive, zero-terminated list). This is very close to GlyphRanges[] but designed to exclude ranges from a font source, when merging fonts with overlapping glyphs. Use "Input Glyphs Overlap Detection Tool" to find about your overlapping ranges. //ImVec2 GlyphExtraSpacing; // 0, 0 // (REMOVED AT IT SEEMS LARGELY OBSOLETE. PLEASE REPORT IF YOU WERE USING THIS). Extra spacing (in pixels) between glyphs when rendered: essentially add to glyph->AdvanceX. Only X axis is supported for now. ImVec2 GlyphOffset; // 0, 0 // Offset (in pixels) all glyphs from this font input. Absolute value for default size, other sizes will scale this value. float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font. Absolute value for default size, other sizes will scale this value. float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs float GlyphExtraAdvanceX; // 0 // Extra spacing (in pixels) between glyphs. Please contact us if you are using this. // FIXME-NEWATLAS: Intentionally unscaled ImU32 FontNo; // 0 // Index of font within TTF/OTF file unsigned int FontLoaderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. //unsigned int FontBuilderFlags; // -- // [Renamed in 1.92] Use FontLoaderFlags. float RasterizerMultiply; // 1.0f // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future. float RasterizerDensity; // 1.0f // [LEGACY: this only makes sense when ImGuiBackendFlags_RendererHasTextures is not supported] DPI scale multiplier for rasterization. Not altering other font metrics: makes it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered. float ExtraSizeScale; // 1.0f // Extra rasterizer scale over SizePixels. // [Internal] ImFontFlags Flags; // Font flags (don't use just yet, will be exposed in upcoming 1.92.X updates) ImFont* DstFont; // Target font (as we merging fonts, multiple ImFontConfig may target the same font) const ImFontLoader* FontLoader; // Custom font backend for this source (default source is the one stored in ImFontAtlas) void* FontLoaderData; // Font loader opaque storage (per font config) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS bool PixelSnapV; // true // [Obsoleted in 1.91.6] Align Scaled GlyphOffset.y to pixel boundaries. #endif IMGUI_API ImFontConfig(); }; // Hold rendering data for one glyph. // (Note: some language parsers may fail to convert the bitfield members, in this case maybe drop store a single u32 or we can rework this) struct ImFontGlyph { unsigned int Colored : 1; // Flag to indicate glyph is colored and should generally ignore tinting (make it usable with no shift on little-endian as this is used in loops) unsigned int Visible : 1; // Flag to indicate glyph has no visible pixels (e.g. space). Allow early out when rendering. unsigned int SourceIdx : 4; // Index of source in parent font unsigned int Codepoint : 26; // 0x0000..0x10FFFF float AdvanceX; // Horizontal distance to advance cursor/layout position. float X0, Y0, X1, Y1; // Glyph corners. Offsets from current cursor/layout position. float U0, V0, U1, V1; // Texture coordinates for the current value of ImFontAtlas->TexRef. Cached equivalent of calling GetCustomRect() with PackId. int PackId; // [Internal] ImFontAtlasRectId value (FIXME: Cold data, could be moved elsewhere?) ImFontGlyph() { memset((void*)this, 0, sizeof(*this)); PackId = -1; } }; // 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() { Clear(); } inline void Clear() { int size_in_bytes = (IM_UNICODE_CODEPOINT_MAX + 1) / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); } inline bool GetBit(size_t n) const { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); return (UsedChars[off] & mask) != 0; } // Get bit n in the array inline void SetBit(size_t n) { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); UsedChars[off] |= mask; } // Set bit n in the array inline 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 }; // An opaque identifier to a rectangle in the atlas. -1 when invalid. // The rectangle may move and UV may be invalidated, use GetCustomRect() to retrieve it. typedef int ImFontAtlasRectId; #define ImFontAtlasRectId_Invalid -1 // Output of ImFontAtlas::GetCustomRect() when using custom rectangles. // Those values may not be cached/stored as they are only valid for the current value of atlas->TexRef // (this is in theory derived from ImTextureRect but we use separate structures for reasons) struct ImFontAtlasRect { unsigned short x, y; // Position (in current texture) unsigned short w, h; // Size ImVec2 uv0, uv1; // UV coordinates (in current texture) ImFontAtlasRect() { memset((void*)this, 0, sizeof(*this)); } }; // Flags for ImFontAtlas build enum ImFontAtlasFlags_ { ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory) ImFontAtlasFlags_NoBakedLines = 1 << 2, // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU). }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: // - One or more fonts. // - Custom graphics data needed to render the shapes needed by Dear ImGui. // - Mouse cursor shapes for software cursor rendering (unless setting 'Flags |= ImFontAtlasFlags_NoMouseCursors' in the font atlas). // - If you don't call any AddFont*** functions, the default font embedded in the code will be loaded for you. // It is the rendering backend responsibility to upload texture into your graphics API: // - ImGui_ImplXXXX_RenderDrawData() functions generally iterate platform_io->Textures[] to create/update/destroy each ImTextureData instance. // - Backend then set ImTextureData's TexID and BackendUserData. // - Texture id are passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID/ImTextureRef for more details. // Legacy path: // - Call Build() + GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. // - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API. // Common pitfalls: // - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persists 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, // - 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 various other reasons! We will address them in the future! struct ImFontAtlas { IMGUI_API ImFontAtlas(); IMGUI_API ~ImFontAtlas(); IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); // Selects between AddFontDefaultVector() and AddFontDefaultBitmap(). IMGUI_API ImFont* AddFontDefaultVector(const ImFontConfig* font_cfg = NULL); // Embedded scalable font. Recommended at any higher size. IMGUI_API ImFont* AddFontDefaultBitmap(const ImFontConfig* font_cfg = NULL); // Embedded classic pixel-clean font. Recommended at Size 13px with no scaling. IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed. IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. IMGUI_API void RemoveFont(ImFont* font); IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures). IMGUI_API void CompactCache(); // Compact cached glyphs and texture. IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime. // As we are transitioning toward a new font system, we expect to obsolete those soon: IMGUI_API void ClearInputData(); // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. IMGUI_API void ClearFonts(); // [OBSOLETE] Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates). IMGUI_API void ClearTexData(); // [OBSOLETE] Clear CPU-side copy of the texture data. Saves RAM once the texture has been copied to graphics memory. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy path for build atlas + retrieving 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 // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste). // - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures: // - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed. // - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation. IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. 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 void SetTexID(ImTextureID id) { IM_ASSERT(TexRef._TexID == ImTextureID_Invalid); TexRef._TexData->TexID = id; } // Called by legacy backends. May be called before texture creation. void SetTexID(ImTextureRef id) { IM_ASSERT(TexRef._TexID == ImTextureID_Invalid && id._TexData == NULL); TexRef._TexData->TexID = id._TexID; } // Called by legacy backends. bool IsBuilt() const { return Fonts.Size > 0 && TexIsBuilt; } // Bit ambiguous: used to detect when user didn't build texture but effectively we should check TexID != 0 except that would be backend dependent.. #endif //------------------------------------------- // Glyph Ranges //------------------------------------------- // Since 1.92: specifying glyph ranges is only useful/necessary if your backend doesn't support ImGuiBackendFlags_RendererHasTextures! IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // 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. // Read https://github.com/ocornut/imgui/blob/master/docs/FONTS.md/#about-utf-8-encoding for details. // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data. IMGUI_API const ImWchar* GetGlyphRangesGreek(); // Default + Greek and Coptic IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs IMGUI_API const ImWchar* GetGlyphRangesChineseFull(); // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs 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 IMGUI_API const ImWchar* GetGlyphRangesVietnamese(); // Default + Vietnamese characters #endif //------------------------------------------- // [ALPHA] Custom Rectangles/Glyphs API //------------------------------------------- // Register and retrieve custom rectangles // - You can request arbitrary rectangles to be packed into the atlas, for your own purpose. // - Since 1.92.0, packing is done immediately in the function call (previously packing was done during the Build call) // - You can render your pixels into the texture right after calling the AddCustomRect() functions. // - VERY IMPORTANT: // - Texture may be created/resized at any time when calling ImGui or ImFontAtlas functions. // - IT WILL INVALIDATE RECTANGLE DATA SUCH AS UV COORDINATES. Always use latest values from GetCustomRect(). // - UV coordinates are associated to the current texture identifier aka 'atlas->TexRef'. Both TexRef and UV coordinates are typically changed at the same time. // - If you render colored output into your custom rectangles: set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of preferred texture format. // - Read docs/FONTS.md for more details about using colorful icons. // - Note: this API may be reworked further in order to facilitate supporting e.g. multi-monitor, varying DPI settings. // - (Pre-1.92 names) ------------> (1.92 names) // - GetCustomRectByIndex() --> Use GetCustomRect() // - CalcCustomRectUV() --> Use GetCustomRect() and read uv0, uv1 fields. // - AddCustomRectRegular() --> Renamed to AddCustomRect() // - AddCustomRectFontGlyph() --> Prefer using custom ImFontLoader inside ImFontConfig // - ImFontAtlasCustomRect --> Renamed to ImFontAtlasRect IMGUI_API ImFontAtlasRectId AddCustomRect(int width, int height, ImFontAtlasRect* out_r = NULL);// Register a rectangle. Return -1 (ImFontAtlasRectId_Invalid) on error. IMGUI_API void RemoveCustomRect(ImFontAtlasRectId id); // Unregister a rectangle. Existing pixels will stay in texture until resized / garbage collected. IMGUI_API bool GetCustomRect(ImFontAtlasRectId id, ImFontAtlasRect* out_r) const; // Get rectangle coordinates for current texture. Valid immediately, never store this (read above)! //------------------------------------------- // Members //------------------------------------------- // Input ImFontAtlasFlags Flags; // Build flags (see ImFontAtlasFlags_) ImTextureFormat TexDesiredFormat; // Desired texture format (default to ImTextureFormat_RGBA32 but may be changed to ImTextureFormat_Alpha8). int TexGlyphPadding; // FIXME: Should be called "TexPackPadding". 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 (will also need to set AntiAliasedLinesUseTex = false). int TexMinWidth; // Minimum desired texture width. Must be a power of two. Default to 512. int TexMinHeight; // Minimum desired texture height. Must be a power of two. Default to 128. int TexMaxWidth; // Maximum desired texture width. Must be a power of two. Default to 8192. int TexMaxHeight; // Maximum desired texture height. Must be a power of two. Default to 8192. void* UserData; // Store your own atlas related user-data (if e.g. you have multiple font atlas). // Output // - Because textures are dynamically created/resized, the current texture identifier may changed at *ANY TIME* during the frame. // - This should not affect you as you can always use the latest value. But note that any precomputed UV coordinates are only valid for the current TexRef. #ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImTextureRef TexRef; // Latest texture identifier == TexData->GetTexRef(). #else union { ImTextureRef TexRef; ImTextureRef TexID; }; // Latest texture identifier == TexData->GetTexRef(). // RENAMED TexID to TexRef in 1.92.0. #endif ImTextureData* TexData; // Latest texture. // [Internal] ImVector TexList; // Texture list (most often TexList.Size == 1). TexData is always == TexList.back(). DO NOT USE DIRECTLY, USE GetDrawData().Textures[]/GetPlatformIO().Textures[] instead! bool Locked; // Marked as locked during ImGui::NewFrame()..EndFrame() scope if TexUpdates are not supported. Any attempt to modify the atlas will assert. bool RendererHasTextures;// Copy of (BackendFlags & ImGuiBackendFlags_RendererHasTextures) from supporting context. bool TexIsBuilt; // Set when texture was built matching current font input. Mostly useful for legacy IsBuilt() call. bool TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format or conversion process. ImVec2 TexUvScale; // = (1.0f/TexData->TexWidth, 1.0f/TexData->TexHeight). May change as new texture gets created. ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel. May change as new texture gets created. ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. ImVector Sources; // Source/configuration data ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines int TexNextUniqueID; // Next value to be stored in TexData->UniqueID int FontNextUniqueID; // Next value to be stored in ImFont->FontID ImVector DrawListSharedDatas; // List of users for this atlas. Typically one per Dear ImGui context. ImFontAtlasBuilder* Builder; // Opaque interface to our data that doesn't need to be public and may be discarded when rebuilding. const ImFontLoader* FontLoader; // Font loader opaque interface (default to use FreeType when IMGUI_ENABLE_FREETYPE is defined, otherwise default to use stb_truetype). Use SetFontLoader() to change this at runtime. const char* FontLoaderName; // Font loader name (for display e.g. in About box) == FontLoader->Name void* FontLoaderData; // Font backend opaque storage unsigned int FontLoaderFlags; // Shared flags (for all fonts) for font loader. THIS IS BUILD IMPLEMENTATION DEPENDENT (e.g. Per-font override is also available in ImFontConfig). int RefCount; // Number of contexts using this atlas ImGuiContext* OwnerContext; // Context which own the atlas will be in charge of updating and destroying it. // [Internal] Shadow data ImFontAtlasRectId ShadowRectIds[2]; // IDs of rect for shadow textures ImVec4 ShadowRectUvs[10]; // UV coordinates for shadow textures, 9 for the rectangle shadows and the final entry for the convex shape shadows ImFontAtlasShadowTexConfig ShadowTexConfig; // Shadow texture baking config // [Obsolete] #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy: You can request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. --> Prefer using a custom ImFontLoader. ImFontAtlasRect TempRect; // For old GetCustomRectByIndex() API inline ImFontAtlasRectId AddCustomRectRegular(int w, int h) { return AddCustomRect(w, h); } // RENAMED in 1.92.0 inline const ImFontAtlasRect* GetCustomRectByIndex(ImFontAtlasRectId id) { return GetCustomRect(id, &TempRect) ? &TempRect : NULL; } // OBSOLETED in 1.92.0 inline void CalcCustomRectUV(const ImFontAtlasRect* r, ImVec2* out_uv_min, ImVec2* out_uv_max) const { *out_uv_min = r->uv0; *out_uv_max = r->uv1; } // OBSOLETED in 1.92.0 IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // OBSOLETED in 1.92.0: Use custom ImFontLoader in ImFontConfig IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // ADDED AND OBSOLETED in 1.92.0 #endif //unsigned int FontBuilderFlags; // OBSOLETED in 1.92.0: Renamed to FontLoaderFlags. //int TexDesiredWidth; // OBSOLETED in 1.92.0: Force texture width before calling 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. //typedef ImFontAtlasRect ImFontAtlasCustomRect; // OBSOLETED in 1.92.0 //typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ }; // Font runtime data for a given size // Important: pointers to ImFontBaked are only valid for the current frame. struct ImFontBaked { // [Internal] 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 info, and are often bottleneck in large UI). float FallbackAdvanceX; // 4 // out // FindGlyph(FallbackChar)->AdvanceX float Size; // 4 // in // Height of characters/line, set during loading (doesn't change after loading) float RasterizerDensity; // 4 // in // Density this is baked at // [Internal] Members: Hot ~28/36 bytes (for RenderText loop) ImVector IndexLookup; // 12-16 // out // Sparse. Index glyphs by Unicode code-point. ImVector Glyphs; // 12-16 // out // All glyphs. int FallbackGlyphIndex; // 4 // out // Index of FontFallbackChar // [Internal] Members: Cold float Ascent, Descent; // 4+4 // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled) unsigned int MetricsTotalSurface:26;// 3 // 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) unsigned int WantDestroy:1; // 0 // // Queued for destroy unsigned int LoadNoFallback:1; // 0 // // Disable loading fallback in lower-level calls. unsigned int LoadNoRenderOnLayout:1;// 0 // // Enable a two-steps mode where CalcTextSize() calls will load AdvanceX *without* rendering/packing glyphs. Only advantageous if you know that the glyph is unlikely to actually be rendered, otherwise it is slower because we'd do one query on the first CalcTextSize and one query on the first Draw. int LastUsedFrame; // 4 // // Record of that time this was bounds ImGuiID BakedId; // 4 // // Unique ID for this baked storage ImFont* OwnerFont; // 4-8 // in // Parent font void* FontLoaderDatas; // 4-8 // // Font loader opaque storage (per baked font * sources): single contiguous buffer allocated by imgui, passed to loader. // Functions IMGUI_API ImFontBaked(); IMGUI_API void ClearOutputData(); IMGUI_API ImFontGlyph* FindGlyph(ImWchar c); // Return U+FFFD glyph if requested glyph doesn't exists. IMGUI_API ImFontGlyph* FindGlyphNoFallback(ImWchar c); // Return NULL if glyph doesn't exist IMGUI_API float GetCharAdvance(ImWchar c); IMGUI_API bool IsGlyphLoaded(ImWchar c); }; // Font flags // (in future versions as we redesign font loading API, this will become more important and better documented. for now please consider this as internal/advanced use) enum ImFontFlags_ { ImFontFlags_None = 0, ImFontFlags_NoLoadError = 1 << 1, // Disable throwing an error/assert when calling AddFontXXX() with missing file/data. Calling code is expected to check AddFontXXX() return value. ImFontFlags_NoLoadGlyphs = 1 << 2, // [Internal] Disable loading new glyphs. ImFontFlags_LockBakedSizes = 1 << 3, // [Internal] Disable loading new baked sizes, disable garbage collecting current ones. e.g. if you want to lock a font to a single size. Important: if you use this to preload given sizes, consider the possibility of multiple font density used on Retina display. }; // Font runtime data and rendering // - ImFontAtlas automatically loads a default embedded font for you if you didn't load one manually. // - Since 1.92.0 a font may be rendered as any size! Therefore a font doesn't have one specific size. // - Use 'font->GetFontBaked(size)' to retrieve the ImFontBaked* corresponding to a given size. // - If you used g.Font + g.FontSize (which is frequent from the ImGui layer), you can use g.FontBaked as a shortcut, as g.FontBaked == g.Font->GetFontBaked(g.FontSize). struct ImFont { // [Internal] Members: Hot ~12-20 bytes ImFontBaked* LastBaked; // 4-8 // Cache last bound baked. NEVER USE DIRECTLY. Use GetFontBaked(). ImFontAtlas* OwnerAtlas; // 4-8 // What we have been loaded into. ImFontFlags Flags; // 4 // Font flags. float CurrentRasterizerDensity; // Current rasterizer density. This is a varying state of the font. // [Internal] Members: Cold ~24-52 bytes // Conceptually Sources[] is the list of font sources merged to create this font. ImGuiID FontId; // Unique identifier for the font float LegacySize; // 4 // in // Font size passed to AddFont(). Use for old code calling PushFont() expecting to use that size. (use ImGui::GetFontBaked() to get font baked at current bound size). ImVector Sources; // 16 // in // List of sources. Pointers within OwnerAtlas->Sources[] ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...'). If you ever want to temporarily swap this for an alternative/dummy char, make sure to clear EllipsisAutoBake. ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?') ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 17 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 8K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. bool EllipsisAutoBake; // 1 // // Mark when the "..." glyph (== EllipsisChar) needs to be generated by combining multiple '.'. ImGuiStorage RemapPairs; // 16 // // Remapping pairs when using AddRemapChar(), otherwise empty. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS float Scale; // 4 // in // Legacy base font scale (~1.0f), multiplied by the per-window font scale which you can adjust with SetWindowFontScale() #endif // Methods IMGUI_API ImFont(); IMGUI_API ~ImFont(); IMGUI_API bool IsGlyphInFont(ImWchar c); bool IsLoaded() const { return OwnerAtlas != NULL; } const char* GetDebugName() const { return Sources.Size ? Sources[0]->Name : ""; } // Fill ImFontConfig::Name. // [Internal] Don't use! // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. IMGUI_API ImFontBaked* GetFontBaked(float font_size, float density = -1.0f); // Get or create baked data for given size IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL); IMGUI_API const char* CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL); IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); } #endif // [Internal] Don't use! IMGUI_API void ClearOutputData(); IMGUI_API void AddRemapChar(ImWchar from_codepoint, ImWchar to_codepoint); // Makes 'from_codepoint' character points to 'to_codepoint' glyph. IMGUI_API bool IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last); }; // This is provided for consistency (but we don't actually use this) inline ImTextureID ImTextureRef::GetTexID() const { IM_ASSERT(!(_TexData != NULL && _TexID != ImTextureID_Invalid)); return _TexData ? _TexData->TexID : _TexID; } // Using an indirection to avoid patching ImDrawCmd after a SetTexID() call (but this could be an alternative solution too) inline ImTextureID ImDrawCmd::GetTexID() const { // If you are getting this assert with ImTextureID_Invalid == 0 and your ImTextureID is used to store an index: // - You can add '#define ImTextureID_Invalid ((ImTextureID)-1)' in your imconfig file. // If you are getting this assert with a renderer backend with support for ImGuiBackendFlags_RendererHasTextures (1.92+): // - You must correctly iterate and handle ImTextureData requests stored in ImDrawData::Textures[]. See docs/BACKENDS.md. ImTextureID tex_id = TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID; // == TexRef.GetTexID() above. if (TexRef._TexData != NULL) IM_ASSERT(tex_id != ImTextureID_Invalid && "ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexID() after handling ImTextureStatus_WantCreate request!"); return tex_id; } //----------------------------------------------------------------------------- // [SECTION] Viewports //----------------------------------------------------------------------------- // Flags stored in ImGuiViewport::Flags, giving indications to the platform backends. enum ImGuiViewportFlags_ { ImGuiViewportFlags_None = 0, ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet) ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: Is created/managed by the user application? (rather than our backend) ImGuiViewportFlags_NoDecoration = 1 << 3, // Platform Window: Disable platform decorations: title bar, borders, etc. (generally set all windows, but if ImGuiConfigFlags_ViewportsDecoration is set we only set this on popups/tooltips) ImGuiViewportFlags_NoTaskBarIcon = 1 << 4, // Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcon is set) ImGuiViewportFlags_NoFocusOnAppearing = 1 << 5, // Platform Window: Don't take focus when created. ImGuiViewportFlags_NoFocusOnClick = 1 << 6, // Platform Window: Don't take focus when clicked on. ImGuiViewportFlags_NoInputs = 1 << 7, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. ImGuiViewportFlags_NoRendererClear = 1 << 8, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). ImGuiViewportFlags_NoAutoMerge = 1 << 9, // Platform Window: Avoid merging this window into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!). ImGuiViewportFlags_TopMost = 1 << 10, // Platform Window: Display on top (for tooltips only). ImGuiViewportFlags_CanHostOtherWindows = 1 << 11, // Viewport can host multiple imgui windows (secondary viewports are associated to a single window). // FIXME: In practice there's still probably code making the assumption that this is always and only on the MainViewport. Will fix once we add support for "no main viewport". // Output status flags (from Platform) ImGuiViewportFlags_IsMinimized = 1 << 12, // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. ImGuiViewportFlags_IsFocused = 1 << 13, // Platform Window: Window is focused (last call to Platform_GetWindowFocus() returned true) }; // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. // - With multi-viewport enabled, we extend this concept to have multiple active viewports. // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. // - About Main Area vs Work Area: // - Main Area = entire viewport. // - Work Area = entire viewport minus sections used by main menu bars (for platform windows), or by task bar (for platform monitor). // - Windows are generally trying to stay within the Work Area of their host viewport. struct ImGuiViewport { ImGuiID ID; // Unique identifier for the viewport ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ ImVec2 Pos; // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates) ImVec2 Size; // Main Area: Size of the viewport. ImVec2 FramebufferScale; // Density of the viewport for Retina display (always 1,1 on Windows, may be 2,2 etc on macOS/iOS). This will affect font rasterizer density. ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size) float DpiScale; // 1.0f = 96 DPI = No extra scale. ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform backend to setup a parent/child relationship between platform windows. ImGuiViewport* ParentViewport; // (Advanced) Direct shortcut to ImGui::FindViewportByID(ParentViewportId). NULL: no parent. ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). // Platform/Backend Dependent Data // Our design separate the Renderer and Platform backends to facilitate combining default backends with each others. // When our create your own backend for a custom engine, it is possible that both Renderer and Platform will be handled // by the same system and you may not need to use all the UserData/Handle fields. // The library never uses those fields, they are merely storage to facilitate backend implementation. void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). generally set by your Renderer_CreateWindow function. void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). generally set by your Platform_CreateWindow function. void* PlatformHandle; // void* to hold higher-level, platform window handle (e.g. HWND for Win32 backend, Uint32 WindowID for SDL, GLFWWindow* for GLFW), for FindViewportByPlatformHandle(). void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (always HWND on Win32 platform, unused for other platforms). bool PlatformWindowCreated; // Platform window has been created (Platform_CreateWindow() has been called). This is false during the first frame where a viewport is being created. bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) ImGuiViewport() { memset((void*)this, 0, sizeof(*this)); } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } // Helpers ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } ImVec2 GetWorkCenter() const { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); } }; //----------------------------------------------------------------------------- // [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformMonitor, ImGuiPlatformImeData) //----------------------------------------------------------------------------- // [BETA] (Optional) Multi-Viewport Support! // If you are new to Dear ImGui and trying to integrate it into your engine, you can probably ignore this for now. // // This feature allows you to seamlessly drag Dear ImGui windows outside of your application viewport. // This is achieved by creating new Platform/OS windows on the fly, and rendering into them. // Dear ImGui manages the viewport structures, and the backend create and maintain one Platform/OS window for each of those viewports. // // See Recap: https://github.com/ocornut/imgui/wiki/Multi-Viewports // See Glossary https://github.com/ocornut/imgui/wiki/Glossary for details about some of the terminology. // // About the coordinates system: // - When multi-viewports are enabled, all Dear ImGui coordinates become absolute coordinates (same as OS coordinates!) // - So e.g. ImGui::SetNextWindowPos(ImVec2(0,0)) will position a window relative to your primary monitor! // - If you want to position windows relative to your main application viewport, use ImGui::GetMainViewport()->Pos as a base position. // // Steps to use multi-viewports in your application, when using a default backend from the examples/ folder: // - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // - Backend: The backend initialization will setup all necessary ImGuiPlatformIO's functions and update monitors info every frame. // - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). // - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. // // Steps to use multi-viewports in your application, when using a custom backend: // - Important: THIS IS NOT EASY TO DO and comes with many subtleties not described here! // It's also an experimental feature, so some of the requirements may evolve. // Consider using default backends if you can. Either way, carefully follow and refer to examples/ backends for details. // - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // - Backend: Hook ImGuiPlatformIO's Platform_* and Renderer_* callbacks (see below). // Set 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports' and 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports'. // Update ImGuiPlatformIO's Monitors list every frame. // Update MousePos every frame, in absolute coordinates. // - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). // You may skip calling RenderPlatformWindowsDefault() if its API is not convenient for your needs. Read comments below. // - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. // // About ImGui::RenderPlatformWindowsDefault(): // - This function is a mostly a _helper_ for the common-most cases, and to facilitate using default backends. // - You can check its simple source code to understand what it does. // It basically iterates secondary viewports and call 4 functions that are setup in ImGuiPlatformIO, if available: // Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() // Those functions pointers exists only for the benefit of RenderPlatformWindowsDefault(). // - If you have very specific rendering needs (e.g. flipping multiple swap-chain simultaneously, unusual sync/threading issues, etc.), // you may be tempted to ignore RenderPlatformWindowsDefault() and write customized code to perform your renderingg. // You may decide to setup the platform_io's *RenderWindow and *SwapBuffers pointers and call your functions through those pointers, // or you may decide to never setup those pointers and call your code directly. They are a convenience, not an obligatory interface. //----------------------------------------------------------------------------- // Access via ImGui::GetPlatformIO() struct ImGuiPlatformIO { IMGUI_API ImGuiPlatformIO(); //------------------------------------------------------------------ // Input - Interface with OS and Platform backend (most common stuff) //------------------------------------------------------------------ // 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* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx); // Should return NULL on failure (e.g. clipboard data is not text). void (*Platform_SetClipboardTextFn)(ImGuiContext* ctx, const char* text); void* Platform_ClipboardUserData; // Optional: Open link/folder/file in OS Shell // (default to use ShellExecuteW() on Windows, system() on Linux/Mac. expected to return false on failure, but some platforms may always return true) bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path); void* Platform_OpenInShellUserData; // 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 (*Platform_SetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); void* Platform_ImeUserData; //void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to platform_io.PlatformSetImeDataFn in 1.91.1] // Optional: Platform locale // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point ImWchar Platform_LocaleDecimalPoint; // '.' //------------------------------------------------------------------ // Input - Interface with Renderer Backend //------------------------------------------------------------------ // Optional: Maximum texture size supported by renderer (used to adjust how we size textures). 0 if not known. int Renderer_TextureMaxWidth; int Renderer_TextureMaxHeight; // Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure. void* Renderer_RenderState; //------------------------------------------------------------------ // Input - Interface with Platform & Renderer backends for Multi-Viewport support //------------------------------------------------------------------ // For reference, the second column shows which function are generally calling the Platform Functions: // N = ImGui::NewFrame() ~ beginning of the dear imgui frame: read info from platform/OS windows (latest size/position) // F = ImGui::Begin(), ImGui::EndFrame() ~ during the dear imgui frame // U = ImGui::UpdatePlatformWindows() ~ after the dear imgui frame: create and update all platform/OS windows // R = ImGui::RenderPlatformWindowsDefault() ~ render // D = ImGui::DestroyPlatformWindows() ~ shutdown // The general idea is that NewFrame() we will read the current Platform/OS state, and UpdatePlatformWindows() will write to it. // The handlers are designed so we can mix and match two imgui_impl_xxxx files, one Platform backend and one Renderer backend. // Custom engine backends will often provide both Platform and Renderer interfaces together and so may not need to use all functions. // Platform functions are typically called _before_ their Renderer counterpart, apart from Destroy which are called the other way. // Platform Backend functions (e.g. Win32, GLFW, SDL) ------------------- Called by ----- void (*Platform_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create a new platform window for the given viewport void (*Platform_DestroyWindow)(ImGuiViewport* vp); // N . U . D // void (*Platform_ShowWindow)(ImGuiViewport* vp); // . . U . . // Newly created windows are initially hidden so SetWindowPos/Size/Title can be called on them before showing the window void (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos); // . . U . . // Set platform window position (given the upper-left corner of client area) ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp); // N . . . . // void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // . . U . . // Set platform window client area size (ignoring OS decorations such as OS title bar etc.) ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); // N . . . . // Get platform window client area size ImVec2 (*Platform_GetWindowFramebufferScale)(ImGuiViewport* vp); // N . . . . // Return viewport density. Always 1,1 on Windows, often 2,2 on Retina display on macOS/iOS. MUST BE INTEGER VALUES. void (*Platform_SetWindowFocus)(ImGuiViewport* vp); // N . . . . // Move window to front and set input focus bool (*Platform_GetWindowFocus)(ImGuiViewport* vp); // . . U . . // bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); // N . . . . // Get platform window minimized state. When minimized, we generally won't attempt to get/set size and contents will be culled more easily void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str); // . . U . . // Set platform window title (given an UTF-8 string) void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // . . U . . // (Optional) Setup global transparency (not per-pixel transparency) void (*Platform_UpdateWindow)(ImGuiViewport* vp); // . . U . . // (Optional) Called by UpdatePlatformWindows(). Optional hook to allow the platform backend from doing general book-keeping every frame. void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Main rendering (platform side! This is often unused, or just setting a "current" context for OpenGL bindings). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // . F . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so backend has a chance to swap fonts to adjust style. ImVec4 (*Platform_GetWindowWorkAreaInsets)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] Get initial work area inset for the viewport (won't be covered by main menu bar, dockspace over viewport etc.). Default to (0,0),(0,0). 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For a Vulkan Renderer to call into Platform code (since the surface creation needs to tie them both). // Renderer Backend functions (e.g. DirectX, OpenGL, Vulkan) ------------ Called by ----- void (*Renderer_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create swap chain, frame buffers etc. (called after Platform_CreateWindow) void (*Renderer_DestroyWindow)(ImGuiViewport* vp); // N . U . D // Destroy swap chain, frame buffers etc. (called before Platform_DestroyWindow) void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // . . U . . // Resize swap chain, frame buffers etc. (called after Platform_SetWindowSize) void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Clear framebuffer, setup render target, then render the viewport->DrawData. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). // (Optional) Monitor list // - Updated by: app/backend. Update every frame to dynamically support changing monitor or DPI configuration. // - Used by: dear imgui to query DPI info, clamp popups/tooltips within same monitor and not have them straddle monitors. ImVector Monitors; //------------------------------------------------------------------ // Output //------------------------------------------------------------------ // Textures list (the list is updated by calling ImGui::EndFrame or ImGui::Render) // The ImGui_ImplXXXX_RenderDrawData() function of each backend generally access this via ImDrawData::Textures which points to this. The array is available here mostly because backends will want to destroy textures on shutdown. ImVector Textures; // List of textures used by Dear ImGui (most often 1) + contents of external texture list is automatically appended into this. // Viewports list (the list is updated by calling ImGui::EndFrame or ImGui::Render) // (in the future we will attempt to organize this feature to remove the need for a "main viewport") ImVector Viewports; // Main viewports, followed by all secondary viewports. //------------------------------------------------------------------ // Functions //------------------------------------------------------------------ IMGUI_API void ClearPlatformHandlers(); // Clear all Platform_XXX fields. Typically called on Platform Backend shutdown. IMGUI_API void ClearRendererHandlers(); // Clear all Renderer_XXX fields. Typically called on Renderer Backend shutdown. }; // (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI. // We use this information for multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors. struct ImGuiPlatformMonitor { ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) ImVec2 WorkPos, WorkSize; // Coordinates without task bars / side bars / menu bars. Used to avoid positioning popups/tooltips inside this region. If you don't have this info, please copy the value for MainPos/MainSize. float DpiScale; // 1.0f = 96 DPI void* PlatformHandle; // Backend dependant data (e.g. HMONITOR, GLFWmonitor*, SDL Display Index, NSScreen*) ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0, 0); DpiScale = 1.0f; PlatformHandle = NULL; } }; // (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. Handler is called during EndFrame(). struct ImGuiPlatformImeData { bool WantVisible; // A widget wants the IME to be visible. bool WantTextInput; // A widget wants text input, not necessarily IME to be visible. This is automatically set to the upcoming value of io.WantTextInput. ImVec2 InputPos; // Position of input cursor (for IME). float InputLineHeight; // Line height (for IME). ImGuiID ViewportId; // ID of platform window/viewport. ImGuiPlatformImeData() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- // [SECTION] Obsolete functions and types // (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.92.0 (from June 2025) inline void PushFont(ImFont* font) { PushFont(font, font ? font->LegacySize : 0.0f); } IMGUI_API void SetWindowFontScale(float scale); // Set font scale factor for current window. Prefer using PushFont(NULL, style.FontSizeBase * factor) or use style.FontScaleMain to scale all windows. // OBSOLETED in 1.91.9 (from February 2025) IMGUI_API void Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col); // <-- 'border_col' was removed in favor of ImGuiCol_ImageBorder. If you use 'tint_col', use ImageWithBg() instead. // OBSOLETED in 1.91.0 (from July 2024) inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } inline void PopButtonRepeat() { PopItemFlag(); } inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } inline void PopTabStop() { PopItemFlag(); } // You do not need those functions! See #7838 on GitHub for more info. IMGUI_API ImVec2 GetContentRegionMax(); // Content boundaries max (e.g. window boundaries including scrolling, or current column boundaries). You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMin(); // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMax(); // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) // OBSOLETED in 1.90.0 (from September 2023) //IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); // Getter signature changed. See 2023/09/15 and 2026/02/27 commits. //IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); // Getter signature changed. See 2023/09/15 and 2026/02/27 commits. //inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders //inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders //inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, flags); } //inline void EndChildFrame() { EndChild(); } //inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } // OBSOLETED in 1.89.7 (from June 2023) //IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() _before_ item. //-- OBSOLETED in 1.89.4 (from March 2023) //static inline void PushAllowKeyboardFocus(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } //static inline void PopAllowKeyboardFocus() { PopItemFlag(); } //-- OBSOLETED in 1.89 (from August 2022) //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)); // --> Use new ImageButton() signature (explicit item id, regular FramePadding). Refer to code in 1.91 if you want to grab a copy of this version. //-- OBSOLETED in 1.88 (from May 2022) //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. //-- OBSOLETED in 1.87 (from February 2022, more formally obsoleted April 2024) //IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); const ImGuiKeyData* key_data = GetKeyData(key); return (ImGuiKey)(key_data - g.IO.KeysData); } // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value! //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; } //-- OBSOLETED in 1.86 (from November 2021) //IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper. //-- OBSOLETED in 1.85 (from August 2021) //static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } //-- OBSOLETED in 1.81 (from February 2021) //static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } //static inline bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1) { float height = GetTextLineHeightWithSpacing() * ((height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f) + GetStyle().FramePadding.y * 2.0f; return BeginListBox(label, ImVec2(0.0f, height)); } // Helper to calculate size from items_count and height_in_items //static inline void ListBoxFooter() { EndListBox(); } //-- OBSOLETED in 1.79 (from August 2020) //static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry! //-- OBSOLETED in 1.78 (from June 2020): Old drag/sliders functions that took a 'float power > 1.0f' argument instead of ImGuiSliderFlags_Logarithmic. See github.com/ocornut/imgui/issues/3361 for details. //IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f) // OBSOLETED in 1.78 (from June 2020) //IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f); // OBSOLETED in 1.78 (from June 2020) //IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power = 1.0f); // OBSOLETED in 1.78 (from June 2020) //IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power = 1.0f); // OBSOLETED in 1.78 (from June 2020) //static inline bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) //static inline bool DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) //static inline bool DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) //static inline bool DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) //static inline bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) //static inline bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) //static inline bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) //static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) //-- OBSOLETED in 1.77 and before //static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } // OBSOLETED in 1.77 (from June 2020) //static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } // OBSOLETED in 1.72 (from July 2019) //static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.71 (from June 2019) //static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } // OBSOLETED in 1.70 (from May 2019) //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) //-- OBSOLETED in 1.60 and before //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) //IMGUI_API bool Begin(char* name, bool* p_open, ImVec2 size_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags=0); // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017): Equivalent of using SetNextWindowSize(size, ImGuiCond_FirstUseEver) and SetNextWindowBgAlpha(). //static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017) //static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017) //static inline void SetNextWindowPosCenter(ImGuiCond c=0) { SetNextWindowPos(GetMainViewport()->GetCenter(), c, ImVec2(0.5f,0.5f)); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017) //static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) //static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017): This was misleading and partly broken. You probably want to use the io.WantCaptureMouse flag instead. //static inline bool IsMouseHoveringAnyWindow() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) //static inline bool IsMouseHoveringWindow() { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) //-- OBSOLETED in 1.50 and before //static inline bool CollapsingHeader(char* label, const char* str_id, bool framed = true, bool default_open = false) { return CollapsingHeader(label, (default_open ? (1 << 5) : 0)); } // OBSOLETED in 1.49 //static inline ImFont*GetWindowFont() { return GetFont(); } // OBSOLETED in 1.48 //static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETED in 1.48 //static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETED in 1.42 } //-- OBSOLETED in 1.92.0: ImFontAtlasCustomRect becomes ImTextureRect // - ImFontAtlasCustomRect::X,Y --> ImTextureRect::x,y // - ImFontAtlasCustomRect::Width,Height --> ImTextureRect::w,h // - ImFontAtlasCustomRect::GlyphColored --> if you need to write to this, instead you can write to 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph() // We could make ImTextureRect an union to use old names, but 1) this would be confusing 2) the fix is easy 3) ImFontAtlasCustomRect was always a rather esoteric api. typedef ImFontAtlasRect ImFontAtlasCustomRect; /*struct ImFontAtlasCustomRect { unsigned short X, Y; // Output // Packed position in Atlas unsigned short Width, Height; // Input // [Internal] Desired rectangle dimension unsigned int GlyphID:31; // Input // [Internal] For custom font glyphs only (ID < 0x110000) unsigned int GlyphColored:1; // Input // [Internal] For custom font glyphs only: glyph is colored, removed tinting. float GlyphAdvanceX; // Input // [Internal] For custom font glyphs only: glyph xadvance ImVec2 GlyphOffset; // Input // [Internal] For custom font glyphs only: glyph display offset ImFont* Font; // Input // [Internal] For custom font glyphs only: target font ImFontAtlasCustomRect() { X = Y = 0xFFFF; Width = Height = 0; GlyphID = 0; GlyphColored = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; } bool IsPacked() const { return X != 0xFFFF; } };*/ //-- OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() //typedef ImDrawFlags ImDrawCornerFlags; //enum ImDrawCornerFlags_ //{ // ImDrawCornerFlags_None = ImDrawFlags_RoundCornersNone, // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit // ImDrawCornerFlags_TopLeft = ImDrawFlags_RoundCornersTopLeft, // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally). // ImDrawCornerFlags_TopRight = ImDrawFlags_RoundCornersTopRight, // Was == 0x02 (1 << 1) prior to 1.82. // ImDrawCornerFlags_BotLeft = ImDrawFlags_RoundCornersBottomLeft, // Was == 0x04 (1 << 2) prior to 1.82. // ImDrawCornerFlags_BotRight = ImDrawFlags_RoundCornersBottomRight, // Was == 0x08 (1 << 3) prior to 1.82. // ImDrawCornerFlags_All = ImDrawFlags_RoundCornersAll, // Was == 0x0F prior to 1.82 // ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, // ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, // ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, // ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight, //}; // RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022) // RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obsolescence schedule to reduce confusion and because they were not meant to be used in the first place. //typedef ImGuiKeyChord ImGuiModFlags; // == int. We generally use ImGuiKeyChord to mean "a ImGuiKey or-ed with any number of ImGuiMod_XXX value", so you may store mods in there. //enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super }; //typedef ImGuiKeyChord ImGuiKeyModFlags; // == int //enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super }; //#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // OBSOLETED IN 1.90 (now using C++11 standard version) #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #define IM_ARRAYSIZE IM_COUNTOF // RENAMED IN 1.92.6: IM_ARRAYSIZE -> IM_COUNTOF // RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022) #ifdef IMGUI_DISABLE_METRICS_WINDOW #error IMGUI_DISABLE_METRICS_WINDOW was renamed to IMGUI_DISABLE_DEBUG_TOOLS, please use new name. #endif //----------------------------------------------------------------------------- #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif #ifdef _MSC_VER #pragma warning (pop) #endif // Include imgui_user.h at the end of imgui.h // May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. #ifdef IMGUI_INCLUDE_IMGUI_USER_H #ifdef IMGUI_USER_H_FILENAME #include IMGUI_USER_H_FILENAME #else #include "imgui_user.h" #endif #endif #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui/include/imgui_internal.h ================================================ // dear imgui, v1.92.7 WIP // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. /* Index of this file: // [SECTION] Header mess // [SECTION] Forward declarations // [SECTION] Context pointer // [SECTION] STB libraries includes // [SECTION] Macros // [SECTION] Generic helpers // [SECTION] ImDrawList support // [SECTION] Style support // [SECTION] Data types support // [SECTION] Widgets support: flags, enums, data structures // [SECTION] Popup support // [SECTION] Inputs support // [SECTION] Clipper support // [SECTION] Navigation support // [SECTION] Typing-select support // [SECTION] Columns support // [SECTION] Box-select support // [SECTION] Multi-select support // [SECTION] Docking support // [SECTION] Viewport support // [SECTION] Settings support // [SECTION] Localization support // [SECTION] Error handling, State recovery support // [SECTION] Metrics, Debug tools // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiWindowTempData, ImGuiWindow // [SECTION] Tab bar, Tab item support // [SECTION] Table support // [SECTION] ImGui internal API // [SECTION] ImFontLoader // [SECTION] ImFontAtlas internal API // [SECTION] Test Engine specific hooks (imgui_test_engine) */ #pragma once #ifndef IMGUI_DISABLE //----------------------------------------------------------------------------- // [SECTION] Header mess //----------------------------------------------------------------------------- #ifndef IMGUI_VERSION #include "imgui.h" #endif #include // FILE*, sscanf #include // NULL, malloc, free, qsort, atoi, atof #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf #include // INT_MIN, INT_MAX // Enable SSE intrinsics if available #if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE) && !defined(_M_ARM64) && !defined(_M_ARM64EC) #define IMGUI_ENABLE_SSE #include #if (defined __AVX__ || defined __SSE4_2__) #define IMGUI_ENABLE_SSE4_2 #include #endif #endif // Emscripten has partial SSE 4.2 support where _mm_crc32_u32 is not available. See https://emscripten.org/docs/porting/simd.html#id11 and #8213 #if defined(IMGUI_ENABLE_SSE4_2) && !defined(IMGUI_USE_LEGACY_CRC32_ADLER) && !defined(__EMSCRIPTEN__) #define IMGUI_ENABLE_SSE4_2_CRC #endif // Visual Studio warnings #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) #pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #pragma clang diagnostic push #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloor() #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif // In 1.89.4, we moved the implementation of "courtesy maths operators" from imgui_internal.h in imgui.h // As they are frequently requested, we do not want to encourage to many people using imgui_internal.h #if defined(IMGUI_DEFINE_MATH_OPERATORS) && !defined(IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED) #error Please '#define IMGUI_DEFINE_MATH_OPERATORS' _BEFORE_ including imgui.h! #endif // Legacy defines #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Renamed in 1.74 #error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS #endif #ifdef IMGUI_DISABLE_MATH_FUNCTIONS // Renamed in 1.74 #error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #endif // Enable stb_truetype by default unless FreeType is enabled. // You can compile with both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE together. #ifndef IMGUI_ENABLE_FREETYPE #define IMGUI_ENABLE_STB_TRUETYPE #endif //----------------------------------------------------------------------------- // [SECTION] Forward declarations //----------------------------------------------------------------------------- // Utilities // (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImStableVector<>, ImPool<>, ImChunkStream<>) struct ImBitVector; // Store 1-bit per value struct ImRect; // An axis-aligned rectangle (2 points) struct ImGuiTextIndex; // Maintain a line index for a text buffer. // ImDrawList/ImFontAtlas struct ImDrawDataBuilder; // Helper to build a ImDrawData instance struct ImDrawListSharedData; // Data shared between all ImDrawList instances struct ImFontAtlasBuilder; // Internal storage for incrementally packing and building a ImFontAtlas struct ImFontAtlasPostProcessData; // Data available to potential texture post-processing functions struct ImFontAtlasRectEntry; // Packed rectangle lookup entry // ImGui struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others) struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it struct ImGuiContext; // Main Dear ImGui context struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiDeactivatedItemData; // Data for IsItemDeactivated()/IsItemDeactivatedAfterEdit() function. struct ImGuiDockContext; // Docking system context struct ImGuiDockRequest; // Docking system dock/undock queued request struct ImGuiDockNode; // Docking system node (hold a list of Windows OR two child dock nodes) struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session) struct ImGuiErrorRecoveryState; // Storage of stack sizes for error handling and recovery struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiLocEntry; // A localization entry. struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only struct ImGuiMultiSelectState; // Multi-selection persistent state (for focused selection). struct ImGuiMultiSelectTempData; // Multi-selection temporary state (while traversing). struct ImGuiNavItemData; // Result of a keyboard/gamepad directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions struct ImGuiNextItemData; // Storage for SetNextItem** functions struct ImGuiOldColumnData; // Storage data for a single column for legacy Columns() api struct ImGuiOldColumns; // Storage data for a columns set for legacy Columns() api struct ImGuiPopupData; // 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 ImGuiStyleShadowTexConfig; // Shadow Texture baking config struct ImGuiStyleVarInfo; // Style variable information (e.g. to access style variables from an enum) struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabItem; // Storage for a tab item (within a tab bar) struct ImGuiTable; // Storage for a table struct ImGuiTableHeaderData; // Storage for TableAngledHeadersRow() struct ImGuiTableColumn; // Storage for one column of a table struct ImGuiTableInstanceData; // Storage for one instance of a same table struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. struct ImGuiTableSettings; // Storage for a table .ini settings struct ImGuiTableColumnsSettings; // Storage for a column .ini settings struct ImGuiTreeNodeStackData; // Temporary storage for TreeNode(). struct ImGuiTypingSelectState; // Storage for GetTypingSelectRequest() struct ImGuiTypingSelectRequest; // Storage for GetTypingSelectRequest() (aimed to be public) struct ImGuiWindow; // Storage for one window struct ImGuiWindowDockStyle; // Storage for window-style data which needs to be stored for docking purpose struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) // Enumerations // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. enum ImGuiLocKey : int; // -> enum ImGuiLocKey // Enum: a localization entry for translation. typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // Enum: for storing the source authority (dock node vs window) of a field typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical // Flags typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Flags: for ImTextCalcWordWrapPositionEx() typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiLogFlags; // -> enum ImGuiLogFlags_ // Flags: for LogBegin() text capturing function typedef int ImGuiNavRenderCursorFlags; // -> enum ImGuiNavRenderCursorFlags_//Flags: for RenderNavCursor() typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // Flags: for ScrollToItem() and navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest() typedef int ImGuiWindowBgClickFlags; // -> enum ImGuiWindowBgClickFlags_ // Flags: for overriding behavior of clicking on window background/void. typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy() // Table column indexing typedef ImS16 ImGuiTableColumnIdx; typedef ImU16 ImGuiTableDrawChannelIdx; //----------------------------------------------------------------------------- // [SECTION] Context pointer // See implementation of this variable in imgui.cpp for comments and details. //----------------------------------------------------------------------------- #ifndef GImGui extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #endif //----------------------------------------------------------------------------- // [SECTION] Macros //----------------------------------------------------------------------------- // Internal Drag and Drop payload types. String starting with '_' are reserved for Dear ImGui. #define IMGUI_PAYLOAD_TYPE_WINDOW "_IMWINDOW" // Payload == ImGuiWindow* // Debug Printing Into TTY // (since IMGUI_VERSION_NUM >= 18729: IMGUI_DEBUG_LOG was reworked into IMGUI_DEBUG_PRINTF (and removed framecount from it). If you were using a #define IMGUI_DEBUG_LOG please rename) #ifndef IMGUI_DEBUG_PRINTF #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS #define IMGUI_DEBUG_PRINTF(_FMT,...) printf(_FMT, __VA_ARGS__) #else #define IMGUI_DEBUG_PRINTF(_FMT,...) ((void)0) #endif #endif // Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. #define IMGUI_DEBUG_LOG_ERROR(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g.DebugLogSkippedErrors++; } while (0) #define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FONT(...) do { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context. #define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_DOCKING(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventDocking) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_VIEWPORT(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventViewport) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Static Asserts #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") // "Paranoid" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much. // We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code. //#define IMGUI_DEBUG_PARANOID #ifdef IMGUI_DEBUG_PARANOID #define IM_ASSERT_PARANOID(_EXPR) IM_ASSERT(_EXPR) #else #define IM_ASSERT_PARANOID(_EXPR) #endif // Misc Macros #define IM_PI 3.14159265358979323846f #ifdef _WIN32 #define IM_NEWLINE "\r\n" // Play it nice with Windows users (Update: since 2018-05, Notepad finally appears to support Unix-style carriage returns!) #else #define IM_NEWLINE "\n" #endif #ifndef IM_TABSIZE // Until we move this to runtime and/or add proper tab support, at least allow users to compile-time override #define IM_TABSIZE (4) #endif #define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 #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 #define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // Positive values only! ImTrunc() is not inlined in MSVC debug builds #define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only! //#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023) // Hint for branch prediction #if (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L)) #define IM_LIKELY [[likely]] #define IM_UNLIKELY [[unlikely]] #else #define IM_LIKELY #define IM_UNLIKELY #endif // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall #ifdef _MSC_VER #define IMGUI_CDECL __cdecl #else #define IMGUI_CDECL #endif // Warnings #if defined(_MSC_VER) && !defined(__clang__) #define IM_MSVC_WARNING_SUPPRESS(XXXX) __pragma(warning(suppress: XXXX)) #else #define IM_MSVC_WARNING_SUPPRESS(XXXX) #endif // Debug Tools // Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item. // This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference. #ifndef IM_DEBUG_BREAK #if defined (_MSC_VER) #define IM_DEBUG_BREAK() __debugbreak() #elif defined(__clang__) #define IM_DEBUG_BREAK() __builtin_debugtrap() #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #define IM_DEBUG_BREAK() __asm__ volatile("int3;nop") #elif defined(__GNUC__) && defined(__thumb__) #define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") #elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) #define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0") #else #define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! #endif #endif // #ifndef IM_DEBUG_BREAK // Format specifiers, printing 64-bit hasn't been decently standardized... // In a real application you should be using PRId64 and PRIu64 from (non-windows) and on Windows define them yourself. #if defined(_MSC_VER) && !defined(__clang__) #define IM_PRId64 "I64d" #define IM_PRIu64 "I64u" #define IM_PRIX64 "I64X" #else #define IM_PRId64 "lld" #define IM_PRIu64 "llu" #define IM_PRIX64 "llX" #endif //----------------------------------------------------------------------------- // [SECTION] Generic helpers // Note that the ImXXX helpers functions are lower-level than ImGui functions. // ImGui functions or the ImGui context are never called/used from other ImXXX functions. //----------------------------------------------------------------------------- // - Helpers: Hashing // - Helpers: Sorting // - Helpers: Bit manipulation // - Helpers: String // - Helpers: Formatting // - Helpers: UTF-8 <> wchar conversions // - Helpers: ImVec2/ImVec4 operators // - Helpers: Maths // - Helpers: Geometry // - Helper: ImVec1 // - Helper: ImVec2ih // - Helper: ImRect // - Helper: ImBitArray // - Helper: ImBitVector // - Helper: ImSpan<>, ImSpanAllocator<> // - Helper: ImStableVector<> // - Helper: ImPool<> // - Helper: ImChunkStream<> // - Helper: ImGuiTextIndex // - Helper: ImGuiStorage //----------------------------------------------------------------------------- // Helpers: Hashing IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImGuiID seed = 0); IMGUI_API ImGuiID ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0); IMGUI_API const char* ImHashSkipUncontributingPrefix(const char* label); // Helpers: Sorting #ifndef ImQsort inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } #endif // Helpers: Color Blending IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); // Helpers: Bit manipulation inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; } inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } inline unsigned int ImCountSetBits(unsigned int v) { unsigned int count = 0; while (v > 0) { v = v & (v - 1); count++; } return count; } // Helpers: String #define ImStrlen strlen #define ImMemchr memchr IMGUI_API int ImStricmp(const char* str1, const char* str2); // Case insensitive compare. IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); // Case insensitive compare to a certain count. IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); // Copy to a certain count and always zero terminate (strncpy doesn't). IMGUI_API char* ImStrdup(const char* str); // Duplicate a string. IMGUI_API void* ImMemdup(const void* src, size_t size); // Duplicate a chunk of memory. IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); // Copy in provided buffer, recreate buffer if needed. IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); // Find first occurrence of 'c' in string range. IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); // Find a substring in a string range. IMGUI_API void ImStrTrimBlanks(char* str); // Remove leading and trailing blanks from a buffer. IMGUI_API const char* ImStrSkipBlank(const char* str); // Find first non-blank character. IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string) IMGUI_API const char* ImStrbol(const char* buf_mid_line, const char* buf_begin); // Find beginning-of-line IM_MSVC_RUNTIME_CHECKS_OFF inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Formatting IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); IMGUI_API void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, 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, size_t buf_size); IMGUI_API void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size); IMGUI_API const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size); IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); // Helpers: UTF-8 <> wchar conversions IMGUI_API int ImTextCharToUtf8(char out_buf[5], unsigned int c); // return output UTF-8 bytes count IMGUI_API int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_p); // return previous UTF-8 code-point. IMGUI_API const char* ImTextFindValidUtf8CodepointEnd(const char* in_text_start, const char* in_text_end, const char* in_p); // return previous UTF-8 code-point if 'in_p' is not the end of a valid one. IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line. // Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES) enum ImDrawTextFlags_ { ImDrawTextFlags_None = 0, ImDrawTextFlags_CpuFineClip = 1 << 0, // Must be == 1/true for legacy with 'bool cpu_fine_clip' arg to RenderText() ImDrawTextFlags_WrapKeepBlanks = 1 << 1, ImDrawTextFlags_StopOnNewLine = 1 << 2, }; IMGUI_API ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags); IMGUI_API const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags = 0); IMGUI_API const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags = 0); // trim trailing space and find beginning of next line // Character classification for word-wrapping logic enum ImWcharClass { ImWcharClass_Blank, ImWcharClass_Punct, ImWcharClass_Other }; IMGUI_API void ImTextInitClassifiers(); IMGUI_API void ImTextClassifierClear(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class); IMGUI_API void ImTextClassifierSetCharClass(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, unsigned int c); IMGUI_API void ImTextClassifierSetCharClassFromStr(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, const char* s); // Helpers: File System #ifdef IMGUI_DISABLE_FILE_FUNCTIONS #define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS typedef void* ImFileHandle; inline ImFileHandle ImFileOpen(const char*, const char*) { return NULL; } inline bool ImFileClose(ImFileHandle) { return false; } inline ImU64 ImFileGetSize(ImFileHandle) { return (ImU64)-1; } inline ImU64 ImFileRead(void*, ImU64, ImU64, ImFileHandle) { return 0; } inline ImU64 ImFileWrite(const void*, ImU64, ImU64, ImFileHandle) { return 0; } #endif #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS typedef FILE* ImFileHandle; IMGUI_API ImFileHandle ImFileOpen(const char* filename, const char* mode); IMGUI_API bool ImFileClose(ImFileHandle file); IMGUI_API ImU64 ImFileGetSize(ImFileHandle file); IMGUI_API ImU64 ImFileRead(void* data, ImU64 size, ImU64 count, ImFileHandle file); IMGUI_API ImU64 ImFileWrite(const void* data, ImU64 size, ImU64 count, ImFileHandle file); #else #define IMGUI_DISABLE_TTY_FUNCTIONS // Can't use stdout, fflush if we are not using default file functions #endif IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0); // Helpers: Maths IM_MSVC_RUNTIME_CHECKS_OFF // - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) #ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #define ImFabs(X) fabsf(X) #define ImSqrt(X) sqrtf(X) #define ImFmod(X, Y) fmodf((X), (Y)) #define ImCos(X) cosf(X) #define ImSin(X) sinf(X) #define ImAcos(X) acosf(X) #define ImAtan2(Y, X) atan2f((Y), (X)) #define ImAtof(STR) atof(STR) #define ImCeil(X) ceilf(X) inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision inline double ImPow(double x, double y) { return pow(x, y); } inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision inline double ImLog(double x) { return log(x); } inline int ImAbs(int x) { return x < 0 ? -x : x; } inline float ImAbs(float x) { return fabsf(x); } inline double ImAbs(double x) { return fabs(x); } inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } #ifdef IMGUI_ENABLE_SSE inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } #else inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } #endif inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } #endif // - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double // (Exceptionally using templates here but we could also redefine them for those types) template T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } template T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } template T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } template T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } template void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } template 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 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 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); } 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); } inline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx){ return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); } inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } inline float ImLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImSqrt(d); return fail_value; } inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } inline float ImTrunc(float f) { return (float)(int)(f); } inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } inline float ImTrunc64(float f) { return (float)(ImS64)(f); } inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); } // FIXME: Positive values only. inline int ImModPositive(int a, int b) { return (a + b) % b; } inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } 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); } 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; } inline float ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; } inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } inline float ImExponentialMovingAverage(float avg, float sample, int n){ avg -= avg / (float)n; avg += sample / (float)n; return avg; } IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); IMGUI_API ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments); // For curves with explicit number of segments IMGUI_API ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol IMGUI_API ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t); IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); 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); inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; } inline bool ImTriangleIsClockwise(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) > 0.0f; } // Helper: ImVec1 (1D vector) // (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec1 { float x; constexpr ImVec1() : x(0.0f) { } constexpr ImVec1(float _x) : x(_x) { } }; // Helper: ImVec2i (2D vector, integer) struct ImVec2i { int x, y; constexpr ImVec2i() : x(0), y(0) {} constexpr ImVec2i(int _x, int _y) : x(_x), y(_y) {} }; // Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage) struct ImVec2ih { short x, y; constexpr ImVec2ih() : x(0), y(0) {} constexpr ImVec2ih(short _x, short _y) : x(_x), y(_y) {} constexpr explicit ImVec2ih(const ImVec2& rhs) : x((short)rhs.x), y((short)rhs.y) {} }; // Helper: ImRect (2D axis aligned bounding-box) // NB: we can't rely on ImVec2 math operators being available here! struct IMGUI_API ImRect { ImVec2 Min; // Upper-left ImVec2 Max; // Lower-right constexpr ImRect() : Min(0.0f, 0.0f), Max(0.0f, 0.0f) {} constexpr ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} constexpr ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} constexpr ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} ImVec2 GetCenter() const { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); } ImVec2 GetSize() const { return ImVec2(Max.x - Min.x, Max.y - Min.y); } float GetWidth() const { return Max.x - Min.x; } float GetHeight() const { return Max.y - Min.y; } float GetArea() const { return (Max.x - Min.x) * (Max.y - Min.y); } ImVec2 GetTL() const { return Min; } // Top-left ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left ImVec2 GetBR() const { return Max; } // Bottom-right bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; } bool ContainsWithPad(const ImVec2& p, const ImVec2& pad) const { return p.x >= Min.x - pad.x && p.y >= Min.y - pad.y && p.x < Max.x + pad.x && p.y < Max.y + pad.y; } bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; } void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; } void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; } void TranslateX(float dx) { Min.x += dx; Max.x += dx; } void TranslateY(float dy) { Min.y += dy; Max.y += dy; } void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } const ImVec4& AsVec4() const { return *(const ImVec4*)&Min.x; } }; // Helper: ImBitArray #define IM_BITARRAY_TESTBIT(_ARRAY, _N) ((_ARRAY[(_N) >> 5] & ((ImU32)1 << ((_N) & 31))) != 0) // Macro version of ImBitArrayTestBit(): ensure args have side-effect or are costly! #define IM_BITARRAY_CLEARBIT(_ARRAY, _N) ((_ARRAY[(_N) >> 5] &= ~((ImU32)1 << ((_N) & 31)))) // Macro version of ImBitArrayClearBit(): ensure args have side-effect or are costly! inline size_t ImBitArrayGetStorageSizeInBytes(int bitcount) { return (size_t)((bitcount + 31) >> 5) << 2; } inline void ImBitArrayClearAllBits(ImU32* arr, int bitcount){ memset(arr, 0, ImBitArrayGetStorageSizeInBytes(bitcount)); } inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } inline void ImBitArrayClearBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; } inline void ImBitArraySetBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; } inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on range [n..n2) { n2--; while (n <= n2) { int a_mod = (n & 31); int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1; ImU32 mask = (ImU32)(((ImU64)1 << b_mod) - 1) & ~(ImU32)(((ImU64)1 << a_mod) - 1); arr[n >> 5] |= mask; n = (n + 32) & ~31; } } typedef ImU32* ImBitArrayPtr; // Name for use in structs // Helper: ImBitArray class (wrapper over ImBitArray functions) // Store 1-bit per value. template struct ImBitArray { ImU32 Data[(BITCOUNT + 31) >> 5]; ImBitArray() { ClearAllBits(); } void ClearAllBits() { memset(Data, 0, sizeof(Data)); } void SetAllBits() { memset(Data, 255, sizeof(Data)); } bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Data, n); } void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Data, n); } void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Data, n); } void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Data, n, n2); } // Works on range [n..n2) bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Data, n); } }; // Helper: ImBitVector // Store 1-bit per value. struct IMGUI_API ImBitVector { ImVector Storage; void Create(int sz) { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); } void Clear() { Storage.clear(); } bool TestBit(int n) const { IM_ASSERT(n < (Storage.Size << 5)); return IM_BITARRAY_TESTBIT(Storage.Data, n); } void SetBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArraySetBit(Storage.Data, n); } void ClearBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArrayClearBit(Storage.Data, n); } }; IM_MSVC_RUNTIME_CHECKS_RESTORE // Helper: ImSpan<> // Pointing to a span of data we don't own. template struct ImSpan { T* Data; T* DataEnd; // Constructors, destructor inline ImSpan() { Data = DataEnd = NULL; } inline ImSpan(T* data, int size) { Data = data; DataEnd = data + size; } inline ImSpan(T* data, T* data_end) { Data = data; DataEnd = data_end; } inline void set(T* data, int size) { Data = data; DataEnd = data + size; } inline void set(T* data, T* data_end) { Data = data; DataEnd = data_end; } inline int size() const { return (int)(ptrdiff_t)(DataEnd - Data); } inline int size_in_bytes() const { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); } inline T& operator[](int i) { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; } inline const T& operator[](int i) const { const T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; } inline T* begin() { return Data; } inline const T* begin() const { return Data; } inline T* end() { return DataEnd; } inline const T* end() const { return DataEnd; } // Utilities inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < DataEnd); const ptrdiff_t off = it - Data; return (int)off; } }; // Helper: ImSpanAllocator<> // Facilitate storing multiple chunks into a single large block (the "arena") // - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges. template struct ImSpanAllocator { char* BasePtr; int CurrOff; int CurrIdx; int Offsets[CHUNKS]; int Sizes[CHUNKS]; ImSpanAllocator() { memset((void*)this, 0, sizeof(*this)); } inline void Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; } inline int GetArenaSizeInBytes() { return CurrOff; } inline void SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; } inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); } inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); } template inline void GetSpan(int n, ImSpan* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); } }; // Helper: ImStableVector<> // Allocating chunks of BLOCKSIZE items. Objects pointers are never invalidated when growing, only by clear(). // Important: does not destruct anything! // Implemented only the minimum set of functions we need for it. template struct ImStableVector { int Size = 0; int Capacity = 0; ImVector Blocks; // Functions inline ~ImStableVector() { for (T* block : Blocks) IM_FREE(block); } inline void clear() { Size = Capacity = 0; Blocks.clear_delete(); } inline void resize(int new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; } inline void reserve(int new_cap) { new_cap = IM_MEMALIGN(new_cap, BLOCKSIZE); int old_count = Capacity / BLOCKSIZE; int new_count = new_cap / BLOCKSIZE; if (new_count <= old_count) return; Blocks.resize(new_count); for (int n = old_count; n < new_count; n++) Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCKSIZE); Capacity = new_cap; } inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; } inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; } inline T* push_back(const T& v) { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCKSIZE); void* ptr = &Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; } }; // 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 ImPool { ImVector Buf; // Contiguous data ImGuiStorage Map; // ID->Index ImPoolIdx FreeIdx; // Next free idx to use ImPoolIdx AliveCount; // Number of active/alive items (for display purpose) ImPool() { FreeIdx = AliveCount = 0; } ~ImPool() { Clear(); } T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; } T* GetByIndex(ImPoolIdx n) { return &Buf[n]; } ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); } T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); } bool Contains(const T* p) const { return (p >= Buf.Data && p < Buf.Data + Buf.Size); } void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; } T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; } void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; } void Reserve(int capacity) { Buf.reserve(capacity); Map.Data.reserve(capacity); } // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... } // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize() int GetAliveCount() const { return AliveCount; } // Number of active/alive items in the pool (for display purpose) int GetBufSize() const { return Buf.Size; } int GetMapSize() const { return Map.Data.Size; } // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere T* TryGetMapData(ImPoolIdx n) { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); } }; // Helper: ImChunkStream<> // Build and iterate a contiguous stream of variable-sized structures. // This is used by Settings to store persistent data while reducing allocation count. // We store the chunk size first, and align the final size on 4 bytes boundaries. // The tedious/zealous amount of casting is to avoid -Wcast-align warnings. template struct ImChunkStream { ImVector Buf; void clear() { Buf.clear(); } bool empty() const { return Buf.Size == 0; } int size() const { return Buf.Size; } T* alloc_chunk(size_t sz) { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); } T* begin() { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); } T* next_chunk(T* p) { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; } int chunk_size(const T* p) { return ((const int*)p)[-1]; } T* end() { return (T*)(void*)(Buf.Data + Buf.Size); } int offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; } T* ptr_from_offset(int off) { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); } void swap(ImChunkStream& rhs) { rhs.Buf.swap(Buf); } }; // Helper: ImGuiTextIndex // Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API. struct ImGuiTextIndex { ImVector Offsets; int EndOffset = 0; // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?) void clear() { Offsets.clear(); EndOffset = 0; } int size() { return Offsets.Size; } const char* get_line_begin(const char* base, int n) { return base + (Offsets.Size != 0 ? Offsets[n] : 0); } const char* get_line_end(const char* base, int n) { return base + (n + 1 < Offsets.Size ? (Offsets[n + 1] - 1) : EndOffset); } void append(const char* base, int old_size, int new_size); }; // Helper: ImGuiStorage IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key); //----------------------------------------------------------------------------- // [SECTION] ImDrawList support //----------------------------------------------------------------------------- // ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value. // Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693 // Number of segments (N) is calculated using equation: // N = ceil ( pi / acos(1 - error / r) ) where r > 0, error <= r // Our equation is significantly simpler that one in the post thanks for choosing segment that is // perpendicular to X axis. Follow steps in the article from this starting condition and you will // will get this result. // // Rendering circles with an odd number of segments, while mathematically correct will produce // asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.) #define IM_ROUNDUP_TO_EVEN(_V) ((((_V) + 1) / 2) * 2) #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN 4 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX 512 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR) ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) // Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'. #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR) ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI)))) #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD) ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD)) // ImDrawList: Lookup table size for adaptive arc drawing, cover full circle. #ifndef IM_DRAWLIST_ARCFAST_TABLE_SIZE #define IM_DRAWLIST_ARCFAST_TABLE_SIZE 48 // Number of samples in lookup table. #endif #define IM_DRAWLIST_ARCFAST_SAMPLE_MAX IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle. // Data shared between all ImDrawList instances // Conceptually this could have been called e.g. ImDrawListSharedContext // Typically one ImGui context would create and maintain one of this. // You may want to create your own instance of you try to ImDrawList completely without ImGui. In that case, watch out for future changes to this structure. struct IMGUI_API ImDrawListSharedData { ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas (== FontAtlas->TexUvWhitePixel) const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas (== FontAtlas->TexUvLines) ImFontAtlas* FontAtlas; // Current font atlas ImFont* Font; // Current font (used for simplified AddText overload) float FontSize; // Current font size (used for for simplified AddText overload) float FontScale; // Current font scale (== FontSize / Font->FontSize) float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc float InitialFringeScale; // Initial scale to apply to AA fringe ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() ImVector TempBuffer; // Temporary write buffer ImVector DrawLists; // All draw lists associated to this ImDrawListSharedData ImGuiContext* Context; // [OPTIONAL] Link to Dear ImGui context. 99% of ImDrawList/ImFontAtlas can function without an ImGui context, but this facilitate handling one legacy edge case. // Lookup tables ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle. float ArcFastRadiusCutoff; // Cutoff radius after which arc drawing will fallback to slower PathArcTo() ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead) int* ShadowRectIds; // IDs of rects for shadow texture (2 entries) const ImVec4* ShadowRectUvs; // UV coordinates for shadow texture (10 entries) ImDrawListSharedData(); ~ImDrawListSharedData(); void SetCircleTessellationMaxError(float max_error); }; struct ImDrawDataBuilder { ImVector* Layers[2]; // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData. ImVector LayerData1; ImDrawDataBuilder() { memset((void*)this, 0, sizeof(*this)); } }; struct ImFontStackData { ImFont* Font; float FontSizeBeforeScaling; // ~~ style.FontSizeBase float FontSizeAfterScaling; // ~~ g.FontSize }; //----------------------------------------------------------------------------- // [SECTION] Style support //----------------------------------------------------------------------------- struct ImGuiStyleVarInfo { ImU32 Count : 8; // 1+ ImGuiDataType DataType : 8; ImU32 Offset : 16; // Offset in parent structure void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); } }; // Stacked color modifier, backup of modified data so we can restore it struct ImGuiColorMod { ImGuiCol Col; ImVec4 BackupValue; }; // Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. struct ImGuiStyleMod { ImGuiStyleVar VarIdx; union { int BackupInt[2]; float BackupFloat[2]; }; ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; } ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; } ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } }; //----------------------------------------------------------------------------- // [SECTION] Data types support //----------------------------------------------------------------------------- struct ImGuiDataTypeStorage { ImU8 Data[8]; // Opaque storage to fit any data up to ImGuiDataType_COUNT }; // Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). struct ImGuiDataTypeInfo { size_t Size; // Size in bytes const char* Name; // Short descriptive name for the type, for debugging const char* PrintFmt; // Default printf format for the type const char* ScanFmt; // Default scanf format for the type }; // Extend ImGuiDataType_ enum ImGuiDataTypePrivate_ { ImGuiDataType_Pointer = ImGuiDataType_COUNT, ImGuiDataType_ID, }; //----------------------------------------------------------------------------- // [SECTION] Widgets support: flags, enums, data structures //----------------------------------------------------------------------------- // Extend ImGuiItemFlags // - input: PushItemFlag() manipulates g.CurrentItemFlags, g.NextItemData.ItemFlags, ItemAdd() calls may add extra flags too. // - output: stored in g.LastItemData.ItemFlags enum ImGuiItemFlagsPrivate_ { // Controlled by user ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable() ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. ImGuiItemFlags_NoNavDisableMouseHover = 1 << 15, // false // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false). ImGuiItemFlags_NoMarkEdited = 1 << 16, // false // Skip calling MarkItemEdited() ImGuiItemFlags_NoFocus = 1 << 17, // false // [EXPERIMENTAL: Not very well specced] Clicking doesn't take focus. Automatically sets ImGuiButtonFlags_NoFocus + ImGuiButtonFlags_NoNavFocus in ButtonBehavior(). // Controlled by widget code ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() ImGuiItemFlags_IsMultiSelect = 1 << 22, // false // Set by SetNextItemSelectionUserData() ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups, // Please don't change, use PushItemFlag() instead. // Obsolete //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior }; // Status flags for an already submitted item // - output: stored in g.LastItemData.StatusFlags enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. ImGuiItemStatusFlags_Visible = 1 << 8, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()). ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid. ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd(). //ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb. // Additional status + semantic for ImGuiTestEngine #ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiItemStatusFlags_Openable = 1 << 20, // Item is an openable (e.g. TreeNode) ImGuiItemStatusFlags_Opened = 1 << 21, // Opened status ImGuiItemStatusFlags_Checkable = 1 << 22, // Item is a checkable (e.g. CheckBox, MenuItem) ImGuiItemStatusFlags_Checked = 1 << 23, // Checked status ImGuiItemStatusFlags_Inputable = 1 << 24, // Item is a text-inputable (e.g. InputText, SliderXXX, DragXXX) #endif }; // Extend ImGuiHoveredFlags_ enum ImGuiHoveredFlagsPrivate_ { ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay, ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary, ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_, }; // Extend ImGuiInputTextFlags_ enum ImGuiInputTextFlagsPrivate_ { // [Internal] ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() ImGuiInputTextFlags_MergedItem = 1 << 27, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 28, // For internal use by InputScalar() and TempInputScalar() }; // Extend ImGuiButtonFlags_ enum ImGuiButtonFlagsPrivate_ { ImGuiButtonFlags_PressedOnClick = 1 << 4, // return true on click (mouse down event) ImGuiButtonFlags_PressedOnClickRelease = 1 << 5, // [Default] return true on click + release on same item <-- this is what the majority of Button are using ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item ImGuiButtonFlags_PressedOnRelease = 1 << 7, // return true on release (default requires click+release) ImGuiButtonFlags_PressedOnDoubleClick = 1 << 8, // return true on double-click (default requires click+release) ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) //ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat -> use ImGuiItemFlags_ButtonRepeat instead. ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping ImGuiButtonFlags_AllowOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable. //ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModsAllowed = 1 << 16, // disable mouse interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.ItemFlags) ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) ImGuiButtonFlags_NoFocus = 1 << 22, // [EXPERIMENTAL: Not very well specced]. Don't focus parent window when clicking. ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold, ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease, //ImGuiButtonFlags_NoKeyModifiers = ImGuiButtonFlags_NoKeyModsAllowed, // Renamed in 1.91.4 }; // Extend ImGuiComboFlags_ enum ImGuiComboFlagsPrivate_ { ImGuiComboFlags_CustomPreview = 1 << 20, // enable BeginComboPreview() }; // Extend ImGuiSliderFlags_ enum ImGuiSliderFlagsPrivate_ { ImGuiSliderFlags_Vertical = 1 << 20, // Should this slider be orientated vertically? ImGuiSliderFlags_ReadOnly = 1 << 21, // Consider using g.NextItemData.ItemFlags |= ImGuiItemFlags_ReadOnly instead. }; // Extend ImGuiSelectableFlags_ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26, // Disable padding each side with ItemSpacing * 0.5f ImGuiSelectableFlags_NoSetKeyOwner = 1 << 27, // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) }; // Extend ImGuiTreeNodeFlags_ enum ImGuiTreeNodeFlagsPrivate_ { ImGuiTreeNodeFlags_NoNavFocus = 1 << 27,// Don't claim nav focus when interacting with this item (#8551) ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader() ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517) ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow, ImGuiTreeNodeFlags_DrawLinesMask_ = ImGuiTreeNodeFlags_DrawLinesNone | ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes, }; enum ImGuiSeparatorFlags_ { ImGuiSeparatorFlags_None = 0, ImGuiSeparatorFlags_Horizontal = 1 << 0, // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar ImGuiSeparatorFlags_Vertical = 1 << 1, ImGuiSeparatorFlags_SpanAllColumns = 1 << 2, // Make separator cover all columns of a legacy Columns() set. }; // Flags for FocusWindow(). This is not called ImGuiFocusFlags to avoid confusion with public-facing ImGuiFocusedFlags. // FIXME: Once we finishing replacing more uses of GetTopMostPopupModal()+IsWindowWithinBeginStackOf() // and FindBlockingModal() with this, we may want to change the flag to be opt-out instead of opt-in. enum ImGuiFocusRequestFlags_ { ImGuiFocusRequestFlags_None = 0, ImGuiFocusRequestFlags_RestoreFocusedChild = 1 << 0, // Find last focused child (if any) and focus it instead. ImGuiFocusRequestFlags_UnlessBelowModal = 1 << 1, // Do not set focus if the window is below a modal. }; enum ImGuiTextFlags_ { ImGuiTextFlags_None = 0, ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0, }; enum ImGuiTooltipFlags_ { ImGuiTooltipFlags_None = 0, ImGuiTooltipFlags_OverridePrevious = 1 << 1, // Clear/ignore previously submitted tooltip (defaults to append) }; // 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_Horizontal = 0, ImGuiLayoutType_Vertical = 1 }; // Flags for LogBegin() text capturing function enum ImGuiLogFlags_ { ImGuiLogFlags_None = 0, ImGuiLogFlags_OutputTTY = 1 << 0, ImGuiLogFlags_OutputFile = 1 << 1, ImGuiLogFlags_OutputBuffer = 1 << 2, ImGuiLogFlags_OutputClipboard = 1 << 3, ImGuiLogFlags_OutputMask_ = ImGuiLogFlags_OutputTTY | ImGuiLogFlags_OutputFile | ImGuiLogFlags_OutputBuffer | ImGuiLogFlags_OutputClipboard, }; // X/Y enums are fixed to 0/1 so they may be used to index ImVec2 enum ImGuiAxis { ImGuiAxis_None = -1, ImGuiAxis_X = 0, ImGuiAxis_Y = 1 }; enum ImGuiPlotType { ImGuiPlotType_Lines, ImGuiPlotType_Histogram, }; // Storage data for BeginComboPreview()/EndComboPreview() struct IMGUI_API ImGuiComboPreviewData { ImRect PreviewRect; ImVec2 BackupCursorPos; ImVec2 BackupCursorMaxPos; ImVec2 BackupCursorPosPrevLine; float BackupPrevLineTextBaseOffset; ImGuiLayoutType BackupLayout; ImGuiComboPreviewData() { memset((void*)this, 0, sizeof(*this)); } }; // Stacked storage data for BeginGroup()/EndGroup() struct IMGUI_API ImGuiGroupData { ImGuiID WindowID; ImVec2 BackupCursorPos; ImVec2 BackupCursorMaxPos; ImVec2 BackupCursorPosPrevLine; ImVec1 BackupIndent; ImVec1 BackupGroupOffset; ImVec2 BackupCurrLineSize; float BackupCurrLineTextBaseOffset; ImGuiID BackupActiveIdIsAlive; bool BackupActiveIdHasBeenEditedThisFrame; bool BackupDeactivatedIdIsAlive; bool BackupHoveredIdIsAlive; bool BackupIsSameLine; bool EmitItem; }; // 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 { ImU32 TotalWidth; ImU32 NextTotalWidth; ImU16 Spacing; ImU16 OffsetIcon; // Always zero for now ImU16 OffsetLabel; // Offsets are locked in Update() ImU16 OffsetShortcut; ImU16 OffsetMark; ImU16 Widths[4]; // Width of: Icon, Label, Shortcut, Mark (accumulators for current frame) ImGuiMenuColumns() { memset((void*)this, 0, sizeof(*this)); } void Update(float spacing, bool window_reappearing); float DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark); void CalcNextTotalWidth(bool update_offsets); }; // Internal temporary state for deactivating InputText() instances. struct IMGUI_API ImGuiInputTextDeactivatedState { ImGuiID ID; // widget id owning the text state (which just got deactivated) ImVector TextA; // text buffer ImGuiInputTextDeactivatedState() { memset((void*)this, 0, sizeof(*this)); } void ClearFreeMemory() { ID = 0; TextA.clear(); } }; // Forward declare imstb_textedit.h structure + make its main configuration define accessible #undef IMSTB_TEXTEDIT_STRING #undef IMSTB_TEXTEDIT_CHARTYPE #define IMSTB_TEXTEDIT_STRING ImGuiInputTextState #define IMSTB_TEXTEDIT_CHARTYPE char #define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f) #define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 #define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 namespace ImStb { struct STB_TexteditState; } typedef ImStb::STB_TexteditState ImStbTexteditState; // Internal state of the currently focused/edited text input box // For a given item ID, access with ImGui::GetInputTextState() struct IMGUI_API ImGuiInputTextState { ImGuiContext* Ctx; // parent UI context (needs to be set explicitly by parent). ImStbTexteditState* Stb; // State for stb_textedit.h ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. ImGuiID ID; // widget id owning the text state int TextLen; // UTF-8 length of the string in TextA (in bytes) const char* TextSrc; // == TextA.Data unless read-only, in which case == buf passed to InputText(). For _ReadOnly fields, pointer will be null outside the InputText() call. ImVector TextA; // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1). ImVector TextToRevertTo; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered) ImVector CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack int BufCapacity; // end-user buffer capacity (include zero terminator) ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y) int LineCount; // last line count (solely for debugging) float WrapWidth; // word-wrapping width 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 CursorCenterY; // set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. ImS8 LastMoveDirectionLR; // ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s int ReloadSelectionStart; int ReloadSelectionEnd; ImGuiInputTextState(); ~ImGuiInputTextState(); void ClearText() { TextLen = 0; TextA[0] = 0; CursorClamp(); } void ClearFreeMemory() { TextA.clear(); TextToRevertTo.clear(); } void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation void OnCharPressed(unsigned int c); float GetPreferredOffsetX() const; // Cursor & Selection void CursorAnimReset(); void CursorClamp(); bool HasSelection() const; void ClearSelection(); int GetCursorPos() const; int GetSelectionStart() const; int GetSelectionEnd() const; void SetSelection(int start, int end); void SelectAll(); // Reload user buf (WIP #2890) // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this) // strcpy(my_buf, "hello"); // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item // state->ReloadUserBufAndSelectAll(); void ReloadUserBufAndSelectAll(); void ReloadUserBufAndKeepSelection(); void ReloadUserBufAndMoveToEnd(); }; enum ImGuiWindowRefreshFlags_ { ImGuiWindowRefreshFlags_None = 0, ImGuiWindowRefreshFlags_TryToAvoidRefresh = 1 << 0, // [EXPERIMENTAL] Try to keep existing contents, USER MUST NOT HONOR BEGIN() RETURNING FALSE AND NOT APPEND. ImGuiWindowRefreshFlags_RefreshOnHover = 1 << 1, // [EXPERIMENTAL] Always refresh on hover ImGuiWindowRefreshFlags_RefreshOnFocus = 1 << 2, // [EXPERIMENTAL] Always refresh on focus // Refresh policy/frequency, Load Balancing etc. }; enum ImGuiWindowBgClickFlags_ { ImGuiWindowBgClickFlags_None = 0, ImGuiWindowBgClickFlags_Move = 1 << 0, // Click on bg/void + drag to move window. Cleared by default when using io.ConfigWindowsMoveFromTitleBarOnly. }; enum ImGuiNextWindowDataFlags_ { ImGuiNextWindowDataFlags_None = 0, ImGuiNextWindowDataFlags_HasPos = 1 << 0, ImGuiNextWindowDataFlags_HasSize = 1 << 1, ImGuiNextWindowDataFlags_HasContentSize = 1 << 2, ImGuiNextWindowDataFlags_HasCollapsed = 1 << 3, ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4, ImGuiNextWindowDataFlags_HasFocus = 1 << 5, ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6, ImGuiNextWindowDataFlags_HasScroll = 1 << 7, ImGuiNextWindowDataFlags_HasWindowFlags = 1 << 8, ImGuiNextWindowDataFlags_HasChildFlags = 1 << 9, ImGuiNextWindowDataFlags_HasRefreshPolicy = 1 << 10, ImGuiNextWindowDataFlags_HasViewport = 1 << 11, ImGuiNextWindowDataFlags_HasDock = 1 << 12, ImGuiNextWindowDataFlags_HasWindowClass = 1 << 13, }; // Storage for SetNexWindow** functions struct ImGuiNextWindowData { ImGuiNextWindowDataFlags HasFlags; // Members below are NOT cleared. Always rely on HasFlags. ImGuiCond PosCond; ImGuiCond SizeCond; ImGuiCond CollapsedCond; ImGuiCond DockCond; ImVec2 PosVal; ImVec2 PosPivotVal; ImVec2 SizeVal; ImVec2 ContentSizeVal; ImVec2 ScrollVal; ImGuiWindowFlags WindowFlags; // Only honored by BeginTable() ImGuiChildFlags ChildFlags; bool PosUndock; bool CollapsedVal; ImRect SizeConstraintRect; ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; // Override background alpha ImGuiID ViewportId; ImGuiID DockId; ImGuiWindowClass WindowClass; ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) ImGuiWindowRefreshFlags RefreshFlagsVal; ImGuiNextWindowData() { memset((void*)this, 0, sizeof(*this)); } inline void ClearFlags() { HasFlags = ImGuiNextWindowDataFlags_None; } }; enum ImGuiNextItemDataFlags_ { ImGuiNextItemDataFlags_None = 0, ImGuiNextItemDataFlags_HasWidth = 1 << 0, ImGuiNextItemDataFlags_HasOpen = 1 << 1, ImGuiNextItemDataFlags_HasShortcut = 1 << 2, ImGuiNextItemDataFlags_HasRefVal = 1 << 3, ImGuiNextItemDataFlags_HasStorageID = 1 << 4, ImGuiNextItemDataFlags_HasColorMarker = 1 << 5, }; struct ImGuiNextItemData { ImGuiNextItemDataFlags HasFlags; // Called HasFlags instead of Flags to avoid mistaking this ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData. // Members below are NOT cleared by ItemAdd() meaning they are still valid during e.g. NavProcessItem(). Always rely on HasFlags. ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData() ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values) float Width; // Set by SetNextItemWidth() ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut() ImGuiInputFlags ShortcutFlags; // Set by SetNextItemShortcut() bool OpenVal; // Set by SetNextItemOpen() ImU8 OpenCond; // Set by SetNextItemOpen() ImGuiDataTypeStorage RefVal; // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal ImGuiID StorageId; // Set by SetNextItemStorageID() ImU32 ColorMarker; // Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags_ColorMarkers. ImGuiNextItemData() { memset((void*)this, 0, sizeof(*this)); SelectionUserData = -1; } inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! }; // Status storage for the last submitted item struct ImGuiLastItemData { ImGuiID ID; ImGuiItemFlags ItemFlags; // See ImGuiItemFlags_ (called 'InFlags' before v1.91.4). ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ ImRect Rect; // Full rectangle ImRect NavRect; // Navigation scoring rectangle (not displayed) // Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags are set. ImRect DisplayRect; // Display rectangle. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) is set. ImRect ClipRect; // Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasClipRect) is set.. ImGuiKeyChord Shortcut; // Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasShortcut) is set.. ImGuiLastItemData() { memset((void*)this, 0, sizeof(*this)); } }; // Store data emitted by TreeNode() for usage by TreePop() // - To implement ImGuiTreeNodeFlags_NavLeftJumpsToParent: store the minimum amount of data // which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult(). // Only stored when the node is a potential candidate for landing on a Left arrow jump. struct ImGuiTreeNodeStackData { ImGuiID ID; ImGuiTreeNodeFlags TreeFlags; ImGuiItemFlags ItemFlags; // Used for nav landing ImRect NavRect; // Used for nav landing float DrawLinesX1; float DrawLinesToNodesY2; ImGuiTableColumnIdx DrawLinesTableColumn; }; // sizeof() = 20 struct IMGUI_API ImGuiErrorRecoveryState { short SizeOfWindowStack; short SizeOfIDStack; short SizeOfTreeStack; short SizeOfColorStack; short SizeOfStyleVarStack; short SizeOfFontStack; short SizeOfFocusScopeStack; short SizeOfGroupStack; short SizeOfItemFlagsStack; short SizeOfBeginPopupStack; short SizeOfDisabledStack; ImGuiErrorRecoveryState() { memset((void*)this, 0, sizeof(*this)); } }; // Data saved for each window pushed into the stack struct ImGuiWindowStackData { ImGuiWindow* Window; ImGuiLastItemData ParentLastItemDataBackup; ImGuiErrorRecoveryState StackSizesInBegin; // Store size of various stacks for asserting bool DisabledOverrideReenable; // Non-child window override disabled flag float DisabledOverrideReenableAlphaBackup; }; struct ImGuiShrinkWidthItem { int Index; float Width; float InitialWidth; }; struct ImGuiPtrOrIndex { void* Ptr; // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool. int Index; // Usually index in a main pool. ImGuiPtrOrIndex(void* ptr) { Ptr = ptr; Index = -1; } ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; // Data used by IsItemDeactivated()/IsItemDeactivatedAfterEdit() functions struct ImGuiDeactivatedItemData { ImGuiID ID; int ElapseFrame; bool HasBeenEditedBefore; bool IsAlive; }; //----------------------------------------------------------------------------- // [SECTION] Popup support //----------------------------------------------------------------------------- enum ImGuiPopupPositionPolicy { ImGuiPopupPositionPolicy_Default, ImGuiPopupPositionPolicy_ComboBox, ImGuiPopupPositionPolicy_Tooltip, }; // Storage for popup stacks (g.OpenPopupStack and g.BeginPopupStack) struct ImGuiPopupData { ImGuiID PopupId; // Set on OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() ImGuiWindow* RestoreNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value int OpenFrameCount; // Set on OpenPopup() 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 ImGuiPopupData() { memset((void*)this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; } }; //----------------------------------------------------------------------------- // [SECTION] Inputs support //----------------------------------------------------------------------------- // Bit array for named keys typedef ImBitArray ImBitArrayForNamedKeys; // [Internal] Key ranges #define ImGuiKey_LegacyNativeKey_BEGIN 0 #define ImGuiKey_LegacyNativeKey_END 512 #define ImGuiKey_Keyboard_BEGIN (ImGuiKey_NamedKey_BEGIN) #define ImGuiKey_Keyboard_END (ImGuiKey_GamepadStart) #define ImGuiKey_Gamepad_BEGIN (ImGuiKey_GamepadStart) #define ImGuiKey_Gamepad_END (ImGuiKey_GamepadRStickDown + 1) #define ImGuiKey_Mouse_BEGIN (ImGuiKey_MouseLeft) #define ImGuiKey_Mouse_END (ImGuiKey_MouseWheelY + 1) #define ImGuiKey_Aliases_BEGIN (ImGuiKey_Mouse_BEGIN) #define ImGuiKey_Aliases_END (ImGuiKey_Mouse_END) // [Internal] Named shortcuts for Navigation #define ImGuiKey_NavKeyboardTweakSlow ImGuiMod_Ctrl #define ImGuiKey_NavKeyboardTweakFast ImGuiMod_Shift #define ImGuiKey_NavGamepadTweakSlow ImGuiKey_GamepadL1 #define ImGuiKey_NavGamepadTweakFast ImGuiKey_GamepadR1 #define ImGuiKey_NavGamepadActivate (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceRight : ImGuiKey_GamepadFaceDown) #define ImGuiKey_NavGamepadCancel (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceDown : ImGuiKey_GamepadFaceRight) #define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft // Toggle menu layer. Hold to enable Windowing. #define ImGuiKey_NavGamepadContextMenu ImGuiKey_GamepadFaceUp // Open context menu (same as Shift+F10) enum ImGuiInputEventType { ImGuiInputEventType_None = 0, ImGuiInputEventType_MousePos, ImGuiInputEventType_MouseWheel, ImGuiInputEventType_MouseButton, ImGuiInputEventType_MouseViewport, ImGuiInputEventType_Key, ImGuiInputEventType_Text, ImGuiInputEventType_Focus, ImGuiInputEventType_COUNT }; enum ImGuiInputSource : int { ImGuiInputSource_None = 0, ImGuiInputSource_Mouse, // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them. ImGuiInputSource_Keyboard, ImGuiInputSource_Gamepad, ImGuiInputSource_COUNT }; // FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension? // Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor' struct ImGuiInputEventMousePos { float PosX, PosY; ImGuiMouseSource MouseSource; }; struct ImGuiInputEventMouseWheel { float WheelX, WheelY; ImGuiMouseSource MouseSource; }; struct ImGuiInputEventMouseButton { int Button; bool Down; ImGuiMouseSource MouseSource; }; struct ImGuiInputEventMouseViewport { ImGuiID HoveredViewportID; }; struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; struct ImGuiInputEventText { unsigned int Char; }; struct ImGuiInputEventAppFocused { bool Focused; }; struct ImGuiInputEvent { ImGuiInputEventType Type; ImGuiInputSource Source; ImU32 EventId; // Unique, sequential increasing integer to identify an event (if you need to correlate them to other data). union { ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton ImGuiInputEventMouseViewport MouseViewport; // if Type == ImGuiInputEventType_MouseViewport ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus }; bool AddedByTestEngine; ImGuiInputEvent() { memset((void*)this, 0, sizeof(*this)); } }; // Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior. #define ImGuiKeyOwner_Any ((ImGuiID)0) // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. #define ImGuiKeyOwner_NoOwner ((ImGuiID)-1) // Require key to have no owner. //#define ImGuiKeyOwner_None ImGuiKeyOwner_NoOwner // We previously called this 'ImGuiKeyOwner_None' but it was inconsistent with our pattern that _None values == 0 and quite dangerous. Also using _NoOwner makes the IsKeyPressed() calls more explicit. typedef ImS16 ImGuiKeyRoutingIndex; // Routing table entry (sizeof() == 16 bytes) struct ImGuiKeyRoutingData { ImGuiKeyRoutingIndex NextEntryIndex; ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImU16 RoutingCurrScore; // [DEBUG] For debug display ImU16 RoutingNextScore; // Lower is better (0: perfect score) ImGuiID RoutingCurr; ImGuiID RoutingNext; ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 0; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } }; // Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. // Stored in main context (1 instance) struct ImGuiKeyRoutingTable { ImGuiKeyRoutingIndex Index[ImGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[] ImVector Entries; ImVector EntriesNext; // Double-buffer to avoid reallocation (could use a shared buffer) ImGuiKeyRoutingTable() { Clear(); } void Clear() { for (int n = 0; n < IM_COUNTOF(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); } }; // This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features) // Stored in main context (1 per named key). In the future it might be merged into ImGuiKeyData. struct ImGuiKeyOwnerData { ImGuiID OwnerCurr; ImGuiID OwnerNext; bool LockThisFrame; // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame. bool LockUntilRelease; // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well. ImGuiKeyOwnerData() { OwnerCurr = OwnerNext = ImGuiKeyOwner_NoOwner; LockThisFrame = LockUntilRelease = false; } }; // Extend ImGuiInputFlags_ // Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() // Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function) enum ImGuiInputFlagsPrivate_ { // Flags for IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(), Shortcut() // - Repeat mode: Repeat rate selection ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default) ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster // - Repeat mode: Specify when repeating key pressed can be interrupted. // - In theory ImGuiInputFlags_RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in. ImGuiInputFlags_RepeatUntilRelease = 1 << 4, // Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior. ImGuiInputFlags_RepeatUntilKeyModsChange = 1 << 5, // Stop repeating when released OR if keyboard mods are changed (default for Shortcut) ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6, // Stop repeating when released OR if keyboard mods are leaving the None state. Allows going from Mod+Key to Key by releasing Mod. ImGuiInputFlags_RepeatUntilOtherKeyPress = 1 << 7, // Stop repeating when released OR if any other keyboard key is pressed during the repeat // Flags for SetKeyOwner(), SetItemKeyOwner() // - Locking key away from non-input aware code. Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary. ImGuiInputFlags_LockThisFrame = 1 << 20, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. ImGuiInputFlags_LockUntilRelease = 1 << 21, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. // - Condition for SetItemKeyOwner() ImGuiInputFlags_CondHovered = 1 << 22, // Only set if item is hovered (default to both) ImGuiInputFlags_CondActive = 1 << 23, // Only set if item is active (default to both) ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, // [Internal] Mask of which function support which flags ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak, ImGuiInputFlags_RepeatUntilMask_ = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress, ImGuiInputFlags_RepeatMask_ = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_, ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, ImGuiInputFlags_RouteTypeMask_ = ImGuiInputFlags_RouteActive | ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteAlways, ImGuiInputFlags_RouteOptionsMask_ = ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused | ImGuiInputFlags_RouteFromRootWindow, ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_RepeatMask_, ImGuiInputFlags_SupportedByIsMouseClicked = ImGuiInputFlags_Repeat, ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_, ImGuiInputFlags_SupportedBySetNextItemShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_ | ImGuiInputFlags_Tooltip, ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease, ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_, }; //----------------------------------------------------------------------------- // [SECTION] Clipper support //----------------------------------------------------------------------------- // Note that Max is exclusive, so perhaps should be using a Begin/End convention. struct ImGuiListClipperRange { int Min; int Max; bool PosToIndexConvert; // Begin/End are absolute position (will be converted to indices later) ImS8 PosToIndexOffsetMin; // Add to Min after converting to indices ImS8 PosToIndexOffsetMax; // Add to Min after converting to indices static ImGuiListClipperRange FromIndices(int min, int max) { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; } static ImGuiListClipperRange FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; } }; // Temporary clipper data, buffers shared/reused between instances struct ImGuiListClipperData { ImGuiListClipper* ListClipper; float LossynessOffset; int StepNo; int ItemsFrozen; ImVector Ranges; ImGuiListClipperData() { memset((void*)this, 0, sizeof(*this)); } void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); } }; //----------------------------------------------------------------------------- // [SECTION] Navigation support //----------------------------------------------------------------------------- enum ImGuiActivateFlags_ { ImGuiActivateFlags_None = 0, ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key. ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used. ImGuiActivateFlags_TryToPreserveState = 1 << 2, // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request (ImGuiNavMoveFlags_IsTabbing) ImGuiActivateFlags_FromShortcut = 1 << 4, // Activation requested by an item shortcut via SetNextItemShortcut() function. ImGuiActivateFlags_FromFocusApi = 1 << 5, // Activation requested by an api request (ImGuiNavMoveFlags_FocusApi) }; // Early work-in-progress API for ScrollToItem() enum ImGuiScrollFlags_ { ImGuiScrollFlags_None = 0, ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis] ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible] ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, // If item is not visible: scroll to make the item centered on X axis [rarely used] ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, // If item is not visible: scroll to make the item centered on Y axis ImGuiScrollFlags_AlwaysCenterX = 1 << 4, // Always center the result item on X axis [rarely used] ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window) ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to). ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY, }; enum ImGuiNavRenderCursorFlags_ { ImGuiNavRenderCursorFlags_None = 0, ImGuiNavRenderCursorFlags_Compact = 1 << 1, // Compact highlight, no padding/distance from focused item ImGuiNavRenderCursorFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) even when g.NavCursorVisible == false, aka even when using the mouse. ImGuiNavRenderCursorFlags_NoRounding = 1 << 3, #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiNavHighlightFlags_None = ImGuiNavRenderCursorFlags_None, // Renamed in 1.91.4 ImGuiNavHighlightFlags_Compact = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.91.4 ImGuiNavHighlightFlags_AlwaysDraw = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4 ImGuiNavHighlightFlags_NoRounding = ImGuiNavRenderCursorFlags_NoRounding, // Renamed in 1.91.4 //ImGuiNavHighlightFlags_TypeThin = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.90.2 #endif }; enum ImGuiNavMoveFlags_ { ImGuiNavMoveFlags_None = 0, ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side ImGuiNavMoveFlags_LoopY = 1 << 1, ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness ImGuiNavMoveFlags_WrapMask_ = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY, ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword as ImGuiScrollFlags ImGuiNavMoveFlags_Forwarded = 1 << 7, ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result ImGuiNavMoveFlags_FocusApi = 1 << 9, // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details) ImGuiNavMoveFlags_IsTabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight ImGuiNavMoveFlags_IsPageMove = 1 << 11, // Identify a PageDown/PageUp request. ImGuiNavMoveFlags_Activate = 1 << 12, // Activate/select target item. ImGuiNavMoveFlags_NoSelect = 1 << 13, // Don't trigger selection by not setting g.NavJustMovedTo ImGuiNavMoveFlags_NoSetNavCursorVisible = 1 << 14, // Do not alter the nav cursor visible state ImGuiNavMoveFlags_NoClearActiveId = 1 << 15, // (Experimental) Do not clear active id when applying move result }; enum ImGuiNavLayer { ImGuiNavLayer_Main = 0, // Main scrolling layer ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt) ImGuiNavLayer_COUNT }; // Storage for navigation query/results struct ImGuiNavItemData { ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) ImGuiID ID; // Init,Move // Best candidate item ID ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space ImGuiItemFlags ItemFlags; // ????,Move // Best candidate item flags float DistBox; // Move // Best candidate box distance to current NavId float DistCenter; // Move // Best candidate center distance to current NavId float DistAxial; // Move // Best candidate axial distance to current NavId ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionUserData() value. Valid if (ItemFlags & ImGuiItemFlags_HasSelectionUserData) ImGuiNavItemData() { Clear(); } void Clear() { Window = NULL; ID = FocusScopeId = 0; ItemFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; } }; // Storage for PushFocusScope(), g.FocusScopeStack[], g.NavFocusRoute[] struct ImGuiFocusScopeData { ImGuiID ID; ImGuiID WindowID; }; //----------------------------------------------------------------------------- // [SECTION] Typing-select support //----------------------------------------------------------------------------- // Flags for GetTypingSelectRequest() enum ImGuiTypingSelectFlags_ { ImGuiTypingSelectFlags_None = 0, ImGuiTypingSelectFlags_AllowBackspace = 1 << 0, // Backspace to delete character inputs. If using: ensure GetTypingSelectRequest() is not called more than once per frame (filter by e.g. focus state) ImGuiTypingSelectFlags_AllowSingleCharMode = 1 << 1, // Allow "single char" search mode which is activated when pressing the same character multiple times. }; // Returned by GetTypingSelectRequest(), designed to eventually be public. struct IMGUI_API ImGuiTypingSelectRequest { ImGuiTypingSelectFlags Flags; // Flags passed to GetTypingSelectRequest() int SearchBufferLen; const char* SearchBuffer; // Search buffer contents (use full string. unless SingleCharMode is set, in which case use SingleCharSize). bool SelectRequest; // Set when buffer was modified this frame, requesting a selection. bool SingleCharMode; // Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication. ImS8 SingleCharSize; // Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input. }; // Storage for GetTypingSelectRequest() struct IMGUI_API ImGuiTypingSelectState { ImGuiTypingSelectRequest Request; // User-facing data char SearchBuffer[64]; // Search buffer: no need to make dynamic as this search is very transient. ImGuiID FocusScope; int LastRequestFrame = 0; float LastRequestTime = 0.0f; bool SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing. ImGuiTypingSelectState() { memset((void*)this, 0, sizeof(*this)); } void Clear() { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging }; //----------------------------------------------------------------------------- // [SECTION] Columns support //----------------------------------------------------------------------------- // Flags for internal's BeginColumns(). This is an obsolete API. Prefer using BeginTable() nowadays! enum ImGuiOldColumnFlags_ { ImGuiOldColumnFlags_None = 0, ImGuiOldColumnFlags_NoBorder = 1 << 0, // Disable column dividers ImGuiOldColumnFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers ImGuiOldColumnFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns ImGuiOldColumnFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4, // Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None, //ImGuiColumnsFlags_NoBorder = ImGuiOldColumnFlags_NoBorder, //ImGuiColumnsFlags_NoResize = ImGuiOldColumnFlags_NoResize, //ImGuiColumnsFlags_NoPreserveWidths = ImGuiOldColumnFlags_NoPreserveWidths, //ImGuiColumnsFlags_NoForceWithinWindow = ImGuiOldColumnFlags_NoForceWithinWindow, //ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize, #endif }; struct ImGuiOldColumnData { float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) float OffsetNormBeforeResize; ImGuiOldColumnFlags Flags; // Not exposed ImRect ClipRect; ImGuiOldColumnData() { memset((void*)this, 0, sizeof(*this)); } }; struct ImGuiOldColumns { ImGuiID ID; ImGuiOldColumnFlags Flags; bool IsFirstFrame; bool IsBeingResized; int Current; int Count; float OffMinX, OffMaxX; // Offsets from HostWorkRect.Min.x float LineMinY, LineMaxY; float HostCursorPosY; // Backup of CursorPos at the time of BeginColumns() float HostCursorMaxPosX; // Backup of CursorMaxPos at the time of BeginColumns() ImRect HostInitialClipRect; // Backup of ClipRect at the time of BeginColumns() ImRect HostBackupClipRect; // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground() ImRect HostBackupParentWorkRect;//Backup of WorkRect at the time of BeginColumns() ImVector Columns; ImDrawListSplitter Splitter; ImGuiOldColumns() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- // [SECTION] Box-select support //----------------------------------------------------------------------------- struct ImGuiBoxSelectState { // Active box-selection data (persistent, 1 active at a time) ImGuiID ID; bool IsActive; bool IsStarting; bool IsStartedFromVoid; // Starting click was not from an item. bool IsStartedSetNavIdOnce; bool RequestClear; ImGuiKeyChord KeyMods : 16; // Latched key-mods for box-select logic. ImVec2 StartPosRel; // Start position in window-contents relative space (to support scrolling) ImVec2 EndPosRel; // End position in window-contents relative space ImVec2 ScrollAccum; // Scrolling accumulator (to behave at high-frame spaces) ImGuiWindow* Window; // Temporary/Transient data bool UnclipMode; // (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select. ImRect UnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos) ImRect BoxSelectRectCurr; ImGuiBoxSelectState() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- // [SECTION] Multi-select support //----------------------------------------------------------------------------- // We always assume that -1 is an invalid value (which works for indices and pointers) #define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1) // Temporary storage for multi-select struct IMGUI_API ImGuiMultiSelectTempData { ImGuiMultiSelectIO IO; // MUST BE FIRST FIELD. Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop. ImGuiMultiSelectState* Storage; ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; ImVec2 ScopeRectMin; ImVec2 BackupCursorMaxPos; ImGuiSelectionUserData LastSubmittedItem; // Copy of last submitted item data, used to merge output ranges. ImGuiID BoxSelectId; ImGuiKeyChord KeyMods; ImS8 LoopRequestSetAll; // -1: no operation, 0: clear all, 1: select all. bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsKeyboardSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. ImGuiMultiSelectTempData() { Clear(); } void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. void ClearIO() { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = ImGuiSelectionUserData_Invalid; IO.NavIdSelected = IO.RangeSrcReset = false; } }; // Persistent storage for multi-select (as long as selection is alive) struct IMGUI_API ImGuiMultiSelectState { ImGuiWindow* Window; ImGuiID ID; int LastFrameActive; // Last used frame-count, for GC. int LastSelectionSize; // Set by BeginMultiSelect() based on optional info provided by user. May be -1 if unknown. ImS8 RangeSelected; // -1 (don't have) or true/false ImS8 NavIdSelected; // -1 (don't have) or true/false ImGuiSelectionUserData RangeSrcItem; // ImGuiSelectionUserData NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = LastSelectionSize = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; } }; //----------------------------------------------------------------------------- // [SECTION] Docking support //----------------------------------------------------------------------------- #define DOCKING_HOST_DRAW_CHANNEL_BG 0 // Dock host: background fill #define DOCKING_HOST_DRAW_CHANNEL_FG 1 // Dock host: decorations and contents #ifdef IMGUI_HAS_DOCK // Extend ImGuiDockNodeFlags_ enum ImGuiDockNodeFlagsPrivate_ { // [Internal] ImGuiDockNodeFlags_DockSpace = 1 << 10, // Saved // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. ImGuiDockNodeFlags_CentralNode = 1 << 11, // Saved // The central node has 2 main properties: stay visible when empty, only use "remaining" spaces from its neighbor. ImGuiDockNodeFlags_NoTabBar = 1 << 12, // Saved // Tab bar is completely unavailable. No triangle in the corner to enable it back. ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Saved // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Saved // Disable window/docking menu (that one that appears instead of the collapse button) ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Saved // Disable close button ImGuiDockNodeFlags_NoResizeX = 1 << 16, // // ImGuiDockNodeFlags_NoResizeY = 1 << 17, // // ImGuiDockNodeFlags_DockedWindowsInFocusRoute= 1 << 18, // // Any docked window will be automatically be focus-route chained (window->ParentWindowForFocusRoute set to this) so Shortcut() in this window can run when any docked window is focused. // Disable docking/undocking actions in this dockspace or individual node (existing docked nodes will be preserved) // Those are not exposed in public because the desirable sharing/inheriting/copy-flag-on-split behaviors are quite difficult to design and understand. // The two public flags ImGuiDockNodeFlags_NoDockingOverCentralNode/ImGuiDockNodeFlags_NoDockingSplit don't have those issues. ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 19, // // Disable this node from splitting other windows/nodes. ImGuiDockNodeFlags_NoDockingOverMe = 1 << 20, // // Disable other windows/nodes from being docked over this node. ImGuiDockNodeFlags_NoDockingOverOther = 1 << 21, // // Disable this node from being docked over another window or non-empty node. ImGuiDockNodeFlags_NoDockingOverEmpty = 1 << 22, // // Disable this node from being docked over an empty node (e.g. DockSpace with no other windows) ImGuiDockNodeFlags_NoDocking = ImGuiDockNodeFlags_NoDockingOverMe | ImGuiDockNodeFlags_NoDockingOverOther | ImGuiDockNodeFlags_NoDockingOverEmpty | ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoDockingSplitOther, // Masks ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, ImGuiDockNodeFlags_NoResizeFlagsMask_ = (int)ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, // When splitting, those local flags are moved to the inheriting child, never duplicated ImGuiDockNodeFlags_LocalFlagsTransferMask_ = (int)ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | (int)ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, }; // Store the source authority (dock node vs window) of a field enum ImGuiDataAuthority_ { ImGuiDataAuthority_Auto, ImGuiDataAuthority_DockNode, ImGuiDataAuthority_Window, }; enum ImGuiDockNodeState { ImGuiDockNodeState_Unknown, ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow, ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing, ImGuiDockNodeState_HostWindowVisible, }; // sizeof() 156~192 struct IMGUI_API ImGuiDockNode { ImGuiID ID; ImGuiDockNodeFlags SharedFlags; // (Write) Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) ImGuiDockNodeFlags LocalFlags; // (Write) Flags specific to this node ImGuiDockNodeFlags LocalFlagsInWindows; // (Write) Flags specific to this node, applied from windows ImGuiDockNodeFlags MergedFlags; // (Read) Effective flags (== SharedFlags | LocalFlagsInNode | LocalFlagsInWindows) ImGuiDockNodeState State; ImGuiDockNode* ParentNode; ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. ImGuiTabBar* TabBar; ImVec2 Pos; // Current position ImVec2 Size; // Current size ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. ImGuiAxis SplitAxis; // [Split node only] Split axis (X or Y) ImGuiWindowClass WindowClass; // [Root node only] ImU32 LastBgColor; ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node. ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy. int CountNodeWithWindows; // [Root node only] int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly int LastFrameActive; // Last frame number the node was updated. int LastFrameFocused; // Last frame number the node was focused. ImGuiID LastFocusedNodeId; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. ImGuiID SelectedTabId; // [Leaf node only] Which of our tab/window is selected. ImGuiID WantCloseTabId; // [Leaf node only] Set when closing a specific tab/window. ImGuiID RefViewportId; // Reference viewport ID from visible window when HostWindow == NULL. ImGuiDataAuthority AuthorityForPos :3; ImGuiDataAuthority AuthorityForSize :3; ImGuiDataAuthority AuthorityForViewport :3; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; bool IsBgDrawnThisFrame :1; bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one. bool HasWindowMenuButton :1; bool HasCentralNodeChild :1; bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window bool WantHiddenTabBarUpdate :1; bool WantHiddenTabBarToggle :1; ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } bool IsDockSpace() const { return (MergedFlags & ImGuiDockNodeFlags_DockSpace) != 0; } bool IsFloatingNode() const { return ParentNode == NULL && (MergedFlags & ImGuiDockNodeFlags_DockSpace) == 0; } bool IsCentralNode() const { return (MergedFlags & ImGuiDockNodeFlags_CentralNode) != 0; } bool IsHiddenTabBar() const { return (MergedFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle bool IsNoTabBar() const { return (MergedFlags & ImGuiDockNodeFlags_NoTabBar) != 0; } // Never show a tab bar bool IsSplitNode() const { return ChildNodes[0] != NULL; } bool IsLeafNode() const { return ChildNodes[0] == NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } void SetLocalFlags(ImGuiDockNodeFlags flags) { LocalFlags = flags; UpdateMergedFlags(); } void UpdateMergedFlags() { MergedFlags = SharedFlags | LocalFlags | LocalFlagsInWindows; } }; // List of colors that are stored at the time of Begin() into Docked Windows. // We currently store the packed colors in a simple array window->DockStyle.Colors[]. // A better solution may involve appending into a log of colors in ImGuiContext + store offsets into those arrays in ImGuiWindow, // but it would be more complex as we'd need to double-buffer both as e.g. drop target may refer to window from last frame. enum ImGuiWindowDockStyleCol { ImGuiWindowDockStyleCol_Text, ImGuiWindowDockStyleCol_TabHovered, ImGuiWindowDockStyleCol_TabFocused, ImGuiWindowDockStyleCol_TabSelected, ImGuiWindowDockStyleCol_TabSelectedOverline, ImGuiWindowDockStyleCol_TabDimmed, ImGuiWindowDockStyleCol_TabDimmedSelected, ImGuiWindowDockStyleCol_TabDimmedSelectedOverline, ImGuiWindowDockStyleCol_UnsavedMarker, ImGuiWindowDockStyleCol_COUNT }; // We don't store style.Alpha: dock_node->LastBgColor embeds it and otherwise it would only affect the docking tab, which intuitively I would say we don't want to. struct ImGuiWindowDockStyle { ImU32 Colors[ImGuiWindowDockStyleCol_COUNT]; }; struct ImGuiDockContext { ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes ImVector Requests; ImVector NodesSettings; bool WantFullRebuild; ImGuiDockContext() { memset((void*)this, 0, sizeof(*this)); } }; #endif // #ifdef IMGUI_HAS_DOCK //----------------------------------------------------------------------------- // [SECTION] Viewport support //----------------------------------------------------------------------------- // ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) // Every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport { ImGuiWindow* Window; // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set) int Idx; int LastFrameActive; // Last frame number this viewport was activated by a window int LastFocusedStampCount; // Last stamp number from when a window hosted by this viewport was focused (by comparing this value between two viewport we have an implicit viewport z-order we use as fallback) ImGuiID LastNameHash; ImVec2 LastPos; ImVec2 LastSize; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; bool LastFocusedHadNavWindow;// Instead of maintaining a LastFocusedWindow (which may harder to correctly maintain), we merely store weither NavWindow != NULL last time the viewport was focused. short PlatformMonitor; int BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData ImVec2 LastPlatformPos; ImVec2 LastPlatformSize; ImVec2 LastRendererSize; // Per-viewport work area // - Insets are >= 0.0f values, distance from viewport corners to work area. // - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents. // - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land. ImVec2 WorkInsetMin; // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect(). ImVec2 WorkInsetMax; // " ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset ImVec2 BuildWorkInsetMax; // " ImGuiViewportP() { Window = NULL; Idx = -1; LastFrameActive = BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = LastFocusedStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; LastFocusedHadNavWindow = false; PlatformMonitor = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) ImVec2 CalcWorkRectPos(const ImVec2& inset_min) const { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); } ImVec2 CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y - inset_max.y)); } void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkInsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkInsetMin, BuildWorkInsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } }; //----------------------------------------------------------------------------- // [SECTION] Settings support //----------------------------------------------------------------------------- // Windows data saved in imgui.ini file // Because we never destroy or rename ImGuiWindowSettings, we can store the names in a separate buffer easily. // (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure) struct ImGuiWindowSettings { ImGuiID ID; ImVec2ih Pos; // NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions. ImVec2ih Size; ImVec2ih ViewportPos; ImGuiID ViewportId; ImGuiID DockId; // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none. ImGuiID ClassId; // ID of window class if specified short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. bool Collapsed; bool IsChild; bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) bool WantDelete; // Set to invalidate/delete the settings entry ImGuiWindowSettings() { memset((void*)this, 0, sizeof(*this)); DockOrder = -1; } char* GetName() { return (char*)(this + 1); } }; struct ImGuiSettingsHandler { const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' ImGuiID TypeHash; // == ImHashStr(TypeName) void (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Clear all settings data void (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called before reading (in registration order) 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 (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order) void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' void* UserData; ImGuiSettingsHandler() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- // [SECTION] Localization support //----------------------------------------------------------------------------- // This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack. enum ImGuiLocKey : int { ImGuiLocKey_VersionStr, ImGuiLocKey_TableSizeOne, ImGuiLocKey_TableSizeAllFit, ImGuiLocKey_TableSizeAllDefault, ImGuiLocKey_TableResetOrder, ImGuiLocKey_WindowingMainMenuBar, ImGuiLocKey_WindowingPopup, ImGuiLocKey_WindowingUntitled, ImGuiLocKey_OpenLink_s, ImGuiLocKey_CopyLink, ImGuiLocKey_DockingHideTabBar, ImGuiLocKey_DockingHoldShiftToDock, ImGuiLocKey_DockingDragToUndockOrMoveNode, ImGuiLocKey_COUNT }; struct ImGuiLocEntry { ImGuiLocKey Key; const char* Text; }; //----------------------------------------------------------------------------- // [SECTION] Error handling, State recovery support //----------------------------------------------------------------------------- // Macros used by Recoverable Error handling // - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro). // - The message will always be a string literal, in order to increase likelihood of being display by an assert handler. // - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling. // - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling. #ifndef IM_ASSERT_USER_ERROR #define IM_ASSERT_USER_ERROR(_EXPR,_MSG) do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } } while (0) // Recoverable User Error #define IM_ASSERT_USER_ERROR_RET(_EXPR,_MSG) do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } return; } } while (0) // Recoverable User Error #define IM_ASSERT_USER_ERROR_RETV(_EXPR,_RETV,_MSG) do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } return _RETV; } } while (0) // Recoverable User Error #endif // The error callback is currently not public, as it is expected that only advanced users will rely on it. typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback //----------------------------------------------------------------------------- // [SECTION] Metrics, Debug Tools //----------------------------------------------------------------------------- // See IMGUI_DEBUG_LOG() and IMGUI_DEBUG_LOG_XXX() macros. enum ImGuiDebugLogFlags_ { // Event types ImGuiDebugLogFlags_None = 0, ImGuiDebugLogFlags_EventError = 1 << 0, // Error submitted by IM_ASSERT_USER_ERROR() ImGuiDebugLogFlags_EventActiveId = 1 << 1, ImGuiDebugLogFlags_EventFocus = 1 << 2, ImGuiDebugLogFlags_EventPopup = 1 << 3, ImGuiDebugLogFlags_EventNav = 1 << 4, ImGuiDebugLogFlags_EventClipper = 1 << 5, ImGuiDebugLogFlags_EventSelection = 1 << 6, ImGuiDebugLogFlags_EventIO = 1 << 7, ImGuiDebugLogFlags_EventFont = 1 << 8, ImGuiDebugLogFlags_EventInputRouting = 1 << 9, ImGuiDebugLogFlags_EventDocking = 1 << 10, ImGuiDebugLogFlags_EventViewport = 1 << 11, ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY ImGuiDebugLogFlags_OutputToDebugger = 1 << 21, // Also send output to Debugger Console [Windows only] ImGuiDebugLogFlags_OutputToTestEngine = 1 << 22, // Also send output to Dear ImGui Test Engine }; struct ImGuiDebugAllocEntry { int FrameCount; ImS16 AllocCount; ImS16 FreeCount; }; struct ImGuiDebugAllocInfo { int TotalAllocCount; // Number of call to MemAlloc(). int TotalFreeCount; ImS16 LastEntriesIdx; // Current index in buffer ImGuiDebugAllocEntry LastEntriesBuf[6]; // Track last 6 frames that had allocations ImGuiDebugAllocInfo() { memset((void*)this, 0, sizeof(*this)); } }; struct ImGuiMetricsConfig { bool ShowDebugLog = false; bool ShowIDStackTool = false; bool ShowWindowsRects = false; bool ShowWindowsBeginOrder = false; bool ShowTablesRects = false; bool ShowDrawCmdMesh = true; bool ShowDrawCmdBoundingBoxes = true; bool ShowTextEncodingViewer = false; bool ShowTextureUsedRect = false; bool ShowDockingNodes = false; int ShowWindowsRectsType = -1; int ShowTablesRectsType = -1; int HighlightMonitorIdx = -1; ImGuiID HighlightViewportID = 0; bool ShowFontPreview = true; }; struct ImGuiStackLevelInfo { ImGuiID ID; ImS8 QueryFrameCount; // >= 1: Sub-query in progress bool QuerySuccess; // Sub-query obtained result from DebugHookIdInfo() ImS8 DataType; // ImGuiDataType int DescOffset; // -1 or offset into parent's ResultsPathsBuf ImGuiStackLevelInfo() { memset((void*)this, 0, sizeof(*this)); DataType = -1; DescOffset = -1; } }; struct ImGuiDebugItemPathQuery { ImGuiID MainID; // ID to query details for. bool Active; // Used to disambiguate the case when ID == 0 and e.g. some code calls PushOverrideID(0). bool Complete; // All sub-queries are finished (some may have failed). ImS8 Step; // -1: query stack + init Results, >= 0: filling individual stack level. ImVector Results; ImGuiTextBuffer ResultsDescBuf; ImGuiTextBuffer ResultPathBuf; ImGuiDebugItemPathQuery() { memset((void*)this, 0, sizeof(*this)); } }; // State for ID Stack tool queries struct ImGuiIDStackTool { bool OptHexEncodeNonAsciiChars; bool OptCopyToClipboardOnCtrlC; int LastActiveFrame; float CopyToClipboardLastTime; ImGuiIDStackTool() { memset((void*)this, 0, sizeof(*this)); LastActiveFrame = -1; OptHexEncodeNonAsciiChars = true; CopyToClipboardLastTime = -FLT_MAX; } }; //----------------------------------------------------------------------------- // [SECTION] Generic context hooks //----------------------------------------------------------------------------- typedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook); enum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ }; struct ImGuiContextHook { ImGuiID HookId; // A unique ID assigned by AddContextHook() ImGuiContextHookType Type; ImGuiID Owner; ImGuiContextHookCallback Callback; void* UserData; ImGuiContextHook() { memset((void*)this, 0, sizeof(*this)); } }; typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section); //----------------------------------------------------------------------------- // [SECTION] ImGuiContext (main Dear ImGui context) //----------------------------------------------------------------------------- struct ImGuiContext { bool Initialized; bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() int FrameCount; int FrameCountEnded; int FrameCountPlatformEnded; int FrameCountRendered; double Time; char ContextName[16]; // Storage for a context name (to facilitate debugging multi-context setups) ImGuiIO IO; ImGuiPlatformIO PlatformIO; ImGuiStyle Style; ImGuiConfigFlags ConfigFlagsCurrFrame; // = g.IO.ConfigFlags at the time of NewFrame() ImGuiConfigFlags ConfigFlagsLastFrame; ImVector FontAtlases; // List of font atlases used by the context (generally only contains g.IO.Fonts aka the main font atlas) ImFont* Font; // Currently bound font. (== FontStack.back().Font) ImFontBaked* FontBaked; // Currently bound font at currently bound size. (== Font->GetFontBaked(FontSize)) float FontSize; // Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function). float FontSizeBase; // Font size before scaling == style.FontSizeBase == value passed to PushFont() when specified. float FontBakedScale; // == FontBaked->Size / FontSize. Scale factor over baked size. Rarely used nowadays, very often == 1.0f. float FontRasterizerDensity; // Current font density. Used by all calls to GetFontBaked(). float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale ImDrawListSharedData DrawListSharedData; ImGuiID WithinEndChildID; // Set within EndChild() void* TestEngine; // Test engine user data // Inputs ImVector InputEventsQueue; // Input events which will be trickled/written into IO structure. ImVector InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail. ImGuiMouseSource InputEventsNextMouseSource; ImU32 InputEventsNextEventId; // Windows state ImVector Windows; // Windows, sorted in display order, back to front ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child ImVector CurrentWindowStack; ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* int WindowsActiveCount; // Number of unique windows submitted by frame float WindowsBorderHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, style.WindowBorderHoverPadding). This isn't so multi-dpi friendly. ImGuiID DebugBreakInWindow; // Set to break in Begin() call. ImGuiWindow* CurrentWindow; // Window being drawn into ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. ImGuiWindow* HoveredWindowBeforeClear; // Window the mouse is hovering. Filled even with _NoMouse. This is currently useful for multi-context compositors. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; int WheelingWindowStartFrame; // This may be set one frame before WheelingWindow is != NULL int WheelingWindowScrolledFrame; float WheelingWindowReleaseTimer; ImVec2 WheelingWindowWheelRemainder; ImVec2 WheelingAxisAvg; // Item/widgets state and tracking information ImGuiID DebugDrawIdConflictsId; // Set when we detect multiple items with the same identifier ImGuiID DebugHookIdInfoId; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; int HoveredIdPreviousFrameItemCount; // Count numbers of items using the same ID as last frame's hovered id float HoveredIdTimer; // Measure contiguous hovering time float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active bool HoveredIdAllowOverlap; bool HoveredIdIsDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. bool ItemUnclipByLog; // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled ImGuiID ActiveId; // Active widget ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) 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 ActiveIdNoClearOnFocusLoss; // Disable losing active id if the active id window gets unfocused. bool ActiveIdHasBeenPressedBefore; // 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 ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state. bool ActiveIdHasBeenEditedThisFrame; bool ActiveIdFromShortcut; ImS8 ActiveIdMouseButton; ImGuiID ActiveIdDisabledId; // When clicking a disabled item we set ActiveId=window->MoveId to avoid interference with widget code. Actual item ID is stored here. ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiInputSource ActiveIdSource; // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad ImGuiWindow* ActiveIdWindow; ImGuiID ActiveIdPreviousFrame; ImGuiDeactivatedItemData DeactivatedItemData; ImGuiDataTypeStorage ActiveIdValueOnActivation; // Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX. 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. // Key/Input Ownership + Shortcut Routing system // - The idea is that instead of "eating" a given key, we can link to an owner. // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID. // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame(). double LastKeyModsChangeTime; // Record the last time key mods changed (affect repeat delay when using shortcut logic) double LastKeyModsChangeFromNoneTime; // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic) double LastKeyboardKeyPressTime; // Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed. ImBitArrayForNamedKeys KeysMayBeCharInput; // Lookup to tell if a key can emit char input, see IsKeyChordPotentiallyCharInput(). sizeof() = 20 bytes ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT]; ImGuiKeyRoutingTable KeysRoutingTable; ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (this is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations) ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls. //ImU32 ActiveIdUsingNavInputMask; // [OBSOLETE] Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes --> 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);' // Next window/item data ImGuiID CurrentFocusScopeId; // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId. ImGuiItemFlags CurrentItemFlags; // Value for currently appending items == g.ItemFlagsStack.back() ImGuiID DebugLocateId; // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions bool DebugShowGroupRects; bool GcCompactAll; // Request full GC // Shared stacks ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line) ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin() ImVector ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() ImVector GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVector OpenPopupStack; // Which popups are open (persistent) ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) ImVectorTreeNodeStack; // Stack for TreeNode() // Viewports ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MouseViewport; ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. ImGuiID PlatformLastFocusedViewportId; ImGuiPlatformMonitor FallbackMonitor; // Virtual monitor used as fallback if backend doesn't provide monitor information. ImRect PlatformMonitorsFullWorkRect; // Bounding box of all platform monitors int ViewportCreatedCount; // Unique sequential creation counter (mostly for testing/debugging) int PlatformWindowsCreatedCount; // Unique sequential creation counter (mostly for testing/debugging) int ViewportFocusedStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter // Keyboard/Gamepad Navigation bool NavCursorVisible; // Nav focus cursor/rectangle is visible? We hide it after a mouse click. We show it after a nav move. bool NavHighlightItemUnderNav; // Disable mouse hovering highlight. Highlight navigation focused item instead of mouse hovered item. //bool NavDisableHighlight; // Old name for !g.NavCursorVisible before 1.91.4 (2024/10/18). OPPOSITE VALUE (g.NavDisableHighlight == !g.NavCursorVisible) //bool NavDisableMouseHover; // Old name for g.NavHighlightItemUnderNav before 1.91.1 (2024/10/18) this was called When user starts using keyboard/gamepad, we hide mouse hovering highlight until mouse is touched again. bool NavMousePosDirty; // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default) bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid ImGuiID NavId; // Focused item for navigation ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope) ImGuiNavLayer NavLayer; // Focused layer (main scrolling layer, or menu/title bar layer) ImGuiItemFlags NavIdItemFlags; ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItemByID() ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) ImGuiActivateFlags NavActivateFlags; ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain. ImGuiID NavHighlightActivatedId; float NavHighlightActivatedTimer; ImGuiID NavOpenContextMenuItemId; ImGuiID NavOpenContextMenuWindowId; ImGuiID NavNextActivateId; // Set by ActivateItemByID(), queued until next frame. ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Gamepad ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data. ImS8 NavCursorHideFrames; //ImGuiID NavActivateInputId; // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606. // Navigation: Init & Move Requests bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() bool NavInitRequest; // Init request for appearing window to select first item bool NavInitRequestFromMove; ImGuiNavItemData NavInitResult; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame() bool NavMoveScoringItems; // Move request submitted, still scoring incoming items bool NavMoveForwardToNextFrame; ImGuiNavMoveFlags NavMoveFlags; ImGuiScrollFlags NavMoveScrollFlags; ImGuiKeyChord NavMoveKeyMods; ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted. Unset/invalid if inverted. int NavScoringDebugCount; // Metrics for debugging int NavTabbingDir; // Generally -1 or +1, 0 when tabbing without a nav id int NavTabbingCounter; // >0 when counting items for tabbing ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy // Navigation: record of last move request ImGuiID NavJustMovedFromFocusScopeId; // Just navigated from this focus scope id (result of a successfully MoveRequest). ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). ImGuiKeyChord NavJustMovedToKeyMods; bool NavJustMovedToIsTabbing; // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags. bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData. // Navigation: extra config options (will be made public eventually) // - Tabbing (Tab, Shift+Tab) and Windowing (Ctrl+Tab, Ctrl+Shift+Tab) are enabled REGARDLESS of ImGuiConfigFlags_NavEnableKeyboard being set. // - Ctrl+Tab is reconfigurable because it is the only shortcut that may be polled when no window are focused. It also doesn't work e.g. Web platforms. bool ConfigNavEnableTabbing; // = true. Enable tabbing (Tab, Shift+Tab). PLEASE LET ME KNOW IF YOU USE THIS. bool ConfigNavWindowingWithGamepad; // = true. Enable Ctrl+Tab by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer. ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). Set to 0 to disable. For reconfiguration (see #4828) ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X) // Navigation: Windowing (Ctrl+Tab for list, or Menu button + keys or directional pads to move/resize) ImGuiWindow* NavWindowingTarget; // Target window when doing Ctrl+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it. ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the Ctrl+Tab contents float NavWindowingTimer; float NavWindowingHighlightAlpha; ImGuiInputSource NavWindowingInputSource; bool NavWindowingToggleLayer; // Set while Alt or GamepadMenu is held, may be cleared by other operations, and processed when releasing the key. ImGuiKey NavWindowingToggleKey; // Keyboard/gamepad key used when toggling to menu layer. ImVec2 NavWindowingAccumDeltaPos; ImVec2 NavWindowingAccumDeltaSize; // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and Ctrl+Tab list) // Drag and Drop bool DragDropActive; bool DragDropWithinSource; // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag source. bool DragDropWithinTarget; // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag target. ImGuiDragDropFlags DragDropSourceFlags; int DragDropSourceFrameCount; int DragDropMouseButton; ImGuiPayload DragDropPayload; ImRect DragDropTargetRect; // Store rectangle of current target candidate (we favor small targets when overlapping) ImRect DragDropTargetClipRect; // Store ClipRect at the time of item's drawing ImGuiID DragDropTargetId; ImGuiID DragDropTargetFullViewport; ImGuiDragDropFlags DragDropAcceptFlagsCurr; ImGuiDragDropFlags DragDropAcceptFlagsPrev; float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface) ImGuiID DragDropAcceptIdCurr; // Target item id (set at the time of accepting the payload) ImGuiID DragDropAcceptIdPrev; // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets) int DragDropAcceptFrameCount; // Last time a target expressed a desire to accept the source ImGuiID DragDropHoldJustPressedId; // Set when holding a payload just made ButtonBehavior() return a press. ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads // Clipper int ClipperTempDataStacked; ImVector ClipperTempData; // Tables ImGuiTable* CurrentTable; ImGuiID DebugBreakInTable; // Set to break in BeginTable() call. int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting) ImPool Tables; // Persistent table data ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) ImVector DrawChannelsTempMergeBuffer; // Tab bars ImGuiTabBar* CurrentTabBar; ImPool TabBars; ImVector CurrentTabBarStack; ImVector ShrinkWidthBuffer; // Multi-Select state ImGuiBoxSelectState BoxSelectState; ImGuiMultiSelectTempData* CurrentMultiSelect; int MultiSelectTempDataStacked; // Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size) ImVector MultiSelectTempData; ImPool MultiSelectStorage; // Hover Delay system ImGuiID HoverItemDelayId; ImGuiID HoverItemDelayIdPreviousFrame; float HoverItemDelayTimer; // Currently used by IsItemHovered() float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. ImGuiID HoverItemUnlockedStationaryId; // Mouse has once been stationary on this item. Only reset after departing the item. ImGuiID HoverWindowUnlockedStationaryId; // Mouse has once been stationary on this window. Only reset after departing the window. // Mouse state ImGuiMouseCursor MouseCursor; float MouseStationaryTimer; // Time the mouse has been stationary (with some loose heuristic) ImVec2 MouseLastValidPos; // Widget state ImGuiInputTextState InputTextState; ImGuiTextIndex InputTextLineIndex; // Temporary storage ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFontBaked InputTextPasswordFontBackupBaked; ImFontFlags InputTextPasswordFontBackupFlags; ImGuiID InputTextReactivateId; // ID of InputText to reactivate on next frame (for io.ConfigInputTextEnterKeepActive behavior) ImGuiID TempInputId; // Temporary text input when using Ctrl+Click on a slider, etc. ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types int BeginMenuDepth; int BeginComboDepth; ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets ImGuiID ColorEditCurrentID; // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others). ImGuiID ColorEditSavedID; // ID we are saving/restoring HS for float ColorEditSavedHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips float ColorEditSavedSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips ImU32 ColorEditSavedColor; // RGB value with alpha set to 0. ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. ImGuiComboPreviewData ComboPreviewData; ImRect WindowResizeBorderExpectedRect; // Expected border rect, switch to relative edit if moving bool WindowResizeRelativeMode; short ScrollbarSeekMode; // 0: scroll to clicked location, -1/+1: prev/next page. float ScrollbarClickDeltaToGrabCenter; // When scrolling to mouse location: distance between mouse and center of grab box, normalized in parent space. float SliderGrabClickOffset; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? bool DragCurrentAccumDirty; float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() short DisabledStackSize; short TooltipOverrideCount; ImGuiWindow* TooltipPreviousWindow; // Window of last tooltip submitted during the frame ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest() // Platform support ImGuiPlatformImeData PlatformImeData; // Data updated by current frame. Will be applied at end of the frame. For some backends, this is required to have WantVisible=true in order to receive text message. ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler. // Extensions // FIXME: We could provide an API to register one slot in an array held in ImGuiContext? ImVector UserTextures; // List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[]. ImGuiDockContext DockContext; void (*DockNodeWindowMenuHandler)(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar); // Settings bool SettingsLoaded; float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero ImGuiTextBuffer SettingsIniData; // In memory .ini settings ImVector SettingsHandlers; // List of .ini settings handlers ImChunkStream SettingsWindows; // ImGuiWindow .ini settings entries ImChunkStream SettingsTables; // ImGuiTable .ini settings entries // Hooks ImVector Hooks; // Hooks for extensions (e.g. test engine) ImGuiID HookIdNext; // Next available HookId ImGuiDemoMarkerCallback DemoMarkerCallback; // Localization const char* LocalizationTable[ImGuiLocKey_COUNT]; // Capture/Logging bool LogEnabled; // Currently capturing bool LogLineFirstItem; ImGuiLogFlags LogFlags; // Capture flags/type ImGuiWindow* LogWindow; ImFileHandle LogFile; // If != NULL log to stdout/ file ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. const char* LogNextPrefix; // See comment in LogSetNextTextDecoration(): doesn't copy underlying data, use carefully! const char* LogNextSuffix; float LogLinePosY; int LogDepthRef; int LogDepthToExpand; int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. // Error Handling ImGuiErrorCallback ErrorCallback; // = NULL. May be exposed in public API eventually. void* ErrorCallbackUserData; // = NULL ImVec2 ErrorTooltipLockedPos; bool ErrorFirst; int ErrorCountCurrentFrame; // [Internal] Number of errors submitted this frame. ImGuiErrorRecoveryState StackSizesInNewFrame; // [Internal] ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow; // [Internal] // Debug Tools // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.) int DebugDrawIdConflictsCount; // Locked count (preserved when holding Ctrl) ImGuiDebugLogFlags DebugLogFlags; ImGuiTextBuffer DebugLogBuf; ImGuiTextIndex DebugLogIndex; int DebugLogSkippedErrors; ImGuiDebugLogFlags DebugLogAutoDisableFlags; ImU8 DebugLogAutoDisableFrames; ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above. bool DebugBreakInLocateId; // Debug break in ItemAdd() call for g.DebugLocateId. ImGuiKeyChord DebugBreakKeyChord; // = ImGuiKey_Pause ImS8 DebugBeginReturnValueCullDepth; // Cycle between 0..9 then wrap around. bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) ImU8 DebugItemPickerMouseButton; ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID float DebugFlashStyleColorTime; ImVec4 DebugFlashStyleColorBackup; ImGuiMetricsConfig DebugMetricsConfig; ImGuiDebugItemPathQuery DebugItemPathQuery; ImGuiIDStackTool DebugIDStackTool; ImGuiDebugAllocInfo DebugAllocInfo; ImGuiDockNode* DebugHoveredDockNode; // Hovered dock node. #if defined(IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS) ImGuiStorage DebugDrawIdConflictsAliveCount; ImGuiStorage DebugDrawIdConflictsHighlightSet; #endif // Misc float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames.. int FramerateSecPerFrameIdx; int FramerateSecPerFrameCount; float FramerateSecPerFrameAccum; int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1. int WantCaptureKeyboardNextFrame; // " int WantTextInputNextFrame; // Copied in EndFrame() from g.PlatformImeData.WantTextInput. Needs to be set for some backends (SDL3) to emit character inputs. ImVector TempBuffer; // Temporary text buffer char TempKeychordName[64]; ImGuiContext(ImFontAtlas* shared_font_atlas); ~ImGuiContext(); }; //----------------------------------------------------------------------------- // [SECTION] ImGuiWindowTempData, ImGuiWindow //----------------------------------------------------------------------------- #define IMGUI_WINDOW_HARD_MIN_SIZE 4.0f // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. // (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..) // (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin) struct IMGUI_API ImGuiWindowTempData { // Layout ImVec2 CursorPos; // Current emitting position, in absolute coordinates. ImVec2 CursorPosPrevLine; ImVec2 CursorStartPos; // Initial position after Begin(), generally ~ window position + WindowPadding. ImVec2 CursorMaxPos; // Used to implicitly calculate ContentSize at the beginning of next frame, for scrolling range and auto-resize. Always growing during the frame. ImVec2 IdealMaxPos; // Used to implicitly calculate ContentSizeIdeal at the beginning of next frame, for auto-resize only. Always growing during the frame. ImVec2 CurrLineSize; ImVec2 PrevLineSize; float CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added). float PrevLineTextBaseOffset; bool IsSameLine; bool IsSetPos; ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. ImVec1 GroupOffset; ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensate and fix the most common use case of large scroll area. // Keyboard/Gamepad navigation ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) short NavLayersActiveMask; // Which layers have been written to (result from previous frame) short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) bool NavIsScrollPushableX; // Set when current work location may be scrolled horizontally when moving left / right. This is generally always true UNLESS within a column. bool NavHideHighlightOneFrame; bool NavWindowHasScrollY; // Set per window when scrolling can be used (== ScrollMax.y > 0.0f) // Miscellaneous 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. ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement int TreeDepth; // Current tree depth. ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary. ImU32 TreeRecordsClippedNodesY2Mask; // Store whether we should keep recording Y2. Cleared when passing clip max. Equivalent TreeHasStackDataDepthMask value should always be set. ImVector ChildWindows; ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state) ImGuiOldColumns* CurrentColumns; // Current columns set int CurrentTableIdx; // Current table index (into g.Tables) ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() ImU32 ModalDimBgColor; // Status flags ImGuiItemStatusFlags WindowItemStatusFlags; ImGuiItemStatusFlags ChildItemStatusFlags; ImGuiItemStatusFlags DockTabItemStatusFlags; ImRect DockTabItemRect; // Local parameters stacks // 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. float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). float ItemWidthDefault; float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) }; // Storage for one window struct IMGUI_API ImGuiWindow { ImGuiContext* Ctx; // Parent UI context (needs to be set explicitly by parent). char* Name; // Window name, owned by the window. ImGuiID ID; // == ImHashStr(Name) ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ ImGuiChildFlags ChildFlags; // Set when window is a child window. See enum ImGuiChildFlags_ ImGuiWindowClass WindowClass; // Advanced users only. Set with SetNextWindowClass() ImGuiViewportP* Viewport; // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded. ImGuiID ViewportId; // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive) ImVec2 ViewportPos; // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive) int ViewportAllowPlatformMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor ImVec2 Pos; // Position (always rounded-up to nearest pixel) ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed ImVec2 ContentSize; // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding. ImVec2 ContentSizeIdeal; ImVec2 ContentSizeExplicit; // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize(). ImVec2 WindowPadding; // Window padding at the time of Begin(). float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc. float WindowBorderSize; // Window border size at the time of Begin(). float TitleBarHeight, MenuBarHeight; // Note that those used to be function before 2024/05/28. If you have old code calling TitleBarHeight() you can change it to TitleBarHeight. float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin(). float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y). float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes). int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") ImGuiID TabId; // == window->GetID("#TAB") ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) ImVec2 Scroll; ImVec2 ScrollMax; ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. bool ScrollbarX, ScrollbarY; // Are scrollbars visible? bool ScrollbarXStabilizeEnabled; // Was ScrollbarX previously auto-stabilized? ImU8 ScrollbarXStabilizeToggledHistory; // Used to stabilize scrollbar visibility in case of feedback loops bool ViewportOwned; bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window bool Collapsed; // Set when collapsing window to become only title-bar bool WantCollapseToggle; bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) bool SkipRefresh; // [EXPERIMENTAL] Reuse previous frame drawn contents, Begin() returns false. bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== HiddenFrames*** > 0) bool IsFallbackWindow; // Set on the "Debug##Default" window. bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHovered; // Current border being hovered for resize (-1: none, otherwise 0-3) 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 BeginCountPreviousFrame; // Number of Begin() during the previous frame short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. ImGuiDir AutoPosLastDirection; ImS8 AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only ImS8 DisableInputsFrames; // Disable window interactions for N frames ImGuiWindowBgClickFlags BgClickFlags : 8; // Configure behavior of click+dragging on window bg/void or over items. Default sets by io.ConfigWindowsMoveFromTitleBarOnly. If you use this please report in #3379. ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. ImGuiCond SetWindowDockAllowFlags : 8; // store acceptable condition flags for SetNextWindowDock() use. ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right. ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure) ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. // The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer. // The main 'OuterRect', omitted as a field, is window->Rect(). ImRect OuterRectClipped; // == Window->Rect() just after setup in Begin(). == window->Rect() for root window. ImRect InnerRect; // Inner rectangle (omit title bar, menu bar, scroll bar) ImRect InnerClipRect; // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect. ImRect WorkRect; // Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward). ImRect ParentWorkRect; // Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. // FIXME-WORKRECT: Could be a stack? ImRect ClipRect; // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back(). ImRect ContentRegionRect; // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on. ImVec2ih HitTestHoleSize; // Define an optional rectangular hole where mouse will pass-through the window. ImVec2ih HitTestHoleOffset; int LastFrameActive; // Last frame number the window was Active. int LastFrameJustFocused; // Last frame number the window was made Focused. float LastTimeActive; // Last timestamp the window was Active (using float as we don't need high precision there) ImGuiStorage StateStorage; ImVector ColumnsStorage; float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale() float FontWindowScaleParents; float FontRefSize; // This is a copy of window->CalcFontSize() at the time of Begin(), trying to phase out CalcFontSize() especially as it may be called on non-current window. int SettingsOffset; // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back) ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. ImGuiWindow* ParentWindowInBeginStack; ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowDockTree; // Point to ourself or first ancestor that is not a child window. Cross through dock nodes. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honored (e.g. Tool linked to Document) 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[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space ImVec2 NavPreferredScoringPosRel[ImGuiNavLayer_COUNT]; // Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX. ImGuiID NavRootFocusScopeId; // Focus Scope ID at the time of Begin() int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy int MemoryDrawListVtxCapacity; bool MemoryCompacted; // Set when window extraneous data have been garbage collected // Docking bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). bool DockNodeIsVisible :1; bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected? bool DockTabWantClose :1; short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. ImGuiWindowDockStyle DockStyle; ImGuiDockNode* DockNode; // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden. ImGuiDockNode* DockNodeAsHost; // Which node are we owning (for parent windows) ImGuiID DockId; // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more public: ImGuiWindow(ImGuiContext* context, const char* name); ~ImGuiWindow(); ImGuiID GetID(const char* str, const char* str_end = NULL); ImGuiID GetID(const void* ptr); ImGuiID GetID(int n); ImGuiID GetIDFromPos(const ImVec2& p_abs); ImGuiID GetIDFromRectangle(const ImRect& r_abs); // We don't use g.FontSize because the window may be != g.CurrentWindow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); } // [OBSOLETE] ImGuiWindow::CalcFontSize() was removed in 1.92.0 because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window. //float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontSizeBase * FontWindowScale * FontDpiScale * FontWindowScaleParents; }; //----------------------------------------------------------------------------- // [SECTION] Tab bar, Tab item support //----------------------------------------------------------------------------- // Extend ImGuiTabBarFlags_ 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 }; // Extend ImGuiTabItemFlags_ enum ImGuiTabItemFlagsPrivate_ { ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) ImGuiTabItemFlags_Button = 1 << 21, // Used by TabItemButton, change the tab item behavior to mimic a button ImGuiTabItemFlags_Invisible = 1 << 22, // To reserve space e.g. with ImGuiTabItemFlags_Leading ImGuiTabItemFlags_Unsorted = 1 << 23, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. }; // Storage for one active tab item (sizeof() 48 bytes) struct ImGuiTabItem { ImGuiID ID; ImGuiTabItemFlags Flags; ImGuiWindow* Window; // When TabItem is part of a DockNode's TabBar, we hold on to a window. int LastFrameVisible; int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance float Offset; // Position relative to beginning of tab bar float Width; // Width currently displayed float ContentWidth; // Width of label + padding, stored during BeginTabItem() call (misnamed as "Content" would normally imply width of label only) float RequestedWidth; // Width optionally requested by caller, -1.0f is unused ImS32 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames ImS16 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable ImS16 IndexDuringLayout; // Index only used during TabBarLayout(). Tabs gets reordered so 'Tabs[n].IndexDuringLayout == n' but may mismatch during additions. bool WantClose; // Marked as closed by SetTabItemClosed() ImGuiTabItem() { memset((void*)this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } }; // Storage for a tab bar (sizeof() 160 bytes) struct IMGUI_API ImGuiTabBar { ImGuiWindow* Window; ImVector Tabs; ImGuiTabBarFlags Flags; ImGuiID ID; // Zero for tab-bars used by docking ImGuiID SelectedTabId; // Selected tab/window ImGuiID NextSelectedTabId; // Next selected tab/window. Will also trigger a scrolling animation ImGuiID NextScrollToTabId; ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for Ctrl+Tab preview) int CurrFrameVisible; int PrevFrameVisible; ImRect BarRect; float BarRectPrevWidth; // Backup of previous width. When width change we enforce keep horizontal scroll on focused tab. float CurrTabsContentsHeight; float PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar float WidthAllTabs; // Actual width of all tabs (locked during layout) float WidthAllTabsIdeal; // Ideal width if all tabs were visible and not clipped float ScrollingAnim; float ScrollingTarget; float ScrollingTargetDistToVisibility; float ScrollingSpeed; float ScrollingRectMinX; float ScrollingRectMaxX; float SeparatorMinX; float SeparatorMaxX; ImGuiID ReorderRequestTabId; ImS16 ReorderRequestOffset; ImS8 BeginCount; bool WantLayout; bool VisibleTabWasSubmitted; bool TabsAddedNew; // Set to true when a new tab item or button has been added to the tab bar during last frame bool ScrollButtonEnabled; ImS16 TabsActiveCount; // Number of tabs submitted this frame. ImS16 LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem() float ItemSpacingY; ImVec2 FramePadding; // style.FramePadding locked at the time of BeginTabBar() ImVec2 BackupCursorPos; ImGuiTextBuffer TabsNames; // For non-docking tab bar we re-append names in a contiguous buffer. ImGuiTabBar(); }; //----------------------------------------------------------------------------- // [SECTION] Table support //----------------------------------------------------------------------------- #define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color. #define IMGUI_TABLE_MAX_COLUMNS 512 // Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx. // [Internal] sizeof() ~ 112 // We use the terminology "Enabled" to refer to a column that is not Hidden by user/api. // We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping. // This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped". struct ImGuiTableColumn { ImGuiTableColumnFlags Flags; // Flags after some patching (not directly same as provided by user). See ImGuiTableColumnFlags_ float WidthGiven; // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space. float MinX; // Absolute positions float MaxX; float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout() float WidthAuto; // Automatic width float WidthMax; // Maximum width (FIXME: overwritten by each instance) float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially. float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_). ImRect ClipRect; // Clipping rectangle for the column ImGuiID UserID; // Optional, value passed to TableSetupColumn() float WorkMinX; // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column float WorkMaxX; // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2) float ItemWidth; // Current item width for the column, preserved across rows float ContentMaxXFrozen; // Contents maximum position for frozen rows (apart from headers), from which we can infer content width. float ContentMaxXUnfrozen; float ContentMaxXHeadersUsed; // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls float ContentMaxXHeadersIdeal; ImS16 NameOffset; // Offset into parent ColumnsNames[] ImGuiTableColumnIdx DisplayOrder; // Index within Table's IndexToDisplayOrder[] (column may be reordered by users) ImGuiTableColumnIdx IndexWithinEnabledSet; // Index within enabled/visible set (<= IndexToDisplayOrder) ImGuiTableColumnIdx PrevEnabledColumn; // Index of prev enabled/visible column within Columns[], -1 if first enabled/visible column ImGuiTableColumnIdx NextEnabledColumn; // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column ImGuiTableColumnIdx SortOrder; // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort ImGuiTableDrawChannelIdx DrawChannelCurrent; // Index within DrawSplitter.Channels[] ImGuiTableDrawChannelIdx DrawChannelFrozen; // Draw channels for frozen rows (often headers) ImGuiTableDrawChannelIdx DrawChannelUnfrozen; // Draw channels for unfrozen rows bool IsEnabled; // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0 bool IsUserEnabled; // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling). bool IsUserEnabledNextFrame; bool IsVisibleX; // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled). bool IsVisibleY; bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not. bool IsSkipItems; // Do we want item submissions to this column to be completely ignored (no layout will happen). bool IsPreserveWidthAuto; ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte ImU8 AutoFitQueue; // Queue of 8 values for the next 8 frames to request auto-fit ImU8 CannotSkipItemsQueue; // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem ImU8 SortDirection : 2; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending ImU8 SortDirectionsAvailCount : 2; // Number of available sort directions (0 to 3) ImU8 SortDirectionsAvailMask : 4; // Mask of available sort directions (1-bit each) ImU8 SortDirectionsAvailList; // Ordered list of available sort directions (2-bits each, total 8-bits) ImGuiTableColumn() { memset((void*)this, 0, sizeof(*this)); StretchWeight = WidthRequest = -1.0f; NameOffset = -1; DisplayOrder = IndexWithinEnabledSet = -1; PrevEnabledColumn = NextEnabledColumn = -1; SortOrder = -1; SortDirection = ImGuiSortDirection_None; DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1; } }; // Transient cell data stored per row. // sizeof() ~ 6 bytes struct ImGuiTableCellData { ImU32 BgColor; // Actual color ImGuiTableColumnIdx Column; // Column number }; // Parameters for TableAngledHeadersRowEx() // This may end up being refactored for more general purpose. // sizeof() ~ 12 bytes struct ImGuiTableHeaderData { ImGuiTableColumnIdx Index; // Column index ImU32 TextColor; ImU32 BgColor0; ImU32 BgColor1; }; // Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?) // sizeof() ~ 24 bytes struct ImGuiTableInstanceData { ImGuiID TableInstanceID; float LastOuterHeight; // Outer height from last frame float LastTopHeadersRowHeight; // Height of first consecutive header rows from last frame (FIXME: this is used assuming consecutive headers are in same frozen set) float LastFrozenHeight; // Height of frozen section from last frame int HoveredRowLast; // Index of row which was hovered last frame. int HoveredRowNext; // Index of row hovered this frame, set after encountering it. ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastTopHeadersRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; } }; // sizeof() ~ 592 bytes + heap allocs described in TableBeginInitMemory() struct IMGUI_API ImGuiTable { ImGuiID ID; ImGuiTableFlags Flags; void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[], and RowCellData[] ImGuiTableTempData* TempData; // Transient data while table is active. Point within g.CurrentTableStack[] ImSpan Columns; // Point within RawData[] ImSpan DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) ImSpan RowCellData; // Point within RawData[]. Store cells background requests for current row. ImBitArrayPtr EnabledMaskByDisplayOrder; // Column DisplayOrder -> IsEnabled map ImBitArrayPtr EnabledMaskByIndex; // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data ImBitArrayPtr VisibleMaskByIndex; // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect) ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order) int SettingsOffset; // Offset in g.SettingsTables int LastFrameActive; int ColumnsCount; // Number of columns declared in BeginTable() int CurrentRow; int CurrentColumn; ImS16 InstanceCurrent; // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple tables with the same ID are multiple tables, they are just synced. ImS16 InstanceInteracted; // Mark which instance (generally 0) of the same ID is being interacted with float RowPosY1; float RowPosY2; float RowMinHeight; // Height submitted to TableNextRow() float RowCellPaddingY; // Top and bottom padding. Reloaded during row change. float RowTextBaseline; float RowIndentOffsetX; ImGuiTableRowFlags RowFlags : 16; // Current row flags, see ImGuiTableRowFlags_ ImGuiTableRowFlags LastRowFlags : 16; int RowBgColorCounter; // Counter for alternating background colors (can be fast-forwarded by e.g clipper), not same as CurrentRow because header rows typically don't increase this. ImU32 RowBgColor[2]; // Background color override for current row. ImU32 BorderColorStrong; ImU32 BorderColorLight; float BorderX1; float BorderX2; float HostIndentX; float MinColumnWidth; float OuterPaddingX; float CellPaddingX; // Padding from each borders. Locked in BeginTable()/Layout. float CellSpacingX1; // Spacing between non-bordered cells. Locked in BeginTable()/Layout. float CellSpacingX2; float InnerWidth; // User value passed to BeginTable(), see comments at the top of BeginTable() for details. float ColumnsGivenWidth; // Sum of current column width float ColumnsAutoFitWidth; // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window float ColumnsStretchSumWeights; // Sum of weight of all enabled stretching columns float ResizedColumnNextWidth; float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table. float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. float AngledHeadersHeight; // Set by TableAngledHeadersRow(), used in TableUpdateLayout() float AngledHeadersSlope; // Set by TableAngledHeadersRow(), used in TableUpdateLayout() ImRect OuterRect; // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable(). ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is " ImRect WorkRect; ImRect InnerClipRect; ImRect BgClipRect; // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. ImRect HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() ImGuiWindow* OuterWindow; // Parent window for the table ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly ImGuiTableInstanceData InstanceDataFirst; ImVector InstanceDataExtra; // FIXME-OPT: Using a small-vector pattern would be good. ImGuiTableColumnSortSpecs SortSpecsSingle; ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns using fixed width (<= ColumnsCount) ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() ImGuiTableColumnIdx AngledHeadersCount; // Count columns with angled headers ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column! ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing). ImGuiTableColumnIdx HighlightColumnHeader; // Index of column which should be highlighted. ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit. ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0. ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. ImGuiTableColumnIdx HeldHeaderColumn; // Index of column header being held. ImGuiTableColumnIdx ReorderColumn; // Index of column being reordered. (not cleared) ImGuiTableColumnIdx ReorderColumnDir; // -1 or +1 ImGuiTableColumnIdx LeftMostEnabledColumn; // Index of left-most non-hidden column. ImGuiTableColumnIdx RightMostEnabledColumn; // Index of right-most non-hidden column. ImGuiTableColumnIdx LeftMostStretchedColumn; // Index of left-most stretched column. ImGuiTableColumnIdx RightMostStretchedColumn; // Index of right-most stretched column. ImGuiTableColumnIdx ContextPopupColumn; // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot ImGuiTableColumnIdx FreezeRowsRequest; // Requested frozen rows count ImGuiTableColumnIdx FreezeRowsCount; // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset) ImGuiTableColumnIdx FreezeColumnsRequest; // Requested frozen columns count ImGuiTableColumnIdx FreezeColumnsCount; // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset) ImGuiTableColumnIdx RowCellDataCurrent; // Index of current RowCellData[] entry in current row ImGuiTableDrawChannelIdx DummyDrawChannel; // Redirect non-visible columns here. ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[] ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen; ImS8 NavLayer; // ImGuiNavLayer at the time of BeginTable(). bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row. bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow(). bool IsInitializing; bool IsSortSpecsDirty; bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag. bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted). bool DisableDefaultContextMenu; // Disable default context menu. You may submit your own using TableBeginContextMenuPopup()/EndPopup() bool IsSettingsRequestLoad; bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSettings data. bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1) bool IsResetAllRequest; bool IsResetDisplayOrderRequest; bool IsUnfrozenRows; // Set when we got past the frozen row. bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() bool IsActiveIdAliveBeforeTable; bool IsActiveIdInTable; bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame. bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous. bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis ImGuiTable() { memset((void*)this, 0, sizeof(*this)); LastFrameActive = -1; } ~ImGuiTable() { IM_FREE(RawData); } }; // Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). // - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. // - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. // FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs. // sizeof() ~ 136 bytes. struct IMGUI_API ImGuiTableTempData { ImGuiID WindowID; // Shortcut to g.Tables[TableIndex]->OuterWindow->ID. int TableIndex; // Index in g.Tables.Buf[] pool float LastTimeActive; // Last timestamp this structure was used float AngledHeadersExtraWidth; // Used in EndTable() ImVector AngledHeadersRequests; // Used in TableAngledHeadersRow() ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() ImDrawListSplitter DrawSplitter; ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() ImGuiTableTempData() { memset((void*)this, 0, sizeof(*this)); LastTimeActive = -1.0f; } }; // sizeof() ~ 16 struct ImGuiTableColumnSettings { float WidthOrWeight; ImGuiID UserID; ImGuiTableColumnIdx Index; ImGuiTableColumnIdx DisplayOrder; ImGuiTableColumnIdx SortOrder; ImU8 SortDirection : 2; ImS8 IsEnabled : 2; // "Visible" in ini file ImU8 IsStretch : 1; ImGuiTableColumnSettings() { WidthOrWeight = 0.0f; UserID = 0; Index = -1; DisplayOrder = SortOrder = -1; SortDirection = ImGuiSortDirection_None; IsEnabled = -1; IsStretch = 0; } }; // This is designed to be stored in a single ImChunkStream (1 header followed by N ImGuiTableColumnSettings, etc.) struct ImGuiTableSettings { ImGuiID ID; // Set to 0 to invalidate/delete the setting ImGuiTableFlags SaveFlags; // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..) float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. ImGuiTableColumnIdx ColumnsCount; ImGuiTableColumnIdx ColumnsCountMax; // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) ImGuiTableSettings() { memset((void*)this, 0, sizeof(*this)); } ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } }; //----------------------------------------------------------------------------- // [SECTION] ImGui internal API // No guarantee of forward compatibility here! //----------------------------------------------------------------------------- namespace ImGui { // Windows // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) // If this ever crashes because g.CurrentWindow is NULL, it means that either: // - ImGui::NewFrame() has never been called, which is illegal. // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. IMGUI_API ImGuiIO& GetIO(ImGuiContext* ctx); IMGUI_API ImGuiPlatformIO& GetPlatformIO(ImGuiContext* ctx); inline float GetScale() { ImGuiContext& g = *GImGui; return g.Style._MainScale; } // FIXME-DPI: I don't want to formalize this just yet. Because reasons. Please don't use. 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 UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API void UpdateWindowSkipRefresh(ImGuiWindow* window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy); IMGUI_API bool IsWindowInBeginStack(ImGuiWindow* window); IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); IMGUI_API void SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window); inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } // You may also use SetNextWindowClass()'s FocusRouteParentWindowId field. inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); } inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0); IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window); IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window); IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); // Windows: Idle, Refresh Policies [EXPERIMENTAL] IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags); // Fonts, drawing IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL: DO NOT USE YET. IMGUI_API void UnregisterUserTexture(ImTextureData* tex); IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas); IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas); IMGUI_API void SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling); IMGUI_API void UpdateCurrentFontSize(float restore_font_size_after_scaling); IMGUI_API void SetFontRasterizerDensity(float rasterizer_density); inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; } inline float GetRoundedFontSize(float size) { return IM_ROUND(size); } IMGUI_API ImFont* GetDefaultFont(); IMGUI_API void PushPasswordFont(); IMGUI_API void PopPasswordFont(); inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { return GetForegroundDrawList(window->Viewport); } IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list); // Init IMGUI_API void Initialize(); IMGUI_API void Shutdown(); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). // Context name & generic context hooks IMGUI_API void SetContextName(ImGuiContext* ctx, const char* name); IMGUI_API ImGuiID AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook); IMGUI_API void RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_to_remove); IMGUI_API void CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType type); // NewFrame IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); IMGUI_API void UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos); IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock); IMGUI_API void StopMouseMovingWindow(); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); // Viewports IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); IMGUI_API void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport); IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); IMGUI_API const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); IMGUI_API ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos); // Settings IMGUI_API void MarkIniSettingsDirty(); IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); IMGUI_API void ClearIniSettings(); IMGUI_API void AddSettingsHandler(const ImGuiSettingsHandler* handler); IMGUI_API void RemoveSettingsHandler(const char* type_name); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); // Settings - Windows IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name); IMGUI_API ImGuiWindowSettings* FindWindowSettingsByID(ImGuiID id); IMGUI_API ImGuiWindowSettings* FindWindowSettingsByWindow(ImGuiWindow* window); IMGUI_API void ClearWindowSettings(const char* name); // Localization IMGUI_API void LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count); inline const char* LocalizeGetMsg(ImGuiLocKey key) { ImGuiContext& g = *GImGui; const char* msg = g.LocalizationTable[key]; return msg ? msg : "*Missing Text*"; } // Scrolling IMGUI_API void SetScrollX(ImGuiWindow* window, float scroll_x); IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio); // Early work-in-progress API (ScrollToItem() will become public) IMGUI_API void ScrollToItem(ImGuiScrollFlags flags = 0); IMGUI_API void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); IMGUI_API ImVec2 ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); //#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline void ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); } //#endif // Basic Accessors inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); IMGUI_API void ClearActiveID(); IMGUI_API ImGuiID GetHoveredID(); IMGUI_API void SetHoveredID(ImGuiID id); IMGUI_API void KeepAliveID(ImGuiID id); IMGUI_API void MarkItemEdited(ImGuiID id); // Mark data associated to given item as "edited", used by IsItemDeactivatedAfterEdit() function. IMGUI_API void PushOverrideID(ImGuiID id); // Push given value as-is at the top of the ID stack (whereas PushID combines old and new hashes) IMGUI_API ImGuiID GetIDWithSeed(const char* str_id_begin, const char* str_id_end, ImGuiID seed); IMGUI_API ImGuiID GetIDWithSeed(int n, ImGuiID seed); // Basic Helpers for widget code IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min. IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags); IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min); IMGUI_API void CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end); // Parameter stacks (shared) IMGUI_API const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx); IMGUI_API void BeginDisabledOverrideReenable(); IMGUI_API void EndDisabledOverrideReenable(); // Logging/Capture IMGUI_API void LogBegin(ImGuiLogFlags flags, 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 IMGUI_API void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL); IMGUI_API void LogSetNextTextDecoration(const char* prefix, const char* suffix); // Childs IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags); // Popups, Modals IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags); IMGUI_API bool BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags); IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsExceptModals(); IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); IMGUI_API ImGuiWindow* FindBlockingModal(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); IMGUI_API ImGuiMouseButton GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags); IMGUI_API bool IsPopupOpenRequestForItem(ImGuiPopupFlags flags, ImGuiID id); IMGUI_API bool IsPopupOpenRequestForWindow(ImGuiPopupFlags flags); // Tooltips IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); IMGUI_API bool BeginTooltipHidden(); // Menus IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); // Combos IMGUI_API bool BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags); IMGUI_API bool BeginComboPreview(); IMGUI_API void EndComboPreview(); // Keyboard/Gamepad Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); IMGUI_API void NavInitRequestApplyResult(); IMGUI_API bool NavMoveRequestButNoResultYet(); IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); IMGUI_API void NavHighlightActivated(ImGuiID id); IMGUI_API void NavClearPreferredPosForAxis(ImGuiAxis axis); IMGUI_API void SetNavCursorVisibleAfterMove(); IMGUI_API void NavUpdateCurrentWindowIsScrollPushableX(); IMGUI_API void SetNavWindow(ImGuiWindow* window); IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); IMGUI_API void SetNavFocusScope(ImGuiID focus_scope_id); // Focus/Activation // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones. IMGUI_API void FocusItem(); // Focus last item (no selection/activation). IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. Was called 'ActivateItem()' before 1.89.7. // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } inline bool IsNamedKeyOrMod(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super; } inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } inline bool IsKeyboardKey(ImGuiKey key) { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; } inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } inline bool IsLRModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; } ImGuiKeyChord FixupKeyChord(ImGuiKeyChord key_chord); inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key) { if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl; if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift; if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt; if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper; return key; } IMGUI_API ImGuiKeyData* GetKeyData(ImGuiContext* ctx, ImGuiKey key); inline ImGuiKeyData* GetKeyData(ImGuiKey key) { ImGuiContext& g = *GImGui; return GetKeyData(&g, key); } IMGUI_API const char* GetKeyChordName(ImGuiKeyChord key_chord); inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); IMGUI_API ImVec2 GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down); IMGUI_API float GetNavTweakPressedAmount(ImGuiAxis axis); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate); IMGUI_API void TeleportMousePos(const ImVec2& pos); IMGUI_API void SetActiveIdUsingAllKeyboardKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } // [EXPERIMENTAL] Low-Level: Key/Input Ownership // - The idea is that instead of "eating" a given input, we can link to an owner id. // - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead). // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID. // - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0). // - Input ownership is automatically released on the frame after a key is released. Therefore: // - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case). // - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover). // - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state. // - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step. // Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved. IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership // - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag. // - Expected to be later promoted to public API, the prototypes are designed to replace existing ones (since owner_id can default to Any == 0) // - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'. // Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API. IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id); IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requires explicit ImGuiInputFlags_Repeat. IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id); IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id = 0); IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id); IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0); IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id); IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id); // Shortcut Testing & Routing // - Set Shortcut() and SetNextItemShortcut() in imgui.h // - When a policy (except for ImGuiInputFlags_RouteAlways *) is set, Shortcut() will register itself with SetShortcutRouting(), // allowing the system to decide where to route the input among other route-aware calls. // (* using ImGuiInputFlags_RouteAlways is roughly equivalent to calling IsKeyChordPressed(key) and bypassing route registration and check) // - When using one of the routing option: // - The default route is ImGuiInputFlags_RouteFocused (accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.) // - Routes are requested given a chord (key + modifiers) and a routing policy. // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame. // - Each route may be granted to a single owner. When multiple requests are made we have policies to select the winning route (e.g. deep most window). // - Multiple read sites may use the same owner id can all access the granted route. // - When owner_id is 0 we use the current Focus Scope ID as a owner ID in order to identify our location. // - You can chain two unrelated windows in the focus stack using SetWindowParentWindowForFocusRoute() // e.g. if you have a tool window associated to a document, and you want document shortcuts to run when the tool is focused. IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); // owner_id needs to be explicit and cannot be 0 IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id); IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord); // Docking // (some functions are only declared in imgui.cpp, see Docking section) IMGUI_API void DockContextInitialize(ImGuiContext* ctx); IMGUI_API void DockContextShutdown(ImGuiContext* ctx); IMGUI_API void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs); // Use root_id==0 to clear all IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); IMGUI_API void DockContextEndFrame(ImGuiContext* ctx); IMGUI_API ImGuiID DockContextGenNodeID(ImGuiContext* ctx); IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); IMGUI_API void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true); IMGUI_API void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); IMGUI_API ImGuiDockNode*DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); IMGUI_API void DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar); IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node); IMGUI_API void DockNodeEndAmendTabBar(); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } inline bool DockNodeIsInHierarchyOf(ImGuiDockNode* node, ImGuiDockNode* parent) { while (node) { if (node == parent) return true; node = node->ParentNode; } return false; } inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } inline ImGuiID DockNodeGetWindowMenuButtonId(const ImGuiDockNode* node) { return ImHashStr("#COLLAPSE", 0, node->ID); } inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginDockableDragDropSource(ImGuiWindow* window); IMGUI_API void BeginDockableDragDropTarget(ImGuiWindow* window); IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); // Docking - Builder function needs to be generally called before the node is used/submitted. // - The DockBuilderXXX functions are designed to _eventually_ become a public API, but it is too early to expose it and guarantee stability. // - Do not hold on ImGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame. // - To create a DockSpace() node, make sure to set the ImGuiDockNodeFlags_DockSpace flag when calling DockBuilderAddNode(). // You can create dockspace nodes (attached to a window) _or_ floating nodes (carry its own window) with this API. // - DockBuilderSplitNode() create 2 child nodes within 1 node. The initial node becomes a parent node. // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure // to call DockBuilderSetNodeSize() beforehand. If you don't, the resulting split sizes may not be reliable. // - Call DockBuilderFinish() after you are done. IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id = 0, ImGuiDockNodeFlags flags = 0); IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_settings_refs = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the remaining root node (node_id). IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir); // Create 2 child nodes in this parent node. IMGUI_API void DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); IMGUI_API void DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name); IMGUI_API void DockBuilderFinish(ImGuiID node_id); // [EXPERIMENTAL] Focus Scope // This is generally used to identify a unique input location (for e.g. a selection set) // There is one per window (automatically set in Begin), but: // - Selection patterns generally need to react (e.g. clear a selection) when landing on one item of the set. // So in order to identify a set multiple lists in same window may each need a focus scope. // If you imagine an hypothetical BeginSelectionGroup()/EndSelectionGroup() api, it would likely call PushFocusScope()/EndFocusScope() // - Shortcut routing also use focus scope as a default location identifier if an owner is not provided. // We don't use the ID Stack for this as it is common to want them separate. IMGUI_API void PushFocusScope(ImGuiID id); IMGUI_API void PopFocusScope(); inline ImGuiID GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; } // Focus scope we are outputting into, set by PushFocusScope() // Drag and Drop IMGUI_API bool IsDragDropActive(); IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API bool BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb = NULL); IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); IMGUI_API void RenderDragDropTargetRectForItem(const ImRect& bb); IMGUI_API void RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb); // Typing-Select API // (provide Windows Explorer style "select items by typing partial name" + "cycle through items by typing same letter" feature) // (this is currently not documented nor used by main library, but should work. See "widgets_typingselect" in imgui_test_suite for usage code. Please let us know if you use this!) IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None); IMGUI_API int TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); IMGUI_API int TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); // Box-Select API IMGUI_API bool BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags); IMGUI_API void EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags); // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); IMGUI_API void MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected); IMGUI_API void MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item); inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } inline ImGuiMultiSelectState* GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); } // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). IMGUI_API void EndColumns(); // close columns IMGUI_API void PushColumnClipRect(int column_index); IMGUI_API void PushColumnsBackground(); IMGUI_API void PopColumnsBackground(); IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count); IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id); IMGUI_API float GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm); IMGUI_API float GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset); // Tables: Candidates for public API IMGUI_API void TableOpenContextMenu(int column_n = -1); IMGUI_API void TableSetColumnWidth(int column_n, float width); IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet. IMGUI_API float TableGetHeaderRowHeight(); IMGUI_API float TableGetHeaderAngledMaxLabelWidth(); IMGUI_API void TablePushBackgroundChannel(); IMGUI_API void TablePopBackgroundChannel(); IMGUI_API void TablePushColumnChannel(int column_n); IMGUI_API void TablePopColumnChannel(); IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count); // Tables: Internals inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } IMGUI_API ImGuiTable* TableFindByID(ImGuiID id); IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f); IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count); IMGUI_API void TableBeginApplyRequests(ImGuiTable* table); IMGUI_API void TableSetupDrawChannels(ImGuiTable* table); IMGUI_API void TableUpdateLayout(ImGuiTable* table); IMGUI_API void TableUpdateBorders(ImGuiTable* table); IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table); IMGUI_API void TableDrawBorders(ImGuiTable* table); IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display); IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table); IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } inline ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no) { return TableGetInstanceData(table, instance_no)->TableInstanceID; } IMGUI_API void TableFixDisplayOrder(ImGuiTable* table); IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); IMGUI_API void TableSortSpecsBuild(ImGuiTable* table); IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column); IMGUI_API void TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column); IMGUI_API float TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column); IMGUI_API void TableBeginRow(ImGuiTable* table); IMGUI_API void TableEndRow(ImGuiTable* table); IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n); IMGUI_API void TableEndCell(ImGuiTable* table); IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n); IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); IMGUI_API ImGuiID TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0); IMGUI_API float TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); IMGUI_API void TableGcCompactSettings(); // Tables: Settings IMGUI_API void TableLoadSettings(ImGuiTable* table); IMGUI_API void TableSaveSettings(ImGuiTable* table); IMGUI_API void TableResetSettings(ImGuiTable* table); IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table); IMGUI_API void TableSettingsAddSettingsHandler(); IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count); IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id); // Tab Bars inline ImGuiTabBar* GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; } IMGUI_API ImGuiTabBar* TabBarFindByID(ImGuiID id); IMGUI_API void TabBarRemove(ImGuiTabBar* tab_bar); IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order); IMGUI_API ImGuiTabItem* TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar); IMGUI_API ImGuiTabItem* TabBarGetCurrentTab(ImGuiTabBar* tab_bar); inline int TabBarGetTabOrder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { return tab_bar->Tabs.index_from_ptr(tab); } IMGUI_API const char* TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name); IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset); IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); IMGUI_API void TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker); IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); IMGUI_API void TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped); // 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 RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); IMGUI_API void RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None); // Navigation highlight #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4 #endif IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. IMGUI_API void RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); // Render helpers (those functions don't access any ImGui state!) IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); IMGUI_API void RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col); IMGUI_API void RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding); IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding); IMGUI_API ImDrawFlags CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold); // Widgets: Text IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API void TextAligned(float align_x, float size_x, const char* fmt, ...); // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024) IMGUI_API void TextAlignedV(float align_x, float size_x, const char* fmt, va_list args); // Widgets IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f); IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width); IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); // Widgets: Window Decorations IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node); IMGUI_API void Scrollbar(ImGuiAxis axis); IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags draw_rounding_flags = 0); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners IMGUI_API ImGuiID GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir); IMGUI_API void ExtendHitBoxWhenNearViewportEdge(ImGuiWindow* window, ImRect* bb, float threshold, ImGuiAxis axis); // 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* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, 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, ImU32 bg_col = 0); // Widgets: Tree Nodes IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API void TreeNodeDrawLineToChildNode(const ImVec2& target_pos); IMGUI_API void TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data); IMGUI_API void TreePushOverrideID(ImGuiID id); IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open); IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. // 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 float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float logarithmic_zero_epsilon, float zero_deadzone_size); template IMGUI_API T ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, float logarithmic_zero_epsilon, float zero_deadzone_size); template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags 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, ImGuiSliderFlags flags, ImRect* out_grab_bb); template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); template IMGUI_API bool CheckboxFlagsT(const char* label, T* flags, T flags_value); // Data type helpers IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format); IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2); IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL); IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2); IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max); IMGUI_API bool DataTypeIsZero(ImGuiDataType data_type, const void* p_data); // InputText 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 void InputTextDeactivateHook(ImGuiID id); IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return g.ActiveId == id && g.TempInputId == id; } inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active IMGUI_API void SetNextItemRefVal(ImGuiDataType data_type, void* p_data); inline bool IsItemActiveAsInputText() { ImGuiContext& g = *GImGui; return g.ActiveId != 0 && g.ActiveId == g.LastItemData.ID && g.InputTextState.ID == g.LastItemData.ID; } // This may be useful to apply workaround that a based on distinguish whenever an item is active as a text input field. // 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); inline void SetNextItemColorMarker(ImU32 col) { ImGuiContext& g = *GImGui; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasColorMarker; g.NextItemData.ColorMarker = col; } // Plot IMGUI_API int 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, const ImVec2& size_arg); // 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); IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); IMGUI_API void ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out); // Garbage collection IMGUI_API void GcCompactTransientMiscBuffers(); IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); // Error handling, State Recovery IMGUI_API bool ErrorLog(const char* msg); IMGUI_API void ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out); IMGUI_API void ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in); IMGUI_API void ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in); IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); IMGUI_API void ErrorCheckEndFrameFinalizeErrorTooltip(); IMGUI_API bool BeginErrorTooltip(); IMGUI_API void EndErrorTooltip(); // Demo Doc Marker for e.g. imgui_explorer IMGUI_API void DemoMarker(const char* file, int line, const char* section); // Debug Tools IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free IMGUI_API void DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end); IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time! IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time! IMGUI_API void DebugLocateItemResolveWithLastItem(); IMGUI_API void DebugBreakClearData(); IMGUI_API bool DebugBreakButton(const char* label, const char* description_of_location); IMGUI_API void DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location); IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); IMGUI_API ImU64 DebugTextureIDToU64(ImTextureID tex_id); IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); IMGUI_API void DebugNodeDockNode(ImGuiDockNode* node, const char* label); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label); IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); IMGUI_API void DebugNodeFont(ImFont* font); IMGUI_API void DebugNodeFontGlyphsForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask); IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph); IMGUI_API void DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect = NULL); // ID used to facilitate persisting the "current" texture. IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); IMGUI_API void DebugNodeTable(ImGuiTable* table); IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings); IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state); IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state); IMGUI_API void DebugNodeMultiSelectState(ImGuiMultiSelectState* state); IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); IMGUI_API void DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx); IMGUI_API void DebugRenderKeyboardPreview(ImDrawList* draw_list); IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); // Obsolete functions #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //inline void SetItemUsingMouseWheel() { SetItemKeyOwner(ImGuiKey_MouseWheelY); } // Changed in 1.89 //inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89 //inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity! // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister(): // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0' // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))' //inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() //inline void FocusableItemUnregister(ImGuiWindow* window) // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem #endif } // namespace ImGui //----------------------------------------------------------------------------- // [SECTION] ImFontLoader //----------------------------------------------------------------------------- // Hooks and storage for a given font backend. // This structure is likely to evolve as we add support for incremental atlas updates. // Conceptually this could be public, but API is still going to be evolve. struct ImFontLoader { const char* Name; bool (*LoaderInit)(ImFontAtlas* atlas); void (*LoaderShutdown)(ImFontAtlas* atlas); bool (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src); void (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src); bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint); bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x); // Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations. // FIXME: At this point the two other types of buffers may be managed by core to be consistent? size_t FontBakedSrcLoaderDataSize; ImFontLoader() { memset((void*)this, 0, sizeof(*this)); } }; #ifdef IMGUI_ENABLE_STB_TRUETYPE IMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype(); #endif #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92.0] The types are not actually compatible but we provide this as a compile-time error report helper. #endif //----------------------------------------------------------------------------- // [SECTION] ImFontAtlas internal API //----------------------------------------------------------------------------- #define IMGUI_FONT_SIZE_MAX (512.0f) #define IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE (128.0f) // Helpers: ImTextureRef ==/!= operators provided as convenience // (note that _TexID and _TexData are never set simultaneously) inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID == rhs._TexID && lhs._TexData == rhs._TexData; } inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; } // Refer to ImFontAtlasPackGetRect() to better understand how this works. #define ImFontAtlasRectId_IndexMask_ (0x0007FFFF) // 20-bits signed: index to access builder->RectsIndex[]. #define ImFontAtlasRectId_GenerationMask_ (0x3FF00000) // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers. #define ImFontAtlasRectId_GenerationShift_ (20) inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_IndexMask_); } inline unsigned int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (unsigned int)(id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; } inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx >= 0 && index_idx <= ImFontAtlasRectId_IndexMask_ && gen_idx <= (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); } // Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles) // User are returned ImFontAtlasRectId values which are meant to be persistent. // We handle this with an indirection. While Rects[] may be in theory shuffled, compacted etc., RectsIndex[] cannot it is keyed by ImFontAtlasRectId. // RectsIndex[] is used both as an index into Rects[] and an index into itself. This is basically a free-list. See ImFontAtlasBuildAllocRectIndexEntry() code. // Having this also makes it easier to e.g. sort rectangles during repack. struct ImFontAtlasRectEntry { int TargetIndex : 20; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list. unsigned int Generation : 10; // Increased each time the entry is reused for a new rectangle. unsigned int IsUsed : 1; }; // Data available to potential texture post-processing functions struct ImFontAtlasPostProcessData { ImFontAtlas* FontAtlas; ImFont* Font; ImFontConfig* FontSrc; ImFontBaked* FontBaked; ImFontGlyph* Glyph; // Pixel data void* Pixels; ImTextureFormat Format; int Pitch; int Width; int Height; }; // We avoid dragging imstb_rectpack.h into public header (partly because binding generators are having issues with it) #ifdef IMGUI_STB_NAMESPACE namespace IMGUI_STB_NAMESPACE { struct stbrp_node; } typedef IMGUI_STB_NAMESPACE::stbrp_node stbrp_node_im; #else struct stbrp_node; typedef stbrp_node stbrp_node_im; #endif struct stbrp_context_opaque { char data[80]; }; // Internal storage for incrementally packing and building a ImFontAtlas struct ImFontAtlasBuilder { stbrp_context_opaque PackContext; // Actually 'stbrp_context' but we don't want to define this in the header file. ImVector PackNodes; ImVector Rects; ImVector RectsIndex; // ImFontAtlasRectId -> index into Rects[] ImVector TempBuffer; // Misc scratch buffer int RectsIndexFreeListStart;// First unused entry int RectsPackedCount; // Number of packed rectangles. int RectsPackedSurface; // Number of packed pixels. Used when compacting to heuristically find the ideal texture size. int RectsDiscardedCount; int RectsDiscardedSurface; int FrameCount; // Current frame count ImVec2i MaxRectSize; // Largest rectangle to pack (de-facto used as a "minimum texture size") ImVec2i MaxRectBounds; // Bottom-right most used pixels bool LockDisableResize; // Disable resizing texture bool PreloadedAllGlyphsRanges; // Set when missing ImGuiBackendFlags_RendererHasTextures features forces atlas to preload everything. // Cache of all ImFontBaked ImStableVector BakedPool; ImGuiStorage BakedMap; // BakedId --> ImFontBaked* int BakedDiscardedCount; // Custom rectangle identifiers ImFontAtlasRectId PackIdMouseCursors; // White pixel + mouse cursors. Also happen to be fallback in case of packing failure. ImFontAtlasRectId PackIdLinesTexData; ImFontAtlasBuilder() { memset((void*)this, 0, sizeof(*this)); FrameCount = -1; RectsIndexFreeListStart = -1; PackIdMouseCursors = PackIdLinesTexData = -1; } }; IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildDestroy(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMain(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader); IMGUI_API void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font); IMGUI_API void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char); IMGUI_API void ImFontAtlasBuildClear(ImFontAtlas* atlas); // Clear output and custom rects IMGUI_API ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h); IMGUI_API void ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h); IMGUI_API void ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_w = -1, int old_h = -1); IMGUI_API void ImFontAtlasTextureCompact(ImFontAtlas* atlas); IMGUI_API ImVec2i ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src); IMGUI_API void ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas); // Legacy IMGUI_API void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v); IMGUI_API void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames); IMGUI_API bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src); IMGUI_API void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src); IMGUI_API void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src); IMGUI_API bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font); // Using FontDestroyOutput/FontInitOutput sequence useful notably if font loader params have changed IMGUI_API void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font); IMGUI_API void ImFontAtlasFontRebuildOutput(ImFontAtlas* atlas, ImFont* font); IMGUI_API void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames); IMGUI_API ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density); IMGUI_API ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density); IMGUI_API ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density); IMGUI_API ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id); IMGUI_API void ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked); IMGUI_API ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph); IMGUI_API void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x); IMGUI_API void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph); IMGUI_API void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch); IMGUI_API void ImFontAtlasPackInit(ImFontAtlas* atlas); IMGUI_API ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry = NULL); IMGUI_API ImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id); IMGUI_API ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id); IMGUI_API void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id); IMGUI_API void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures); IMGUI_API void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data); IMGUI_API void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data); IMGUI_API void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex); IMGUI_API void ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h); IMGUI_API void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data); IMGUI_API void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor); IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col); IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h); IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h); IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format); IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status); IMGUI_API const char* ImTextureDataGetFormatName(ImTextureFormat format); #ifndef IMGUI_DISABLE_DEBUG_TOOLS IMGUI_API void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas); #endif IMGUI_API bool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]); //----------------------------------------------------------------------------- // [SECTION] Test Engine specific hooks (imgui_test_engine) //----------------------------------------------------------------------------- #ifdef IMGUI_ENABLE_TEST_ENGINE extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data); // item_data may be NULL extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id); // In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data); #define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log #else #define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) ((void)0) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) #endif //----------------------------------------------------------------------------- #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif #ifdef _MSC_VER #pragma warning (pop) #endif #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui/include/imstb_rectpack.h ================================================ // [DEAR IMGUI] // This is a slightly modified version of stb_rect_pack.h 1.01. // Grep for [DEAR IMGUI] to find the changes. // // stb_rect_pack.h - v1.01 - public domain - rectangle packing // Sean Barrett 2014 // // Useful for e.g. packing rectangular textures into an atlas. // Does not do rotation. // // Before #including, // // #define STB_RECT_PACK_IMPLEMENTATION // // in the file that you want to have the implementation. // // Not necessarily the awesomest packing method, but better than // the totally naive one in stb_truetype (which is primarily what // this is meant to replace). // // Has only had a few tests run, may have issues. // // More docs to come. // // No memory allocations; uses qsort() and assert() from stdlib. // Can override those by defining STBRP_SORT and STBRP_ASSERT. // // This library currently uses the Skyline Bottom-Left algorithm. // // Please note: better rectangle packers are welcome! Please // implement them to the same API, but with a different init // function. // // Credits // // Library // Sean Barrett // Minor features // Martins Mozeiko // github:IntellectualKitty // // Bugfixes / warning fixes // Jeremy Jaussaud // Fabian Giesen // // Version history: // // 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section // 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles // 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 // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort // 0.05: added STBRP_ASSERT to allow replacing assert // 0.04: fixed minor bug in STBRP_LARGE_RECTS support // 0.01: initial release // // LICENSE // // See end of file for license information. ////////////////////////////////////////////////////////////////////////////// // // INCLUDE SECTION // #ifndef STB_INCLUDE_STB_RECT_PACK_H #define STB_INCLUDE_STB_RECT_PACK_H #define STB_RECT_PACK_VERSION 1 #ifdef STBRP_STATIC #define STBRP_DEF static #else #define STBRP_DEF extern #endif #ifdef __cplusplus extern "C" { #endif typedef struct stbrp_context stbrp_context; typedef struct stbrp_node stbrp_node; typedef struct stbrp_rect stbrp_rect; typedef int stbrp_coord; #define STBRP__MAXVAL 0x7fffffff // Mostly for internal use, but this is the maximum supported coordinate value. STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); // Assign packed locations to rectangles. The rectangles are of type // 'stbrp_rect' defined below, stored in the array 'rects', and there // are 'num_rects' many of them. // // Rectangles which are successfully packed have the 'was_packed' flag // set to a non-zero value and 'x' and 'y' store the minimum location // on each axis (i.e. bottom-left in cartesian coordinates, top-left // if you imagine y increasing downwards). Rectangles which do not fit // have the 'was_packed' flag set to 0. // // You should not try to access the 'rects' array from another thread // while this function is running, as the function temporarily reorders // the array while it executes. // // To pack into another rectangle, you need to call stbrp_init_target // again. To continue packing into the same rectangle, you can call // this function again. Calling this multiple times with multiple rect // arrays will probably produce worse packing results than calling it // a single time with the full rectangle array, but the option is // available. // // The function returns 1 if all of the rectangles were successfully // packed and 0 otherwise. struct stbrp_rect { // reserved for your use: int id; // input: stbrp_coord w, h; // output: stbrp_coord x, y; int was_packed; // non-zero if valid packing }; // 16 bytes, nominally STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); // Initialize a rectangle packer to: // pack a rectangle that is 'width' by 'height' in dimensions // using temporary storage provided by the array 'nodes', which is 'num_nodes' long // // You must call this function every time you start packing into a new target. // // There is no "shutdown" function. The 'nodes' memory must stay valid for // the following stbrp_pack_rects() call (or calls), but can be freed after // the call (or calls) finish. // // Note: to guarantee best results, either: // 1. make sure 'num_nodes' >= 'width' // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' // // If you don't do either of the above things, widths will be quantized to multiples // of small integers to guarantee the algorithm doesn't run out of temporary storage. // // If you do #2, then the non-quantized algorithm will be used, but the algorithm // may run out of temporary storage and be unable to pack some rectangles. STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); // Optionally call this function after init but before doing any packing to // change the handling of the out-of-temp-memory scenario, described above. // If you call init again, this will be reset to the default (false). STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); // Optionally select which packing heuristic the library should use. Different // heuristics will produce better/worse results for different data sets. // If you call init again, this will be reset to the default. enum { STBRP_HEURISTIC_Skyline_default=0, STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, STBRP_HEURISTIC_Skyline_BF_sortHeight }; ////////////////////////////////////////////////////////////////////////////// // // the details of the following structures don't matter to you, but they must // be visible so you can handle the memory allocations for them struct stbrp_node { stbrp_coord x,y; stbrp_node *next; }; struct stbrp_context { int width; int height; int align; int init_mode; int heuristic; int num_nodes; stbrp_node *active_head; stbrp_node *free_head; stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' }; #ifdef __cplusplus } #endif #endif ////////////////////////////////////////////////////////////////////////////// // // IMPLEMENTATION SECTION // #ifdef STB_RECT_PACK_IMPLEMENTATION #ifndef STBRP_SORT #include #define STBRP_SORT qsort #endif #ifndef STBRP_ASSERT #include #define STBRP_ASSERT assert #endif #ifdef _MSC_VER #define STBRP__NOTUSED(v) (void)(v) #define STBRP__CDECL __cdecl #else #define STBRP__NOTUSED(v) (void)sizeof(v) #define STBRP__CDECL #endif enum { STBRP__INIT_skyline = 1 }; STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) { switch (context->init_mode) { case STBRP__INIT_skyline: STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); context->heuristic = heuristic; break; default: STBRP_ASSERT(0); } } STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) { if (allow_out_of_mem) // if it's ok to run out of memory, then don't bother aligning them; // this gives better packing, but may fail due to OOM (even though // the rectangles easily fit). @TODO a smarter approach would be to only // quantize once we've hit OOM, then we could get rid of this parameter. context->align = 1; else { // if it's not ok to run out of memory, then quantize the widths // so that num_nodes is always enough nodes. // // I.e. num_nodes * align >= width // align >= width / num_nodes // align = ceil(width/num_nodes) context->align = (context->width + context->num_nodes-1) / context->num_nodes; } } STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) { int i; for (i=0; i < num_nodes-1; ++i) nodes[i].next = &nodes[i+1]; nodes[i].next = NULL; context->init_mode = STBRP__INIT_skyline; context->heuristic = STBRP_HEURISTIC_Skyline_default; context->free_head = &nodes[0]; context->active_head = &context->extra[0]; context->width = width; context->height = height; context->num_nodes = num_nodes; stbrp_setup_allow_out_of_mem(context, 0); // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) context->extra[0].x = 0; context->extra[0].y = 0; context->extra[0].next = &context->extra[1]; context->extra[1].x = (stbrp_coord) width; context->extra[1].y = (1<<30); context->extra[1].next = NULL; } // find minimum y position if it starts at x1 static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) { stbrp_node *node = first; int x1 = x0 + width; int min_y, visited_width, waste_area; STBRP__NOTUSED(c); STBRP_ASSERT(first->x <= x0); #if 0 // skip in case we're past the node while (node->next->x <= x0) ++node; #else STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency #endif STBRP_ASSERT(node->x <= x0); min_y = 0; waste_area = 0; visited_width = 0; while (node->x < x1) { if (node->y > min_y) { // raise min_y higher. // we've accounted for all waste up to min_y, // but we'll now add more waste for everything we've visited waste_area += visited_width * (node->y - min_y); min_y = node->y; // the first time through, visited_width might be reduced if (node->x < x0) visited_width += node->next->x - x0; else visited_width += node->next->x - node->x; } else { // add waste area int under_width = node->next->x - node->x; if (under_width + visited_width > width) under_width = width - visited_width; waste_area += under_width * (min_y - node->y); visited_width += under_width; } node = node->next; } *pwaste = waste_area; return min_y; } typedef struct { int x,y; stbrp_node **prev_link; } stbrp__findresult; static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) { int best_waste = (1<<30), best_x, best_y = (1 << 30); stbrp__findresult fr; stbrp_node **prev, *node, *tail, **best = NULL; // align to multiple of c->align width = (width + c->align - 1); width -= width % c->align; STBRP_ASSERT(width % c->align == 0); // if it can't possibly fit, bail immediately if (width > c->width || height > c->height) { fr.prev_link = NULL; fr.x = fr.y = 0; return fr; } node = c->active_head; prev = &c->active_head; while (node->x + width <= c->width) { int y,waste; y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL // bottom left if (y < best_y) { best_y = y; best = prev; } } else { // best-fit if (y + height <= c->height) { // can only use it if it first vertically if (y < best_y || (y == best_y && waste < best_waste)) { best_y = y; best_waste = waste; best = prev; } } } prev = &node->next; node = node->next; } best_x = (best == NULL) ? 0 : (*best)->x; // if doing best-fit (BF), we also have to try aligning right edge to each node position // // e.g, if fitting // // ____________________ // |____________________| // // into // // | | // | ____________| // |____________| // // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned // // This makes BF take about 2x the time if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { tail = c->active_head; node = c->active_head; prev = &c->active_head; // find first node that's admissible while (tail->x < width) tail = tail->next; while (tail) { int xpos = tail->x - width; int y,waste; STBRP_ASSERT(xpos >= 0); // find the left position that matches this while (node->next->x <= xpos) { prev = &node->next; node = node->next; } STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); if (y + height <= c->height) { if (y <= best_y) { if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { best_x = xpos; //STBRP_ASSERT(y <= best_y); [DEAR IMGUI] best_y = y; best_waste = waste; best = prev; } } } tail = tail->next; } } fr.prev_link = best; fr.x = best_x; fr.y = best_y; return fr; } static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) { // find best position according to heuristic stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); stbrp_node *node, *cur; // bail if: // 1. it failed // 2. the best node doesn't fit (we don't always check this) // 3. we're out of memory if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { res.prev_link = NULL; return res; } // on success, create new node node = context->free_head; node->x = (stbrp_coord) res.x; node->y = (stbrp_coord) (res.y + height); context->free_head = node->next; // insert the new node into the right starting point, and // let 'cur' point to the remaining nodes needing to be // stitched back in cur = *res.prev_link; if (cur->x < res.x) { // preserve the existing one, so start testing with the next one stbrp_node *next = cur->next; cur->next = node; cur = next; } else { *res.prev_link = node; } // from here, traverse cur and free the nodes, until we get to one // that shouldn't be freed while (cur->next && cur->next->x <= res.x + width) { stbrp_node *next = cur->next; // move the current node to the free list cur->next = context->free_head; context->free_head = cur; cur = next; } // stitch the list back in node->next = cur; if (cur->x < res.x + width) cur->x = (stbrp_coord) (res.x + width); #ifdef _DEBUG cur = context->active_head; while (cur->x < context->width) { STBRP_ASSERT(cur->x < cur->next->x); cur = cur->next; } STBRP_ASSERT(cur->next == NULL); { int count=0; cur = context->active_head; while (cur) { cur = cur->next; ++count; } cur = context->free_head; while (cur) { cur = cur->next; ++count; } STBRP_ASSERT(count == context->num_nodes+2); } #endif return res; } static int STBRP__CDECL rect_height_compare(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; const stbrp_rect *q = (const stbrp_rect *) b; if (p->h > q->h) return -1; if (p->h < q->h) return 1; return (p->w > q->w) ? -1 : (p->w < q->w); } static int STBRP__CDECL rect_original_order(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; const stbrp_rect *q = (const stbrp_rect *) b; return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); } STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) { int i, all_rects_packed = 1; // we use the 'was_packed' field internally to allow sorting/unsorting for (i=0; i < num_rects; ++i) { rects[i].was_packed = i; } // sort according to heuristic STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); for (i=0; i < num_rects; ++i) { if (rects[i].w == 0 || rects[i].h == 0) { rects[i].x = rects[i].y = 0; // empty rect needs no space } else { stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); if (fr.prev_link) { rects[i].x = (stbrp_coord) fr.x; rects[i].y = (stbrp_coord) fr.y; } else { rects[i].x = rects[i].y = STBRP__MAXVAL; } } } // unsort STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); // set was_packed flags and all_rects_packed status for (i=0; i < num_rects; ++i) { rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); if (!rects[i].was_packed) all_rects_packed = 0; } // return the all_rects_packed status return all_rects_packed; } #endif /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ================================================ FILE: lib/third_party/imgui/imgui/include/imstb_textedit.h ================================================ // [DEAR IMGUI] // This is a slightly modified version of stb_textedit.h 1.14. // Those changes would need to be pushed into nothings/stb: // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) // - Added name to struct or it may be forward declared in our code. // - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925) // - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion. // Grep for [DEAR IMGUI] to find some changes. // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* // stb_textedit.h - v1.14 - 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 // widget; you implement display, word-wrapping, and low-level string // insertion/deletion, and stb_textedit will map user inputs into // insertions & deletions, plus updates to the cursor position, // selection state, and undo state. // // It is intended for use in games and other systems that need to build // their own custom widgets and which do not have heavy text-editing // requirements (this library is not recommended for use for editing large // texts, as its performance does not scale and it has limited undo). // // Non-trivial behaviors are modelled after Windows text controls. // // // LICENSE // // See end of file for license information. // // // DEPENDENCIES // // Uses the C runtime function 'memmove', which you can override // by defining IMSTB_TEXTEDIT_memmove before the implementation. // Uses no other functions. Performs no runtime allocations. // // // VERSION HISTORY // // !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion. // 1.14 (2021-07-11) page up/down, various fixes // 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) suppress warnings about casting away const with -Wcast-qual // 1.9 (2016-08-27) customizable move-by-word // 1.8 (2016-04-02) better keyboard handling when mouse button is down // 1.7 (2015-09-13) change y range handling in case baseline is non-0 // 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove // 1.5 (2014-09-10) add support for secondary keys for OS X // 1.4 (2014-08-17) fix signed/unsigned warnings // 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary // 1.2 (2014-05-27) fix some RAD types that had crept into the new code // 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) // 1.0 (2012-07-26) improve documentation, initial public release // 0.3 (2012-02-24) bugfixes, single-line mode; insert mode // 0.2 (2011-11-28) fixes to undo/redo // 0.1 (2010-07-08) initial version // // ADDITIONAL CONTRIBUTORS // // Ulf Winklemann: move-by-word in 1.1 // Fabian Giesen: secondary key inputs in 1.5 // Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6 // Louis Schnellbach: page up/down in 1.14 // // Bugfixes: // Scott Graham // Daniel Keller // Omar Cornut // Dan Thompson // // USAGE // // This file behaves differently depending on what symbols you define // before including it. // // // Header-file mode: // // If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, // it will operate in "header file" mode. In this mode, it declares a // single public symbol, STB_TexteditState, which encapsulates the current // state of a text widget (except for the string, which you will store // separately). // // To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a // primitive type that defines a single character (e.g. char, wchar_t, etc). // // To save space or increase undo-ability, you can optionally define the // following things that are used by the undo system: // // STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer // // If you don't define these, they are set to permissive types and // moderate sizes. The undo system does no memory allocations, so // it grows STB_TexteditState by the worst-case storage which is (in bytes): // // [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATECOUNT // + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHARCOUNT // // // Implementation mode: // // If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it // will compile the implementation of the text edit widget, depending // on a large number of symbols which must be defined before the include. // // The implementation is defined only as static functions. You will then // need to provide your own APIs in the same file which will access the // static functions. // // The basic concept is that you provide a "string" object which // behaves like an array of characters. stb_textedit uses indices to // refer to positions in the string, implicitly representing positions // in the displayed textedit. This is true for both plain text and // rich text; even with rich text stb_truetype interacts with your // code as if there was an array of all the displayed characters. // // Symbols that must be the same in header-file and implementation mode: // // STB_TEXTEDIT_CHARTYPE the character type // STB_TEXTEDIT_POSITIONTYPE small type that is a valid cursor position // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer // // Symbols you must define for implementation mode: // // STB_TEXTEDIT_STRING the type of object representing a string being edited, // typically this is a wrapper object with other data you need // // STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) // STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters // starting from character #n (see discussion below) // STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character // to the xpos of the i+1'th char for a line of characters // starting at character #n (i.e. accounts for kerning // with previous char) // STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character // (return type is int, -1 means not valid to insert) // (not supported if you want to use UTF-8, see below) // STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based // STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize // as manually wordwrapping for end-of-line positioning // // STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i // STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) // returns number of characters actually inserted. [DEAR IMGUI] // // STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key // // STB_TEXTEDIT_K_LEFT keyboard input to move cursor left // STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right // STB_TEXTEDIT_K_UP keyboard input to move cursor up // STB_TEXTEDIT_K_DOWN keyboard input to move cursor down // STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page // STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page // STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME // STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END // STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME // STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END // STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor // STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor // STB_TEXTEDIT_K_UNDO keyboard input to perform undo // STB_TEXTEDIT_K_REDO keyboard input to perform redo // // Optional: // STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode // STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), // required for default WORDLEFT/WORDRIGHT handlers // STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to // STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to // STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT // STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT // STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line // STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line // STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text // STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text // // To support UTF-8: // // STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character // STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character // Do NOT define STB_TEXTEDIT_KEYTOTEXT. // Instead, call stb_textedit_text() directly for text contents. // // Keyboard input must be encoded as a single integer value; e.g. a character code // and some bitflags that represent shift states. to simplify the interface, SHIFT must // be a bitflag, so we can test the shifted state of cursor movements to allow selection, // i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. // // You can encode other things, such as CONTROL or ALT, in additional bits, and // then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, // my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN // bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, // and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the // API below. The control keys will only match WM_KEYDOWN events because of the // keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN // bit so it only decodes WM_CHAR events. // // STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed // row of characters assuming they start on the i'th character--the width and // the height and the number of characters consumed. This allows this library // to traverse the entire layout incrementally. You need to compute word-wrapping // here. // // Each textfield keeps its own insert mode state, which is not how normal // applications work. To keep an app-wide insert mode, update/copy the // "insert_mode" field of STB_TexteditState before/after calling API functions. // // API // // void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) // // void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) // void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) // void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len) // // Each of these functions potentially updates the string and updates the // state. // // initialize_state: // set the textedit state to a known good default state when initially // constructing the textedit. // // click: // call this with the mouse x,y on a mouse down; it will update the cursor // and reset the selection start/end to the cursor point. the x,y must // be relative to the text widget, with (0,0) being the top left. // // drag: // call this with the mouse x,y on a mouse drag/up; it will update the // cursor and the selection end point // // cut: // call this to delete the current selection; returns true if there was // one. you should FIRST copy the current selection to the system paste buffer. // (To copy, just copy the current selection out of the string yourself.) // // paste: // call this to paste text at the current cursor point or over the current // selection if there is one. // // key: // call this for keyboard inputs sent to the textfield. you can use it // for "key down" events or for "translated" key events. if you need to // do both (as in Win32), or distinguish Unicode characters from control // inputs, set a high bit to distinguish the two; then you can define the // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to // anything other type you want before including. // if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are // transformed into text and stb_textedit_text() is automatically called. // // text: (added 2025) // call this to directly send text input the textfield, which is required // for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT() // cannot infer text length. // // // When rendering, you can read the cursor position and selection state from // the STB_TexteditState. // // // Notes: // // This is designed to be usable in IMGUI, so it allows for the possibility of // running in an IMGUI that has NOT cached the multi-line layout. For this // reason, it provides an interface that is compatible with computing the // layout incrementally--we try to make sure we make as few passes through // as possible. (For example, to locate the mouse pointer in the text, we // could define functions that return the X and Y positions of characters // and binary search Y and then X, but if we're doing dynamic layout this // will run the layout algorithm many times, so instead we manually search // forward in one pass. Similar logic applies to e.g. up-arrow and // down-arrow movement.) // // If it's run in a widget that *has* cached the layout, then this is less // efficient, but it's not horrible on modern computers. But you wouldn't // want to edit million-line files with it. //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //// //// Header-file mode //// //// #ifndef INCLUDE_IMSTB_TEXTEDIT_H #define INCLUDE_IMSTB_TEXTEDIT_H //////////////////////////////////////////////////////////////////////// // // STB_TexteditState // // Definition of STB_TexteditState which you should store // per-textfield; it includes cursor position, selection state, // and undo state. // #ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT #define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 #endif #ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT #define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 #endif #ifndef IMSTB_TEXTEDIT_CHARTYPE #define IMSTB_TEXTEDIT_CHARTYPE int #endif #ifndef IMSTB_TEXTEDIT_POSITIONTYPE #define IMSTB_TEXTEDIT_POSITIONTYPE int #endif typedef struct { // private data IMSTB_TEXTEDIT_POSITIONTYPE where; IMSTB_TEXTEDIT_POSITIONTYPE insert_length; IMSTB_TEXTEDIT_POSITIONTYPE delete_length; int char_storage; } StbUndoRecord; typedef struct { // private data StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT]; IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT]; short undo_point, redo_point; int undo_char_point, redo_char_point; } StbUndoState; typedef struct STB_TexteditState { ///////////////////// // // public data // int cursor; // position of the text cursor within the string int select_start; // selection start point int select_end; // selection start and end point in characters; if equal, no selection. // note that start may be less than or greater than end (e.g. when // dragging the mouse, start is where the initial click was, and you // can drag in either direction) unsigned char insert_mode; // each textfield keeps its own insert mode state. to keep an app-wide // insert mode, copy this value in/out of the app state int row_count_per_page; // page size in number of row. // this value MUST be set to >0 for pageup or pagedown in multilines documents. ///////////////////// // // private data // unsigned char cursor_at_end_of_line; // not implemented yet unsigned char initialized; unsigned char has_preferred_x; unsigned char single_line; unsigned char padding1, padding2, padding3; float preferred_x; // this determines where the cursor up/down tries to seek to along x StbUndoState undostate; } STB_TexteditState; //////////////////////////////////////////////////////////////////////// // // StbTexteditRow // // Result of layout query, used by stb_textedit to determine where // the text in each row is. // result of layout query typedef struct { float x0,x1; // starting x location, end x location (allows for align=right, etc) float baseline_y_delta; // position of baseline relative to previous row's baseline float ymin,ymax; // height of row above and below baseline int num_chars; } StbTexteditRow; #endif //INCLUDE_IMSTB_TEXTEDIT_H //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //// //// Implementation mode //// //// // implementation isn't include-guarded, since it might have indirectly // included just the "header" portion #ifdef IMSTB_TEXTEDIT_IMPLEMENTATION #ifndef IMSTB_TEXTEDIT_memmove #include #define IMSTB_TEXTEDIT_memmove memmove #endif // [DEAR IMGUI] // Functions must be implemented for UTF8 support // Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit. // There is not necessarily a '[DEAR IMGUI]' at the usage sites. #ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX #define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1) #endif #ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX #define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1) #endif ///////////////////////////////////////////////////////////////////////////// // // Mouse input handling // // traverse the layout to locate the nearest character to a display position static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line) { StbTexteditRow r; int n = STB_TEXTEDIT_STRINGLEN(str); float base_y = 0, prev_x; int i=0, k; r.x0 = r.x1 = 0; r.ymin = r.ymax = 0; r.num_chars = 0; *out_side_on_line = 0; // search rows to find one that straddles 'y' while (i < n) { STB_TEXTEDIT_LAYOUTROW(&r, str, i); if (r.num_chars <= 0) return n; if (i==0 && y < base_y + r.ymin) return 0; if (y < base_y + r.ymax) break; i += r.num_chars; base_y += r.baseline_y_delta; } // below all text, return 'after' last character if (i >= n) { *out_side_on_line = 1; return n; } // check if it's before the beginning of the line if (x < r.x0) return i; // check if it's before the end of the line if (x < r.x1) { // search characters in row for one that straddles 'x' prev_x = r.x0; for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) { float w = STB_TEXTEDIT_GETWIDTH(str, i, k); if (x < prev_x+w) { *out_side_on_line = (k == 0) ? 0 : 1; if (x < prev_x+w/2) return k+i; else return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k); } prev_x += w; } // shouldn't happen, but if it does, fall through to end-of-line case } // if the last character is a newline, return that. otherwise return 'after' the last character *out_side_on_line = 1; if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) return i+r.num_chars-1; else return i+r.num_chars; } // API click: on mouse down, move the cursor to the clicked location, and reset the selection static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) { // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse // goes off the top or bottom of the text int side_on_line; if( state->single_line ) { StbTexteditRow r; STB_TEXTEDIT_LAYOUTROW(&r, str, 0); y = r.ymin; } state->cursor = stb_text_locate_coord(str, x, y, &side_on_line); state->select_start = state->cursor; state->select_end = state->cursor; state->has_preferred_x = 0; str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left); } // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) { int p = 0; int side_on_line; // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse // goes off the top or bottom of the text if( state->single_line ) { StbTexteditRow r; STB_TEXTEDIT_LAYOUTROW(&r, str, 0); y = r.ymin; } if (state->select_start == state->select_end) state->select_start = state->cursor; p = stb_text_locate_coord(str, x, y, &side_on_line); state->cursor = state->select_end = p; str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left); } ///////////////////////////////////////////////////////////////////////////// // // Keyboard input handling // // forward declarations static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state); static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state); static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); typedef struct { float x,y; // position of n'th character float height; // height of line int first_char, length; // first char of row, and length int prev_first; // first char of previous row } StbFindState; // find the x/y location of a character, and remember info about the previous row in // case we get a move-up event (for page up, we'll have to rescan) static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line) { StbTexteditRow r; int prev_start = 0; int z = STB_TEXTEDIT_STRINGLEN(str); int i=0, first; if (n == z && single_line) { // special case if it's at the end (may not be needed?) STB_TEXTEDIT_LAYOUTROW(&r, str, 0); find->y = 0; find->first_char = 0; find->length = z; find->height = r.ymax - r.ymin; find->x = r.x1; return; } // search rows to find the one that straddles character n find->y = 0; for(;;) { STB_TEXTEDIT_LAYOUTROW(&r, str, i); if (n < i + r.num_chars) break; if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling break; if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line break; // [DEAR IMGUI] prev_start = i; i += r.num_chars; find->y += r.baseline_y_delta; if (i == z) // [DEAR IMGUI] { r.num_chars = 0; // [DEAR IMGUI] break; // [DEAR IMGUI] } } find->first_char = first = i; find->length = r.num_chars; find->height = r.ymax - r.ymin; find->prev_first = prev_start; // now scan to find xpos find->x = r.x0; for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first) find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); } #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) // make the selection/cursor state valid if client altered the string static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { int n = STB_TEXTEDIT_STRINGLEN(str); if (STB_TEXT_HAS_SELECTION(state)) { if (state->select_start > n) state->select_start = n; if (state->select_end > n) state->select_end = n; // if clamping forced them to be equal, move the cursor to match if (state->select_start == state->select_end) state->cursor = state->select_start; } if (state->cursor > n) state->cursor = n; } // delete characters while updating undo static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) { stb_text_makeundo_delete(str, state, where, len); STB_TEXTEDIT_DELETECHARS(str, where, len); state->has_preferred_x = 0; } // delete the section static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { stb_textedit_clamp(str, state); if (STB_TEXT_HAS_SELECTION(state)) { if (state->select_start < state->select_end) { stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); state->select_end = state->cursor = state->select_start; } else { stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); state->select_start = state->cursor = state->select_end; } state->has_preferred_x = 0; } } // canoncialize the selection so start <= end static void stb_textedit_sortselection(STB_TexteditState *state) { if (state->select_end < state->select_start) { int temp = state->select_end; state->select_end = state->select_start; state->select_start = temp; } } // move cursor to first character of selection static void stb_textedit_move_to_first(STB_TexteditState *state) { if (STB_TEXT_HAS_SELECTION(state)) { stb_textedit_sortselection(state); state->cursor = state->select_start; state->select_end = state->select_start; state->has_preferred_x = 0; } } // move cursor to last character of selection static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { if (STB_TEXT_HAS_SELECTION(state)) { stb_textedit_sortselection(state); stb_textedit_clamp(str, state); state->cursor = state->select_end; state->select_start = state->select_end; state->has_preferred_x = 0; } } // [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping. #ifndef STB_TEXTEDIT_MOVELINESTART static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor) { if (state->single_line) return 0; while (cursor > 0) { int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor); if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE) break; cursor = prev; } return cursor; } #define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start #endif #ifndef STB_TEXTEDIT_MOVELINEEND static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor) { int n = STB_TEXTEDIT_STRINGLEN(str); if (state->single_line) return n; while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE) cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor); return cursor; } #define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end #endif #ifdef STB_TEXTEDIT_IS_SPACE static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx ) { return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; } #ifndef STB_TEXTEDIT_MOVEWORDLEFT static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c ) { c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character while (c >= 0 && !is_word_boundary(str, c)) c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c); if( c < 0 ) c = 0; return c; } #define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous #endif #ifndef STB_TEXTEDIT_MOVEWORDRIGHT static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c ) { const int len = STB_TEXTEDIT_STRINGLEN(str); c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character while( c < len && !is_word_boundary( str, c ) ) c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); if( c > len ) c = len; return c; } #define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next #endif #endif // update selection and cursor to match each other static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) { if (!STB_TEXT_HAS_SELECTION(state)) state->select_start = state->select_end = state->cursor; else state->cursor = state->select_end; } // API cut: delete selection static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { if (STB_TEXT_HAS_SELECTION(state)) { stb_textedit_delete_selection(str,state); // implicitly clamps state->has_preferred_x = 0; return 1; } return 0; } // API paste: replace existing selection with passed-in text static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len) { // if there's a selection, the paste should delete it stb_textedit_clamp(str, state); stb_textedit_delete_selection(str,state); // try to insert the characters len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len); if (len) { stb_text_makeundo_insert(state, state->cursor, len); state->cursor += len; state->has_preferred_x = 0; return 1; } // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details) return 0; } #ifndef STB_TEXTEDIT_KEYTYPE #define STB_TEXTEDIT_KEYTYPE int #endif // API key: process text input // [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility. static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len) { // can't add newline in single-line mode if (text[0] == '\n' && state->single_line) return; if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { stb_text_makeundo_replace(str, state, state->cursor, 1, 1); STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len); if (text_len) { state->cursor += text_len; state->has_preferred_x = 0; } } else { stb_textedit_delete_selection(str, state); // implicitly clamps text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len); if (text_len) { stb_text_makeundo_insert(state, state->cursor, text_len); state->cursor += text_len; state->has_preferred_x = 0; } } } // API key: process a keyboard input static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) { retry: switch (key) { default: { #ifdef STB_TEXTEDIT_KEYTOTEXT // This is not suitable for UTF-8 support. int c = STB_TEXTEDIT_KEYTOTEXT(key); if (c > 0) { IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c; stb_textedit_text(str, state, &ch, 1); } #endif break; } #ifdef STB_TEXTEDIT_K_INSERT case STB_TEXTEDIT_K_INSERT: state->insert_mode = !state->insert_mode; break; #endif case STB_TEXTEDIT_K_UNDO: stb_text_undo(str, state); state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_REDO: stb_text_redo(str, state); state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_LEFT: // if currently there's a selection, move cursor to start of selection if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_first(state); else if (state->cursor > 0) state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_RIGHT: // if currently there's a selection, move cursor to end of selection if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_last(str, state); else state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); stb_textedit_clamp(str, state); state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: stb_textedit_clamp(str, state); stb_textedit_prep_selection_at_cursor(state); // move selection left if (state->select_end > 0) state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end); state->cursor = state->select_end; state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_MOVEWORDLEFT case STB_TEXTEDIT_K_WORDLEFT: if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_first(state); else { state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); stb_textedit_clamp( str, state ); } break; case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: if( !STB_TEXT_HAS_SELECTION( state ) ) stb_textedit_prep_selection_at_cursor(state); state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); state->select_end = state->cursor; stb_textedit_clamp( str, state ); break; #endif #ifdef STB_TEXTEDIT_MOVEWORDRIGHT case STB_TEXTEDIT_K_WORDRIGHT: if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_last(str, state); else { state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); stb_textedit_clamp( str, state ); } break; case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: if( !STB_TEXT_HAS_SELECTION( state ) ) stb_textedit_prep_selection_at_cursor(state); state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); state->select_end = state->cursor; stb_textedit_clamp( str, state ); break; #endif case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: stb_textedit_prep_selection_at_cursor(state); // move selection right state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end); stb_textedit_clamp(str, state); state->cursor = state->select_end; state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_DOWN: case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_PGDOWN: case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: { StbFindState find; StbTexteditRow row; int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN; int row_count = is_page ? state->row_count_per_page : 1; if (!is_page && state->single_line) { // on windows, up&down in single-line behave like left&right key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); goto retry; } if (sel) stb_textedit_prep_selection_at_cursor(state); else if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_last(str, state); // compute current position of cursor point stb_textedit_clamp(str, state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); for (j = 0; j < row_count; ++j) { float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x; int start = find.first_char + find.length; if (find.length == 0) break; // [DEAR IMGUI] // going down while being on the last line shouldn't bring us to that line end //if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE) // break; // now find character position down a row state->cursor = start; STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); x = row.x0; for (i=0; i < row.num_chars; ) { float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) break; #endif x += dx; if (x > goal_x) break; i += next - state->cursor; state->cursor = next; } stb_textedit_clamp(str, state); if (state->cursor == find.first_char + find.length) str->LastMoveDirectionLR = ImGuiDir_Left; state->has_preferred_x = 1; state->preferred_x = goal_x; if (sel) state->select_end = state->cursor; // go to next line find.first_char = find.first_char + find.length; find.length = row.num_chars; } break; } case STB_TEXTEDIT_K_UP: case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_PGUP: case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: { StbFindState find; StbTexteditRow row; int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP; int row_count = is_page ? state->row_count_per_page : 1; if (!is_page && state->single_line) { // on windows, up&down become left&right key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); goto retry; } if (sel) stb_textedit_prep_selection_at_cursor(state); else if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_first(state); // compute current position of cursor point stb_textedit_clamp(str, state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); for (j = 0; j < row_count; ++j) { float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x; // can only go up if there's a previous row if (find.prev_first == find.first_char) break; // now find character position up a row state->cursor = find.prev_first; STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); x = row.x0; for (i=0; i < row.num_chars; ) { float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) break; #endif x += dx; if (x > goal_x) break; i += next - state->cursor; state->cursor = next; } stb_textedit_clamp(str, state); if (state->cursor == find.first_char) str->LastMoveDirectionLR = ImGuiDir_Right; else if (state->cursor == find.prev_first) str->LastMoveDirectionLR = ImGuiDir_Left; state->has_preferred_x = 1; state->preferred_x = goal_x; if (sel) state->select_end = state->cursor; // go to previous line // (we need to scan previous line the hard way. maybe we could expose this as a new API function?) prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0; while (prev_scan > 0) { int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan); if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE) break; prev_scan = prev; } find.first_char = find.prev_first; find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan); } break; } case STB_TEXTEDIT_K_DELETE: case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_delete_selection(str, state); else { int n = STB_TEXTEDIT_STRINGLEN(str); if (state->cursor < n) stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor); } state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_BACKSPACE: case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_delete_selection(str, state); else { stb_textedit_clamp(str, state); if (state->cursor > 0) { int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); stb_textedit_delete(str, state, prev, state->cursor - prev); state->cursor = prev; } } state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_K_TEXTSTART2 case STB_TEXTEDIT_K_TEXTSTART2: #endif case STB_TEXTEDIT_K_TEXTSTART: state->cursor = state->select_start = state->select_end = 0; state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_K_TEXTEND2 case STB_TEXTEDIT_K_TEXTEND2: #endif case STB_TEXTEDIT_K_TEXTEND: state->cursor = STB_TEXTEDIT_STRINGLEN(str); state->select_start = state->select_end = 0; state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_K_TEXTSTART2 case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: #endif case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: stb_textedit_prep_selection_at_cursor(state); state->cursor = state->select_end = 0; state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_K_TEXTEND2 case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: #endif case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: stb_textedit_prep_selection_at_cursor(state); state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_K_LINESTART2 case STB_TEXTEDIT_K_LINESTART2: #endif case STB_TEXTEDIT_K_LINESTART: stb_textedit_clamp(str, state); stb_textedit_move_to_first(state); state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor); state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_K_LINEEND2 case STB_TEXTEDIT_K_LINEEND2: #endif case STB_TEXTEDIT_K_LINEEND: { stb_textedit_clamp(str, state); stb_textedit_move_to_last(str, state); state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor); state->has_preferred_x = 0; break; } #ifdef STB_TEXTEDIT_K_LINESTART2 case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: #endif case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: stb_textedit_clamp(str, state); stb_textedit_prep_selection_at_cursor(state); state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor); state->select_end = state->cursor; state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_K_LINEEND2 case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: #endif case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { stb_textedit_clamp(str, state); stb_textedit_prep_selection_at_cursor(state); state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor); state->select_end = state->cursor; state->has_preferred_x = 0; break; } } } ///////////////////////////////////////////////////////////////////////////// // // Undo processing // // @OPTIMIZE: the undo/redo buffer should be circular static void stb_textedit_flush_redo(StbUndoState *state) { state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT; state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT; } // discard the oldest entry in the undo list static void stb_textedit_discard_undo(StbUndoState *state) { if (state->undo_point > 0) { // if the 0th undo state has characters, clean those up if (state->undo_rec[0].char_storage >= 0) { int n = state->undo_rec[0].insert_length, i; // delete n characters from all other records state->undo_char_point -= n; IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE))); for (i=0; i < state->undo_point; ++i) if (state->undo_rec[i].char_storage >= 0) state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it } --state->undo_point; IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); } } // discard the oldest entry in the redo list--it's bad if this // ever happens, but because undo & redo have to store the actual // characters in different cases, the redo character buffer can // fill up even though the undo buffer didn't static void stb_textedit_discard_redo(StbUndoState *state) { int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1; if (state->redo_point <= k) { // if the k'th undo state has characters, clean those up if (state->undo_rec[k].char_storage >= 0) { int n = state->undo_rec[k].insert_length, i; // move the remaining redo character data to the end of the buffer state->redo_char_point += n; IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE))); // adjust the position of all the other records to account for above memmove for (i=state->redo_point; i < k; ++i) if (state->undo_rec[i].char_storage >= 0) 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' // [DEAR IMGUI] size_t move_size = (size_t)((IMSTB_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); IMSTB_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; } } static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) { // any time we create a new undo record, we discard redo stb_textedit_flush_redo(state); // if we have no free records, we have to make room, by sliding the // existing records down if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) stb_textedit_discard_undo(state); // if the characters to store won't possibly fit in the buffer, we can't undo if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) { state->undo_point = 0; state->undo_char_point = 0; return NULL; } // if we don't have enough free characters in the buffer, we have to make room while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) stb_textedit_discard_undo(state); return &state->undo_rec[state->undo_point++]; } static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) { StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); if (r == NULL) return NULL; r->where = pos; r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len; r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len; if (insert_len == 0) { r->char_storage = -1; return NULL; } else { r->char_storage = state->undo_char_point; state->undo_char_point += insert_len; return &state->undo_char[r->char_storage]; } } static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { StbUndoState *s = &state->undostate; StbUndoRecord u, *r; if (s->undo_point == 0) return; // we need to do two things: apply the undo record, and create a redo record u = s->undo_rec[s->undo_point-1]; r = &s->undo_rec[s->redo_point-1]; r->char_storage = -1; r->insert_length = u.delete_length; r->delete_length = u.insert_length; r->where = u.where; if (u.delete_length) { // if the undo record says to delete characters, then the redo record will // need to re-insert the characters that get deleted, so we need to store // them. // there are three cases: // there's enough room to store the characters // characters stored for *redoing* don't leave room for redo // characters stored for *undoing* don't leave room for redo // if the last is true, we have to bail if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) { // the undo records take up too much character space; there's no space to store the redo characters r->insert_length = 0; } else { int i; // there's definitely room to store the characters eventually while (s->undo_char_point + u.delete_length > s->redo_char_point) { // should never happen: if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) return; // there's currently not enough room, so discard a redo record stb_textedit_discard_redo(s); } r = &s->undo_rec[s->redo_point-1]; r->char_storage = s->redo_char_point - u.delete_length; s->redo_char_point = s->redo_char_point - u.delete_length; // now save the characters for (i=0; i < u.delete_length; ++i) s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); } // now we can carry out the deletion STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); } // check type of recorded action: if (u.insert_length) { // easy case: was a deletion, so we need to insert n characters u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); s->undo_char_point -= u.insert_length; } state->cursor = u.where + u.insert_length; s->undo_point--; s->redo_point--; } static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { StbUndoState *s = &state->undostate; StbUndoRecord *u, r; if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) return; // we need to do two things: apply the redo record, and create an undo record u = &s->undo_rec[s->undo_point]; r = s->undo_rec[s->redo_point]; // we KNOW there must be room for the undo record, because the redo record // was derived from an undo record u->delete_length = r.insert_length; u->insert_length = r.delete_length; u->where = r.where; u->char_storage = -1; if (r.delete_length) { // the redo record requires us to delete characters, so the undo record // needs to store the characters if (s->undo_char_point + u->insert_length > s->redo_char_point) { u->insert_length = 0; u->delete_length = 0; } else { int i; u->char_storage = s->undo_char_point; s->undo_char_point = s->undo_char_point + u->insert_length; // now save the characters for (i=0; i < u->insert_length; ++i) s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); } STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); } if (r.insert_length) { // easy case: need to insert n characters r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); s->redo_char_point += r.insert_length; } state->cursor = r.where + r.insert_length; s->undo_point++; s->redo_point++; } static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) { stb_text_createundo(&state->undostate, where, 0, length); } static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) { int i; IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); if (p) { for (i=0; i < length; ++i) p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); } } static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) { int i; IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); if (p) { for (i=0; i < old_length; ++i) p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); } } // reset the state to default static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) { state->undostate.undo_point = 0; state->undostate.undo_char_point = 0; state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT; state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT; state->select_end = state->select_start = 0; state->cursor = 0; state->has_preferred_x = 0; state->preferred_x = 0; state->cursor_at_end_of_line = 0; state->initialized = 1; state->single_line = (unsigned char) is_single_line; state->insert_mode = 0; state->row_count_per_page = 0; } // API initialize static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) { stb_textedit_clear_state(state, is_single_line); } #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" #endif static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len) { return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len); } #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif #endif//IMSTB_TEXTEDIT_IMPLEMENTATION /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ================================================ FILE: lib/third_party/imgui/imgui/include/imstb_truetype.h ================================================ // [DEAR IMGUI] // This is a slightly modified version of stb_truetype.h 1.26. // Mostly fixing for compiler and static analyzer warnings. // Grep for [DEAR IMGUI] to find the changes. // stb_truetype.h - v1.26 - public domain // authored from 2009-2021 by Sean Barrett / RAD Game Tools // // ======================================================================= // // NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES // // This library does no range checking of the offsets found in the file, // meaning an attacker can use it to read arbitrary memory. // // ======================================================================= // // This library processes TrueType files: // parse files // extract glyph metrics // extract glyph shapes // render glyphs to one-channel bitmaps with antialiasing (box filter) // render glyphs to one-channel SDF bitmaps (signed-distance field/function) // // Todo: // non-MS cmaps // crashproof on bad data // hinting? (no longer patented) // cleartype-style AA? // optimize: use simple memory allocator for intermediates // optimize: build edge-list directly from curves // optimize: rasterize directly from curves? // // ADDITIONAL CONTRIBUTORS // // Mikko Mononen: compound shape support, more cmap formats // Tor Andersson: kerning, subpixel rendering // Dougall Johnson: OpenType / Type 2 font handling // Daniel Ribeiro Maciel: basic GPOS-based kerning // // Misc other: // Ryan Gordon // Simon Glass // github:IntellectualKitty // Imanol Celaya // Daniel Ribeiro Maciel // // Bug/warning reports/fixes: // "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe // Cass Everitt Martins Mozeiko github:aloucks // stoiko (Haemimont Games) Cap Petschulat github:oyvindjam // Brian Hook Omar Cornut github:vassvik // Walter van Niftrik Ryan Griege // David Gow Peter LaValle // David Given Sergey Popov // Ivan-Assen Ivanov Giumo X. Clanjor // Anthony Pesch Higor Euripedes // Johan Duparc Thomas Fields // Hou Qiming Derek Vinyard // Rob Loach Cort Stratton // Kenney Phillis Jr. Brian Costabile // Ken Voskuil (kaesve) // // VERSION HISTORY // // 1.26 (2021-08-28) fix broken rasterizer // 1.25 (2021-07-11) many fixes // 1.24 (2020-02-05) fix warning // 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) // 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined // 1.21 (2019-02-25) fix warning // 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 // 1.16 (2017-07-12) SDF support // 1.15 (2017-03-03) make more arguments const // 1.14 (2017-01-16) num-fonts-in-TTC function // 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts // 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual // 1.11 (2016-04-02) fix unused-variable warning // 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; // variant PackFontRanges to pack and render in separate phases; // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); // fixed an assert() bug in the new rasterizer // replace assert() with STBTT_assert() in new rasterizer // // Full history can be found at the end of this file. // // LICENSE // // See end of file for license information. // // USAGE // // 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 // implementation into that C/C++ file. // // To make the implementation private to the file that generates the implementation, // #define STBTT_STATIC // // Simple 3D API (don't ship this, but it's fine for tools and quick start) // stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture // stbtt_GetBakedQuad() -- compute quad to draw for a given char // // Improved 3D API (more shippable): // #include "stb_rect_pack.h" -- optional, but you really want it // stbtt_PackBegin() // stbtt_PackSetOversampling() -- for improved quality on small fonts // stbtt_PackFontRanges() -- pack and renders // stbtt_PackEnd() // stbtt_GetPackedQuad() // // "Load" a font file from a memory buffer (you have to keep the buffer loaded) // stbtt_InitFont() // stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections // stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections // // Render a unicode codepoint to a bitmap // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be // // Character advance/positioning // stbtt_GetCodepointHMetrics() // stbtt_GetFontVMetrics() // stbtt_GetFontVMetricsOS2() // stbtt_GetCodepointKernAdvance() // // Starting with version 1.06, the rasterizer was replaced with a new, // faster and generally-more-precise rasterizer. The new rasterizer more // accurately measures pixel coverage for anti-aliasing, except in the case // where multiple shapes overlap, in which case it overestimates the AA pixel // coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If // this turns out to be a problem, you can re-enable the old rasterizer with // #define STBTT_RASTERIZER_VERSION 1 // which will incur about a 15% speed hit. // // ADDITIONAL DOCUMENTATION // // Immediately after this block comment are a series of sample programs. // // After the sample programs is the "header file" section. This section // includes documentation for each API function. // // Some important concepts to understand to use this library: // // Codepoint // Characters are defined by unicode codepoints, e.g. 65 is // uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is // the hiragana for "ma". // // Glyph // A visual character shape (every codepoint is rendered as // some glyph) // // Glyph index // A font-specific integer ID representing a glyph // // Baseline // Glyph shapes are defined relative to a baseline, which is the // bottom of uppercase characters. Characters extend both above // and below the baseline. // // Current Point // As you draw text to the screen, you keep track of a "current point" // which is the origin of each character. The current point's vertical // position is the baseline. Even "baked fonts" use this model. // // Vertical Font Metrics // The vertical qualities of the font, used to vertically position // and space the characters. See docs for stbtt_GetFontVMetrics. // // Font Size in Pixels or Points // The preferred interface for specifying font sizes in stb_truetype // is to specify how tall the font's vertical extent should be in pixels. // If that sounds good enough, skip the next paragraph. // // Most font APIs instead use "points", which are a common typographic // measurement for describing font size, defined as 72 points per inch. // stb_truetype provides a point API for compatibility. However, true // "per inch" conventions don't make much sense on computer displays // since different monitors have different number of pixels per // inch. For example, Windows traditionally uses a convention that // there are 96 pixels per inch, thus making 'inch' measurements have // nothing to do with inches, and thus effectively defining a point to // be 1.333 pixels. Additionally, the TrueType font data provides // an explicit scale factor to scale a given font's glyphs to points, // but the author has observed that this scale factor is often wrong // for non-commercial fonts, thus making fonts scaled in points // according to the TrueType spec incoherently sized in practice. // // DETAILED USAGE: // // Scale: // Select how high you want the font to be, in points or pixels. // Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute // a scale factor SF that will be used by all other functions. // // Baseline: // You need to select a y-coordinate that is the baseline of where // your text will appear. Call GetFontBoundingBox to get the baseline-relative // bounding box for all characters. SF*-y0 will be the distance in pixels // that the worst-case character could extend above the baseline, so if // you want the top edge of characters to appear at the top of the // screen where y=0, then you would set the baseline to SF*-y0. // // Current point: // Set the current point where the first character will appear. The // first character could extend left of the current point; this is font // dependent. You can either choose a current point that is the leftmost // point and hope, or add some padding, or check the bounding box or // left-side-bearing of the first character to be displayed and set // the current point based on that. // // Displaying a character: // Compute the bounding box of the character. It will contain signed values // relative to . I.e. if it returns x0,y0,x1,y1, // then the character should be displayed in the rectangle from // to = 32 && *text < 128) { stbtt_aligned_quad q; stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); } ++text; } glEnd(); } #endif // // ////////////////////////////////////////////////////////////////////////////// // // Complete program (this compiles): get a single bitmap, print as ASCII art // #if 0 #include #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation #include "stb_truetype.h" char ttf_buffer[1<<25]; int main(int argc, char **argv) { stbtt_fontinfo font; unsigned char *bitmap; int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); for (j=0; j < h; ++j) { for (i=0; i < w; ++i) putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); putchar('\n'); } return 0; } #endif // // Output: // // .ii. // @@@@@@. // V@Mio@@o // :i. V@V // :oM@@M // :@@@MM@M // @@o o@M // :@@. M@M // @@@o@@@@ // :M@@V:@@. // ////////////////////////////////////////////////////////////////////////////// // // Complete program: print "Hello World!" banner, with bugs // #if 0 char buffer[24<<20]; unsigned char screen[20][79]; int main(int arg, char **argv) { stbtt_fontinfo font; int i,j,ascent,baseline,ch=0; float scale, xpos=2; // leave a little padding in case the character extends left char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); stbtt_InitFont(&font, buffer, 0); scale = stbtt_ScaleForPixelHeight(&font, 15); stbtt_GetFontVMetrics(&font, &ascent,0,0); baseline = (int) (ascent*scale); while (text[ch]) { int advance,lsb,x0,y0,x1,y1; float x_shift = xpos - (float) floor(xpos); stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong // because this API is really for baking character bitmaps into textures. if you want to render // a sequence of characters, you really need to render each bitmap to a temp buffer, then // "alpha blend" that into the working buffer xpos += (advance * scale); if (text[ch+1]) xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); ++ch; } for (j=0; j < 20; ++j) { for (i=0; i < 78; ++i) putchar(" .:ioVM@"[screen[j][i]>>5]); putchar('\n'); } return 0; } #endif ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //// //// INTEGRATION WITH YOUR CODEBASE //// //// The following sections allow you to supply alternate definitions //// of C library functions used by stb_truetype, e.g. if you don't //// link with the C runtime library. #ifdef STB_TRUETYPE_IMPLEMENTATION // #define your own (u)stbtt_int8/16/32 before including to override this #ifndef stbtt_uint8 typedef unsigned char stbtt_uint8; typedef signed char stbtt_int8; typedef unsigned short stbtt_uint16; typedef signed short stbtt_int16; typedef unsigned int stbtt_uint32; typedef signed int stbtt_int32; #endif typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h #ifndef STBTT_ifloor #include #define STBTT_ifloor(x) ((int) floor(x)) #define STBTT_iceil(x) ((int) ceil(x)) #endif #ifndef STBTT_sqrt #include #define STBTT_sqrt(x) sqrt(x) #define STBTT_pow(x,y) pow(x,y) #endif #ifndef STBTT_fmod #include #define STBTT_fmod(x,y) fmod(x,y) #endif #ifndef STBTT_cos #include #define STBTT_cos(x) cos(x) #define STBTT_acos(x) acos(x) #endif #ifndef STBTT_fabs #include #define STBTT_fabs(x) fabs(x) #endif // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h #ifndef STBTT_malloc #include #define STBTT_malloc(x,u) ((void)(u),malloc(x)) #define STBTT_free(x,u) ((void)(u),free(x)) #endif #ifndef STBTT_assert #include #define STBTT_assert(x) assert(x) #endif #ifndef STBTT_strlen #include #define STBTT_strlen(x) strlen(x) #endif #ifndef STBTT_memcpy #include #define STBTT_memcpy memcpy #define STBTT_memset memset #endif #endif /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //// //// INTERFACE //// //// #ifndef __STB_INCLUDE_STB_TRUETYPE_H__ #define __STB_INCLUDE_STB_TRUETYPE_H__ #ifdef STBTT_STATIC #define STBTT_DEF static #else #define STBTT_DEF extern #endif #ifdef __cplusplus extern "C" { #endif // private structure typedef struct { unsigned char *data; int cursor; int size; } stbtt__buf; ////////////////////////////////////////////////////////////////////////////// // // TEXTURE BAKING API // // If you use this API, you only have to call two functions ever. // typedef struct { unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap float xoff,yoff,xadvance; } stbtt_bakedchar; STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) float pixel_height, // height of font in pixels unsigned char *pixels, int pw, int ph, // bitmap to be filled in int first_char, int num_chars, // characters to bake stbtt_bakedchar *chardata); // you allocate this, it's num_chars long // if return is positive, the first unused row of the bitmap // if return is negative, returns the negative of the number of characters that fit // if return is 0, no characters fit and no rows were used // This uses a very crappy packing. typedef struct { float x0,y0,s0,t0; // top-left float x1,y1,s1,t1; // bottom-right } stbtt_aligned_quad; STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *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 stbtt_aligned_quad *q, // output: quad to draw int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier // Call GetBakedQuad with char_index = 'character - first_char', and it // creates the quad you need to draw and advances the current position. // // The coordinate system used assumes y increases downwards. // // Characters will extend both above and below the current position; // see discussion of "BASELINE" above. // // 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. ////////////////////////////////////////////////////////////////////////////// // // NEW TEXTURE BAKING API // // This provides options for packing multiple fonts into one atlas, not // perfectly but better than nothing. typedef struct { unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap float xoff,yoff,xadvance; float xoff2,yoff2; } stbtt_packedchar; typedef struct stbtt_pack_context stbtt_pack_context; typedef struct stbtt_fontinfo stbtt_fontinfo; #ifndef STB_RECT_PACK_VERSION typedef struct stbrp_rect stbrp_rect; #endif STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); // Initializes a packing context stored in the passed-in stbtt_pack_context. // Future calls using this context will pack characters into the bitmap passed // in here: a 1-channel bitmap that is width * height. stride_in_bytes is // the distance from one row to the next (or 0 to mean they are packed tightly // together). "padding" is the amount of padding to leave between each // character (normally you want '1' for bitmaps you'll use as textures with // bilinear filtering). // // Returns 0 on failure, 1 on success. STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); // Cleans up the packing context and frees all memory. #define STBTT_POINT_SIZE(x) (-(x)) STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); // Creates character bitmaps from the font_index'th font found in fontdata (use // font_index=0 if you don't know what that is). It creates num_chars_in_range // bitmaps for characters with unicode values starting at first_unicode_char_in_range // and increasing. Data for how to render them is stored in chardata_for_range; // pass these to stbtt_GetPackedQuad to get back renderable quads. // // font_size is the full height of the character from ascender to descender, // as computed by stbtt_ScaleForPixelHeight. To use a point size as computed // by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() // and pass that result as 'font_size': // ..., 20 , ... // font max minus min y is 20 pixels tall // ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall typedef struct { float font_size; int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints int num_chars; stbtt_packedchar *chardata_for_range; // output unsigned char h_oversample, v_oversample; // don't set these, they're used internally } stbtt_pack_range; STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); // Creates character bitmaps from multiple ranges of characters stored in // ranges. This will usually create a better-packed bitmap than multiple // calls to stbtt_PackFontRange. Note that you can call this multiple // times within a single PackBegin/PackEnd. STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); // Oversampling a font increases the quality by allowing higher-quality subpixel // positioning, and is especially valuable at smaller text sizes. // // This function sets the amount of oversampling for all following calls to // stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given // pack context. The default (no oversampling) is achieved by h_oversample=1 // and v_oversample=1. The total number of pixels required is // h_oversample*v_oversample larger than the default; for example, 2x2 // oversampling requires 4x the storage of 1x1. For best results, render // oversampled textures with bilinear filtering. Look at the readme in // stb/tests/oversample for information about oversampled fonts // // 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 received 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 stbtt_aligned_quad *q, // output: quad to draw int align_to_integer); STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); // Calling these functions in sequence is roughly equivalent to calling // stbtt_PackFontRanges(). If you more control over the packing of multiple // fonts, or if you want to pack custom data into a font texture, take a look // at the source to of stbtt_PackFontRanges() and create a custom version // using these functions, e.g. call GatherRects multiple times, // building up a single array of rects, then call PackRects once, // then call RenderIntoRects repeatedly. This may result in a // better packing than calling PackFontRanges multiple times // (or it may not). // this is an opaque structure that you shouldn't mess with which holds // all the context needed from PackBegin to PackEnd. struct stbtt_pack_context { void *user_allocator_context; void *pack_info; int width; int height; int stride_in_bytes; int padding; int skip_missing; unsigned int h_oversample, v_oversample; unsigned char *pixels; void *nodes; }; ////////////////////////////////////////////////////////////////////////////// // // FONT LOADING // // STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); // This function will determine the number of fonts in a font file. TrueType // collection (.ttc) files may contain multiple fonts, while TrueType font // (.ttf) files only contain one font. The number of fonts can be used for // indexing with the previous function where the index is between zero and one // less than the total fonts. If an error occurs, -1 is returned. STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); // Each .ttf/.ttc file may have more than one font. Each font has a sequential // index number starting from 0. Call this function to get the font offset for // a given index; it returns -1 if the index is out of range. A regular .ttf // 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 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 { void * userdata; unsigned char * data; // pointer to .ttf file int fontstart; // offset of start of font int numGlyphs; // number of glyphs, needed for range checking int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf int index_map; // a cmap mapping for our chosen character encoding int indexToLocFormat; // format needed to map from glyph index to glyph stbtt__buf cff; // cff font data stbtt__buf charstrings; // the charstring index stbtt__buf gsubrs; // global charstring subroutines index stbtt__buf subrs; // private charstring subroutines index stbtt__buf fontdicts; // array of font dicts stbtt__buf fdselect; // map from glyph to fontdict }; STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); // Given an offset into the file that defines a font, this function builds // the necessary cached info for the rest of the system. You must allocate // the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't // need to do anything special to free it, because the contents are pure // value data with no additional data structures. Returns 0 on failure. ////////////////////////////////////////////////////////////////////////////// // // CHARACTER TO GLYPH-INDEX CONVERSIOn STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); // If you're going to perform multiple operations on the same character // 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. ////////////////////////////////////////////////////////////////////////////// // // CHARACTER PROPERTIES // STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); // computes a scale factor to produce a font whose "height" is 'pixels' tall. // Height is measured as the distance from the highest ascender to the lowest // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics // and computing: // scale = pixels / (ascent - descent) // so if you prefer to measure height by the ascent only, use a similar calculation. STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); // computes a scale factor to produce a font whose EM size is mapped to // 'pixels' tall. This is probably what traditional APIs compute, but // I'm not positive. STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); // ascent is the coordinate above the baseline the font extends; descent // is the coordinate below the baseline the font extends (i.e. it is typically negative) // lineGap is the spacing between one row's descent and the next row's ascent... // so you should advance the vertical position by "*ascent - *descent + *lineGap" // these are expressed in unscaled coordinates, so you must multiply by // the scale factor for a given size STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); // analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 // table (specific to MS/Windows TTF files). // // Returns 1 on success (table present), 0 on failure. STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); // the bounding box around all possible characters STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); // leftSideBearing is the offset from the current horizontal position to the left edge of the character // advanceWidth is the offset from the current horizontal position to the next horizontal position // these are expressed in unscaled coordinates STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); // an additional amount to add to the 'advance' value between ch1 and ch2 STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); // Gets the bounding box of the visible part of the glyph, in unscaled coordinates STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); // as above, but takes one or more glyph indices for greater efficiency typedef struct stbtt_kerningentry { int glyph1; // use stbtt_FindGlyphIndex int glyph2; int advance; } stbtt_kerningentry; STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); // Retrieves a complete list of all of the kerning pairs provided by the font // stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. // The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) ////////////////////////////////////////////////////////////////////////////// // // GLYPH SHAPES (you probably don't need these, but they have to go before // the bitmaps for C declaration-order reasons) // #ifndef STBTT_vmove // you can predefine these to use different values (but why?) enum { STBTT_vmove=1, STBTT_vline, STBTT_vcurve, STBTT_vcubic }; #endif #ifndef stbtt_vertex // you can predefine this to use different values // (we share this with other code at RAD) #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file typedef struct { stbtt_vertex_type x,y,cx,cy,cx1,cy1; unsigned char type,padding; } stbtt_vertex; #endif STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); // returns non-zero if nothing is drawn for this glyph STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); // returns # of vertices and fills *vertices with the pointer to them // these are expressed in "unscaled" coordinates // // 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 // draws a quadratic bezier from previous endpoint to // its x,y, using cx,cy as the bezier control point. STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); // frees the data allocated above STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); // fills svg with the character's SVG data. // returns data size or 0 if SVG not found. ////////////////////////////////////////////////////////////////////////////// // // BITMAP RENDERING // STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); // frees the bitmap allocated below STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); // allocates a large-enough single-channel 8bpp bitmap and renders the // specified character/glyph at the specified scale into it, with // antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). // *width & *height are filled out with the width & height of the bitmap, // which is stored left-to-right, top-to-bottom. // // xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); // the same as stbtt_GetCodepointBitmap, but you can specify a subpixel // shift for the character STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the // width and height and positioning info for it first. STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel // shift for the character STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); // same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering // is performed (see stbtt_PackSetOversampling) STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); // get the bbox of the bitmap centered around the glyph origin; so the // bitmap width is ix1-ix0, height is iy1-iy0, and location to place // the bitmap top left is (leftSideBearing*scale,iy0). // (Note that the bitmap uses y-increases-down, but the shape uses // y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); // same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel // shift for the character // the following functions are equivalent to the above functions, but operate // on glyph indices instead of Unicode codepoints (for efficiency) STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); // @TODO: don't expose this structure typedef struct { int w,h,stride; unsigned char *pixels; } stbtt__bitmap; // rasterize a shape with quadratic beziers into a bitmap STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into float flatness_in_pixels, // allowable error of curve in pixels stbtt_vertex *vertices, // array of vertices defining shape int num_verts, // number of vertices in above array float scale_x, float scale_y, // scale applied to input vertices float shift_x, float shift_y, // translation applied to input vertices int x_off, int y_off, // another translation applied to input int invert, // if non-zero, vertically flip shape void *userdata); // context for to STBTT_MALLOC ////////////////////////////////////////////////////////////////////////////// // // Signed Distance Function (or Field) rendering STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); // frees the SDF bitmap allocated below STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); 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 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 // padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), // which allows effects like bit outlines // onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) // pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) // if positive, > onedge_value is inside; if negative, < onedge_value is inside // width,height -- output height & width of the SDF bitmap (including padding) // xoff,yoff -- output origin of the character // return value -- a 2D array of bytes 0..255, width*height in size // // pixel_dist_scale & onedge_value are a scale & bias that allows you to make // optimal use of the limited 0..255 for your application, trading off precision // and special effects. SDF values outside the range 0..255 are clamped to 0..255. // // Example: // scale = stbtt_ScaleForPixelHeight(22) // padding = 5 // onedge_value = 180 // pixel_dist_scale = 180/5.0 = 36.0 // // This will create an SDF bitmap in which the character is about 22 pixels // high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled // shape, sample the SDF at each pixel and fill the pixel if the SDF value // is greater than or equal to 180/255. (You'll actually want to antialias, // which is beyond the scope of this example.) Additionally, you can compute // offset outlines (e.g. to stroke the character border inside & outside, // or only outside). For example, to fill outside the character up to 3 SDF // pixels, you would compare against (180-36.0*3)/255 = 72/255. The above // choice of variables maps a range from 5 pixels outside the shape to // 2 pixels inside the shape to 0..255; this is intended primarily for apply // outside effects only (the interior range is needed to allow proper // antialiasing of the font at *smaller* sizes) // // The function computes the SDF analytically at each SDF pixel, not by e.g. // building a higher-res bitmap and approximating it. In theory the quality // should be as high as possible for an SDF of this size & representation, but // unclear if this is true in practice (perhaps building a higher-res bitmap // and computing from that can allow drop-out prevention). // // The algorithm has not been optimized at all, so expect it to be slow // if computing lots of characters or very large sizes. ////////////////////////////////////////////////////////////////////////////// // // Finding the right font... // // You should really just solve this offline, keep your own tables // of what font is what, and don't try to get it out of the .ttf file. // That's because getting it out of the .ttf file is really hard, because // the names in the file can appear in many possible encodings, in many // possible languages, and e.g. if you need a case-insensitive comparison, // the details of that depend on the encoding & language in a complex way // (actually underspecified in truetype, but also gigantic). // // But you can use the provided functions in two possible ways: // stbtt_FindMatchingFont() will use *case-sensitive* comparisons on // unicode-encoded names to try to find the font you want; // you can run this before calling stbtt_InitFont() // // stbtt_GetFontNameString() lets you get any of the various strings // from the file yourself and do your own comparisons on them. // You have to have called stbtt_InitFont() first. STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); // returns the offset (not index) of the font that matches, or -1 if none // if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". // if you use any other flag, use a font name like "Arial"; this checks // the 'macStyle' header field; i don't know if fonts set this consistently #define STBTT_MACSTYLE_DONTCARE 0 #define STBTT_MACSTYLE_BOLD 1 #define STBTT_MACSTYLE_ITALIC 2 #define STBTT_MACSTYLE_UNDERSCORE 4 #define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); // returns 1/0 whether the first string interpreted as utf8 is identical to // the second string interpreted as big-endian utf16... useful for strings from next func STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); // returns the string (which may be big-endian double byte, e.g. for unicode) // and puts the length in bytes in *length. // // some of the values for the IDs are below; for more see the truetype spec: // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html // http://www.microsoft.com/typography/otspec/name.htm enum { // platformID STBTT_PLATFORM_ID_UNICODE =0, STBTT_PLATFORM_ID_MAC =1, STBTT_PLATFORM_ID_ISO =2, STBTT_PLATFORM_ID_MICROSOFT =3 }; enum { // encodingID for STBTT_PLATFORM_ID_UNICODE STBTT_UNICODE_EID_UNICODE_1_0 =0, STBTT_UNICODE_EID_UNICODE_1_1 =1, STBTT_UNICODE_EID_ISO_10646 =2, STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 }; enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT STBTT_MS_EID_SYMBOL =0, STBTT_MS_EID_UNICODE_BMP =1, STBTT_MS_EID_SHIFTJIS =2, STBTT_MS_EID_UNICODE_FULL =10 }; enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 }; enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D }; enum { // languageID for STBTT_PLATFORM_ID_MAC STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 }; #ifdef __cplusplus } #endif #endif // __STB_INCLUDE_STB_TRUETYPE_H__ /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //// //// IMPLEMENTATION //// //// #ifdef STB_TRUETYPE_IMPLEMENTATION #ifndef STBTT_MAX_OVERSAMPLE #define STBTT_MAX_OVERSAMPLE 8 #endif #if STBTT_MAX_OVERSAMPLE > 255 #error "STBTT_MAX_OVERSAMPLE cannot be > 255" #endif typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; #ifndef STBTT_RASTERIZER_VERSION #define STBTT_RASTERIZER_VERSION 2 #endif #ifdef _MSC_VER #define STBTT__NOTUSED(v) (void)(v) #else #define STBTT__NOTUSED(v) (void)sizeof(v) #endif ////////////////////////////////////////////////////////////////////////// // // stbtt__buf helpers to parse data from file // static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) { if (b->cursor >= b->size) return 0; return b->data[b->cursor++]; } static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) { if (b->cursor >= b->size) return 0; return b->data[b->cursor]; } static void stbtt__buf_seek(stbtt__buf *b, int o) { STBTT_assert(!(o > b->size || o < 0)); b->cursor = (o > b->size || o < 0) ? b->size : o; } static void stbtt__buf_skip(stbtt__buf *b, int o) { stbtt__buf_seek(b, b->cursor + o); } static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) { stbtt_uint32 v = 0; int i; STBTT_assert(n >= 1 && n <= 4); for (i = 0; i < n; i++) v = (v << 8) | stbtt__buf_get8(b); return v; } static stbtt__buf stbtt__new_buf(const void *p, size_t size) { stbtt__buf r; STBTT_assert(size < 0x40000000); r.data = (stbtt_uint8*) p; r.size = (int) size; r.cursor = 0; return r; } #define stbtt__buf_get16(b) stbtt__buf_get((b), 2) #define stbtt__buf_get32(b) stbtt__buf_get((b), 4) static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) { stbtt__buf r = stbtt__new_buf(NULL, 0); if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; r.data = b->data + o; r.size = s; return r; } static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) { int count, start, offsize; start = b->cursor; count = stbtt__buf_get16(b); if (count) { offsize = stbtt__buf_get8(b); STBTT_assert(offsize >= 1 && offsize <= 4); stbtt__buf_skip(b, offsize * count); stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); } return stbtt__buf_range(b, start, b->cursor - start); } static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) { int b0 = stbtt__buf_get8(b); if (b0 >= 32 && b0 <= 246) return b0 - 139; else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; else if (b0 == 28) return stbtt__buf_get16(b); else if (b0 == 29) return stbtt__buf_get32(b); STBTT_assert(0); return 0; } static void stbtt__cff_skip_operand(stbtt__buf *b) { int v, b0 = stbtt__buf_peek8(b); STBTT_assert(b0 >= 28); if (b0 == 30) { stbtt__buf_skip(b, 1); while (b->cursor < b->size) { v = stbtt__buf_get8(b); if ((v & 0xF) == 0xF || (v >> 4) == 0xF) break; } } else { stbtt__cff_int(b); } } static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) { stbtt__buf_seek(b, 0); while (b->cursor < b->size) { int start = b->cursor, end, op; while (stbtt__buf_peek8(b) >= 28) stbtt__cff_skip_operand(b); end = b->cursor; op = stbtt__buf_get8(b); if (op == 12) op = stbtt__buf_get8(b) | 0x100; if (op == key) return stbtt__buf_range(b, start, end-start); } return stbtt__buf_range(b, 0, 0); } static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) { int i; stbtt__buf operands = stbtt__dict_get(b, key); for (i = 0; i < outcount && operands.cursor < operands.size; i++) out[i] = stbtt__cff_int(&operands); } static int stbtt__cff_index_count(stbtt__buf *b) { stbtt__buf_seek(b, 0); return stbtt__buf_get16(b); } static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) { int count, offsize, start, end; stbtt__buf_seek(&b, 0); count = stbtt__buf_get16(&b); offsize = stbtt__buf_get8(&b); STBTT_assert(i >= 0 && i < count); STBTT_assert(offsize >= 1 && offsize <= 4); stbtt__buf_skip(&b, i*offsize); start = stbtt__buf_get(&b, offsize); end = stbtt__buf_get(&b, offsize); return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); } ////////////////////////////////////////////////////////////////////////// // // accessors to parse data from file // // on platforms that don't allow misaligned reads, if we want to allow // truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE #define ttBYTE(p) (* (stbtt_uint8 *) (p)) #define ttCHAR(p) (* (stbtt_int8 *) (p)) #define ttFixed(p) ttLONG(p) static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } #define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) #define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) static int stbtt__isfont(stbtt_uint8 *font) { // check the version number if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts return 0; } // @OPTIMIZE: binary search static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) { stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); stbtt_uint32 tabledir = fontstart + 12; stbtt_int32 i; for (i=0; i < num_tables; ++i) { stbtt_uint32 loc = tabledir + 16*i; if (stbtt_tag(data+loc+0, tag)) return ttULONG(data+loc+8); } return 0; } static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) { // if it's just a font, there's only one valid index if (stbtt__isfont(font_collection)) return index == 0 ? 0 : -1; // check if it's a TTC if (stbtt_tag(font_collection, "ttcf")) { // version 1? if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { stbtt_int32 n = ttLONG(font_collection+8); if (index >= n) return -1; return ttULONG(font_collection+12+index*4); } } return -1; } static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) { // if it's just a font, there's only one valid font if (stbtt__isfont(font_collection)) return 1; // check if it's a TTC if (stbtt_tag(font_collection, "ttcf")) { // version 1? if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { return ttLONG(font_collection+8); } } return 0; } static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) { stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; stbtt__buf pdict; stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); if (!subrsoff) return stbtt__new_buf(NULL, 0); stbtt__buf_seek(&cff, private_loc[1]+subrsoff); return stbtt__cff_get_index(&cff); } // since most people won't use this, find this table the first time it's needed static int stbtt__get_svg(stbtt_fontinfo *info) { stbtt_uint32 t; if (info->svg < 0) { t = stbtt__find_table(info->data, info->fontstart, "SVG "); if (t) { stbtt_uint32 offset = ttULONG(info->data + t + 2); info->svg = t + offset; } else { info->svg = 0; } } return info->svg; } static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) { stbtt_uint32 cmap, t; stbtt_int32 i,numTables; info->data = data; info->fontstart = fontstart; info->cff = stbtt__new_buf(NULL, 0); cmap = stbtt__find_table(data, fontstart, "cmap"); // required info->loca = stbtt__find_table(data, fontstart, "loca"); // required info->head = stbtt__find_table(data, fontstart, "head"); // required info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required info->kern = stbtt__find_table(data, fontstart, "kern"); // not required info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required if (!cmap || !info->head || !info->hhea || !info->hmtx) return 0; if (info->glyf) { // required for truetype if (!info->loca) return 0; } else { // initialization for CFF / Type2 fonts (OTF) stbtt__buf b, topdict, topdictidx; stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; stbtt_uint32 cff; cff = stbtt__find_table(data, fontstart, "CFF "); if (!cff) return 0; info->fontdicts = stbtt__new_buf(NULL, 0); info->fdselect = stbtt__new_buf(NULL, 0); // @TODO this should use size from table (not 512MB) info->cff = stbtt__new_buf(data+cff, 512*1024*1024); b = info->cff; // read the header stbtt__buf_skip(&b, 2); stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize // @TODO the name INDEX could list multiple fonts, // but we just use the first one. stbtt__cff_get_index(&b); // name INDEX topdictidx = stbtt__cff_get_index(&b); topdict = stbtt__cff_index_get(topdictidx, 0); stbtt__cff_get_index(&b); // string INDEX info->gsubrs = stbtt__cff_get_index(&b); stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); info->subrs = stbtt__get_subrs(b, topdict); // we only support Type 2 charstrings if (cstype != 2) return 0; if (charstrings == 0) return 0; if (fdarrayoff) { // looks like a CID font if (!fdselectoff) return 0; stbtt__buf_seek(&b, fdarrayoff); info->fontdicts = stbtt__cff_get_index(&b); info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); } stbtt__buf_seek(&b, charstrings); info->charstrings = stbtt__cff_get_index(&b); } t = stbtt__find_table(data, fontstart, "maxp"); if (t) info->numGlyphs = ttUSHORT(data+t+4); else info->numGlyphs = 0xffff; info->svg = -1; // find a cmap encoding table we understand *now* to avoid searching // later. (todo: could make this installable) // the same regardless of glyph. numTables = ttUSHORT(data + cmap + 2); info->index_map = 0; for (i=0; i < numTables; ++i) { stbtt_uint32 encoding_record = cmap + 4 + 8 * i; // find an encoding we understand: switch(ttUSHORT(data+encoding_record)) { case STBTT_PLATFORM_ID_MICROSOFT: switch (ttUSHORT(data+encoding_record+2)) { case STBTT_MS_EID_UNICODE_BMP: case STBTT_MS_EID_UNICODE_FULL: // MS/Unicode info->index_map = cmap + ttULONG(data+encoding_record+4); break; } break; case STBTT_PLATFORM_ID_UNICODE: // Mac/iOS has these // all the encodingIDs are unicode, so we don't bother to check it info->index_map = cmap + ttULONG(data+encoding_record+4); break; } } if (info->index_map == 0) return 0; info->indexToLocFormat = ttUSHORT(data+info->head + 50); return 1; } STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) { stbtt_uint8 *data = info->data; stbtt_uint32 index_map = info->index_map; stbtt_uint16 format = ttUSHORT(data + index_map + 0); if (format == 0) { // apple byte encoding stbtt_int32 bytes = ttUSHORT(data + index_map + 2); if (unicode_codepoint < bytes-6) return ttBYTE(data + index_map + 6 + unicode_codepoint); return 0; } else if (format == 6) { stbtt_uint32 first = ttUSHORT(data + index_map + 6); stbtt_uint32 count = ttUSHORT(data + index_map + 8); if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); return 0; } else if (format == 2) { STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean return 0; } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; // do a binary search of the segments stbtt_uint32 endCount = index_map + 14; stbtt_uint32 search = endCount; if (unicode_codepoint > 0xffff) return 0; // they lie from endCount .. endCount + segCount // but searchRange is the nearest power of two, so... if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) search += rangeShift*2; // now decrement to bias correctly to find smallest search -= 2; while (entrySelector) { stbtt_uint16 end; searchRange >>= 1; end = ttUSHORT(data + search + searchRange*2); if (unicode_codepoint > end) search += searchRange*2; --entrySelector; } search += 2; { stbtt_uint16 offset, start, last; stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); last = ttUSHORT(data + endCount + 2*item); if (unicode_codepoint < start || unicode_codepoint > last) return 0; offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); if (offset == 0) return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); } } else if (format == 12 || format == 13) { stbtt_uint32 ngroups = ttULONG(data+index_map+12); stbtt_int32 low,high; low = 0; high = (stbtt_int32)ngroups; // Binary search the right group. while (low < high) { stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); if ((stbtt_uint32) unicode_codepoint < start_char) high = mid; else if ((stbtt_uint32) unicode_codepoint > end_char) low = mid+1; else { stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); if (format == 12) return start_glyph + unicode_codepoint-start_char; else // format == 13 return start_glyph; } } return 0; // not found } // @TODO STBTT_assert(0); return 0; } STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) { return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); } static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) { v->type = type; v->x = (stbtt_int16) x; v->y = (stbtt_int16) y; v->cx = (stbtt_int16) cx; v->cy = (stbtt_int16) cy; } static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) { int g1,g2; STBTT_assert(!info->cff.size); if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format if (info->indexToLocFormat == 0) { g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; } else { g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); } return g1==g2 ? -1 : g1; // if length is 0, return -1 } static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) { if (info->cff.size) { stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); } else { int g = stbtt__GetGlyfOffset(info, glyph_index); if (g < 0) return 0; if (x0) *x0 = ttSHORT(info->data + g + 2); if (y0) *y0 = ttSHORT(info->data + g + 4); if (x1) *x1 = ttSHORT(info->data + g + 6); if (y1) *y1 = ttSHORT(info->data + g + 8); } return 1; } STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) { return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); } STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) { stbtt_int16 numberOfContours; int g; if (info->cff.size) return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; g = stbtt__GetGlyfOffset(info, glyph_index); if (g < 0) return 1; numberOfContours = ttSHORT(info->data + g); return numberOfContours == 0; } static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) { if (start_off) { if (was_off) stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); } else { if (was_off) stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); else stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); } return num_vertices; } static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { stbtt_int16 numberOfContours; stbtt_uint8 *endPtsOfContours; stbtt_uint8 *data = info->data; stbtt_vertex *vertices=0; int num_vertices=0; int g = stbtt__GetGlyfOffset(info, glyph_index); *pvertices = NULL; if (g < 0) return 0; numberOfContours = ttSHORT(data + g); if (numberOfContours > 0) { stbtt_uint8 flags=0,flagcount; stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; stbtt_uint8 *points; endPtsOfContours = (data + g + 10); ins = ttUSHORT(data + g + 10 + numberOfContours * 2); points = data + g + 10 + numberOfContours * 2 + 2 + ins; n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); m = n + 2*numberOfContours; // a loose bound on how many vertices we might need vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); if (vertices == 0) return 0; next_move = 0; flagcount=0; // in first pass, we load uninterpreted data into the allocated array // above, shifted to the end of the array so we won't overwrite it when // we create our final data starting from the front off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated // first load flags for (i=0; i < n; ++i) { if (flagcount == 0) { flags = *points++; if (flags & 8) flagcount = *points++; } else --flagcount; vertices[off+i].type = flags; } // now load x coordinates x=0; for (i=0; i < n; ++i) { flags = vertices[off+i].type; if (flags & 2) { stbtt_int16 dx = *points++; x += (flags & 16) ? dx : -dx; // ??? } else { if (!(flags & 16)) { x = x + (stbtt_int16) (points[0]*256 + points[1]); points += 2; } } vertices[off+i].x = (stbtt_int16) x; } // now load y coordinates y=0; for (i=0; i < n; ++i) { flags = vertices[off+i].type; if (flags & 4) { stbtt_int16 dy = *points++; y += (flags & 32) ? dy : -dy; // ??? } else { if (!(flags & 32)) { y = y + (stbtt_int16) (points[0]*256 + points[1]); points += 2; } } vertices[off+i].y = (stbtt_int16) y; } // now convert them to our format num_vertices=0; sx = sy = cx = cy = scx = scy = 0; for (i=0; i < n; ++i) { flags = vertices[off+i].type; x = (stbtt_int16) vertices[off+i].x; y = (stbtt_int16) vertices[off+i].y; if (next_move == i) { if (i != 0) num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); // now start the new one start_off = !(flags & 1); if (start_off) { // if we start off with an off-curve point, then when we need to find a point on the curve // where we can start, and we need to save some state for when we wraparound. scx = x; scy = y; if (!(vertices[off+i+1].type & 1)) { // next point is also a curve point, so interpolate an on-point curve sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; } else { // otherwise just use the next point as our start point sx = (stbtt_int32) vertices[off+i+1].x; sy = (stbtt_int32) vertices[off+i+1].y; ++i; // we're using point i+1 as the starting point, so skip it } } else { sx = x; sy = y; } stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); was_off = 0; next_move = 1 + ttUSHORT(endPtsOfContours+j*2); ++j; } else { if (!(flags & 1)) { // if it's a curve if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); cx = x; cy = y; was_off = 1; } else { if (was_off) stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); else stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); was_off = 0; } } } num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); } else if (numberOfContours < 0) { // Compound shapes. int more = 1; stbtt_uint8 *comp = data + g + 10; num_vertices = 0; vertices = 0; while (more) { stbtt_uint16 flags, gidx; int comp_num_verts = 0, i; stbtt_vertex *comp_verts = 0, *tmp = 0; float mtx[6] = {1,0,0,1,0,0}, m, n; flags = ttSHORT(comp); comp+=2; gidx = ttSHORT(comp); comp+=2; if (flags & 2) { // XY values if (flags & 1) { // shorts mtx[4] = ttSHORT(comp); comp+=2; mtx[5] = ttSHORT(comp); comp+=2; } else { mtx[4] = ttCHAR(comp); comp+=1; mtx[5] = ttCHAR(comp); comp+=1; } } else { // @TODO handle matching point STBTT_assert(0); } if (flags & (1<<3)) { // WE_HAVE_A_SCALE mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; mtx[1] = mtx[2] = 0; } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; mtx[1] = mtx[2] = 0; mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; } // Find transformation scales. m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); // Get indexed glyph. comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); if (comp_num_verts > 0) { // Transform vertices. for (i = 0; i < comp_num_verts; ++i) { stbtt_vertex* v = &comp_verts[i]; stbtt_vertex_type x,y; x=v->x; y=v->y; v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); x=v->cx; y=v->cy; v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); } // Append vertices. tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); if (!tmp) { if (vertices) STBTT_free(vertices, info->userdata); if (comp_verts) STBTT_free(comp_verts, info->userdata); return 0; } if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); if (vertices) STBTT_free(vertices, info->userdata); vertices = tmp; STBTT_free(comp_verts, info->userdata); num_vertices += comp_num_verts; } // More components ? more = flags & (1<<5); } } else { // numberOfCounters == 0, do nothing } *pvertices = vertices; return num_vertices; } typedef struct { int bounds; int started; float first_x, first_y; float x, y; stbtt_int32 min_x, max_x, min_y, max_y; stbtt_vertex *pvertices; int num_vertices; } stbtt__csctx; #define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) { if (x > c->max_x || !c->started) c->max_x = x; if (y > c->max_y || !c->started) c->max_y = y; if (x < c->min_x || !c->started) c->min_x = x; if (y < c->min_y || !c->started) c->min_y = y; c->started = 1; } static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) { if (c->bounds) { stbtt__track_vertex(c, x, y); if (type == STBTT_vcubic) { stbtt__track_vertex(c, cx, cy); stbtt__track_vertex(c, cx1, cy1); } } else { stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; } c->num_vertices++; } static void stbtt__csctx_close_shape(stbtt__csctx *ctx) { if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); } static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) { stbtt__csctx_close_shape(ctx); ctx->first_x = ctx->x = ctx->x + dx; ctx->first_y = ctx->y = ctx->y + dy; stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); } static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) { ctx->x += dx; ctx->y += dy; stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); } static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) { float cx1 = ctx->x + dx1; float cy1 = ctx->y + dy1; float cx2 = cx1 + dx2; float cy2 = cy1 + dy2; ctx->x = cx2 + dx3; ctx->y = cy2 + dy3; stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); } static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) { int count = stbtt__cff_index_count(&idx); int bias = 107; if (count >= 33900) bias = 32768; else if (count >= 1240) bias = 1131; n += bias; if (n < 0 || n >= count) return stbtt__new_buf(NULL, 0); return stbtt__cff_index_get(idx, n); } static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) { stbtt__buf fdselect = info->fdselect; int nranges, start, end, v, fmt, fdselector = -1, i; stbtt__buf_seek(&fdselect, 0); fmt = stbtt__buf_get8(&fdselect); if (fmt == 0) { // untested stbtt__buf_skip(&fdselect, glyph_index); fdselector = stbtt__buf_get8(&fdselect); } else if (fmt == 3) { nranges = stbtt__buf_get16(&fdselect); start = stbtt__buf_get16(&fdselect); for (i = 0; i < nranges; i++) { v = stbtt__buf_get8(&fdselect); end = stbtt__buf_get16(&fdselect); if (glyph_index >= start && glyph_index < end) { fdselector = v; break; } start = end; } } if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422 return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); } static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) { int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; int has_subrs = 0, clear_stack; float s[48]; stbtt__buf subr_stack[10], subrs = info->subrs, b; float f; #define STBTT__CSERR(s) (0) // this currently ignores the initial width value, which isn't needed if we have hmtx b = stbtt__cff_index_get(info->charstrings, glyph_index); while (b.cursor < b.size) { i = 0; clear_stack = 1; b0 = stbtt__buf_get8(&b); switch (b0) { // @TODO implement hinting case 0x13: // hintmask case 0x14: // cntrmask if (in_header) maskbits += (sp / 2); // implicit "vstem" in_header = 0; stbtt__buf_skip(&b, (maskbits + 7) / 8); break; case 0x01: // hstem case 0x03: // vstem case 0x12: // hstemhm case 0x17: // vstemhm maskbits += (sp / 2); break; case 0x15: // rmoveto in_header = 0; if (sp < 2) return STBTT__CSERR("rmoveto stack"); stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); break; case 0x04: // vmoveto in_header = 0; if (sp < 1) return STBTT__CSERR("vmoveto stack"); stbtt__csctx_rmove_to(c, 0, s[sp-1]); break; case 0x16: // hmoveto in_header = 0; if (sp < 1) return STBTT__CSERR("hmoveto stack"); stbtt__csctx_rmove_to(c, s[sp-1], 0); break; case 0x05: // rlineto if (sp < 2) return STBTT__CSERR("rlineto stack"); for (; i + 1 < sp; i += 2) stbtt__csctx_rline_to(c, s[i], s[i+1]); break; // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical // starting from a different place. case 0x07: // vlineto if (sp < 1) return STBTT__CSERR("vlineto stack"); goto vlineto; case 0x06: // hlineto if (sp < 1) return STBTT__CSERR("hlineto stack"); for (;;) { if (i >= sp) break; stbtt__csctx_rline_to(c, s[i], 0); i++; vlineto: if (i >= sp) break; stbtt__csctx_rline_to(c, 0, s[i]); i++; } break; case 0x1F: // hvcurveto if (sp < 4) return STBTT__CSERR("hvcurveto stack"); goto hvcurveto; case 0x1E: // vhcurveto if (sp < 4) return STBTT__CSERR("vhcurveto stack"); for (;;) { if (i + 3 >= sp) break; stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); i += 4; hvcurveto: if (i + 3 >= sp) break; stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); i += 4; } break; case 0x08: // rrcurveto if (sp < 6) return STBTT__CSERR("rcurveline stack"); for (; i + 5 < sp; i += 6) stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); break; case 0x18: // rcurveline if (sp < 8) return STBTT__CSERR("rcurveline stack"); for (; i + 5 < sp - 2; i += 6) stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); stbtt__csctx_rline_to(c, s[i], s[i+1]); break; case 0x19: // rlinecurve if (sp < 8) return STBTT__CSERR("rlinecurve stack"); for (; i + 1 < sp - 6; i += 2) stbtt__csctx_rline_to(c, s[i], s[i+1]); if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); break; case 0x1A: // vvcurveto case 0x1B: // hhcurveto if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); f = 0.0; if (sp & 1) { f = s[i]; i++; } for (; i + 3 < sp; i += 4) { if (b0 == 0x1B) stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); else stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); f = 0.0; } break; case 0x0A: // callsubr if (!has_subrs) { if (info->fdselect.size) subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); has_subrs = 1; } // FALLTHROUGH case 0x1D: // callgsubr if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); v = (int) s[--sp]; if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); subr_stack[subr_stack_height++] = b; b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); if (b.size == 0) return STBTT__CSERR("subr not found"); b.cursor = 0; clear_stack = 0; break; case 0x0B: // return if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); b = subr_stack[--subr_stack_height]; clear_stack = 0; break; case 0x0E: // endchar stbtt__csctx_close_shape(c); return 1; case 0x0C: { // two-byte escape float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; float dx, dy; int b1 = stbtt__buf_get8(&b); switch (b1) { // @TODO These "flex" implementations ignore the flex-depth and resolution, // and always draw beziers. case 0x22: // hflex if (sp < 7) return STBTT__CSERR("hflex stack"); dx1 = s[0]; dx2 = s[1]; dy2 = s[2]; dx3 = s[3]; dx4 = s[4]; dx5 = s[5]; dx6 = s[6]; stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); break; case 0x23: // flex if (sp < 13) return STBTT__CSERR("flex stack"); dx1 = s[0]; dy1 = s[1]; dx2 = s[2]; dy2 = s[3]; dx3 = s[4]; dy3 = s[5]; dx4 = s[6]; dy4 = s[7]; dx5 = s[8]; dy5 = s[9]; dx6 = s[10]; dy6 = s[11]; //fd is s[12] stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); break; case 0x24: // hflex1 if (sp < 9) return STBTT__CSERR("hflex1 stack"); dx1 = s[0]; dy1 = s[1]; dx2 = s[2]; dy2 = s[3]; dx3 = s[4]; dx4 = s[5]; dx5 = s[6]; dy5 = s[7]; dx6 = s[8]; stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); break; case 0x25: // flex1 if (sp < 11) return STBTT__CSERR("flex1 stack"); dx1 = s[0]; dy1 = s[1]; dx2 = s[2]; dy2 = s[3]; dx3 = s[4]; dy3 = s[5]; dx4 = s[6]; dy4 = s[7]; dx5 = s[8]; dy5 = s[9]; dx6 = dy6 = s[10]; dx = dx1+dx2+dx3+dx4+dx5; dy = dy1+dy2+dy3+dy4+dy5; if (STBTT_fabs(dx) > STBTT_fabs(dy)) dy6 = -dy; else dx6 = -dx; stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); break; default: return STBTT__CSERR("unimplemented"); } } break; default: if (b0 != 255 && b0 != 28 && b0 < 32) return STBTT__CSERR("reserved operator"); // push immediate if (b0 == 255) { f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; } else { stbtt__buf_skip(&b, -1); f = (float)(stbtt_int16)stbtt__cff_int(&b); } if (sp >= 48) return STBTT__CSERR("push stack overflow"); s[sp++] = f; clear_stack = 0; break; } if (clear_stack) sp = 0; } return STBTT__CSERR("no endchar"); #undef STBTT__CSERR } static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { // runs the charstring twice, once to count and once to output (to avoid realloc) stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); output_ctx.pvertices = *pvertices; if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); return output_ctx.num_vertices; } } *pvertices = NULL; return 0; } static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) { stbtt__csctx c = STBTT__CSCTX_INIT(1); int r = stbtt__run_charstring(info, glyph_index, &c); if (x0) *x0 = r ? c.min_x : 0; if (y0) *y0 = r ? c.min_y : 0; if (x1) *x1 = r ? c.max_x : 0; if (y1) *y1 = r ? c.max_y : 0; return r ? c.num_vertices : 0; } STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { if (!info->cff.size) return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); else return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); } STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) { stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); if (glyph_index < numOfLongHorMetrics) { if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); } else { if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); } } STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) { stbtt_uint8 *data = info->data + info->kern; // we only look at the first table. it must be 'horizontal' and format 0. if (!info->kern) return 0; if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 return 0; if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format return 0; return ttUSHORT(data+10); } STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) { stbtt_uint8 *data = info->data + info->kern; int k, length; // we only look at the first table. it must be 'horizontal' and format 0. if (!info->kern) return 0; if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 return 0; if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format return 0; length = ttUSHORT(data+10); if (table_length < length) length = table_length; for (k = 0; k < length; k++) { table[k].glyph1 = ttUSHORT(data+18+(k*6)); table[k].glyph2 = ttUSHORT(data+20+(k*6)); table[k].advance = ttSHORT(data+22+(k*6)); } return length; } static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) { stbtt_uint8 *data = info->data + info->kern; stbtt_uint32 needle, straw; int l, r, m; // we only look at the first table. it must be 'horizontal' and format 0. if (!info->kern) return 0; if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 return 0; if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format return 0; l = 0; r = ttUSHORT(data+10) - 1; needle = glyph1 << 16 | glyph2; while (l <= r) { m = (l + r) >> 1; straw = ttULONG(data+18+(m*6)); // note: unaligned read if (needle < straw) r = m - 1; else if (needle > straw) l = m + 1; else return ttSHORT(data+22+(m*6)); } return 0; } static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) { stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); switch (coverageFormat) { case 1: { stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); // Binary search. stbtt_int32 l=0, r=glyphCount-1, m; int straw, needle=glyph; while (l <= r) { stbtt_uint8 *glyphArray = coverageTable + 4; stbtt_uint16 glyphID; m = (l + r) >> 1; glyphID = ttUSHORT(glyphArray + 2 * m); straw = glyphID; if (needle < straw) r = m - 1; else if (needle > straw) l = m + 1; else { return m; } } break; } case 2: { stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); stbtt_uint8 *rangeArray = coverageTable + 4; // Binary search. stbtt_int32 l=0, r=rangeCount-1, m; int strawStart, strawEnd, needle=glyph; while (l <= r) { stbtt_uint8 *rangeRecord; m = (l + r) >> 1; rangeRecord = rangeArray + 6 * m; strawStart = ttUSHORT(rangeRecord); strawEnd = ttUSHORT(rangeRecord + 2); if (needle < strawStart) r = m - 1; else if (needle > strawEnd) l = m + 1; else { stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); return startCoverageIndex + glyph - strawStart; } } break; } default: return -1; // unsupported } return -1; } static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) { stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); switch (classDefFormat) { case 1: { stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); stbtt_uint8 *classDef1ValueArray = classDefTable + 6; if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); break; } case 2: { stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); stbtt_uint8 *classRangeRecords = classDefTable + 4; // Binary search. stbtt_int32 l=0, r=classRangeCount-1, m; int strawStart, strawEnd, needle=glyph; while (l <= r) { stbtt_uint8 *classRangeRecord; m = (l + r) >> 1; classRangeRecord = classRangeRecords + 6 * m; strawStart = ttUSHORT(classRangeRecord); strawEnd = ttUSHORT(classRangeRecord + 2); if (needle < strawStart) r = m - 1; else if (needle > strawEnd) l = m + 1; else return (stbtt_int32)ttUSHORT(classRangeRecord + 4); } break; } default: return -1; // Unsupported definition type, return an error. } // "All glyphs not assigned to a class fall into class 0". (OpenType spec) return 0; } // Define to STBTT_assert(x) if you want to break on unimplemented formats. #define STBTT_GPOS_TODO_assert(x) static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) { stbtt_uint16 lookupListOffset; stbtt_uint8 *lookupList; stbtt_uint16 lookupCount; stbtt_uint8 *data; stbtt_int32 i, sti; if (!info->gpos) return 0; data = info->data + info->gpos; if (ttUSHORT(data+0) != 1) return 0; // Major version 1 if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 lookupListOffset = ttUSHORT(data+8); lookupList = data + lookupListOffset; lookupCount = ttUSHORT(lookupList); for (i=0; i= pairSetCount) return 0; needle=glyph2; r=pairValueCount-1; l=0; // Binary search. while (l <= r) { stbtt_uint16 secondGlyph; stbtt_uint8 *pairValue; m = (l + r) >> 1; pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; secondGlyph = ttUSHORT(pairValue); straw = secondGlyph; if (needle < straw) r = m - 1; else if (needle > straw) l = m + 1; else { stbtt_int16 xAdvance = ttSHORT(pairValue + 2); return xAdvance; } } } else return 0; break; } case 2: { stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); stbtt_uint16 class1Count = ttUSHORT(table + 12); stbtt_uint16 class2Count = ttUSHORT(table + 14); stbtt_uint8 *class1Records, *class2Records; stbtt_int16 xAdvance; if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed class1Records = table + 16; class2Records = class1Records + 2 * (glyph1class * class2Count); xAdvance = ttSHORT(class2Records + 2 * glyph2class); return xAdvance; } else return 0; break; } default: return 0; // Unsupported position format } } } return 0; } STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) { int xAdvance = 0; if (info->gpos) xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); else if (info->kern) xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); return xAdvance; } STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) { if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs return 0; return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); } STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) { stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); } STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) { if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); if (descent) *descent = ttSHORT(info->data+info->hhea + 6); if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); } STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) { int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); if (!tab) return 0; if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); return 1; } STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) { *x0 = ttSHORT(info->data + info->head + 36); *y0 = ttSHORT(info->data + info->head + 38); *x1 = ttSHORT(info->data + info->head + 40); *y1 = ttSHORT(info->data + info->head + 42); } STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) { int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); return (float) height / fheight; } STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) { int unitsPerEm = ttUSHORT(info->data + info->head + 18); return pixels / unitsPerEm; } STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) { STBTT_free(v, info->userdata); } STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) { int i; stbtt_uint8 *data = info->data; stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); int numEntries = ttUSHORT(svg_doc_list); stbtt_uint8 *svg_docs = svg_doc_list + 2; for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) return svg_doc; } return 0; } STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) { stbtt_uint8 *data = info->data; stbtt_uint8 *svg_doc; if (info->svg == 0) return 0; svg_doc = stbtt_FindSVGDoc(info, gl); if (svg_doc != NULL) { *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); return ttULONG(svg_doc + 8); } else { return 0; } } STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) { return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); } ////////////////////////////////////////////////////////////////////////////// // // antialiasing software rasterizer // STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) { int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { // e.g. space character if (ix0) *ix0 = 0; if (iy0) *iy0 = 0; if (ix1) *ix1 = 0; if (iy1) *iy1 = 0; } else { // move to integral bboxes (treating pixels as little squares, what pixels get touched)? if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); } } STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); } STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); } STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); } ////////////////////////////////////////////////////////////////////////////// // // Rasterizer typedef struct stbtt__hheap_chunk { struct stbtt__hheap_chunk *next; } stbtt__hheap_chunk; typedef struct stbtt__hheap { struct stbtt__hheap_chunk *head; void *first_free; int num_remaining_in_head_chunk; } stbtt__hheap; static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) { if (hh->first_free) { void *p = hh->first_free; hh->first_free = * (void **) p; return p; } else { if (hh->num_remaining_in_head_chunk == 0) { int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); if (c == NULL) return NULL; c->next = hh->head; hh->head = c; hh->num_remaining_in_head_chunk = count; } --hh->num_remaining_in_head_chunk; return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; } } static void stbtt__hheap_free(stbtt__hheap *hh, void *p) { *(void **) p = hh->first_free; hh->first_free = p; } static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) { stbtt__hheap_chunk *c = hh->head; while (c) { stbtt__hheap_chunk *n = c->next; STBTT_free(c, userdata); c = n; } } typedef struct stbtt__edge { float x0,y0, x1,y1; int invert; } stbtt__edge; typedef struct stbtt__active_edge { struct stbtt__active_edge *next; #if STBTT_RASTERIZER_VERSION==1 int x,dx; float ey; int direction; #elif STBTT_RASTERIZER_VERSION==2 float fx,fdx,fdy; float direction; float sy; float ey; #else #error "Unrecognized value of STBTT_RASTERIZER_VERSION" #endif } stbtt__active_edge; #if STBTT_RASTERIZER_VERSION == 1 #define STBTT_FIXSHIFT 10 #define STBTT_FIX (1 << STBTT_FIXSHIFT) #define STBTT_FIXMASK (STBTT_FIX-1) static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) { stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); STBTT_assert(z != NULL); if (!z) return z; // round dx down to avoid overshooting if (dxdy < 0) z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); else z->dx = STBTT_ifloor(STBTT_FIX * dxdy); z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount z->x -= off_x * STBTT_FIX; z->ey = e->y1; z->next = 0; z->direction = e->invert ? 1 : -1; return z; } #elif STBTT_RASTERIZER_VERSION == 2 static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) { stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); STBTT_assert(z != NULL); //STBTT_assert(e->y0 <= start_point); if (!z) return z; z->fdx = dxdy; z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; z->fx = e->x0 + dxdy * (start_point - e->y0); z->fx -= off_x; z->direction = e->invert ? 1.0f : -1.0f; z->sy = e->y0; z->ey = e->y1; z->next = 0; return z; } #else #error "Unrecognized value of STBTT_RASTERIZER_VERSION" #endif #if STBTT_RASTERIZER_VERSION == 1 // note: this routine clips fills that extend off the edges... ideally this // wouldn't happen, but it could happen if the truetype glyph bounding boxes // are wrong, or if the user supplies a too-small bitmap static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) { // non-zero winding fill int x0=0, w=0; while (e) { if (w == 0) { // if we're currently at zero, we need to record the edge start point x0 = e->x; w += e->direction; } else { int x1 = e->x; w += e->direction; // if we went to zero, we need to draw if (w == 0) { int i = x0 >> STBTT_FIXSHIFT; int j = x1 >> STBTT_FIXSHIFT; if (i < len && j >= 0) { if (i == j) { // x0,x1 are the same pixel, so compute combined coverage scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); } else { if (i >= 0) // add antialiasing for x0 scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); else i = -1; // clip if (j < len) // add antialiasing for x1 scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); else j = len; // clip for (++i; i < j; ++i) // fill pixels between x0 and x1 scanline[i] = scanline[i] + (stbtt_uint8) max_weight; } } } } e = e->next; } } static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) { stbtt__hheap hh = { 0, 0, 0 }; stbtt__active_edge *active = NULL; int y,j=0; int max_weight = (255 / vsubsample); // weight per vertical scanline int s; // vertical subsample index unsigned char scanline_data[512], *scanline; if (result->w > 512) scanline = (unsigned char *) STBTT_malloc(result->w, userdata); else scanline = scanline_data; y = off_y * vsubsample; e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; while (j < result->h) { STBTT_memset(scanline, 0, result->w); for (s=0; s < vsubsample; ++s) { // find center of pixel for this scanline float scan_y = y + 0.5f; stbtt__active_edge **step = &active; // update all active edges; // remove all active edges that terminate before the center of this scanline while (*step) { stbtt__active_edge * z = *step; if (z->ey <= scan_y) { *step = z->next; // delete from list STBTT_assert(z->direction); z->direction = 0; stbtt__hheap_free(&hh, z); } else { z->x += z->dx; // advance to position for current scanline step = &((*step)->next); // advance through list } } // resort the list if needed for(;;) { int changed=0; step = &active; while (*step && (*step)->next) { if ((*step)->x > (*step)->next->x) { stbtt__active_edge *t = *step; stbtt__active_edge *q = t->next; t->next = q->next; q->next = t; *step = q; changed = 1; } step = &(*step)->next; } if (!changed) break; } // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline while (e->y0 <= scan_y) { if (e->y1 > scan_y) { stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); if (z != NULL) { // find insertion point if (active == NULL) active = z; else if (z->x < active->x) { // insert at front z->next = active; active = z; } else { // find thing to insert AFTER stbtt__active_edge *p = active; while (p->next && p->next->x < z->x) p = p->next; // at this point, p->next->x is NOT < z->x z->next = p->next; p->next = z; } } } ++e; } // now process all active edges in XOR fashion if (active) stbtt__fill_active_edges(scanline, result->w, active, max_weight); ++y; } STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); ++j; } stbtt__hheap_cleanup(&hh, userdata); if (scanline != scanline_data) STBTT_free(scanline, userdata); } #elif STBTT_RASTERIZER_VERSION == 2 // the edge passed in here does not cross the vertical line at x or the vertical line at x+1 // (i.e. it has already been clipped to those) static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) { if (y0 == y1) return; STBTT_assert(y0 < y1); STBTT_assert(e->sy <= e->ey); if (y0 > e->ey) return; if (y1 < e->sy) return; if (y0 < e->sy) { x0 += (x1-x0) * (e->sy - y0) / (y1-y0); y0 = e->sy; } if (y1 > e->ey) { x1 += (x1-x0) * (e->ey - y1) / (y1-y0); y1 = e->ey; } if (x0 == x) STBTT_assert(x1 <= x+1); else if (x0 == x+1) STBTT_assert(x1 >= x); else if (x0 <= x) STBTT_assert(x1 <= x); else if (x0 >= x+1) STBTT_assert(x1 >= x+1); else STBTT_assert(x1 >= x && x1 <= x+1); if (x0 <= x && x1 <= x) scanline[x] += e->direction * (y1-y0); else if (x0 >= x+1 && x1 >= x+1) ; else { STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position } } static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) { STBTT_assert(top_width >= 0); STBTT_assert(bottom_width >= 0); return (top_width + bottom_width) / 2.0f * height; } static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) { return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); } static float stbtt__sized_triangle_area(float height, float width) { return height * width / 2; } static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) { float y_bottom = y_top+1; while (e) { // brute force every pixel // compute intersection points with top & bottom STBTT_assert(e->ey >= y_top); if (e->fdx == 0) { float x0 = e->fx; if (x0 < len) { if (x0 >= 0) { stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); } else { stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); } } } else { float x0 = e->fx; float dx = e->fdx; float xb = x0 + dx; float x_top, x_bottom; float sy0,sy1; float dy = e->fdy; STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); // compute endpoints of line segment clipped to this scanline (if the // line segment starts on this scanline. x0 is the intersection of the // line with y_top, but that may be off the line segment. if (e->sy > y_top) { x_top = x0 + dx * (e->sy - y_top); sy0 = e->sy; } else { x_top = x0; sy0 = y_top; } if (e->ey < y_bottom) { x_bottom = x0 + dx * (e->ey - y_top); sy1 = e->ey; } else { x_bottom = xb; sy1 = y_bottom; } if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { // from here on, we don't have to range check x values if ((int) x_top == (int) x_bottom) { float height; // simple case, only spans one pixel int x = (int) x_top; height = (sy1 - sy0) * e->direction; STBTT_assert(x >= 0 && x < len); scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); scanline_fill[x] += height; // everything right of this pixel is filled } else { int x,x1,x2; float y_crossing, y_final, step, sign, area; // covers 2+ pixels if (x_top > x_bottom) { // flip scanline vertically; signed area is the same float t; sy0 = y_bottom - (sy0 - y_top); sy1 = y_bottom - (sy1 - y_top); t = sy0, sy0 = sy1, sy1 = t; t = x_bottom, x_bottom = x_top, x_top = t; dx = -dx; dy = -dy; t = x0, x0 = xb, xb = t; } STBTT_assert(dy >= 0); STBTT_assert(dx >= 0); x1 = (int) x_top; x2 = (int) x_bottom; // compute intersection with y axis at x1+1 y_crossing = y_top + dy * (x1+1 - x0); // compute intersection with y axis at x2 y_final = y_top + dy * (x2 - x0); // x1 x_top x2 x_bottom // y_top +------|-----+------------+------------+--------|---+------------+ // | | | | | | // | | | | | | // sy0 | Txxxxx|............|............|............|............| // y_crossing | *xxxxx.......|............|............|............| // | | xxxxx..|............|............|............| // | | /- xx*xxxx........|............|............| // | | dy < | xxxxxx..|............|............| // y_final | | \- | xx*xxx.........|............| // sy1 | | | | xxxxxB...|............| // | | | | | | // | | | | | | // y_bottom +------------+------------+------------+------------+------------+ // // goal is to measure the area covered by '.' in each pixel // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 // @TODO: maybe test against sy1 rather than y_bottom? if (y_crossing > y_bottom) y_crossing = y_bottom; sign = e->direction; // area of the rectangle covered from sy0..y_crossing area = sign * (y_crossing-sy0); // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); // check if final y_crossing is blown up; no test case for this if (y_final > y_bottom) { int denom = (x2 - (x1+1)); y_final = y_bottom; if (denom != 0) { // [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316) dy = (y_final - y_crossing ) / denom; // if denom=0, y_final = y_crossing, so y_final <= y_bottom } } // in second pixel, area covered by line segment found in first pixel // is always a rectangle 1 wide * the height of that line segment; this // is exactly what the variable 'area' stores. it also gets a contribution // from the line segment within it. the THIRD pixel will get the first // pixel's rectangle contribution, the second pixel's rectangle contribution, // and its own contribution. the 'own contribution' is the same in every pixel except // the leftmost and rightmost, a trapezoid that slides down in each pixel. // the second pixel's contribution to the third pixel will be the // rectangle 1 wide times the height change in the second pixel, which is dy. step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, // which multiplied by 1-pixel-width is how much pixel area changes for each step in x // so the area advances by 'step' every time for (x = x1+1; x < x2; ++x) { scanline[x] += area + step/2; // area of trapezoid is 1*step/2 area += step; } STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down STBTT_assert(sy1 > y_final-0.01f); // area covered in the last pixel is the rectangle from all the pixels to the left, // plus the trapezoid filled by the line segment in this pixel all the way to the right edge scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); // the rest of the line is filled based on the total height of the line segment in this pixel scanline_fill[x2] += sign * (sy1-sy0); } } else { // if edge goes outside of box we're drawing, we require // clipping logic. since this does not match the intended use // of this library, we use a different, very slow brute // force implementation // note though that this does happen some of the time because // x_top and x_bottom can be extrapolated at the top & bottom of // the shape and actually lie outside the bounding box int x; for (x=0; x < len; ++x) { // cases: // // there can be up to two intersections with the pixel. any intersection // with left or right edges can be handled by splitting into two (or three) // regions. intersections with top & bottom do not necessitate case-wise logic. // // the old way of doing this found the intersections with the left & right edges, // then used some simple logic to produce up to three segments in sorted order // from top-to-bottom. however, this had a problem: if an x edge was epsilon // across the x border, then the corresponding y position might not be distinct // from the other y segment, and it might ignored as an empty segment. to avoid // that, we need to explicitly produce segments based on x positions. // rename variables to clearly-defined pairs float y0 = y_top; float x1 = (float) (x); float x2 = (float) (x+1); float x3 = xb; float y3 = y_bottom; // x = e->x + e->dx * (y-y_top) // (y-y_top) = (x - e->x) / e->dx // y = (x - e->x) / e->dx + y_top float y1 = (x - x0) / dx + y_top; float y2 = (x+1 - x0) / dx + y_top; if (x0 < x1 && x3 > x2) { // three segments descending down-right stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); } else if (x3 < x1 && x0 > x2) { // three segments descending down-left stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); } else { // one segment stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); } } } } e = e->next; } } // directly AA rasterize edges w/o supersampling static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) { stbtt__hheap hh = { 0, 0, 0 }; stbtt__active_edge *active = NULL; int y,j=0, i; float scanline_data[129], *scanline, *scanline2; STBTT__NOTUSED(vsubsample); if (result->w > 64) scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); else scanline = scanline_data; scanline2 = scanline + result->w; y = off_y; e[n].y0 = (float) (off_y + result->h) + 1; while (j < result->h) { // find center of pixel for this scanline float scan_y_top = y + 0.0f; float scan_y_bottom = y + 1.0f; stbtt__active_edge **step = &active; STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); // update all active edges; // remove all active edges that terminate before the top of this scanline while (*step) { stbtt__active_edge * z = *step; if (z->ey <= scan_y_top) { *step = z->next; // delete from list STBTT_assert(z->direction); z->direction = 0; stbtt__hheap_free(&hh, z); } else { step = &((*step)->next); // advance through list } } // insert all edges that start before the bottom of this scanline while (e->y0 <= scan_y_bottom) { if (e->y0 != e->y1) { stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); if (z != NULL) { 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; } } ++e; } // now process all active edges if (active) stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); { float sum = 0; for (i=0; i < result->w; ++i) { float k; int m; sum += scanline2[i]; k = scanline[i] + sum; k = (float) STBTT_fabs(k)*255 + 0.5f; m = (int) k; if (m > 255) m = 255; result->pixels[j*result->stride + i] = (unsigned char) m; } } // advance all the edges step = &active; while (*step) { stbtt__active_edge *z = *step; z->fx += z->fdx; // advance to position for current scanline step = &((*step)->next); // advance through list } ++y; ++j; } stbtt__hheap_cleanup(&hh, userdata); if (scanline != scanline_data) STBTT_free(scanline, userdata); } #else #error "Unrecognized value of STBTT_RASTERIZER_VERSION" #endif #define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) { int i,j; for (i=1; i < n; ++i) { stbtt__edge t = p[i], *a = &t; j = i; while (j > 0) { stbtt__edge *b = &p[j-1]; int c = STBTT__COMPARE(a,b); if (!c) break; p[j] = p[j-1]; --j; } if (i != j) p[j] = t; } } static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) { /* threshold for transitioning to insertion sort */ while (n > 12) { stbtt__edge t; int c01,c12,c,m,i,j; /* compute median of three */ m = n >> 1; c01 = STBTT__COMPARE(&p[0],&p[m]); c12 = STBTT__COMPARE(&p[m],&p[n-1]); /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ if (c01 != c12) { /* otherwise, we'll need to swap something else to middle */ int z; c = STBTT__COMPARE(&p[0],&p[n-1]); /* 0>mid && midn => n; 0 0 */ /* 0n: 0>n => 0; 0 n */ z = (c == c12) ? 0 : n-1; t = p[z]; p[z] = p[m]; p[m] = t; } /* now p[m] is the median-of-three */ /* swap it to the beginning so it won't move around */ t = p[0]; p[0] = p[m]; p[m] = t; /* partition loop */ i=1; j=n-1; for(;;) { /* handling of equality is crucial here */ /* for sentinels & efficiency with duplicates */ for (;;++i) { if (!STBTT__COMPARE(&p[i], &p[0])) break; } for (;;--j) { if (!STBTT__COMPARE(&p[0], &p[j])) break; } /* make sure we haven't crossed */ if (i >= j) break; t = p[i]; p[i] = p[j]; p[j] = t; ++i; --j; } /* recurse on smaller side, iterate on larger */ if (j < (n-i)) { stbtt__sort_edges_quicksort(p,j); p = p+i; n = n-i; } else { stbtt__sort_edges_quicksort(p+i, n-i); n = j; } } } static void stbtt__sort_edges(stbtt__edge *p, int n) { stbtt__sort_edges_quicksort(p, n); stbtt__sort_edges_ins_sort(p, n); } typedef struct { float x,y; } stbtt__point; static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) { float y_scale_inv = invert ? -scale_y : scale_y; stbtt__edge *e; int n,i,j,k,m; #if STBTT_RASTERIZER_VERSION == 1 int vsubsample = result->h < 8 ? 15 : 5; #elif STBTT_RASTERIZER_VERSION == 2 int vsubsample = 1; #else #error "Unrecognized value of STBTT_RASTERIZER_VERSION" #endif // vsubsample should divide 255 evenly; otherwise we won't reach full opacity // now we have to blow out the windings into explicit edge lists n = 0; for (i=0; i < windings; ++i) n += wcount[i]; e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel if (e == 0) return; n = 0; m=0; for (i=0; i < windings; ++i) { stbtt__point *p = pts + m; m += wcount[i]; j = wcount[i]-1; for (k=0; k < wcount[i]; j=k++) { int a=k,b=j; // skip the edge if horizontal if (p[j].y == p[k].y) continue; // add edge from j to k to the list e[n].invert = 0; if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { e[n].invert = 1; a=j,b=k; } e[n].x0 = p[a].x * scale_x + shift_x; e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; e[n].x1 = p[b].x * scale_x + shift_x; e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; ++n; } } // now sort the edges by their highest point (should snap to integer, and then by x) //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); stbtt__sort_edges(e, n); // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); STBTT_free(e, userdata); } static void stbtt__add_point(stbtt__point *points, int n, float x, float y) { if (!points) return; // during first pass, it's unallocated points[n].x = x; points[n].y = y; } // 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 float mx = (x0 + 2*x1 + x2)/4; float my = (y0 + 2*y1 + y2)/4; // versus directly drawn line float dx = (x0+x2)/2 - mx; float dy = (y0+y2)/2 - my; if (n > 16) // 65536 segments on one curve better be enough! return 1; if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); } else { stbtt__add_point(points, *num_points,x2,y2); *num_points = *num_points+1; } return 1; } static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) { // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough float dx0 = x1-x0; float dy0 = y1-y0; float dx1 = x2-x1; float dy1 = y2-y1; float dx2 = x3-x2; float dy2 = y3-y2; float dx = x3-x0; float dy = y3-y0; float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); float flatness_squared = longlen*longlen-shortlen*shortlen; if (n > 16) // 65536 segments on one curve better be enough! return; if (flatness_squared > objspace_flatness_squared) { float x01 = (x0+x1)/2; float y01 = (y0+y1)/2; float x12 = (x1+x2)/2; float y12 = (y1+y2)/2; float x23 = (x2+x3)/2; float y23 = (y2+y3)/2; float xa = (x01+x12)/2; float ya = (y01+y12)/2; float xb = (x12+x23)/2; float yb = (y12+y23)/2; float mx = (xa+xb)/2; float my = (ya+yb)/2; stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); } else { stbtt__add_point(points, *num_points,x3,y3); *num_points = *num_points+1; } } // returns number of contours static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) { stbtt__point *points=0; int num_points=0; float objspace_flatness_squared = objspace_flatness * objspace_flatness; int i,n=0,start=0, pass; // count how many "moves" there are to get the contour count for (i=0; i < num_verts; ++i) if (vertices[i].type == STBTT_vmove) ++n; *num_contours = n; if (n == 0) return 0; *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); if (*contour_lengths == 0) { *num_contours = 0; return 0; } // make two passes through the points so we don't need to realloc for (pass=0; pass < 2; ++pass) { float x=0,y=0; if (pass == 1) { points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); if (points == NULL) goto error; } num_points = 0; n= -1; for (i=0; i < num_verts; ++i) { switch (vertices[i].type) { case STBTT_vmove: // start the next contour if (n >= 0) (*contour_lengths)[n] = num_points - start; ++n; start = num_points; x = vertices[i].x, y = vertices[i].y; stbtt__add_point(points, num_points++, x,y); break; case STBTT_vline: x = vertices[i].x, y = vertices[i].y; stbtt__add_point(points, num_points++, x, y); break; case STBTT_vcurve: stbtt__tesselate_curve(points, &num_points, x,y, vertices[i].cx, vertices[i].cy, vertices[i].x, vertices[i].y, objspace_flatness_squared, 0); x = vertices[i].x, y = vertices[i].y; break; case STBTT_vcubic: stbtt__tesselate_cubic(points, &num_points, x,y, vertices[i].cx, vertices[i].cy, vertices[i].cx1, vertices[i].cy1, vertices[i].x, vertices[i].y, objspace_flatness_squared, 0); x = vertices[i].x, y = vertices[i].y; break; } } (*contour_lengths)[n] = num_points - start; } return points; error: STBTT_free(points, userdata); STBTT_free(*contour_lengths, userdata); *contour_lengths = 0; *num_contours = 0; return NULL; } STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) { float scale = scale_x > scale_y ? scale_y : scale_x; int winding_count = 0; int *winding_lengths = NULL; stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); if (windings) { stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); STBTT_free(winding_lengths, userdata); STBTT_free(windings, userdata); } } STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) { STBTT_free(bitmap, userdata); } STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) { int ix0,iy0,ix1,iy1; stbtt__bitmap gbm; stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); if (scale_x == 0) scale_x = scale_y; if (scale_y == 0) { if (scale_x == 0) { STBTT_free(vertices, info->userdata); return NULL; } scale_y = scale_x; } stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); // now we get the size gbm.w = (ix1 - ix0); gbm.h = (iy1 - iy0); gbm.pixels = NULL; // in case we error if (width ) *width = gbm.w; if (height) *height = gbm.h; if (xoff ) *xoff = ix0; if (yoff ) *yoff = iy0; if (gbm.w && gbm.h) { gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); if (gbm.pixels) { gbm.stride = gbm.w; stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); } } STBTT_free(vertices, info->userdata); return gbm.pixels; } STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); } STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) { int ix0,iy0; stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); stbtt__bitmap gbm; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); gbm.pixels = output; gbm.w = out_w; gbm.h = out_h; gbm.stride = out_stride; if (gbm.w && gbm.h) stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); STBTT_free(vertices, info->userdata); } STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); } STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); } STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) { stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); } STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); } STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); } STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) { stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); } ////////////////////////////////////////////////////////////////////////////// // // bitmap baking // // This is SUPER-CRAPPY packing to keep source code small static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) float pixel_height, // height of font in pixels unsigned char *pixels, int pw, int ph, // bitmap to be filled in int first_char, int num_chars, // characters to bake stbtt_bakedchar *chardata) { float scale; int x,y,bottom_y, i; stbtt_fontinfo f; f.userdata = NULL; if (!stbtt_InitFont(&f, data, offset)) return -1; STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels x=y=1; bottom_y = 1; scale = stbtt_ScaleForPixelHeight(&f, pixel_height); for (i=0; i < num_chars; ++i) { int advance, lsb, x0,y0,x1,y1,gw,gh; int g = stbtt_FindGlyphIndex(&f, first_char + i); stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); gw = x1-x0; gh = y1-y0; if (x + gw + 1 >= pw) y = bottom_y, x = 1; // advance to next row if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row return -i; STBTT_assert(x+gw < pw); STBTT_assert(y+gh < ph); stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); chardata[i].x0 = (stbtt_int16) x; chardata[i].y0 = (stbtt_int16) y; chardata[i].x1 = (stbtt_int16) (x + gw); chardata[i].y1 = (stbtt_int16) (y + gh); chardata[i].xadvance = scale * advance; chardata[i].xoff = (float) x0; chardata[i].yoff = (float) y0; x = x + gw + 1; if (y+gh+1 > bottom_y) bottom_y = y+gh+1; } return bottom_y; } STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) { float d3d_bias = opengl_fillrule ? 0 : -0.5f; float ipw = 1.0f / pw, iph = 1.0f / ph; const stbtt_bakedchar *b = chardata + char_index; int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); q->x0 = round_x + d3d_bias; q->y0 = round_y + d3d_bias; q->x1 = round_x + b->x1 - b->x0 + d3d_bias; q->y1 = round_y + b->y1 - b->y0 + d3d_bias; q->s0 = b->x0 * ipw; q->t0 = b->y0 * iph; q->s1 = b->x1 * ipw; q->t1 = b->y1 * iph; *xpos += b->xadvance; } ////////////////////////////////////////////////////////////////////////////// // // rectangle packing replacement routines if you don't have stb_rect_pack.h // #ifndef STB_RECT_PACK_VERSION typedef int stbrp_coord; //////////////////////////////////////////////////////////////////////////////////// // // // // // COMPILER WARNING ?!?!? // // // // // // if you get a compile warning due to these symbols being defined more than // // once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // // // //////////////////////////////////////////////////////////////////////////////////// typedef struct { int width,height; int x,y,bottom_y; } stbrp_context; typedef struct { unsigned char x; } stbrp_node; struct stbrp_rect { stbrp_coord x,y; int id,w,h,was_packed; }; static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) { con->width = pw; con->height = ph; con->x = 0; con->y = 0; con->bottom_y = 0; STBTT__NOTUSED(nodes); STBTT__NOTUSED(num_nodes); } static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) { int i; for (i=0; i < num_rects; ++i) { if (con->x + rects[i].w > con->width) { con->x = 0; con->y = con->bottom_y; } if (con->y + rects[i].h > con->height) break; rects[i].x = con->x; rects[i].y = con->y; rects[i].was_packed = 1; con->x += rects[i].w; if (con->y + rects[i].h > con->bottom_y) con->bottom_y = con->y + rects[i].h; } for ( ; i < num_rects; ++i) rects[i].was_packed = 0; } #endif ////////////////////////////////////////////////////////////////////////////// // // bitmap baking // // This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If // stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) { stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); int num_nodes = pw - padding; stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); if (context == NULL || nodes == NULL) { if (context != NULL) STBTT_free(context, alloc_context); if (nodes != NULL) STBTT_free(nodes , alloc_context); return 0; } spc->user_allocator_context = alloc_context; spc->width = pw; spc->height = ph; spc->pixels = pixels; spc->pack_info = context; spc->nodes = nodes; spc->padding = padding; 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); if (pixels) STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels return 1; } STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) { STBTT_free(spc->nodes , spc->user_allocator_context); STBTT_free(spc->pack_info, spc->user_allocator_context); } STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) { STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); if (h_oversample <= STBTT_MAX_OVERSAMPLE) spc->h_oversample = h_oversample; if (v_oversample <= STBTT_MAX_OVERSAMPLE) 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) { unsigned char buffer[STBTT_MAX_OVERSAMPLE]; int safe_w = w - kernel_width; int j; STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze for (j=0; j < h; ++j) { int i; unsigned int total; STBTT_memset(buffer, 0, kernel_width); total = 0; // make kernel_width a constant in common cases so compiler can optimize out the divide switch (kernel_width) { case 2: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / 2); } break; case 3: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / 3); } break; case 4: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / 4); } break; case 5: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / 5); } break; default: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / kernel_width); } break; } for (; i < w; ++i) { STBTT_assert(pixels[i] == 0); total -= buffer[i & STBTT__OVER_MASK]; pixels[i] = (unsigned char) (total / kernel_width); } pixels += stride_in_bytes; } } static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) { unsigned char buffer[STBTT_MAX_OVERSAMPLE]; int safe_h = h - kernel_width; int j; STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze for (j=0; j < w; ++j) { int i; unsigned int total; STBTT_memset(buffer, 0, kernel_width); total = 0; // make kernel_width a constant in common cases so compiler can optimize out the divide switch (kernel_width) { case 2: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / 2); } break; case 3: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / 3); } break; case 4: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / 4); } break; case 5: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / 5); } break; default: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); } break; } for (; i < h; ++i) { STBTT_assert(pixels[i*stride_in_bytes] == 0); total -= buffer[i & STBTT__OVER_MASK]; pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); } pixels += 1; } } static float stbtt__oversample_shift(int oversample) { if (!oversample) return 0.0f; // The prefilter is a box filter of width "oversample", // which shifts phase by (oversample - 1)/2 pixels in // oversampled space. We want to shift in the opposite // direction to counter this. return (float)-(oversample - 1) / (2.0f * (float)oversample); } // rects array must be big enough to accommodate all characters in the given ranges STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k; int missing_glyph_added = 0; k=0; for (i=0; i < num_ranges; ++i) { float fh = ranges[i].font_size; float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); ranges[i].h_oversample = (unsigned char) spc->h_oversample; ranges[i].v_oversample = (unsigned char) spc->v_oversample; for (j=0; j < ranges[i].num_chars; ++j) { 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); if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { 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); if (glyph == 0) missing_glyph_added = 1; } ++k; } } return k; } STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w - (prefilter_x - 1), out_h - (prefilter_y - 1), out_stride, scale_x, scale_y, shift_x, shift_y, glyph); if (prefilter_x > 1) stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); if (prefilter_y > 1) stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); *sub_x = stbtt__oversample_shift(prefilter_x); *sub_y = stbtt__oversample_shift(prefilter_y); } // rects array must be big enough to accommodate all characters in the given ranges STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k, missing_glyph = -1, return_value = 1; // save current values int old_h_over = spc->h_oversample; int old_v_over = spc->v_oversample; k = 0; for (i=0; i < num_ranges; ++i) { float fh = ranges[i].font_size; float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); float recip_h,recip_v,sub_x,sub_y; spc->h_oversample = ranges[i].h_oversample; spc->v_oversample = ranges[i].v_oversample; recip_h = 1.0f / spc->h_oversample; recip_v = 1.0f / spc->v_oversample; sub_x = stbtt__oversample_shift(spc->h_oversample); 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 && 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]; int glyph = stbtt_FindGlyphIndex(info, codepoint); stbrp_coord pad = (stbrp_coord) spc->padding; // pad on left and top r->x += pad; r->y += pad; r->w -= pad; r->h -= pad; stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); stbtt_GetGlyphBitmapBox(info, glyph, scale * spc->h_oversample, scale * spc->v_oversample, &x0,&y0,&x1,&y1); stbtt_MakeGlyphBitmapSubpixel(info, spc->pixels + r->x + r->y*spc->stride_in_bytes, r->w - spc->h_oversample+1, r->h - spc->v_oversample+1, spc->stride_in_bytes, scale * spc->h_oversample, scale * spc->v_oversample, 0,0, glyph); if (spc->h_oversample > 1) stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, r->w, r->h, spc->stride_in_bytes, spc->h_oversample); if (spc->v_oversample > 1) stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, r->w, r->h, spc->stride_in_bytes, spc->v_oversample); bc->x0 = (stbtt_int16) r->x; bc->y0 = (stbtt_int16) r->y; bc->x1 = (stbtt_int16) (r->x + r->w); bc->y1 = (stbtt_int16) (r->y + r->h); bc->xadvance = scale * advance; bc->xoff = (float) x0 * recip_h + sub_x; bc->yoff = (float) y0 * recip_v + sub_y; bc->xoff2 = (x0 + r->w) * recip_h + sub_x; bc->yoff2 = (y0 + r->h) * recip_v + sub_y; if (glyph == 0) missing_glyph = j; } else if (spc->skip_missing) { return_value = 0; } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; } else { return_value = 0; // if any fail, report failure } ++k; } } // restore original values spc->h_oversample = old_h_over; spc->v_oversample = old_v_over; return return_value; } STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) { stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); } STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) { stbtt_fontinfo info; int i, j, n, return_value; // [DEAR IMGUI] removed = 1; //stbrp_context *context = (stbrp_context *) spc->pack_info; stbrp_rect *rects; // flag all characters as NOT packed for (i=0; i < num_ranges; ++i) for (j=0; j < ranges[i].num_chars; ++j) ranges[i].chardata_for_range[j].x0 = ranges[i].chardata_for_range[j].y0 = ranges[i].chardata_for_range[j].x1 = ranges[i].chardata_for_range[j].y1 = 0; n = 0; for (i=0; i < num_ranges; ++i) n += ranges[i].num_chars; rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); if (rects == NULL) return 0; info.userdata = spc->user_allocator_context; stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); stbtt_PackFontRangesPackRects(spc, rects, n); return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); STBTT_free(rects, spc->user_allocator_context); return return_value; } STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) { stbtt_pack_range range; range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; range.array_of_unicode_codepoints = NULL; range.num_chars = num_chars_in_range; range.chardata_for_range = chardata_for_range; range.font_size = font_size; 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; const stbtt_packedchar *b = chardata + char_index; if (align_to_integer) { float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); q->x0 = x; q->y0 = y; q->x1 = x + b->xoff2 - b->xoff; q->y1 = y + b->yoff2 - b->yoff; } else { q->x0 = *xpos + b->xoff; q->y0 = *ypos + b->yoff; q->x1 = *xpos + b->xoff2; q->y1 = *ypos + b->yoff2; } q->s0 = b->x0 * ipw; q->t0 = b->y0 * iph; q->s1 = b->x1 * ipw; q->t1 = b->y1 * iph; *xpos += b->xadvance; } ////////////////////////////////////////////////////////////////////////////// // // sdf computation // #define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) #define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) { float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; float roperp = orig[1]*ray[0] - orig[0]*ray[1]; float a = q0perp - 2*q1perp + q2perp; float b = q1perp - q0perp; float c = q0perp - roperp; float s0 = 0., s1 = 0.; int num_s = 0; if (a != 0.0) { float discr = b*b - a*c; if (discr > 0.0) { float rcpna = -1 / a; float d = (float) STBTT_sqrt(discr); s0 = (b+d) * rcpna; s1 = (b-d) * rcpna; if (s0 >= 0.0 && s0 <= 1.0) num_s = 1; if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { if (num_s == 0) s0 = s1; ++num_s; } } } else { // 2*b*s + c = 0 // s = -c / (2*b) s0 = c / (-2 * b); if (s0 >= 0.0 && s0 <= 1.0) num_s = 1; } if (num_s == 0) return 0; else { float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; float q0d = q0[0]*rayn_x + q0[1]*rayn_y; float q1d = q1[0]*rayn_x + q1[1]*rayn_y; float q2d = q2[0]*rayn_x + q2[1]*rayn_y; float rod = orig[0]*rayn_x + orig[1]*rayn_y; float q10d = q1d - q0d; float q20d = q2d - q0d; float q0rd = q0d - rod; hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; hits[0][1] = a*s0+b; if (num_s > 1) { hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; hits[1][1] = a*s1+b; return 2; } else { return 1; } } } static int equal(float *a, float *b) { return (a[0] == b[0] && a[1] == b[1]); } static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) { int i; float orig[2], ray[2] = { 1, 0 }; float y_frac; int winding = 0; // make sure y never passes through a vertex of the shape y_frac = (float) STBTT_fmod(y, 1.0f); if (y_frac < 0.01f) y += 0.01f; else if (y_frac > 0.99f) y -= 0.01f; orig[0] = x; orig[1] = y; // test a ray from (-infinity,y) to (x,y) for (i=0; i < nverts; ++i) { if (verts[i].type == STBTT_vline) { int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } if (verts[i].type == STBTT_vcurve) { int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); int by = STBTT_max(y0,STBTT_max(y1,y2)); if (y > ay && y < by && x > ax) { float q0[2],q1[2],q2[2]; float hits[2][2]; q0[0] = (float)x0; q0[1] = (float)y0; q1[0] = (float)x1; q1[1] = (float)y1; q2[0] = (float)x2; q2[1] = (float)y2; if (equal(q0,q1) || equal(q1,q2)) { x0 = (int)verts[i-1].x; //-V1048 y0 = (int)verts[i-1].y; //-V1048 x1 = (int)verts[i ].x; y1 = (int)verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } else { int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); if (num_hits >= 1) if (hits[0][0] < 0) winding += (hits[0][1] < 0 ? -1 : 1); if (num_hits >= 2) if (hits[1][0] < 0) winding += (hits[1][1] < 0 ? -1 : 1); } } } } return winding; } static float stbtt__cuberoot( float x ) { if (x<0) return -(float) STBTT_pow(-x,1.0f/3.0f); else return (float) STBTT_pow( x,1.0f/3.0f); } // x^3 + a*x^2 + b*x + c = 0 static int stbtt__solve_cubic(float a, float b, float c, float* r) { float s = -a / 3; float p = b - a*a / 3; float q = a * (2*a*a - 9*b) / 27 + c; float p3 = p*p*p; float d = q*q + 4*p3 / 27; if (d >= 0) { float z = (float) STBTT_sqrt(d); float u = (-q + z) / 2; float v = (-q - z) / 2; u = stbtt__cuberoot(u); v = stbtt__cuberoot(v); r[0] = s + u + v; return 1; } else { float u = (float) STBTT_sqrt(-p/3); float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative float m = (float) STBTT_cos(v); float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; r[0] = s + u * 2 * m; r[1] = s - u * (m + n); r[2] = s - u * (m - n); //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); return 3; } } STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) { float scale_x = scale, scale_y = scale; int ix0,iy0,ix1,iy1; int w,h; unsigned char *data; if (scale == 0) return NULL; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); // if empty, return NULL if (ix0 == ix1 || iy0 == iy1) return NULL; ix0 -= padding; iy0 -= padding; ix1 += padding; iy1 += padding; w = (ix1 - ix0); h = (iy1 - iy0); if (width ) *width = w; if (height) *height = h; if (xoff ) *xoff = ix0; if (yoff ) *yoff = iy0; // invert for y-downwards bitmaps scale_y = -scale_y; { int x,y,i,j; float *precompute; stbtt_vertex *verts; int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); data = (unsigned char *) STBTT_malloc(w * h, info->userdata); precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); for (i=0,j=num_verts-1; i < num_verts; j=i++) { if (verts[i].type == STBTT_vline) { float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; } else if (verts[i].type == STBTT_vcurve) { float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float len2 = bx*bx + by*by; if (len2 != 0.0f) precompute[i] = 1.0f / (bx*bx + by*by); else precompute[i] = 0.0f; } else precompute[i] = 0.0f; } for (y=iy0; y < iy1; ++y) { for (x=ix0; x < ix1; ++x) { float val; float min_dist = 999999.0f; float sx = (float) x + 0.5f; float sy = (float) y + 0.5f; float x_gspace = (sx / scale_x); float y_gspace = (sy / scale_y); int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path for (i=0; i < num_verts; ++i) { float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); if (dist2 < min_dist*min_dist) min_dist = (float) STBTT_sqrt(dist2); // coarse culling against bbox //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; STBTT_assert(i != 0); if (dist < min_dist) { // check position along line // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) float dx = x1-x0, dy = y1-y0; float px = x0-sx, py = y0-sy; // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve float t = -(px*dx + py*dy) / (dx*dx + dy*dy); if (t >= 0.0f && t <= 1.0f) min_dist = dist; } } else if (verts[i].type == STBTT_vcurve) { float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); // coarse culling against bbox to avoid computing cubic unnecessarily if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { int num=0; float ax = x1-x0, ay = y1-y0; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float mx = x0 - sx, my = y0 - sy; float res[3] = {0.f,0.f,0.f}; float px,py,t,it,dist2; float a_inv = precompute[i]; if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula float a = 3*(ax*bx + ay*by); float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); float c = mx*ax+my*ay; if (a == 0.0) { // if a is 0, it's linear if (b != 0.0) { res[num++] = -c/b; } } else { float discriminant = b*b - 4*a*c; if (discriminant < 0) num = 0; else { float root = (float) STBTT_sqrt(discriminant); res[0] = (-b - root)/(2*a); res[1] = (-b + root)/(2*a); num = 2; // don't bother distinguishing 1-solution case, as code below will still work } } } else { float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; float d = (mx*ax+my*ay) * a_inv; num = stbtt__solve_cubic(b, c, d, res); } dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); if (dist2 < min_dist*min_dist) min_dist = (float) STBTT_sqrt(dist2); if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { t = res[0], it = 1.0f - t; px = it*it*x0 + 2*t*it*x1 + t*t*x2; py = it*it*y0 + 2*t*it*y1 + t*t*y2; dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); if (dist2 < min_dist * min_dist) min_dist = (float) STBTT_sqrt(dist2); } if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { t = res[1], it = 1.0f - t; px = it*it*x0 + 2*t*it*x1 + t*t*x2; py = it*it*y0 + 2*t*it*y1 + t*t*y2; dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); if (dist2 < min_dist * min_dist) min_dist = (float) STBTT_sqrt(dist2); } if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { t = res[2], it = 1.0f - t; px = it*it*x0 + 2*t*it*x1 + t*t*x2; py = it*it*y0 + 2*t*it*y1 + t*t*y2; dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); if (dist2 < min_dist * min_dist) min_dist = (float) STBTT_sqrt(dist2); } } } } if (winding == 0) min_dist = -min_dist; // if outside the shape, value is negative val = onedge_value + pixel_dist_scale * min_dist; if (val < 0) val = 0; else if (val > 255) val = 255; data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; } } STBTT_free(precompute, info->userdata); STBTT_free(verts, info->userdata); } return data; } 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) { return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); } STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) { STBTT_free(bitmap, userdata); } ////////////////////////////////////////////////////////////////////////////// // // font name matching -- recommended not to use this // // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) { stbtt_int32 i=0; // convert utf16 to utf8 and compare the results while converting while (len2) { stbtt_uint16 ch = s2[0]*256 + s2[1]; if (ch < 0x80) { if (i >= len1) return -1; if (s1[i++] != ch) return -1; } else if (ch < 0x800) { if (i+1 >= len1) return -1; if (s1[i++] != 0xc0 + (ch >> 6)) return -1; if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; } else if (ch >= 0xd800 && ch < 0xdc00) { stbtt_uint32 c; stbtt_uint16 ch2 = s2[2]*256 + s2[3]; if (i+3 >= len1) return -1; c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; if (s1[i++] != 0xf0 + (c >> 18)) return -1; if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; s2 += 2; // plus another 2 below len2 -= 2; } else if (ch >= 0xdc00 && ch < 0xe000) { return -1; } else { if (i+2 >= len1) return -1; if (s1[i++] != 0xe0 + (ch >> 12)) return -1; if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; } s2 += 2; len2 -= 2; } return i; } static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) { return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); } // returns results in whatever encoding you request... but note that 2-byte encodings // will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) { stbtt_int32 i,count,stringOffset; stbtt_uint8 *fc = font->data; stbtt_uint32 offset = font->fontstart; stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); if (!nm) return NULL; count = ttUSHORT(fc+nm+2); stringOffset = nm + ttUSHORT(fc+nm+4); for (i=0; i < count; ++i) { stbtt_uint32 loc = nm + 6 + 12 * i; if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { *length = ttUSHORT(fc+loc+8); return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); } } return NULL; } static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) { stbtt_int32 i; stbtt_int32 count = ttUSHORT(fc+nm+2); stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); for (i=0; i < count; ++i) { stbtt_uint32 loc = nm + 6 + 12 * i; stbtt_int32 id = ttUSHORT(fc+loc+6); if (id == target_id) { // find the encoding stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); // is this a Unicode encoding? if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { stbtt_int32 slen = ttUSHORT(fc+loc+8); stbtt_int32 off = ttUSHORT(fc+loc+10); // check if there's a prefix match stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); if (matchlen >= 0) { // check for target_id+1 immediately following, with same encoding & language if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { slen = ttUSHORT(fc+loc+12+8); off = ttUSHORT(fc+loc+12+10); if (slen == 0) { if (matchlen == nlen) return 1; } else if (matchlen < nlen && name[matchlen] == ' ') { ++matchlen; if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) return 1; } } else { // if nothing immediately following if (matchlen == nlen) return 1; } } } // @TODO handle other encodings } } return 0; } static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) { stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); stbtt_uint32 nm,hd; if (!stbtt__isfont(fc+offset)) return 0; // check italics/bold/underline flags in macStyle... if (flags) { hd = stbtt__find_table(fc, offset, "head"); if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; } nm = stbtt__find_table(fc, offset, "name"); if (!nm) return 0; if (flags) { // if we checked the macStyle flags, then just check the family and ignore the subfamily if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; } else { if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; } return 0; } static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) { stbtt_int32 i; for (i=0;;++i) { stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); if (off < 0) return off; if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) return off; } } #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" #endif STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, float pixel_height, unsigned char *pixels, int pw, int ph, int first_char, int num_chars, stbtt_bakedchar *chardata) { return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); } STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) { return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); } STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) { return stbtt_GetNumberOfFonts_internal((unsigned char *) data); } STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) { return stbtt_InitFont_internal(info, (unsigned char *) data, offset); } STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) { return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); } STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) { return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); } #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif #endif // STB_TRUETYPE_IMPLEMENTATION // FULL VERSION HISTORY // // 1.25 (2021-07-11) many fixes // 1.24 (2020-02-05) fix warning // 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) // 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined // 1.21 (2019-02-25) fix warning // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() // 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod // 1.18 (2018-01-29) add missing function // 1.17 (2017-07-23) make more arguments const; doc fix // 1.16 (2017-07-12) SDF support // 1.15 (2017-03-03) make more arguments const // 1.14 (2017-01-16) num-fonts-in-TTC function // 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts // 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual // 1.11 (2016-04-02) fix unused-variable warning // 1.10 (2016-04-02) allow user-defined fabs() replacement // fix memory leak if fontsize=0.0 // fix warning from duplicate typedef // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; // allow PackFontRanges to pack and render in separate phases; // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); // fixed an assert() bug in the new rasterizer // replace assert() with STBTT_assert() in new rasterizer // 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) // also more precise AA rasterizer, except if shapes overlap // remove need for STBTT_sort // 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC // 1.04 (2015-04-15) typo in example // 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes // 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ // 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match // non-oversampled; STBTT_POINT_SIZE for packed case only // 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling // 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) // 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID // 0.8b (2014-07-07) fix a warning // 0.8 (2014-05-25) fix a few more warnings // 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back // 0.6c (2012-07-24) improve documentation // 0.6b (2012-07-20) fix a few more warnings // 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, // stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty // 0.5 (2011-12-09) bugfixes: // subpixel glyph renderer computed wrong bounding box // first vertex of shape can be off-curve (FreeSans) // 0.4b (2011-12-03) fixed an error in the font baking example // 0.4 (2011-12-01) kerning, subpixel rendering (tor) // bugfixes for: // codepoint-to-glyph conversion using table fmt=12 // codepoint-to-glyph conversion using table fmt=4 // stbtt_GetBakedQuad with non-square texture (Zer) // updated Hello World! sample to use kerning and subpixel // fixed some warnings // 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) // userdata, malloc-from-userdata, non-zero fill (stb) // 0.2 (2009-03-11) Fix unsigned/signed char warnings // 0.1 (2009-03-09) First public release // /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ================================================ FILE: lib/third_party/imgui/imgui/include/misc/freetype/imgui_freetype.h ================================================ // dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder) // (headers) #pragma once #include "imgui.h" // IMGUI_API #ifndef IMGUI_DISABLE // Usage: // - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support // for imgui_freetype in imgui. It is equivalent to selecting the default loader with: // io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) // Optional support for OpenType SVG fonts: // - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927. // - Add '#define IMGUI_ENABLE_FREETYPE_LUNASVG' to use lunasvg (not provided). See #6591. // Forward declarations struct ImFontAtlas; struct ImFontLoader; // Hinting greatly impacts visuals (and glyph sizes). // - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter. // - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h // - The Default hinting mode usually looks good, but may distort glyphs in an unusual way. // - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer. // You can set those flags globally in ImFontAtlas::FontLoaderFlags // You can set those flags on a per font basis in ImFontConfig::FontLoaderFlags typedef unsigned int ImGuiFreeTypeLoaderFlags; enum ImGuiFreeTypeLoaderFlags_ { ImGuiFreeTypeLoaderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes. ImGuiFreeTypeLoaderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter. ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter. ImGuiFreeTypeLoaderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text. ImGuiFreeTypeLoaderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output. ImGuiFreeTypeLoaderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font? ImGuiFreeTypeLoaderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style? ImGuiFreeTypeLoaderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results! ImGuiFreeTypeLoaderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs ImGuiFreeTypeLoaderFlags_Bitmap = 1 << 9, // Enable FreeType bitmap glyphs // IMHEX PATCH BEGIN ImGuiFreeTypeLoaderFlags_SubPixel = 1 << 10 // Atlas was generated with sub-pixel rendering enabled // IMHEX PATCH END #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiFreeTypeBuilderFlags_NoHinting = ImGuiFreeTypeLoaderFlags_NoHinting, ImGuiFreeTypeBuilderFlags_NoAutoHint = ImGuiFreeTypeLoaderFlags_NoAutoHint, ImGuiFreeTypeBuilderFlags_ForceAutoHint = ImGuiFreeTypeLoaderFlags_ForceAutoHint, ImGuiFreeTypeBuilderFlags_LightHinting = ImGuiFreeTypeLoaderFlags_LightHinting, ImGuiFreeTypeBuilderFlags_MonoHinting = ImGuiFreeTypeLoaderFlags_MonoHinting, ImGuiFreeTypeBuilderFlags_Bold = ImGuiFreeTypeLoaderFlags_Bold, ImGuiFreeTypeBuilderFlags_Oblique = ImGuiFreeTypeLoaderFlags_Oblique, ImGuiFreeTypeBuilderFlags_Monochrome = ImGuiFreeTypeLoaderFlags_Monochrome, ImGuiFreeTypeBuilderFlags_LoadColor = ImGuiFreeTypeLoaderFlags_LoadColor, ImGuiFreeTypeBuilderFlags_Bitmap = ImGuiFreeTypeLoaderFlags_Bitmap, #endif }; // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImGuiFreeTypeLoaderFlags_ ImGuiFreeTypeBuilderFlags_; #endif namespace ImGuiFreeType { // This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'. // If you need to dynamically select between multiple builders: // - you can manually assign this builder with 'atlas->SetFontLoader(ImGuiFreeType::GetFontLoader())' // - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data. IMGUI_API const ImFontLoader* GetFontLoader(); // Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE() // However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired. IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr); // Display UI to edit ImFontAtlas::FontLoaderFlags (shared) or ImFontConfig::FontLoaderFlags (single source) IMGUI_API bool DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags); // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection. //static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE' #endif } #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui/source/imgui.cpp ================================================ // dear imgui, v1.92.7 WIP // (main code and documentation) // Help: // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. // - Read top of imgui.cpp for more details, links and comments. // - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including imgui.h (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. // Resources: // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) // - Homepage ................... https://github.com/ocornut/imgui // - Releases & Changelog ....... https://github.com/ocornut/imgui/releases // - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!) // - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) // - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) // - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines) // - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui // - Issues & support ........... https://github.com/ocornut/imgui/issues // - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps) // - Web version of the Demo .... https://pthom.github.io/imgui_explorer (w/ source code browser) // For FIRST-TIME users having issues compiling/linking/running: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. // Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there. // Since 1.92, we encourage font loading questions to also be posted in 'Issues'. // Copyright (c) 2014-2026 Omar Cornut // 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 needs your support to sustain development and maintenance. // Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts. // PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding // Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. // 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 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you // to a better solution or official support for them. /* Index of this file: DOCUMENTATION - MISSION STATEMENT - CONTROLS GUIDE - PROGRAMMER GUIDE - READ FIRST - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE - HOW A SIMPLE APPLICATION MAY LOOK LIKE - USING CUSTOM BACKEND / CUSTOM ENGINE - API BREAKING CHANGES (read me when you update!) - FREQUENTLY ASKED QUESTIONS (FAQ) - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer) CODE (search for "[SECTION]" in the code to find them) // [SECTION] INCLUDES // [SECTION] FORWARD DECLARATIONS // [SECTION] CONTEXT AND MEMORY ALLOCATORS // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO) // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) // [SECTION] MISC HELPERS/UTILITIES (File functions) // [SECTION] MISC HELPERS/UTILITIES (ImText* functions) // [SECTION] MISC HELPERS/UTILITIES (Color functions) // [SECTION] ImGuiStorage // [SECTION] ImGuiTextFilter // [SECTION] ImGuiTextBuffer, ImGuiTextIndex // [SECTION] ImGuiListClipper // [SECTION] STYLING // [SECTION] RENDER HELPERS // [SECTION] INITIALIZATION, SHUTDOWN // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) // [SECTION] FONTS, TEXTURES // [SECTION] ID STACK // [SECTION] INPUTS // [SECTION] ERROR CHECKING, STATE RECOVERY // [SECTION] ITEM SUBMISSION // [SECTION] LAYOUT // [SECTION] SCROLLING // [SECTION] TOOLTIPS // [SECTION] POPUPS // [SECTION] WINDOW FOCUS // [SECTION] KEYBOARD/GAMEPAD NAVIGATION // [SECTION] DRAG AND DROP // [SECTION] LOGGING/CAPTURING // [SECTION] SETTINGS // [SECTION] LOCALIZATION // [SECTION] VIEWPORTS, PLATFORM WINDOWS // [SECTION] DOCKING // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW // [SECTION] DEBUG LOG WINDOW // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL) */ //----------------------------------------------------------------------------- // DOCUMENTATION //----------------------------------------------------------------------------- /* 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 setup and maintenance. - Minimize state storage on user side. - Minimize state synchronization. - Portable, minimize dependencies, run on target (consoles, phones, etc.). - Efficient runtime and memory consumption. Designed primarily for developers and content-creators, not the typical end-user! Some of the current weaknesses (which we aim to address in the future) includes: - Doesn't look fancy by default. - Limited layout features, intricate layouts are typically crafted in code. CONTROLS GUIDE ============== - MOUSE CONTROLS - Mouse wheel: Scroll vertically. - Shift+Mouse wheel: Scroll horizontally. - Click [X]: Close a window, available when 'bool* p_open' is passed to ImGui::Begin(). - Click ^, Double-Click title: Collapse window. - Drag on corner/border: Resize window (double-click to auto fit window to its contents). - Drag on any empty space: Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true). - Left-click outside popup: Close popup stack (right-click over underlying popup: Partially close popup stack). - TEXT EDITOR - Hold Shift or Drag Mouse: Select text. - Ctrl+Left/Right: Word jump. - Ctrl+Shift+Left/Right: Select words. - Ctrl+A or Double-Click: Select All. - Ctrl+X, Ctrl+C, Ctrl+V: Use OS clipboard. - Ctrl+Z Undo. - Ctrl+Y or Ctrl+Shift+Z: Redo. - ESCAPE: Revert text to its original value. - On macOS, controls are automatically adjusted to match standard macOS text editing and behaviors. (for 99% of shortcuts, Ctrl is replaced by Cmd on macOS). - KEYBOARD CONTROLS - Basic: - Tab, Shift+Tab Cycle through text editable fields. - Ctrl+Tab, Ctrl+Shift+Tab Cycle through windows. - Ctrl+Click Input text into a Slider or Drag widget. - Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`: - Tab, Shift+Tab: Cycle through every items. - Arrow keys Move through items using directional navigation. Tweak value. - Arrow keys + Alt, Shift Tweak slower, tweak faster (when using arrow keys). - Enter Activate item (prefer text input when possible). - Space Activate item (prefer tweaking with arrows when possible). - Escape Deactivate item, leave child window, close popup. - Page Up, Page Down Previous page, next page. - Home, End Scroll to top, scroll to bottom. - Alt Toggle between scrolling layer and menu layer. - Ctrl+Tab then Ctrl+Arrows Move window. Hold Shift to resize instead of moving. - Menu or Shift+F10 Open context menu. - Output when ImGuiConfigFlags_NavEnableKeyboard set, - io.WantCaptureKeyboard flag is set when keyboard is claimed. - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used). - GAMEPAD CONTROLS - Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse! - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets - Backend support: backend needs to: - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys. - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly. Backend 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.). - If you need to share inputs between your game and the Dear ImGui interface, 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. - REMOTE INPUTS SHARING & MOUSE EMULATION - PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback. - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app) in order to share your PC mouse/keyboard. - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions. - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the io.ConfigNavMoveSetMousePos flag. Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements. When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved. When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that. (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!) (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want to set a boolean to ignore your other external mouse positions until the external source is moved again.) PROGRAMMER GUIDE ================ READ FIRST ---------- - Remember to check the wonderful Wiki: https://github.com/ocornut/imgui/wiki - Your code creates the UI every frame of your application loop, 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, fewer bugs. - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. Or browse pthom's online imgui_explorer: https://pthom.github.io/imgui_explorer for a web version w/ source code browser. - 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 in Wiki. - 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 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 is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. - 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. - This codebase aims to be highly optimized: - A typical idle frame should never call malloc/free. - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible. - We put particular energy in making sure performances are decent with typical "Debug" build settings as well. Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all. - This codebase aims to be both highly opinionated and highly flexible: - This code works because of the things it choose to solve or not solve. - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers, and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now). This is to increase compatibility, increase maintainability and facilitate use from other languages. - 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. We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally. - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (so don't use ImVector in your code or at our own risk!). - Building: We don't use nor mandate a build system for the main library. This is in an effort to ensure that it works in the real world aka with any esoteric build setup. This is also because providing a build system for the main library would be of little-value. The build problems are almost never coming from the main library but from specific backends. HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI ---------------------------------------------- - Update submodule or copy/overwrite every file. - About imconfig.h: - You may modify your copy of imconfig.h, in this case don't overwrite it. - or you may locally branch to modify imconfig.h and merge/rebase latest. - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to specify a custom path for your imconfig.h file and instead not have to modify the default one. - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h) - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master". - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file. - 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 likely be a comment about it. Please report any issue to the GitHub page! - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file. - Try to keep your copy of Dear ImGui reasonably up to date! GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE --------------------------------------------------------------- - See https://github.com/ocornut/imgui/wiki/Getting-Started. - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder. - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system. It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL). - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear 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 information is stored into command-lists that you will retrieve after calling ImGui::Render(). - Refer to the backends 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 -------------------------------------- USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder). The sub-folders in examples/ contain examples applications following this structure. // 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 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 backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp) ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // Application main loop while (true) { // Feed inputs to dear imgui, start new frame ImGui_ImplDX11_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); // Any application code here ImGui::Text("Hello, world!"); // Render dear imgui into framebuffer ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); g_pSwapChain->Present(1, 0); } // Shutdown ImGui_ImplDX11_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application, you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this. USING CUSTOM BACKEND / CUSTOM ENGINE ------------------------------------ IMPLEMENTING YOUR PLATFORM BACKEND: -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for basic instructions. -> the Platform backends in impl_impl_XXX.cpp files contain many implementations. IMPLEMENTING YOUR RenderDrawData() function: -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md -> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_RenderDrawData() function. IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md -> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_UpdateTexture() function. Basic application/backend skeleton: // Application init: create a Dear ImGui context, setup some options, load fonts ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); // TODO: set io.ConfigXXX values, e.g. io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable keyboard controls // TODO: Load TTF/OTF fonts if you don't want to use the default font. io.Fonts->AddFontFromFileTTF("NotoSans.ttf"); // Application main loop while (true) { // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc. // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends) io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds) io.DisplaySize.x = 1920.0f; // set the current display width io.DisplaySize.y = 1280.0f; // set the current display height here io.AddMousePosEvent(mouse_x, mouse_y); // update mouse position io.AddMouseButtonEvent(0, mouse_b[0]); // update mouse button states io.AddMouseButtonEvent(1, mouse_b[1]); // update mouse button states // Call NewFrame(), after this point you can use ImGui::* functions anytime // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere) ImGui::NewFrame(); // Most of your application code here ImGui::Text("Hello, world!"); MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); MyGameRender(); // may use any Dear ImGui functions as well! // End the dear imgui frame // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code) ImGui::EndFrame(); // this is automatically called by Render(), but available ImGui::Render(); // Update textures ImDrawData* draw_data = ImGui::GetDrawData(); for (ImTextureData* tex : *draw_data->Textures) if (tex->Status != ImTextureStatus_OK) MyImGuiBackend_UpdateTexture(tex); // Render dear imgui contents, swap buffers MyImGuiBackend_RenderDrawData(draw_data); SwapBuffers(); } // Shutdown ImGui::DestroyContext(); API BREAKING CHANGES ==================== Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. When you are not sure about an 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. (Docking/Viewport Branch) - 2026/XX/XX (1.XXXX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that: - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore. you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos) - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - 2026/03/12 (1.92.7) - Changed default ImTextureID_Invalid to -1 instead of 0 if not #define-d. (#9293, #8745, #8465, #7090) It seems like a better default since it will work with backends storing indices or memory offsets inside ImTextureID, where 0 might be a valid value. If this is causing problem with e.g. your custom ImTextureID definition, you can add '#define ImTextureID_Invalid 0' to your imconfig.h + PLEASE report this to GitHub. If you have hard-coded e.g. 'if (tex_id == 0)' checks they should be updated. e.g. OpenGL2, OpenGL3 and SDLRenderer3 backends incorrectly had 'IM_ASSERT(tex->TexID == 0)' lines which were replaced with 'IM_ASSERT(tex->TexID == ImTextureID_Invalid)'. (#9295) - 2026/02/26 (1.92.7) - Separator: fixed a legacy quirk where Separator() was submitting a zero-height item for layout purpose, even though it draws a 1-pixel separator. The fix could affect code e.g. computing height from multiple widgets in order to allocate vertical space for a footer or multi-line status bar. (#2657, #9263) The "Console" example had such a bug: float footer_height = style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); BeginChild("ScrollingRegion", { 0, -footer_height }); Should be: float footer_height = style.ItemSpacing.y + style.SeparatorSize + ImGui::GetFrameHeightWithSpacing(); BeginChild("ScrollingRegion", { 0, -footer_height }); When such idiom was used and assuming zero-height Separator, it is likely that in 1.92.7 the resulting window will have unexpected 1 pixel scrolling range. - 2026/02/23 (1.92.7) - Commented out legacy signature for Combo(), ListBox(), signatures which were obsoleted in 1.90 (Nov 2023), when the getter callback type was changed. - Old getter type: bool (*getter)(void* user_data, int idx, const char** out_text) // Set label + return bool. False replaced label with placeholder. - New getter type: const char* (*getter)(void* user_data, int idx) // Return label or NULL/empty label if missing - 2026/01/08 (1.92.6) - Commented out legacy names obsoleted in 1.90 (Sept 2023): 'BeginChildFrame()' --> 'BeginChild()' with 'ImGuiChildFlags_FrameStyle'. 'EndChildFrame()' --> 'EndChild()'. 'ShowStackToolWindow()' --> 'ShowIDStackToolWindow()'. 'IM_OFFSETOF()' --> 'offsetof()'. - 2026/01/07 (1.92.6) - Popups: changed compile-time 'ImGuiPopupFlags popup_flags = 1' default value to be '= 0' for BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid(), OpenPopupOnItemClick(). Default value has same meaning before and after. - Refer to GitHub topic #9157 if you have any question. - Before this version, those functions had a 'ImGuiPopupFlags popup_flags = 1' default value in their function signature. Explicitly passing a literal 0 meant ImGuiPopupFlags_MouseButtonLeft. The default literal 1 meant ImGuiPopupFlags_MouseButtonRight. This was introduced by a change on 2020/06/23 (1.77) while changing the signature from 'int mouse_button' to 'ImGuiPopupFlags popup_flags' and trying to preserve then-legacy behavior. We have now changed this behavior to cleanup a very old API quirk, facilitate use by bindings, and to remove the last and error-prone non-zero default value. Also because we deemed it extremely rare to use those helper functions with the Left mouse button! As using the LMB would generally be triggered via another widget, e.g. a Button() + a OpenPopup()/BeginPopup() call. - Before: The default = 1 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 0 means ImGuiPopupFlags_MouseButtonLeft. - After: The default = 0 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 1 also means ImGuiPopupFlags_MouseButtonRight (if legacy behavior are enabled) or will assert (if legacy behavior are disabled). - TL;DR: if you don't want to use right mouse button for popups, always specify it explicitly using a named ImGuiPopupFlags_MouseButtonXXXX value. Recap: - BeginPopupContextItem("foo"); // Behavior unchanged (use Right button) - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonLeft); // Behavior unchanged (use Left button) - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonLeft | xxx); // Behavior unchanged (use Left button + flags) - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonRight | xxx); // Behavior unchanged (use Right button + flags) - BeginPopupContextItem("foo", 1); // Behavior unchanged (as a courtesy we legacy interpret 1 as ImGuiPopupFlags_MouseButtonRight, will assert if disabling legacy behaviors. - BeginPopupContextItem("foo", 0); // !! Behavior changed !! Was Left button. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft. - BeginPopupContextItem("foo", ImGuiPopupFlags_NoReopen); // !! Behavior changed !! Was Left button + flags. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft | xxx. - 2025/12/23 (1.92.6) - Fonts: AddFontDefault() now automatically selects an embedded font between the new scalable AddFontDefaultVector() and the classic pixel-clean AddFontDefaultBitmap(). The default selection is based on (style.FontSizeBase * FontScaleMain * FontScaleDpi) reaching a small threshold, but old codebases may not set any of them properly. As as a result, it is likely that old codebase may still default to AddFontDefaultBitmap(). Prefer calling either based on your own logic. You can call AddFontDefaultBitmap() to ensure legacy behavior. - 2025/12/23 (1.92.6) - Fonts: removed ImFontConfig::PixelSnapV added in 1.92 which turns out is unnecessary (and misdocumented). Post-rescale GlyphOffset is always rounded. - 2025/12/17 (1.92.6) - Renamed helper macro IM_ARRAYSIZE() -> IM_COUNTOF(). Kept redirection/legacy name for now. - 2025/12/11 (1.92.6) - Hashing: handling of "###" operator to reset to seed within a string identifier doesn't include the "###" characters in the output hash anymore. - Before: GetID("Hello###World") == GetID("###World") != GetID("World") - After: GetID("Hello###World") == GetID("###World") == GetID("World") - This has the property of facilitating concatenating and manipulating identifiers using "###", and will allow fixing other dangling issues. - This will invalidate hashes (stored in .ini data) for Tables and Windows that are using the "###" operators. (#713, #1698) - 2025/11/24 (1.92.6) - Fonts: Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which did erroneously make a copy of the font data, essentially defeating the purpose of this flag and wasting memory. (trivia: undetected since July 2015, this is perhaps the oldest bug in Dear ImGui history, albeit for a rarely used feature, see #9086) HOWEVER, fixing this bug is likely to surface bugs in user code using `FontDataOwnedByAtlas = false`. - Prior to 1.92, font data only needed to be available during the atlas->AddFontXXX() call. - Since 1.92, font data needs to available until atlas->RemoveFont(), or more typically until a shutdown of the owning context or font atlas. - The fact that handling of `FontDataOwnedByAtlas = false` was broken bypassed the issue altogether. - 2025/11/06 (1.92.5) - BeginChild: commented out some legacy names which were obsoleted in 1.90.0 (Nov 2023), 1.90.9 (July 2024), 1.91.1 (August 2024): - ImGuiChildFlags_Border --> ImGuiChildFlags_Borders - ImGuiWindowFlags_NavFlattened --> ImGuiChildFlags_NavFlattened (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0) - ImGuiWindowFlags_AlwaysUseWindowPadding --> ImGuiChildFlags_AlwaysUseWindowPadding (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0) - 2025/11/06 (1.92.5) - Keys: commented out legacy names which were obsoleted in 1.89.0 (August 2022): - ImGuiKey_ModCtrl --> ImGuiMod_Ctrl - ImGuiKey_ModShift --> ImGuiMod_Shift - ImGuiKey_ModAlt --> ImGuiMod_Alt - ImGuiKey_ModSuper --> ImGuiMod_Super - 2025/11/06 (1.92.5) - IO: commented out legacy io.ClearInputCharacters() obsoleted in 1.89.8 (Aug 2023). Calling io.ClearInputKeys() is enough. - 2025/11/06 (1.92.5) - Commented out legacy SetItemAllowOverlap() obsoleted in 1.89.7: this never worked right. Use SetNextItemAllowOverlap() _before_ item instead. - 2025/10/14 (1.92.4) - TreeNode, Selectable, Clipper: commented out legacy names which were obsoleted in 1.89.7 (July 2023) and 1.89.9 (Sept 2023); - ImGuiTreeNodeFlags_AllowItemOverlap --> ImGuiTreeNodeFlags_AllowOverlap - ImGuiSelectableFlags_AllowItemOverlap --> ImGuiSelectableFlags_AllowOverlap - ImGuiListClipper::IncludeRangeByIndices() --> ImGuiListClipper::IncludeItemsByIndex() - 2025/09/22 (1.92.4) - Viewports: renamed io.ConfigViewportPlatformFocusSetsImGuiFocus to io.ConfigViewportsPlatformFocusSetsImGuiFocus. Was a typo in the first place. (#6299, #6462) - 2025/08/08 (1.92.2) - Backends: SDL_GPU3: Changed ImTextureID type from SDL_GPUTextureSamplerBinding* to SDL_GPUTexture*, which is more natural and easier for user to manage. If you need to change the current sampler, you can access the ImGui_ImplSDLGPU3_RenderState struct. (#8866, #8163, #7998, #7988) - 2025/07/31 (1.92.2) - Tabs: Renamed ImGuiTabBarFlags_FittingPolicyResizeDown to ImGuiTabBarFlags_FittingPolicyShrink. Kept inline redirection enum (will obsolete). - 2025/06/25 (1.92.0) - Layout: commented out legacy ErrorCheckUsingSetCursorPosToExtendParentBoundaries() fallback obsoleted in 1.89 (August 2022) which allowed a SetCursorPos()/SetCursorScreenPos() call WITHOUT AN ITEM to extend parent window/cell boundaries. Replaced with assert/tooltip that would already happens if previously using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#5548, #4510, #3355, #1760, #1490, #4152, #150) - Incorrect way to make a window content size 200x200: Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); - Correct ways to make a window content size 200x200: Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); Begin(...) + Dummy(ImVec2(200,200)) + End(); - TL;DR; if the assert triggers, you can add a Dummy({0,0}) call to validate extending parent boundaries. - 2025/06/11 (1.92.0) - Renamed/moved ImGuiConfigFlags_DpiEnableScaleFonts -> bool io.ConfigDpiScaleFonts. - Renamed/moved ImGuiConfigFlags_DpiEnableScaleViewports -> bool io.ConfigDpiScaleViewports. **Neither of those flags are very useful in current code. They will be useful once we merge font changes.** [there was a bug on 2025/06/12: when using the old config flags names, they were not imported correctly into the new ones, fixed on 2025/09/12] - 2025/06/11 (1.92.0) - THIS VERSION CONTAINS THE LARGEST AMOUNT OF BREAKING CHANGES SINCE 2015! I TRIED REALLY HARD TO KEEP THEM TO A MINIMUM, REDUCE THE AMOUNT OF INTERFERENCES, BUT INEVITABLY SOME USERS WILL BE AFFECTED. IN ORDER TO HELP US IMPROVE THE TRANSITION PROCESS, INCL. DOCUMENTATION AND COMMENTS, PLEASE REPORT **ANY** DOUBT, CONFUSION, QUESTIONS, FEEDBACK TO: https://github.com/ocornut/imgui/issues/ As part of the plan to reduce impact of API breaking changes, several unfinished changes/features/refactors related to font and text systems and scaling will be part of subsequent releases (1.92.1+). If you are updating from an old version, and expecting a massive or difficult update, consider first updating to 1.91.9 to reduce the amount of changes. - Hard to read? Refer to 'docs/Changelog.txt' for a less compact and more complete version of this! - Fonts: **IMPORTANT**: if your app was solving the OSX/iOS Retina screen specific logical vs display scale problem by setting io.DisplayFramebufferScale (e.g. to 2.0f) + setting io.FontGlobalScale (e.g. to 1.0f/2.0f) + loading fonts at scaled sizes (e.g. size X * 2.0f): This WILL NOT map correctly to the new system! Because font will rasterize as requested size. - With a legacy backend (< 1.92): Instead of setting io.FontGlobalScale = 1.0f/N -> set ImFontCfg::RasterizerDensity = N. This already worked before, but is now pretty much required. - With a new backend (1.92+): This should be all automatic. FramebufferScale is automatically used to set current font RasterizerDensity. FramebufferScale is a per-viewport property provided by backend through the Platform_GetWindowFramebufferScale() handler in 'docking' branch. - Fonts: **IMPORTANT** on Font Sizing: Before 1.92, fonts were of a single size. They can now be dynamically sized. - PushFont() API now has a REQUIRED size parameter. - Before 1.92: PushFont() always used font "default" size specified in AddFont() call. It is equivalent to calling PushFont(font, font->LegacySize). - Since 1.92: PushFont(font, 0.0f) preserve the current font size which is a shared value. - To use old behavior: use 'ImGui::PushFont(font, font->LegacySize)' at call site. - Kept inline single parameter function. Will obsolete. - Fonts: **IMPORTANT** on Font Merging: - When searching for a glyph in multiple merged fonts: we search for the FIRST font source which contains the desired glyph. Because the user doesn't need to provide glyph ranges any more, it is possible that a glyph that you expected to fetch from a secondary/merged icon font may be erroneously fetched from the primary font. - When searching for a glyph in multiple merged fonts: we now search for the FIRST font source which contains the desired glyph. This is technically a different behavior than before! - e.g. If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1. After the update and when using a new backend, those glyphs may now loaded from Font Source 1! - We added `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to exclude from a given font source: // Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig cfg1; cfg1.GlyphExcludeRanges = exclude_ranges; io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); // Add Font Source 2, which expects to use the range above ImFontConfig cfg2; cfg2.MergeMode = true; io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2); - You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to see list of glyphs available in multiple font sources. This can facilitate understanding which font input is providing which glyph. - Fonts: **IMPORTANT** on Thread Safety: - A few functions such as font->CalcTextSizeA() were, by sheer luck (== accidentally) thread-safe even though we had never provided that guarantee. They are definitively not thread-safe anymore as new glyphs may be loaded. - Fonts: ImFont::FontSize was removed and does not make sense anymore. ImFont::LegacySize is the size passed to AddFont(). - Fonts: Removed support for PushFont(NULL) which was a shortcut for "default font". - Fonts: Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'. - Textures: all API functions taking a 'ImTextureID' parameter are now taking a 'ImTextureRef'. Affected functions are: ImGui::Image(), ImGui::ImageWithBg(), ImGui::ImageButton(), ImDrawList::AddImage(), ImDrawList::AddImageQuad(), ImDrawList::AddImageRounded(). - Fonts: obsoleted ImFontAtlas::GetTexDataAsRGBA32(), GetTexDataAsAlpha8(), Build(), SetTexID(), IsBuilt() functions. The new protocol for backends to handle textures doesn't need them. Kept redirection functions (will obsolete). - Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling. - Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese(). - Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327) - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't. - Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors. - Fonts: obsoleted ImFont::Scale which is not useful anymore. - Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things: - ImDrawCmd::TextureId has been changed to ImDrawCmd::TexRef. - ImFontAtlas::TexID has been changed to ImFontAtlas::TexRef. - ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[] - ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourceCount. - Each ImFont has a number of ImFontBaked instances corresponding to actively used sizes. ImFont::GetFontBaked(size) retrieves the one for a given size. - Fields moved from ImFont to ImFontBaked: IndexAdvanceX[], Glyphs[], Ascent, Descent, FindGlyph(), FindGlyphNoFallback(), GetCharAdvance(). - Fields moved from ImFontAtlas to ImFontAtlas->Tex: ImFontAtlas::TexWidth => TexData->Width, ImFontAtlas::TexHeight => TexData->Height, ImFontAtlas::TexPixelsAlpha8/TexPixelsRGBA32 => TexData->GetPixels(). - Widget code may use ImGui::GetFontBaked() instead of ImGui::GetFont() to access font data for current font at current font size (and you may use font->GetFontBaked(size) to access it for any other size.) - Fonts: (users of imgui_freetype): renamed ImFontAtlas::FontBuilderFlags to ImFontAtlas::FontLoaderFlags. Renamed ImFontConfig::FontBuilderFlags to ImFontConfig::FontLoaderFlags. Renamed ImGuiFreeTypeBuilderFlags to ImGuiFreeTypeLoaderFlags. If you used runtime imgui_freetype selection rather than the default IMGUI_ENABLE_FREETYPE compile-time option: Renamed/reworked ImFontBuilderIO into ImFontLoader. Renamed ImGuiFreeType::GetBuilderForFreeType() to ImGuiFreeType::GetFontLoader(). - old: io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType() - new: io.Fonts->FontLoader = ImGuiFreeType::GetFontLoader() - new: io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) to change dynamically at runtime [from 1.92.1] - Fonts: (users of custom rectangles, see #8466): Renamed AddCustomRectRegular() to AddCustomRect(). Added GetCustomRect() as a replacement for GetCustomRectByIndex() + CalcCustomRectUV(). - The output type of GetCustomRect() is now ImFontAtlasRect, which include UV coordinates. X->x, Y->y, Width->w, Height->h. - old: const ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(custom_rect_id); ImVec2 uv0, uv1; atlas->GetCustomRectUV(r, &uv0, &uv1); ImGui::Image(atlas->TexRef, ImVec2(r->w, r->h), uv0, uv1); - new; ImFontAtlasRect r; atlas->GetCustomRect(custom_rect_id, &r); ImGui::Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1); - We added a redirecting typedef but haven't attempted to magically redirect the field names, as this API is rarely used and the fix is simple. - Obsoleted AddCustomRectFontGlyph() as the API does not make sense for scalable fonts. Kept existing function which uses the font "default size" (Sources[0]->LegacySize). Added a helper AddCustomRectFontGlyphForSize() which is immediately marked obsolete, but can facilitate transitioning old code. - Prefer adding a font source (ImFontConfig) using a custom/procedural loader. - DrawList: Renamed ImDrawList::PushTextureID()/PopTextureID() to PushTexture()/PopTexture(). - Backends: removed ImGui_ImplXXXX_CreateFontsTexture()/ImGui_ImplXXXX_DestroyFontsTexture() for all backends that had them. They should not be necessary any more. - 2025/05/23 (1.92.0) - Fonts: changed ImFont::CalcWordWrapPositionA() to ImFont::CalcWordWrapPosition() - old: const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, ....); - new: const char* ImFont::CalcWordWrapPosition (float size, const char* text, ....); The leading 'float scale' parameters was changed to 'float size'. This was necessary as 'scale' is assuming standard font size which is a concept we aim to eliminate in an upcoming update. Kept inline redirection function. - 2025/05/15 (1.92.0) - TreeNode: renamed ImGuiTreeNodeFlags_NavLeftJumpsBackHere to ImGuiTreeNodeFlags_NavLeftJumpsToParent for clarity. Kept inline redirection enum (will obsolete). - 2025/05/15 (1.92.0) - Commented out PushAllowKeyboardFocus()/PopAllowKeyboardFocus() which was obsoleted in 1.89.4. Use PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop)/PopItemFlag() instead. (#3092) - 2025/05/15 (1.92.0) - Commented out ImGuiListClipper::ForceDisplayRangeByIndices() which was obsoleted in 1.89.6. Use ImGuiListClipper::IncludeItemsByIndex() instead. - 2025/03/05 (1.91.9) - BeginMenu(): Internals: reworked mangling of menu windows to use "###Menu_00" etc. instead of "##Menu_00", allowing them to also store the menu name before it. This shouldn't affect code unless directly accessing menu window from their mangled name. - 2025/04/16 (1.91.9) - Internals: RenderTextEllipsis() function removed the 'float clip_max_x' parameter directly preceding 'float ellipsis_max_x'. Values were identical for a vast majority of users. (#8387) - 2025/02/27 (1.91.9) - Image(): removed 'tint_col' and 'border_col' parameter from Image() function. Added ImageWithBg() replacement. (#8131, #8238) - old: void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 tint_col = (1,1,1,1), ImVec4 border_col = (0,0,0,0)); - new: void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1)); - new: void ImageWithBg(ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 bg_col = (0,0,0,0), ImVec4 tint_col = (1,1,1,1)); - TL;DR: 'border_col' had misleading side-effect on layout, 'bg_col' was missing, parameter order couldn't be consistent with ImageButton(). - new behavior always use ImGuiCol_Border color + style.ImageBorderSize / ImGuiStyleVar_ImageBorderSize. - old behavior altered border size (and therefore layout) based on border color's alpha, which caused variety of problems + old behavior a fixed 1.0f for border size which was not tweakable. - kept legacy signature (will obsolete), which mimics the old behavior, but uses Max(1.0f, style.ImageBorderSize) when border_col is specified. - added ImageWithBg() function which has both 'bg_col' (which was missing) and 'tint_col'. It was impossible to add 'bg_col' to Image() with a parameter order consistent with other functions, so we decided to remove 'tint_col' and introduce ImageWithBg(). - 2025/02/25 (1.91.9) - internals: fonts: ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]. ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourcesCount. - 2025/02/06 (1.91.9) - renamed ImFontConfig::GlyphExtraSpacing.x to ImFontConfig::GlyphExtraAdvanceX. - 2025/01/22 (1.91.8) - removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior. prior to 1.91.8: alpha was made opaque in the preview by default _unless_ using ImGuiColorEditFlags_AlphaPreview. We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. the new flags (ImGuiColorEditFlags_AlphaOpaque, ImGuiColorEditFlags_AlphaNoBg + existing ImGuiColorEditFlags_AlphaPreviewHalf) may be combined better and allow finer controls: - 2025/01/14 (1.91.7) - renamed ImGuiTreeNodeFlags_SpanTextWidth to ImGuiTreeNodeFlags_SpanLabelWidth for consistency with other names. Kept redirection enum (will obsolete). (#6937) - 2024/11/27 (1.91.6) - changed CRC32 table from CRC32-adler to CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions. As a result, old .ini data may be partially lost (docking and tables information particularly). Because some users have crafted and storing .ini data as a way to workaround limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER' compile-time option to keep using old CRC32 tables if you cannot afford invalidating old .ini data. - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before) - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022). - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022). - GetKeyIndex() is removed (obsoleted March 2022). The indirection is now unnecessary. - pre-1.87 backends are not supported: - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields. - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields. - for more reference: - read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88. - read https://github.com/ocornut/imgui/issues/4921 - if you have trouble updating a very old codebase using legacy backend-specific key codes: consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest. - obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0). probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END? - fonts: removed const qualifiers from most font functions in prevision for upcoming font improvements. - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete). - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool. moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!). kept legacy names (will obsolete) + code that copies settings once the first time. Dynamically changing the old value won't work. Switch to using the new value! - 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641) this removes the requirement to redefine it for backends which are e.g. storing descriptor sets or other 64-bits structures when building on 32-bits archs. It therefore simplify various building scripts/helpers. you may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID' when passing your types to functions taking ImTextureID values, e.g. ImGui::Image(). in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning: - May warn: ImGui::Image((void*)MyTextureData, ...); - May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...); - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...); - note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like. - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76) - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed). although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76) - 2024/09/10 (1.91.2) - internals: using multiple overlaid ButtonBehavior() with same ID will now have io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030) it was one of the rare case where using same ID is legal. workarounds: (1) use single ButtonBehavior() call with multiple _MouseButton flags, or (2) surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag() - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag. - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure: - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData. - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn + same as above line. - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660) - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278) - access those via GetPlatformIO() instead of GetIO(). some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol. - commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder: - old ImageButton() before 1.89 used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID) - new ImageButton() since 1.89 requires an explicit 'const char* str_id' - old ImageButton() before 1.89 had frame_padding' override argument. - new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar(). - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info) you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way. - instead of: GetWindowContentRegionMax().x - GetCursorPos().x - you can use: GetContentRegionAvail().x - instead of: GetWindowContentRegionMax().x + GetWindowPos().x - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window - instead of: GetContentRegionMax() - you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates - instead of: GetWindowContentRegionMax().x - GetWindowContentRegionMin().x - you can use: GetContentRegionAvail() // when called from left edge of window - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors) - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag(). - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness. - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data); - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); - 2024/06/21 (1.90.9) - BeginChild: added ImGuiChildFlags_NavFlattened as a replacement for the window flag ImGuiWindowFlags_NavFlattened: the feature only ever made sense for BeginChild() anyhow. - old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened); - new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0); - 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does. - 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire. - 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected. - 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages). - 2024/06/06 (1.90.8) - reordered ImGuiInputTextFlags values. This should not be breaking unless you are using generated headers that have values not matching the main library. - 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading. - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022): - old: CaptureKeyboardFromApp(bool) - new: SetNextFrameWantCaptureKeyboard(bool) - old: CaptureMouseFromApp(bool) - new: SetNextFrameWantCaptureMouse(bool) - 2024/05/22 (1.90.7) - inputs (internals): renamed ImGuiKeyOwner_None to ImGuiKeyOwner_NoOwner, to make use more explicit and reduce confusion with the default it is a non-zero value and cannot be the default value (never made public, but disclosing as I expect a few users caught on owner-aware inputs). - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest. - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures: - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0); - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0); - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures. - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0); - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0); for various reasons those changes makes sense. They are being made because making some of those API public. only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL. - 2024/05/21 (1.90.7) - docking: changed signature of DockSpaceOverViewport() to add explicit dockspace id if desired. pass 0 to use old behavior. (#7611) - old: DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...); - new: DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...); - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys. - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps. - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456) - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions. - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282) - old: TreeNode("##Hidden"); SameLine(); Text("Hello"); // <-- This was actually incorrect! BUT appeared to look ok with the default style where ItemSpacing.x == FramePadding.x * 2 (it didn't look aligned otherwise). - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values. with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item. You'll need to change the SameLine() call to SameLine(0,0) to remove this extraneous spacing. This seemed like the more sensible fix that's not making things less consistent. (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth). - 2024/03/18 (1.90.5) - merged the radius_x/radius_y parameters in ImDrawList::AddEllipse(), AddEllipseFilled() and PathEllipticalArcTo() into a single ImVec2 parameter. Exceptionally, because those functions were added in 1.90, we are not adding inline redirection functions. The transition is easy and should affect few users. (#2743, #7417) - 2024/03/08 (1.90.5) - inputs: more formally obsoleted GetKeyIndex() when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is set. It has been unnecessary and a no-op since 1.87 (it returns the same value as passed when used with a 1.87+ backend using io.AddKeyEvent() function). (#4921) - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter. - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges. - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls. - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80. - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete). - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete). those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features. - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior "bool-to-flags" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work. - old: BeginChild("Name", size, true) - new: BeginChild("Name", size, ImGuiChildFlags_Border) - old: BeginChild("Name", size, false) - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None) **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'** - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow. - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding); - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0); - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user). - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() ("Stack Tool") to ShowIDStackToolWindow() ("ID Stack Tool"), as earlier name was misleading. Kept inline redirection function. (#4631) - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of "name getter" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete). - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...) - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...); - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...); - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...); - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions: - GetWindowContentRegionWidth() -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful. - ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources. - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry! - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878) - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15). - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete). - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior. - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610) - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage. - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3. - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago: - ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference) - ListBoxFooter() -> use EndListBox() - 2023/05/15 (1.89.6) - clipper: commented out obsolete redirection constructor 'ImGuiListClipper(int items_count, float items_height = -1.0f)' that was marked obsolete in 1.79. Use default constructor + clipper.Begin(). - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices(). - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago: - ImGuiSliderFlags_ClampOnInput -> use ImGuiSliderFlags_AlwaysClamp - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite - ImDrawList::AddBezierCurve() -> use ImDrawList::AddBezierCubic() - ImDrawList::PathBezierCurveTo() -> use ImDrawList::PathBezierCubicCurveTo() - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete). - 2023/03/09 (1.89.4) - tooltips: Added 'bool' return value to BeginTooltip() for API consistency. Please only submit contents and call EndTooltip() if BeginTooltip() returns true. In reality the function will _currently_ always return true, but further changes down the line may change this, best to clarify API sooner. - 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h. Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA, it has been frequently requested by people to use our own. We had an opt-in define which was previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164) - OK: #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h" - Error: #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h" - 2023/02/07 (1.89.3) - backends: renamed "imgui_impl_sdl.cpp" to "imgui_impl_sdl2.cpp" and "imgui_impl_sdl.h" to "imgui_impl_sdl2.h". (#6146) This is in prevision for the future release of SDL3. - 2022/10/26 (1.89) - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79. - 2022/10/12 (1.89) - removed runtime patching of invalid "%f"/"%0.f" format strings for DragInt()/SliderInt(). This was obsoleted in 1.61 (May 2018). See 1.61 changelog for details. - 2022/09/26 (1.89) - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete). - ImGuiKey_ModCtrl and ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift - ImGuiKey_ModAlt and ImGuiModFlags_Alt -> ImGuiMod_Alt - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends. the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions. exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway. - 2022/09/20 (1.89) - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers. this will require uses of legacy backend-dependent indices to be casted, e.g. - with imgui_impl_glfw: IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A); - with imgui_impl_win32: IsKeyPressed('A') -> IsKeyPressed((ImGuiKey)'A') - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now! - 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr); - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020): - DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f. - SliderScalar(), SliderScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f. - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags) - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries. this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item. - previously this would make the window content size ~200x200: Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); - instead, please submit an item: Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); - alternative: Begin(...) + Dummy(ImVec2(200,200)) + End(); - content size is now only extended when submitting an item! - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert. - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it. - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete). - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter. - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1)); - used the ImTextureID value to create an ID. This was inconsistent with other functions, led to ID conflicts, and caused problems with engines using transient ImTextureID values. - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer. - new signature: bool ImageButton(const char* str_id, ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1)); - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier. - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this. - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes). - Official backends from 1.87+ -> no issue. - Official backends from 1.60 to 1.86 -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating! - Custom backends not writing to io.NavInputs[] -> no issue. - Custom backends writing to io.NavInputs[] -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing! - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[]. - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete). - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary. - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO. - 2022/01/20 (1.87) - inputs: reworded gamepad IO. - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values. - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputting text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). - 2022/01/17 (1.87) - inputs: reworked mouse IO. - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent() - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent() - Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent() - Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only] note: for all calls to IO new functions, the Dear ImGui context should be bound/current. read https://github.com/ocornut/imgui/issues/4921 for details. - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(), ImGui::IsKeyDown(). Removed GetKeyIndex(), now unnecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details. - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to still function with legacy key codes). - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiMod_XXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiMod_XXX values.* - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert. - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID(). - if you are using official backends from the source tree: you have nothing to do. - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID(). - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc. flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API. breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners": - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use) - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use) - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use) - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f. this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok. the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts. legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise). - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018): - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY() - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing. - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future. - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed. - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete). - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete). - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete). - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags. - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags. - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018): - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit(). - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg - ImGuiInputTextCallback -> use ImGuiTextEditCallback - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete). - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added! - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API. - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/. - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018): - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow) - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT - removed redirecting functions names that were marked obsolete in 1.61 (May 2018): - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision. - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter. - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed). - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently). - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory. - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result. - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now! - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar(). replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags). worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions: - if you omitted the 'power' parameter (likely!), you are not affected. - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct. - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f. see https://github.com/ocornut/imgui/issues/3361 for all details. kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used. for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime. - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems. - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79] - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017. - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular(). - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more. - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead. - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value. - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017): - ShowTestWindow() -> use ShowDemoWindow() - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)) - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API. - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it). - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert. - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency. - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency. - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017): - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding() - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f) - ImFont::Glyph -> use ImFontGlyph - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function. if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix. The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay). If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you. - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete). - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete). - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71. - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering. This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows. Please reach out if you are affected. - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete). - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c). - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now. - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete). - 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 an arbitrarily 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. Kept 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. 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. - 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 [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 backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports. when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call. in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function. - 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/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. If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them. - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format", consistent with other functions. Kept redirection functions (will obsolete). - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value. - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch). - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically. - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment. - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display. - 2018/02/07 (1.60) - reorganized context handling to be more explicit, - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. - removed Shutdown() function, as DestroyContext() serve this purpose. - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance. - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts. - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts. - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths. - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete). - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete). - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData. - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side. - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete). - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame. - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set. - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete). - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete). - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete). - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete). - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete). - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed. - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up. Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions. - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency. - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg. - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding. - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency. - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it. - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details. removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly) IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow) IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior] - 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. Kept 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 backend 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)! - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicitly to fix. - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type. - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely. - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete). - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete). - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu. - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options. - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0))' - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetID() and use it instead of passing string to BeginChild(). - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully, breakage should be minimal. - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color: ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); } If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color. - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref GitHub issue #337). - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert. - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you. - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side. GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out! - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project. - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure. you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost. this necessary change will break your rendering function! the fix should be very easy. sorry for that :( - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. - 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). 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. - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering! - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount. - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry! - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons. - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened. - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) - 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. - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..]; - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier); 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. - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID() - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly) - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes FREQUENTLY ASKED QUESTIONS (FAQ) ================================ Read all answers online: https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url) Read all answers locally (with a text editor or ideally a Markdown viewer): docs/FAQ.md Some answers are copied down here to facilitate searching in code. Q&A: Basics =========== Q: Where is the documentation? A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++. - Run the examples/ applications and explore them. - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide. - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. - See pthom's online imgui_explorer (https://pthom.github.io/imgui_explorer) which is a web version of the demo with a source code browser. - The demo covers most features of Dear ImGui, so you can read the code and see its output. - See documentation and comments at the top of imgui.cpp + effectively imgui.h. - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the examples/ folder to explain how to integrate Dear ImGui with your own engine/application. - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. - Your programming IDE is your friend, find the type or function declaration to find comments associated with it. Q: What is this library called? Q: What is the difference between Dear ImGui and traditional UI toolkits? Q: Which version should I get? >> This library is called "Dear ImGui", please don't call it "ImGui" :) >> See https://www.dearimgui.com/faq for details. Q&A: Integration ================ Q: How to get started? A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application? A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this. Q. How can I enable keyboard or gamepad controls? Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display) Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around... Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries... >> See https://www.dearimgui.com/faq Q&A: Usage ---------- Q: About the ID Stack system.. - Why is my widget not reacting when I click on it? - How can I have widgets with an empty label? - How can I have multiple widgets with the same label? - How can I have multiple windows with the same label? Q: How can I display an image? What is ImTextureID, how does it work? Q: How can I use my own math types instead of ImVec2? Q: How can I interact with standard C++ types (such as std::string and std::vector)? Q: How can I display custom shapes? (using low-level ImDrawList API) >> See https://www.dearimgui.com/faq Q&A: Fonts, Text ================ Q: How should I handle DPI in my application? Q: How can I load a different font than the default? Q: How can I easily use icons in my application? Q: How can I load multiple fonts? Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md Q&A: Concerns ============= Q: Who uses Dear ImGui? Q: Can you create elaborate/serious tools with Dear ImGui? Q: Can you reskin the look of Dear ImGui? Q: Why using C++ (as opposed to C)? >> See https://www.dearimgui.com/faq Q&A: Community ============== Q: How can I help? A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui! We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project. >>> See https://github.com/ocornut/imgui/wiki/Funding - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help! - 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. Visuals are ideal as they inspire other programmers. But even without visuals, disclosing your use of dear imgui helps 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). */ //------------------------------------------------------------------------- // [SECTION] INCLUDES //------------------------------------------------------------------------- #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_internal.h" // System includes #include // vsnprintf, sscanf, printf #include // intptr_t // [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled #if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS #endif // [Windows] OS specific includes (optional) #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) #define IMGUI_DISABLE_WIN32_FUNCTIONS #endif #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #ifndef NOMINMAX #define NOMINMAX #endif #ifndef __MINGW32__ #include // _wfopen, OpenClipboard #else #include #endif #if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP) || (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)) // The UWP and GDK Win32 API subsets don't support clipboard nor IME functions #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS #define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #endif #endif // [Apple] OS specific includes #if defined(__APPLE__) #include #endif // 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 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #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" // warning: format specifies type 'int' but the argument has type 'unsigned int' #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 "-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 "-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 is. #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int' #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 #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. #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type #pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label #elif defined(__GNUC__) // We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association. #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #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 #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*' #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers #pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif // Debug options #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Hold Ctrl to display for all candidates. Ctrl+Arrow to change last direction. #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window // Default font size if unspecified in both style.FontSizeBase and AddFontXXX() calls. static const float FONT_DEFAULT_SIZE_BASE = 20.0f; // 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_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut. static const float NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY = 0.60f; // Time to hold activation button (e.g. FaceDown) to turn the activation into a text input. static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. // Tooltip offset static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20); // Multiplied by g.Style.MouseCursorScale static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f); // Multiplied by g.Style.MouseCursorScale // Docking static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport. //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- static void SetCurrentWindow(ImGuiWindow* window); static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); // Settings static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf); // Platform Dependents default implementation for ImGuiPlatformIO functions static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx); static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text); static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); namespace ImGui { // Item static void ItemHandleShortcut(ImGuiID id); // Window Focus static int FindWindowFocusIndex(ImGuiWindow* window); static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags); // Navigation static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingApplyFocus(ImGuiWindow* window); static void NavUpdateWindowingOverlay(); static void NavUpdateCancelRequest(); static void NavUpdateContextMenuRequest(); static void NavUpdateCreateMoveRequest(); static void NavUpdateCreateTabbingRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); static void NavUpdateCreateWrappingRequest(); static void NavEndFrame(); static bool NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb); static void NavApplyItemToResult(ImGuiNavItemData* result); static void NavProcessItem(); static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags); static ImGuiInputSource NavCalcPreferredRefPosSource(ImGuiWindowFlags window_type); static ImVec2 NavCalcPreferredRefPos(ImGuiWindowFlags window_type); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void NavRestoreLayer(ImGuiNavLayer layer); // Error Checking and Debug Tools static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); #ifndef IMGUI_DISABLE_DEBUG_TOOLS static void UpdateDebugToolItemPicker(); static void UpdateDebugToolItemPathQuery(); static void UpdateDebugToolFlashStyleColor(); #endif // Inputs static void UpdateKeyboardInputs(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt); // Misc static void UpdateFontsNewFrame(); static void UpdateFontsEndFrame(); static void UpdateTexturesNewFrame(); static void UpdateTexturesEndFrame(); static void UpdateSettings(); static int UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowShadow(ImGuiWindow* window); static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgrounds(); static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect); static void SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); static void DestroyViewport(ImGuiViewportP* viewport); static void UpdateViewportsNewFrame(); static void UpdateViewportsEndFrame(); static void WindowSelectViewport(ImGuiWindow* window); static void WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack); static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window); static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); static int FindPlatformMonitorForPos(const ImVec2& pos); static int FindPlatformMonitorForRect(const ImRect& r); static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport); } //----------------------------------------------------------------------------- // [SECTION] CONTEXT AND MEMORY ALLOCATORS //----------------------------------------------------------------------------- // DLL users: // - Heaps and globals are not shared across DLL boundaries! // - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from. // - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL). // - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. // - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in). // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. // - ImGui::CreateContext() will automatically set this pointer if it is NULL. // Change to a different context by calling ImGui::SetCurrentContext(). // - 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: // - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h: // struct ImGuiContext; // extern thread_local ImGuiContext* MyImGuiTLS; // #define GImGui MyImGuiTLS // And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. // - Future development aims 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 a different namespace. // - DLL users: read comments above. #ifndef GImGui ImGuiContext* GImGui = NULL; #endif // Memory Allocator functions. Use SetAllocatorFunctions() to change them. // - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. // - DLL users: read comments above. #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS 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) { 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 ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper; static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper; static void* GImAllocatorUserData = NULL; //----------------------------------------------------------------------------- // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO) //----------------------------------------------------------------------------- ImGuiStyle::ImGuiStyle() { FontSizeBase = 0.0f; // Will default to io.Fonts->Fonts[0] on first frame. FontScaleMain = 1.0f; // Main scale factor. May be set by application once, or exposed to end-user. FontScaleDpi = 1.0f; // Additional scale factor from viewport/monitor contents scale. When io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI. Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. WindowPadding = ImVec2(8,8); // Padding within a window WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. WindowBorderHoverPadding = 4.0f; // Hit-testing extent outside/inside resizing border. Also extend determination of hovered window. Generally meaningfully larger than WindowBorderSize to make it easy to reach borders. WindowMinSize = ImVec2(32,32); // Minimum window size WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text WindowMenuButtonPosition = ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) CellPadding = ImVec2(4,2); // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows. TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar ScrollbarPadding = 2.0f; // Padding of scrollbar grab within its frame (same for both axes) GrabMinSize = 12.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. LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. ImageRounding = 0.0f; // Rounding of Image() calls. ImageBorderSize = 0.0f; // Thickness of border around tabs. TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. TabMinWidthBase = 1.0f; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected. TabMinWidthShrink = 80.0f; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy. TabCloseButtonMinWidthSelected = -1.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. TabCloseButtonMinWidthUnselected = 0.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected. TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. TabBarOverlineSize = 1.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar. TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees). TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell TreeLinesFlags = ImGuiTreeNodeFlags_DrawLinesNone; TreeLinesSize = 1.0f; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines. TreeLinesRounding = 0.0f; // Radius of lines connecting child nodes to the vertical line. DragDropTargetRounding = 0.0f; // Radius of the drag and drop target frame. DragDropTargetBorderSize = 2.0f; // Thickness of the drag and drop target border. DragDropTargetPadding = 3.0f; // Size to expand the drag and drop target from actual target item size. ColorMarkerSize = 3.0f; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers. ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. SeparatorSize = 1.0f; // Thickness of border in Separator(). SeparatorTextBorderSize = 3.0f; // Thickness of border in SeparatorText(). SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors 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. DockingNodeHasCloseButton = true; // Docking nodes have their own CloseButton() to close all docked windows. DockingSeparatorSize = 2.0f; // Thickness of resizing border between docked windows MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. WindowShadowSize = 100.0f; // Size (in pixels) of window shadows. WindowShadowOffsetDist = 0.0f; // Offset distance (in pixels) of window shadows from casting window. WindowShadowOffsetAngle = IM_PI * 0.25f; // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top). // Behaviors HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. // [Internal] _MainScale = 1.0f; _NextFrameFontSizeBase = 0.0f; // Default theme ImGui::StyleColorsDark(this); } // Scale all spacing/padding/thickness values. Do not scale fonts. // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times. void ImGuiStyle::ScaleAllSizes(float scale_factor) { _MainScale *= scale_factor; WindowPadding = ImTrunc(WindowPadding * scale_factor); WindowRounding = ImTrunc(WindowRounding * scale_factor); WindowBorderSize = ImTrunc(WindowBorderSize * scale_factor); WindowMinSize = ImTrunc(WindowMinSize * scale_factor); WindowBorderHoverPadding = ImTrunc(WindowBorderHoverPadding * scale_factor); ChildRounding = ImTrunc(ChildRounding * scale_factor); ChildBorderSize = ImTrunc(ChildBorderSize * scale_factor); PopupRounding = ImTrunc(PopupRounding * scale_factor); PopupBorderSize = ImTrunc(PopupBorderSize * scale_factor); FramePadding = ImTrunc(FramePadding * scale_factor); FrameBorderSize = ImTrunc(FrameBorderSize * scale_factor); FrameRounding = ImTrunc(FrameRounding * scale_factor); ItemSpacing = ImTrunc(ItemSpacing * scale_factor); ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor); CellPadding = ImTrunc(CellPadding * scale_factor); TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor); IndentSpacing = ImTrunc(IndentSpacing * scale_factor); ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor); ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor); ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor); ScrollbarPadding = ImTrunc(ScrollbarPadding * scale_factor); GrabMinSize = ImTrunc(GrabMinSize * scale_factor); GrabRounding = ImTrunc(GrabRounding * scale_factor); LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor); ImageRounding = ImTrunc(ImageRounding * scale_factor); ImageBorderSize = ImTrunc(ImageBorderSize * scale_factor); TabRounding = ImTrunc(TabRounding * scale_factor); TabBorderSize = ImTrunc(TabBorderSize * scale_factor); TabMinWidthBase = ImTrunc(TabMinWidthBase * scale_factor); TabMinWidthShrink = ImTrunc(TabMinWidthShrink * scale_factor); TabCloseButtonMinWidthSelected = (TabCloseButtonMinWidthSelected > 0.0f && TabCloseButtonMinWidthSelected != FLT_MAX) ? ImTrunc(TabCloseButtonMinWidthSelected * scale_factor) : TabCloseButtonMinWidthSelected; TabCloseButtonMinWidthUnselected = (TabCloseButtonMinWidthUnselected > 0.0f && TabCloseButtonMinWidthUnselected != FLT_MAX) ? ImTrunc(TabCloseButtonMinWidthUnselected * scale_factor) : TabCloseButtonMinWidthUnselected; TabBarBorderSize = ImTrunc(TabBarBorderSize * scale_factor); TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor); TreeLinesSize = ImTrunc(TreeLinesSize * scale_factor); TreeLinesRounding = ImTrunc(TreeLinesRounding * scale_factor); DragDropTargetRounding = ImTrunc(DragDropTargetRounding * scale_factor); DragDropTargetBorderSize = ImTrunc(DragDropTargetBorderSize * scale_factor); DragDropTargetPadding = ImTrunc(DragDropTargetPadding * scale_factor); ColorMarkerSize = ImTrunc(ColorMarkerSize * scale_factor); SeparatorSize = ImTrunc(SeparatorSize * scale_factor); SeparatorTextBorderSize = ImTrunc(SeparatorTextBorderSize * scale_factor); SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); DockingSeparatorSize = ImTrunc(DockingSeparatorSize * scale_factor); DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor); MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor); } ImGuiIO::ImGuiIO() { // Most fields are initialized with zero memset((void*)this, 0, sizeof(*this)); IM_STATIC_ASSERT(IM_COUNTOF(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_COUNTOF(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Settings ConfigFlags = ImGuiConfigFlags_None; BackendFlags = ImGuiBackendFlags_None; DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f / 60.0f; IniSavingRate = 5.0f; IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). LogFilename = "imgui_log.txt"; UserData = NULL; Fonts = NULL; FontDefault = NULL; FontAllowUserScaling = false; #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS FontGlobalScale = 1.0f; // Use style.FontScaleMain instead! #endif DisplayFramebufferScale = ImVec2(1.0f, 1.0f); // Keyboard/Gamepad Navigation options ConfigNavSwapGamepadButtons = false; ConfigNavMoveSetMousePos = false; ConfigNavCaptureKeyboard = true; ConfigNavEscapeClearFocusItem = true; ConfigNavEscapeClearFocusWindow = false; ConfigNavCursorVisibleAuto = true; ConfigNavCursorVisibleAlways = false; // Docking options (when ImGuiConfigFlags_DockingEnable is set) ConfigDockingNoSplit = false; ConfigDockingNoDockingOver = false; ConfigDockingWithShift = false; ConfigDockingAlwaysTabBar = false; ConfigDockingTransparentPayload = false; // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) ConfigViewportsNoAutoMerge = false; ConfigViewportsNoTaskBarIcon = false; ConfigViewportsNoDecoration = true; ConfigViewportsNoDefaultParent = true; ConfigViewportsPlatformFocusSetsImGuiFocus = true; // Miscellaneous options MouseDrawCursor = false; #ifdef __APPLE__ ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else ConfigMacOSXBehaviors = false; #endif ConfigInputTrickleEventQueue = true; ConfigInputTextCursorBlink = true; ConfigInputTextEnterKeepActive = false; ConfigDragClickToInputText = false; ConfigWindowsResizeFromEdges = true; ConfigWindowsMoveFromTitleBarOnly = false; ConfigWindowsCopyContentsWithCtrlC = false; ConfigScrollbarScrollByPage = true; ConfigMemoryCompactTimer = 60.0f; ConfigDebugIsDebuggerPresent = false; ConfigDebugHighlightIdConflicts = true; ConfigDebugHighlightIdConflictsShowItemPicker = true; ConfigDebugBeginReturnValueOnce = false; ConfigDebugBeginReturnValueLoop = false; ConfigErrorRecovery = true; ConfigErrorRecoveryEnableAssert = true; ConfigErrorRecoveryEnableDebugLog = true; ConfigErrorRecoveryEnableTooltip = true; // Inputs Behaviors MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; MouseDragThreshold = 6.0f; KeyRepeatDelay = 0.275f; KeyRepeatRate = 0.050f; // Platform Functions // Note: Initialize() will setup default clipboard/ime handlers. BackendPlatformName = BackendRendererName = NULL; BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; // Input (NB: we already have memset zero the entire structure!) MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseSource = ImGuiMouseSource_Mouse; for (int i = 0; i < IM_COUNTOF(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; for (int i = 0; i < IM_COUNTOF(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } AppAcceptingEvents = true; } // Pass in translated ASCII characters for text input. // - with glfw you can get those from the callback set in glfwSetCharCallback() // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message // FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API void ImGuiIO::AddInputCharacter(unsigned int c) { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; if (c == 0 || !AppAcceptingEvents) return; ImGuiInputEvent e; e.Type = ImGuiInputEventType_Text; e.Source = ImGuiInputSource_Keyboard; e.EventId = g.InputEventsNextEventId++; e.Text.Char = c; g.InputEventsQueue.push_back(e); } // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so // we should save the high surrogate. void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) { if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents) return; if ((c & 0xFC00) == 0xD800) // High surrogate, must save { if (InputQueueSurrogate != 0) AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID); InputQueueSurrogate = c; return; } ImWchar cp = c; if (InputQueueSurrogate != 0) { if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate { AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID); } else { #if IM_UNICODE_CODEPOINT_MAX == 0xFFFF cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar #else cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); #endif } InputQueueSurrogate = 0; } AddInputCharacter((unsigned)cp); } void ImGuiIO::AddInputCharactersUTF8(const char* str) { if (!AppAcceptingEvents) return; const char* str_end = str + strlen(str); while (*str != 0) { unsigned int c = 0; str += ImTextCharFromUtf8(&c, str, str_end); AddInputCharacter(c); } } // Clear all incoming events. void ImGuiIO::ClearEventsQueue() { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; g.InputEventsQueue.clear(); } // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons. void ImGuiIO::ClearInputKeys() { ImGuiContext& g = *Ctx; for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++) { if (ImGui::IsMouseKey((ImGuiKey)key)) continue; ImGuiKeyData* key_data = &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN]; key_data->Down = false; key_data->DownDuration = -1.0f; key_data->DownDurationPrev = -1.0f; } KeyCtrl = KeyShift = KeyAlt = KeySuper = false; KeyMods = ImGuiMod_None; InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters(). } void ImGuiIO::ClearInputMouse() { for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1)) { ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_NamedKey_BEGIN]; key_data->Down = false; key_data->DownDuration = -1.0f; key_data->DownDurationPrev = -1.0f; } MousePos = ImVec2(-FLT_MAX, -FLT_MAX); for (int n = 0; n < IM_COUNTOF(MouseDown); n++) { MouseDown[n] = false; MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f; } MouseWheel = MouseWheelH = 0.0f; } static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1) { ImGuiContext& g = *ctx; for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--) { ImGuiInputEvent* e = &g.InputEventsQueue[n]; if (e->Type != type) continue; if (type == ImGuiInputEventType_Key && e->Key.Key != arg) continue; if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg) continue; return e; } return NULL; } // Queue a new key down/up event. // - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) // - bool down: Is the key down? use false to signify a key release. // - float analog_value: 0.0f..1.0f // IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE. // WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT. void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) { //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } IM_ASSERT(Ctx != NULL); if (key == ImGuiKey_None || !AppAcceptingEvents) return; ImGuiContext& g = *Ctx; IM_ASSERT(ImGui::IsNamedKeyOrMod(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events. // MacOS: swap Cmd(Super) and Ctrl if (g.IO.ConfigMacOSXBehaviors) { if (key == ImGuiMod_Super) { key = ImGuiMod_Ctrl; } else if (key == ImGuiMod_Ctrl) { key = ImGuiMod_Super; } else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; } else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; } else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; } else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; } } // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed) const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key); const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key); const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down; const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue; if (latest_key_down == down && latest_key_analog == analog_value) return; // Add event ImGuiInputEvent e; e.Type = ImGuiInputEventType_Key; e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard; e.EventId = g.InputEventsNextEventId++; e.Key.Key = key; e.Key.Down = down; e.Key.AnalogValue = analog_value; g.InputEventsQueue.push_back(e); } void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) { if (!AppAcceptingEvents) return; AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f); } // [Optional] Call after AddKeyEvent(). // Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices. // If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index) { if (key == ImGuiKey_None) return; IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512 IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511 IM_UNUSED(key); // Yet unused IM_UNUSED(native_keycode); // Yet unused IM_UNUSED(native_scancode); // Yet unused IM_UNUSED(native_legacy_index); // Yet unused } // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen. void ImGuiIO::SetAppAcceptingEvents(bool accepting_events) { AppAcceptingEvents = accepting_events; } // Queue a mouse move event void ImGuiIO::AddMousePosEvent(float x, float y) { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; if (!AppAcceptingEvents) return; // Apply same flooring as UpdateMouseInputs() ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y); // Filter duplicate const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos); const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos; if (latest_pos.x == pos.x && latest_pos.y == pos.y) return; ImGuiInputEvent e; e.Type = ImGuiInputEventType_MousePos; e.Source = ImGuiInputSource_Mouse; e.EventId = g.InputEventsNextEventId++; e.MousePos.PosX = pos.x; e.MousePos.PosY = pos.y; e.MousePos.MouseSource = g.InputEventsNextMouseSource; g.InputEventsQueue.push_back(e); } void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); if (!AppAcceptingEvents) return; // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button. if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick) { // Order of both statements matters: this event will still release mouse button 1 mouse_button = 1; if (!down) MouseCtrlLeftAsRightClick = false; } // Filter duplicate const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button); const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button]; if (latest_button_down == down) return; // On MacOS X: Convert Ctrl(Super)+Left click into Right-click. // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us. // - At this point we want from !down to down, so this is handling the initial press. if (ConfigMacOSXBehaviors && mouse_button == 0 && down) { const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super); if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper) { IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n"); MouseCtrlLeftAsRightClick = true; AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again. return; } } ImGuiInputEvent e; e.Type = ImGuiInputEventType_MouseButton; e.Source = ImGuiInputSource_Mouse; e.EventId = g.InputEventsNextEventId++; e.MouseButton.Button = mouse_button; e.MouseButton.Down = down; e.MouseButton.MouseSource = g.InputEventsNextMouseSource; g.InputEventsQueue.push_back(e); } // Queue a mouse wheel event (some mouse/API may only have a Y component) void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; // Filter duplicate (unlike most events, wheel values are relative and easy to filter) if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f)) return; ImGuiInputEvent e; e.Type = ImGuiInputEventType_MouseWheel; e.Source = ImGuiInputSource_Mouse; e.EventId = g.InputEventsNextEventId++; e.MouseWheel.WheelX = wheel_x; e.MouseWheel.WheelY = wheel_y; e.MouseWheel.MouseSource = g.InputEventsNextMouseSource; g.InputEventsQueue.push_back(e); } // This is not a real event, the data is latched in order to be stored in actual Mouse events. // This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes. void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source) { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; g.InputEventsNextMouseSource = source; } void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id) { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; //IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport); if (!AppAcceptingEvents) return; // Filter duplicate const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseViewport); const ImGuiID latest_viewport_id = latest_event ? latest_event->MouseViewport.HoveredViewportID : g.IO.MouseHoveredViewport; if (latest_viewport_id == viewport_id) return; ImGuiInputEvent e; e.Type = ImGuiInputEventType_MouseViewport; e.Source = ImGuiInputSource_Mouse; e.MouseViewport.HoveredViewportID = viewport_id; g.InputEventsQueue.push_back(e); } void ImGuiIO::AddFocusEvent(bool focused) { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; // Filter duplicate const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus); const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost; if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused)) return; ImGuiInputEvent e; e.Type = ImGuiInputEventType_Focus; e.EventId = g.InputEventsNextEventId++; e.AppFocused.Focused = focused; g.InputEventsQueue.push_back(e); } ImGuiPlatformIO::ImGuiPlatformIO() { // Most fields are initialized with zero memset((void*)this, 0, sizeof(*this)); Platform_LocaleDecimalPoint = '.'; } //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) { IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau() ImVec2 p_last = p1; ImVec2 p_closest; float p_closest_dist2 = FLT_MAX; float t_step = 1.0f / (float)num_segments; for (int i_step = 1; i_step <= num_segments; i_step++) { ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step); ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); float dist2 = ImLengthSqr(p - p_line); if (dist2 < p_closest_dist2) { p_closest = p_line; p_closest_dist2 = dist2; } p_last = p_current; } return p_closest; } // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) { float dx = x4 - x1; float dy = y4 - y1; float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); d2 = (d2 >= 0) ? d2 : -d2; d3 = (d3 >= 0) ? d3 : -d3; if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) { ImVec2 p_current(x4, y4); ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); float dist2 = ImLengthSqr(p - p_line); if (dist2 < p_closest_dist2) { p_closest = p_line; p_closest_dist2 = dist2; } p_last = p_current; } else if (level < 10) { float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f; float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f; float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f; float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f; float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f; float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f; ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); } } // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically. ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol) { IM_ASSERT(tess_tol > 0.0f); ImVec2 p_last = p1; ImVec2 p_closest; float p_closest_dist2 = FLT_MAX; ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0); return p_closest; } ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) { ImVec2 ap = p - a; ImVec2 ab_dir = b - a; float dot = ap.x * ab_dir.x + ap.y * ab_dir.y; if (dot < 0.0f) return a; float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; if (dot > ab_len_sqr) return b; return a + ab_dir * dot / ab_len_sqr; } bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) { bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; return (b1 == b2) && (b2 == b3); } void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) { ImVec2 v0 = b - a; ImVec2 v1 = c - a; ImVec2 v2 = p - a; const float denom = v0.x * v1.y - v1.x * v0.y; out_v = (v2.x * v1.y - v1.x * v2.y) / denom; out_w = (v0.x * v2.y - v2.x * v0.y) / denom; out_u = 1.0f - out_v - out_w; } ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) { ImVec2 proj_ab = ImLineClosestPoint(a, b, p); ImVec2 proj_bc = ImLineClosestPoint(b, c, p); ImVec2 proj_ca = ImLineClosestPoint(c, a, p); float dist2_ab = ImLengthSqr(p - proj_ab); float dist2_bc = ImLengthSqr(p - proj_bc); float dist2_ca = ImLengthSqr(p - proj_ca); float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca)); if (m == dist2_ab) return proj_ab; if (m == dist2_bc) return proj_bc; return proj_ca; } //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) //----------------------------------------------------------------------------- // 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; while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; } return d; } int ImStrnicmp(const char* str1, const char* str2, size_t count) { int d = 0; while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; } return d; } void ImStrncpy(char* dst, const char* src, size_t count) { if (count < 1) return; if (count > 1) strncpy(dst, src, count - 1); // FIXME-OPT: strncpy not only doesn't guarantee 0-termination, it also always writes the whole array dst[count - 1] = 0; } char* ImStrdup(const char* str) { size_t len = ImStrlen(str); void* buf = IM_ALLOC(len + 1); return (char*)memcpy(buf, (const void*)str, len + 1); } void* ImMemdup(const void* src, size_t size) { void* dst = IM_ALLOC(size); return memcpy(dst, src, size); } char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src) { size_t dst_buf_size = p_dst_size ? *p_dst_size : ImStrlen(dst) + 1; size_t src_size = ImStrlen(src) + 1; if (dst_buf_size < src_size) { IM_FREE(dst); dst = (char*)IM_ALLOC(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) { const char* p = (const char*)ImMemchr(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-bit 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*)ImMemchr(str, '\n', str_end - str); return p ? p : str_end; } const char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line { IM_ASSERT_PARANOID(buf_mid_line >= buf_begin && buf_mid_line <= buf_begin + ImStrlen(buf_begin)); while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') buf_mid_line--; return buf_mid_line; } const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end) { if (!needle_end) needle_end = needle + ImStrlen(needle); const char un0 = (char)ImToUpper(*needle); while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) { if (ImToUpper(*haystack) == un0) { const char* b = needle + 1; for (const char* a = haystack + 1; b < needle_end; a++, b++) if (ImToUpper(*a) != ImToUpper(*b)) break; if (b == needle_end) return haystack; } haystack++; } return NULL; } // Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible. void ImStrTrimBlanks(char* buf) { char* p = buf; while (p[0] == ' ' || p[0] == '\t') // Leading blanks p++; char* p_start = p; while (*p != 0) // Find end of string p++; while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks p--; if (p_start != buf) // Copy memory if we had leading blanks memmove(buf, p_start, p - p_start); buf[p - p_start] = 0; // Zero terminate } const char* ImStrSkipBlank(const char* str) { while (str[0] == ' ' || str[0] == '\t') str++; return str; } // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. // B) When buf==NULL vsnprintf() will return the output size. #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h) // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.) #ifdef IMGUI_USE_STB_SPRINTF #ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION #define STB_SPRINTF_IMPLEMENTATION #endif #ifdef IMGUI_STB_SPRINTF_FILENAME #include IMGUI_STB_SPRINTF_FILENAME #else #include "stb_sprintf.h" #endif #endif // #ifdef IMGUI_USE_STB_SPRINTF #if defined(_MSC_VER) && !defined(vsnprintf) #define vsnprintf _vsnprintf #endif 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; if (w == -1 || w >= (int)buf_size) w = (int)buf_size - 1; buf[w] = 0; return w; } 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) w = (int)buf_size - 1; buf[w] = 0; return w; } #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) { va_list args; va_start(args, fmt); ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args); va_end(args); } // FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller) // by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g. // ImGuiTempBufferToken token; // ImFormatStringToTempBuffer(token, ...); void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) { ImGuiContext& g = *GImGui; if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0) { const char* buf = va_arg(args, const char*); // Skip formatting when using "%s" if (buf == NULL) buf = "(null)"; *out_buf = buf; if (out_buf_end) { *out_buf_end = buf + ImStrlen(buf); } } else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0) { int buf_len = va_arg(args, int); // Skip formatting when using "%.*s" const char* buf = va_arg(args, const char*); if (buf == NULL) { buf = "(null)"; buf_len = ImMin(buf_len, 6); } *out_buf = buf; *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it. } else { int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args); *out_buf = g.TempBuffer.Data; if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; } } } #ifndef IMGUI_ENABLE_SSE4_2_CRC // 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] = { #ifdef IMGUI_USE_LEGACY_CRC32_ADLER // Legacy CRC32-adler table used pre 1.91.6 (before 2024/11/27). Only use if you cannot afford invalidating old .ini data. 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, #else // CRC32c table compatible with SSE 4.2 instructions 0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24, 0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B, 0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA, 0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595, 0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198, 0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7, 0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46, 0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829, 0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C, 0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033, 0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982, 0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED, 0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0, 0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F, 0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E, 0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351 #endif }; #endif // 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. ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed) { ImU32 crc = ~seed; const unsigned char* data = (const unsigned char*)data_p; const unsigned char *data_end = (const unsigned char*)data_p + data_size; #ifndef IMGUI_ENABLE_SSE4_2_CRC const ImU32* crc32_lut = GCrc32LookupTable; while (data < data_end) crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++]; return ~crc; #else while (data + 4 <= data_end) { crc = _mm_crc32_u32(crc, *(ImU32*)data); data += 4; } while (data < data_end) crc = _mm_crc32_u8(crc, *data++); return ~crc; #endif } // Zero-terminated string hash, with support for ### to reset back to seed value. // e.g. "label###id" outputs the same hash as "id" (and "label" is generally displayed by the UI functions) // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) { seed = ~seed; ImU32 crc = seed; const unsigned char* data = (const unsigned char*)data_p; #ifndef IMGUI_ENABLE_SSE4_2_CRC const ImU32* crc32_lut = GCrc32LookupTable; #endif if (data_size != 0) { while (data_size-- > 0) { unsigned char c = *data++; if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') { crc = seed; data += 2; data_size -= 2; continue; } #ifndef IMGUI_ENABLE_SSE4_2_CRC crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; #else crc = _mm_crc32_u8(crc, c); #endif } } else { while (unsigned char c = *data++) { if (c == '#' && data[0] == '#' && data[1] == '#') { crc = seed; data += 2; continue; } #ifndef IMGUI_ENABLE_SSE4_2_CRC crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; #else crc = _mm_crc32_u8(crc, c); #endif } } return ~crc; } // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() // FIXME-OPT: This is not designed to be optimal. Use with care. const char* ImHashSkipUncontributingPrefix(const char* label) { const char* result = label; while (unsigned char c = *label++) if (c == '#' && label[0] == '#' && label[1] == '#') result = label + 2; return result; } //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (File functions) //----------------------------------------------------------------------------- // Default file functions #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS ImFileHandle ImFileOpen(const char* filename, const char* mode) { #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (defined(__MINGW32__) || (!defined(__CYGWIN__) && !defined(__GNUC__))) // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32! const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator. // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314). wchar_t local_temp_stack[FILENAME_MAX]; ImVector local_temp_heap; if (filename_wsize + mode_wsize > IM_COUNTOF(local_temp_stack)) local_temp_heap.resize(filename_wsize + mode_wsize); wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack; wchar_t* mode_wbuf = filename_wbuf + filename_wsize; ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize); ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize); return ::_wfopen(filename_wbuf, mode_wbuf); #else return fopen(filename, mode); #endif } // We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way. bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; } ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; } ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); } ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); } #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Helper: Load file content into memory // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree() // This can't really be used with "rt" because fseek size won't match read size. void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes) { IM_ASSERT(filename && mode); if (out_file_size) *out_file_size = 0; ImFileHandle f; if ((f = ImFileOpen(filename, mode)) == NULL) return NULL; size_t file_size = (size_t)ImFileGetSize(f); if (file_size == (size_t)-1) { ImFileClose(f); return NULL; } void* file_data = IM_ALLOC(file_size + padding_bytes); if (file_data == NULL) { ImFileClose(f); return NULL; } if (ImFileRead(file_data, 1, file_size, f) != file_size) { ImFileClose(f); IM_FREE(file_data); return NULL; } if (padding_bytes > 0) memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); ImFileClose(f); if (out_file_size) *out_file_size = file_size; return file_data; } //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (ImText* functions) //----------------------------------------------------------------------------- IM_MSVC_RUNTIME_CHECKS_OFF // Convert UTF-8 to 32-bit character, process single character input. // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8). // We handle UTF-8 decoding error by skipping forward. int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) { static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 }; static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 }; static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 }; static const int shiftc[] = { 0, 18, 12, 6, 0 }; static const int shifte[] = { 0, 6, 4, 2, 0 }; int len = lengths[*(const unsigned char*)in_text >> 3]; int wanted = len + (len ? 0 : 1); // IMPORTANT: if in_text_end == NULL it assume we have enough space! if (in_text_end == NULL) in_text_end = in_text + wanted; // Max length, nulls will be taken into account. // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here, // so it is fast even with excessive branching. unsigned char s[4]; s[0] = in_text + 0 < in_text_end ? in_text[0] : 0; s[1] = in_text + 1 < in_text_end ? in_text[1] : 0; s[2] = in_text + 2 < in_text_end ? in_text[2] : 0; s[3] = in_text + 3 < in_text_end ? in_text[3] : 0; // Assume a four-byte character and load four bytes. Unused bits are shifted out. *out_char = (uint32_t)(s[0] & masks[len]) << 18; *out_char |= (uint32_t)(s[1] & 0x3f) << 12; *out_char |= (uint32_t)(s[2] & 0x3f) << 6; *out_char |= (uint32_t)(s[3] & 0x3f) << 0; *out_char >>= shiftc[len]; // Accumulate the various error conditions. int e = 0; e = (*out_char < mins[len]) << 6; // non-canonical encoding e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half? e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range we can store in ImWchar (FIXME: May evolve) e |= (s[1] & 0xc0) >> 2; e |= (s[2] & 0xc0) >> 4; e |= (s[3] ) >> 6; e ^= 0x2a; // top two bits of each tail byte correct? e >>= shifte[len]; if (e) { // No bytes are consumed when *in_text == 0 || in_text == in_text_end. // One byte is consumed in case of invalid first byte of in_text. // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes. // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s. wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]); *out_char = IM_UNICODE_CODEPOINT_INVALID; } return wanted; } int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) { ImWchar* buf_out = buf; ImWchar* buf_end = buf + buf_size; while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c; in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); *buf_out++ = (ImWchar)c; } *buf_out = 0; if (in_text_remaining) *in_text_remaining = in_text; return (int)(buf_out - buf); } int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) { int char_count = 0; while ((!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c; in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); char_count++; } return char_count; } // Based on stb_to_utf8() from github.com/nothings/stb/ static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c) { if (c < 0x80) { buf[0] = (char)c; return 1; } if (c < 0x800) { if (buf_size < 2) return 0; buf[0] = (char)(0xc0 + (c >> 6)); buf[1] = (char)(0x80 + (c & 0x3f)); return 2; } if (c < 0x10000) { if (buf_size < 3) return 0; buf[0] = (char)(0xe0 + (c >> 12)); buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); buf[2] = (char)(0x80 + ((c ) & 0x3f)); return 3; } if (c <= 0x10FFFF) { if (buf_size < 4) return 0; buf[0] = (char)(0xf0 + (c >> 18)); buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); buf[3] = (char)(0x80 + ((c ) & 0x3f)); return 4; } // Invalid code point, the max unicode is 0x10FFFF return 0; } int ImTextCharToUtf8(char out_buf[5], unsigned int c) { int count = ImTextCharToUtf8_inline(out_buf, 5, c); out_buf[count] = 0; return count; } // Not optimal but we very rarely use this function. int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) { unsigned int unused = 0; return ImTextCharFromUtf8(&unused, in_text, in_text_end); } static inline int ImTextCountUtf8BytesFromChar(unsigned int c) { if (c < 0x80) return 1; if (c < 0x800) return 2; if (c < 0x10000) return 3; if (c <= 0x10FFFF) return 4; return 3; } int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end) { char* buf_p = out_buf; const char* buf_end = out_buf + out_buf_size; while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c = (unsigned int)(*in_text++); if (c < 0x80) *buf_p++ = (char)c; else buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c); } *buf_p = 0; return (int)(buf_p - out_buf); } int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) { int bytes_count = 0; while ((!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c = (unsigned int)(*in_text++); if (c < 0x80) bytes_count++; else bytes_count += ImTextCountUtf8BytesFromChar(c); } return bytes_count; } const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_p) { while (in_p > in_text_start) { in_p--; if ((*in_p & 0xC0) != 0x80) return in_p; } return in_text_start; } const char* ImTextFindValidUtf8CodepointEnd(const char* in_text_start, const char* in_text_end, const char* in_p) { if (in_text_start == in_p) return in_text_start; const char* prev = ImTextFindPreviousUtf8Codepoint(in_text_start, in_p); unsigned int prev_c; int prev_c_len = ImTextCharFromUtf8(&prev_c, prev, in_text_end); if (prev_c != IM_UNICODE_CODEPOINT_INVALID && prev_c_len <= (int)(in_p - prev)) return in_p; return prev; } int ImTextCountLines(const char* in_text, const char* in_text_end) { if (in_text_end == NULL) in_text_end = in_text + ImStrlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now. int count = 0; while (in_text < in_text_end) { const char* line_end = (const char*)ImMemchr(in_text, '\n', in_text_end - in_text); in_text = line_end ? line_end + 1 : in_text_end; count++; } return count; } IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Color functions) // Note: The Convert functions are early design which are not consistent with other API. //----------------------------------------------------------------------------- IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b) { float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); return IM_COL32(r, g, b, 0xFF); } ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) { float s = 1.0f / 255.0f; return ImVec4( ((in >> IM_COL32_R_SHIFT) & 0xFF) * s, ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, ((in >> IM_COL32_A_SHIFT) & 0xFF) * s); } ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) { ImU32 out; out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; return out; } // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) { float K = 0.f; if (g < b) { ImSwap(g, b); K = -1.f; } if (r < g) { ImSwap(r, g); K = -2.f / 6.f - K; } const float chroma = r - (g < b ? g : b); out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); out_s = chroma / (r + 1e-20f); out_v = r; } // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593 // also http://en.wikipedia.org/wiki/HSL_and_HSV void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) { if (s == 0.0f) { // gray out_r = out_g = out_b = v; return; } h = ImFmod(h, 1.0f) / (60.0f / 360.0f); int i = (int)h; float f = h - (float)i; float p = v * (1.0f - s); float q = v * (1.0f - s * f); float t = v * (1.0f - s * (1.0f - f)); switch (i) { case 0: out_r = v; out_g = t; out_b = p; break; case 1: out_r = q; out_g = v; out_b = p; break; case 2: out_r = p; out_g = v; out_b = t; break; case 3: out_r = p; out_g = q; out_b = v; break; case 4: out_r = t; out_g = p; out_b = v; break; case 5: default: out_r = v; out_g = p; out_b = q; break; } } //----------------------------------------------------------------------------- // [SECTION] ImGuiStorage // Helper: Key->value storage //----------------------------------------------------------------------------- // std::lower_bound but without the bullshit ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key) { ImGuiStoragePair* in_p = in_begin; for (size_t count = (size_t)(in_end - in_p); count > 0; ) { size_t count2 = count >> 1; ImGuiStoragePair* mid = in_p + count2; if (mid->key < key) { in_p = ++mid; count -= count2 + 1; } else { count = count2; } } return in_p; } IM_MSVC_RUNTIME_CHECKS_OFF static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) { // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key; ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key; return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0); } // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. void ImGuiStorage::BuildSortByKey() { ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID); } int ImGuiStorage::GetInt(ImGuiID key, int default_val) const { ImGuiStoragePair* it = ImLowerBound(const_cast(Data.Data), const_cast(Data.Data + Data.Size), key); if (it == Data.Data + Data.Size || it->key != key) return default_val; return it->val_i; } bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const { return GetInt(key, default_val ? 1 : 0) != 0; } float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const { ImGuiStoragePair* it = ImLowerBound(const_cast(Data.Data), const_cast(Data.Data + Data.Size), key); if (it == Data.Data + Data.Size || it->key != key) return default_val; return it->val_f; } void* ImGuiStorage::GetVoidPtr(ImGuiID key) const { ImGuiStoragePair* it = ImLowerBound(const_cast(Data.Data), const_cast(Data.Data + Data.Size), key); if (it == Data.Data + Data.Size || it->key != key) return NULL; return it->val_p; } // 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) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); if (it == Data.Data + Data.Size || it->key != key) it = Data.insert(it, ImGuiStoragePair(key, default_val)); return &it->val_i; } bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) { return (bool*)GetIntRef(key, default_val ? 1 : 0); } float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); if (it == Data.Data + Data.Size || it->key != key) it = Data.insert(it, ImGuiStoragePair(key, default_val)); return &it->val_f; } void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); if (it == Data.Data + Data.Size || it->key != key) it = Data.insert(it, ImGuiStoragePair(key, default_val)); return &it->val_p; } // 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) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); if (it == Data.Data + Data.Size || it->key != key) Data.insert(it, ImGuiStoragePair(key, val)); else it->val_i = val; } void ImGuiStorage::SetBool(ImGuiID key, bool val) { SetInt(key, val ? 1 : 0); } void ImGuiStorage::SetFloat(ImGuiID key, float val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); if (it == Data.Data + Data.Size || it->key != key) Data.insert(it, ImGuiStoragePair(key, val)); else it->val_f = val; } void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); if (it == Data.Data + Data.Size || it->key != key) Data.insert(it, ImGuiStoragePair(key, val)); else it->val_p = val; } void ImGuiStorage::SetAllInt(int v) { for (int i = 0; i < Data.Size; i++) Data[i].val_i = v; } IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] ImGuiTextFilter //----------------------------------------------------------------------------- // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 { InputBuf[0] = 0; CountGrep = 0; if (default_filter) { ImStrncpy(InputBuf, default_filter, IM_COUNTOF(InputBuf)); Build(); } } bool ImGuiTextFilter::Draw(const char* label, float width) { if (width != 0.0f) ImGui::SetNextItemWidth(width); bool value_changed = ImGui::InputText(label, InputBuf, IM_COUNTOF(InputBuf)); if (value_changed) Build(); return value_changed; } void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector* out) const { out->resize(0); const char* wb = b; const char* we = wb; while (we < e) { if (*we == separator) { out->push_back(ImGuiTextRange(wb, we)); wb = we + 1; } we++; } if (wb != we) out->push_back(ImGuiTextRange(wb, we)); } void ImGuiTextFilter::Build() { Filters.resize(0); ImGuiTextRange input_range(InputBuf, InputBuf + ImStrlen(InputBuf)); input_range.split(',', &Filters); CountGrep = 0; for (ImGuiTextRange& f : Filters) { while (f.b < f.e && ImCharIsBlankA(f.b[0])) f.b++; while (f.e > f.b && ImCharIsBlankA(f.e[-1])) f.e--; if (f.empty()) continue; if (f.b[0] != '-') CountGrep += 1; } } bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const { if (Filters.Size == 0) return true; if (text == NULL) text = text_end = ""; for (const ImGuiTextRange& f : Filters) { if (f.b == f.e) continue; if (f.b[0] == '-') { // Subtract if (ImStristr(text, text_end, f.b + 1, f.e) != NULL) return false; } else { // Grep if (ImStristr(text, text_end, f.b, f.e) != NULL) return true; } } // Implicit * grep if (CountGrep == 0) return true; return false; } //----------------------------------------------------------------------------- // [SECTION] ImGuiTextBuffer, ImGuiTextIndex //----------------------------------------------------------------------------- // On some platform vsnprintf() takes va_list by reference and modifies it. // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. #ifndef va_copy #if defined(__GNUC__) || defined(__clang__) #define va_copy(dest, src) __builtin_va_copy(dest, src) #else #define va_copy(dest, src) (dest = src) #endif #endif char ImGuiTextBuffer::EmptyString[1] = { 0 }; void ImGuiTextBuffer::append(const char* str, const char* str_end) { int len = str_end ? (int)(str_end - str) : (int)ImStrlen(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) { va_list args_copy; va_copy(args_copy, args); int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. if (len <= 0) { va_end(args_copy); return; } // 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); ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy); va_end(args_copy); } IM_MSVC_RUNTIME_CHECKS_OFF void ImGuiTextIndex::append(const char* base, int old_size, int new_size) { IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset); if (old_size == new_size) return; if (EndOffset == 0 || base[EndOffset - 1] == '\n') Offsets.push_back(EndOffset); const char* base_end = base + new_size; for (const char* p = base + old_size; (p = (const char*)ImMemchr(p, '\n', base_end - p)) != 0; ) if (++p < base_end) // Don't push a trailing offset on last \n Offsets.push_back((int)(intptr_t)(p - base)); EndOffset = ImMax(EndOffset, new_size); } IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] ImGuiListClipper //----------------------------------------------------------------------------- // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell. // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous. static bool GetSkipItemForListClipping() { ImGuiContext& g = *GImGui; return g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems; } static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) { if (ranges.Size - offset <= 1) return; // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries) for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end) for (int i = offset; i < sort_end + offset; ++i) if (ranges[i].Min > ranges[i + 1].Min) ImSwap(ranges[i], ranges[i + 1]); // Now fuse ranges together as much as possible. for (int i = 1 + offset; i < ranges.Size; i++) { IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert); if (ranges[i - 1].Max < ranges[i].Min) continue; ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min); ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max); ranges.erase(ranges.Data + i); i--; } } static void ImGuiListClipper_SeekCursorAndSetupPrevLine(ImGuiListClipper* clipper, float pos_y, float line_height) { // 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 final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek? ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float off_y = pos_y - window->DC.CursorPos.y; window->DC.CursorPos.y = pos_y; window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y); 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 - g.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 (ImGuiOldColumns* columns = window->DC.CurrentColumns) columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly if (ImGuiTable* table = g.CurrentTable) { if (table->IsInsideRow) ImGui::TableEndRow(table); const int row_increase = (int)((off_y / line_height) + 0.5f); if (row_increase > 0 && (clipper->Flags & ImGuiListClipperFlags_NoSetTableRowCounters) == 0) // If your clipper item height is != from actual table row height, consider using ImGuiListClipperFlags_NoSetTableRowCounters. See #8886. { table->CurrentRow += row_increase; table->RowBgColorCounter += row_increase; } table->RowPosY2 = window->DC.CursorPos.y; } } ImGuiListClipper::ImGuiListClipper() { memset((void*)this, 0, sizeof(*this)); } ImGuiListClipper::~ImGuiListClipper() { End(); } void ImGuiListClipper::Begin(int items_count, float items_height) { if (Ctx == NULL) Ctx = ImGui::GetCurrentContext(); ImGuiContext& g = *Ctx; ImGuiWindow* window = g.CurrentWindow; IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name); if (ImGuiTable* table = g.CurrentTable) if (table->IsInsideRow) ImGui::TableEndRow(table); StartPosY = window->DC.CursorPos.y; ItemsHeight = items_height; ItemsCount = items_count; DisplayStart = -1; DisplayEnd = 0; // Acquire temporary buffer if (++g.ClipperTempDataStacked > g.ClipperTempData.Size) g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData()); ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; data->Reset(this); data->LossynessOffset = window->DC.CursorStartPosLossyness.y; TempData = data; StartSeekOffsetY = data->LossynessOffset; } void ImGuiListClipper::End() { if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) { // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. ImGuiContext& g = *Ctx; IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name); if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) SeekCursorForItem(ItemsCount); // Restore temporary buffer and fix back pointers which may be invalidated when nesting IM_ASSERT(data->ListClipper == this); data->StepNo = data->Ranges.Size; if (--g.ClipperTempDataStacked > 0) { data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; data->ListClipper->TempData = data; } TempData = NULL; } DisplayStart = DisplayEnd = ItemsCount; // Clear this so code which may be reused past last Step() won't trip on a non-empty range. ItemsCount = -1; } void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end) { ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. IM_ASSERT(item_begin <= item_end); if (item_begin < item_end) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end)); } // This is already called while stepping. // The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand. void ImGuiListClipper::SeekCursorForItem(int item_n) { // - Perform the add and multiply with double to allow seeking through larger ranges. // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight). // - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done. float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight); ImGuiListClipper_SeekCursorAndSetupPrevLine(this, pos_y, ItemsHeight); } static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) { ImGuiContext& g = *clipper->Ctx; ImGuiWindow* window = g.CurrentWindow; ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?"); ImGuiTable* table = g.CurrentTable; if (table && table->IsInsideRow) ImGui::TableEndRow(table); // No items if (clipper->ItemsCount == 0 || GetSkipItemForListClipping()) return false; // While we are in frozen row state, keep displaying items one by one, unclipped // FIXME: Could be stored as a table-agnostic state. if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows) { clipper->DisplayStart = data->ItemsFrozen; clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount); if (clipper->DisplayStart < clipper->DisplayEnd) data->ItemsFrozen++; return true; } // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) bool calc_clipping = false; if (data->StepNo == 0) { clipper->StartPosY = window->DC.CursorPos.y; if (clipper->ItemsHeight <= 0.0f) { // Submit the first item (or range) so we can measure its height (generally the first range is 0..1) data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1)); clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount); data->StepNo = 1; return true; } calc_clipping = true; // If on the first step with known item height, calculate clipping. } // Step 1: Let the clipper infer height from first range if (clipper->ItemsHeight <= 0.0f) { IM_ASSERT(data->StepNo == 1); if (table) IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y); bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision((float)clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); if (affected_by_floating_point_precision) { // Mitigation/hack for very large range: assume last time height constitute line height. clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. window->DC.CursorPos.y = (float)(clipper->StartPosY + clipper->ItemsHeight); } else { clipper->ItemsHeight = (float)(window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart); } if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode. return false; IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. } // Step 0 or 1: Calculate the actual ranges of visible elements. const int already_submitted = clipper->DisplayEnd; if (calc_clipping) { // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight; if (g.LogEnabled) { // If logging is active, do not perform any clipping data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount)); } else { // Add range selected to be included for navigation const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); const int nav_off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; const int nav_off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; if (is_nav_request) { data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, nav_off_min, nav_off_max)); if (!g.NavScoringNoClipRect.IsInverted()) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, nav_off_min, nav_off_max)); } if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount)); // Add focused/active item ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]); if (g.NavId != 0 && window->NavLastIds[0] == g.NavId) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); float min_y = window->ClipRect.Min.y; float max_y = window->ClipRect.Max.y; // Add box selection range ImGuiBoxSelectState* bs = &g.BoxSelectState; if (bs->IsActive && bs->Window == window) { // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos. // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that. // As a workaround we currently half ItemSpacing worth on each side. min_y -= g.Style.ItemSpacing.y; max_y += g.Style.ItemSpacing.y; // Box-select on 2D area requires different clipping. if (bs->UnclipMode) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0)); } // Add main visible range data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, nav_off_min, nav_off_max)); } // Convert position ranges to item index ranges // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping. // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list, // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted. for (ImGuiListClipperRange& range : data->Ranges) if (range.PosToIndexConvert) { int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight); int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f); range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1); range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount); range.PosToIndexConvert = false; } ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo); } // Step 0+ (if item height is given in advance) or 1+: Display the next range in line. while (data->StepNo < data->Ranges.Size) { clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount); data->StepNo++; if (clipper->DisplayStart >= clipper->DisplayEnd) continue; if (clipper->DisplayStart > already_submitted) clipper->SeekCursorForItem(clipper->DisplayStart); return true; } // After the last step: Let 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. if (clipper->ItemsCount < INT_MAX) clipper->SeekCursorForItem(clipper->ItemsCount); return false; } bool ImGuiListClipper::Step() { ImGuiContext& g = *Ctx; bool need_items_height = (ItemsHeight <= 0.0f); bool ret = ImGuiListClipper_StepInternal(this); if (ret && (DisplayStart >= DisplayEnd)) ret = false; if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false) IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n"); if (need_items_height && ItemsHeight > 0.0f) IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight); if (ret) { IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd); } else { IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n"); End(); } return ret; } // Generic helper, equivalent to old ImGui::CalcListClipping() but stateless void ImGui::CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end) { *out_visible_start = ImMax((int)((clip_rect.Min.y - pos.y) / items_height), 0); *out_visible_end = ImMax((int)ImCeil((clip_rect.Max.y - pos.y) / items_height), *out_visible_start); } //----------------------------------------------------------------------------- // [SECTION] STYLING //----------------------------------------------------------------------------- ImGuiStyle& ImGui::GetStyle() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); return GImGui->Style; } ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) { ImGuiStyle& style = GImGui->Style; ImVec4 c = style.Colors[idx]; c.w *= style.Alpha * alpha_mul; return ColorConvertFloat4ToU32(c); } ImU32 ImGui::GetColorU32(const ImVec4& col) { ImGuiStyle& style = GImGui->Style; ImVec4 c = col; c.w *= style.Alpha; return ColorConvertFloat4ToU32(c); } const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) { ImGuiStyle& style = GImGui->Style; return style.Colors[idx]; } ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul) { ImGuiStyle& style = GImGui->Style; alpha_mul *= style.Alpha; if (alpha_mul >= 1.0f) return col; ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range. return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); } // FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) { ImGuiContext& g = *GImGui; ImGuiColorMod backup; backup.Col = idx; backup.BackupValue = g.Style.Colors[idx]; g.ColorStack.push_back(backup); if (g.DebugFlashStyleColorIdx != idx) g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); } void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) { ImGuiContext& g = *GImGui; ImGuiColorMod backup; backup.Col = idx; backup.BackupValue = g.Style.Colors[idx]; g.ColorStack.push_back(backup); if (g.DebugFlashStyleColorIdx != idx) g.Style.Colors[idx] = col; } void ImGui::PopStyleColor(int count) { ImGuiContext& g = *GImGui; if (g.ColorStack.Size < count) { IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!"); count = g.ColorStack.Size; } while (count > 0) { ImGuiColorMod& backup = g.ColorStack.back(); g.Style.Colors[backup.Col] = backup.BackupValue; g.ColorStack.pop_back(); count--; } } static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] = { ImGuiCol_Text, ImGuiCol_TabHovered, ImGuiCol_Tab, ImGuiCol_TabSelected, ImGuiCol_TabSelectedOverline, ImGuiCol_TabDimmed, ImGuiCol_TabDimmedSelected, ImGuiCol_TabDimmedSelectedOverline, ImGuiCol_UnsavedMarker, }; static const ImGuiStyleVarInfo GStyleVarsInfo[] = { { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarPadding) }, // ImGuiStyleVar_ScrollbarPadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageRounding) }, // ImGuiStyleVar_ImageRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) }, // ImGuiStyleVar_ImageBorderSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthBase) }, // ImGuiStyleVar_TabMinWidthBase { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthShrink) }, // ImGuiStyleVar_TabMinWidthShrink { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) }, // ImGuiStyleVar_TabBarOverlineSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)}, // ImGuiStyleVar_TreeLinesRounding { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorSize)}, // ImGuiStyleVar_SeparatorSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DockingSeparatorSize) }, // ImGuiStyleVar_DockingSeparatorSize }; const ImGuiStyleVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx) { IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); IM_STATIC_ASSERT(IM_COUNTOF(GStyleVarsInfo) == ImGuiStyleVar_COUNT); return &GStyleVarsInfo[idx]; } void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) { ImGuiContext& g = *GImGui; const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 1, "Calling PushStyleVar() variant with wrong type!"); float* pvar = (float*)var_info->GetVarPtr(&g.Style); g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); *pvar = val; } void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x) { ImGuiContext& g = *GImGui; const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, "Calling PushStyleVar() variant with wrong type!"); ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); pvar->x = val_x; } void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y) { ImGuiContext& g = *GImGui; const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, "Calling PushStyleVar() variant with wrong type!"); ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); pvar->y = val_y; } void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) { ImGuiContext& g = *GImGui; const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, "Calling PushStyleVar() variant with wrong type!"); ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); *pvar = val; } void ImGui::PopStyleVar(int count) { ImGuiContext& g = *GImGui; if (g.StyleVarStack.Size < count) { IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!"); count = g.StyleVarStack.Size; } while (count > 0) { // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. ImGuiStyleMod& backup = g.StyleVarStack.back(); const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(backup.VarIdx); void* data = var_info->GetVarPtr(&g.Style); if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } else if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } g.StyleVarStack.pop_back(); count--; } } const char* ImGui::GetStyleColorName(ImGuiCol idx) { // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; switch (idx) { case ImGuiCol_Text: return "Text"; case ImGuiCol_TextDisabled: return "TextDisabled"; case ImGuiCol_WindowBg: return "WindowBg"; case ImGuiCol_ChildBg: return "ChildBg"; case ImGuiCol_PopupBg: return "PopupBg"; case ImGuiCol_Border: return "Border"; case ImGuiCol_BorderShadow: return "BorderShadow"; case ImGuiCol_FrameBg: return "FrameBg"; case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; case ImGuiCol_FrameBgActive: return "FrameBgActive"; case ImGuiCol_TitleBg: return "TitleBg"; case ImGuiCol_TitleBgActive: return "TitleBgActive"; case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; case ImGuiCol_MenuBarBg: return "MenuBarBg"; case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; case ImGuiCol_CheckMark: return "CheckMark"; case ImGuiCol_SliderGrab: return "SliderGrab"; case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; case ImGuiCol_Button: return "Button"; case ImGuiCol_ButtonHovered: return "ButtonHovered"; case ImGuiCol_ButtonActive: return "ButtonActive"; case ImGuiCol_Header: return "Header"; case ImGuiCol_HeaderHovered: return "HeaderHovered"; case ImGuiCol_HeaderActive: return "HeaderActive"; case ImGuiCol_Separator: return "Separator"; case ImGuiCol_SeparatorHovered: return "SeparatorHovered"; case ImGuiCol_SeparatorActive: return "SeparatorActive"; case ImGuiCol_ResizeGrip: return "ResizeGrip"; case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; case ImGuiCol_InputTextCursor: return "InputTextCursor"; case ImGuiCol_TabHovered: return "TabHovered"; case ImGuiCol_Tab: return "Tab"; case ImGuiCol_TabSelected: return "TabSelected"; case ImGuiCol_TabSelectedOverline: return "TabSelectedOverline"; case ImGuiCol_TabDimmed: return "TabDimmed"; case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected"; case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline"; case ImGuiCol_DockingPreview: return "DockingPreview"; case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg"; case ImGuiCol_PlotLines: return "PlotLines"; case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; case ImGuiCol_PlotHistogram: return "PlotHistogram"; case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; case ImGuiCol_TableHeaderBg: return "TableHeaderBg"; case ImGuiCol_TableBorderStrong: return "TableBorderStrong"; case ImGuiCol_TableBorderLight: return "TableBorderLight"; case ImGuiCol_TableRowBg: return "TableRowBg"; case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt"; case ImGuiCol_TextLink: return "TextLink"; case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; case ImGuiCol_TreeLines: return "TreeLines"; case ImGuiCol_DragDropTarget: return "DragDropTarget"; case ImGuiCol_DragDropTargetBg: return "DragDropTargetBg"; case ImGuiCol_UnsavedMarker: return "UnsavedMarker"; case ImGuiCol_NavCursor: return "NavCursor"; case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg"; case ImGuiCol_WindowShadow: return "WindowShadow"; } IM_ASSERT(0); return "Unknown"; } //----------------------------------------------------------------------------- // [SECTION] RENDER HELPERS // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change, // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context. // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context. //----------------------------------------------------------------------------- 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 + ImStrlen(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 + ImStrlen(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) // FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especially for text above draw_list->DrawList. // Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take // better advantage of the render function taking size into account for coarse clipping. 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) { // 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); draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); } else { 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_min, text, text_display_end); } // Another overly complex function until we reorganize everything into a nice all-in-one helper. // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it. // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. // (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users. void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) { ImGuiContext& g = *GImGui; if (text_end_full == NULL) text_end_full = FindRenderedTextEnd(text); const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f); //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255)); //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255)); // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels. if (text_size.x > pos_max.x - pos_min.x) { // Hello wo... // | | | // min max ellipsis_max // <-> this is generally some padding value ImFont* font = draw_list->_Data->Font; const float font_size = draw_list->_Data->FontSize; const float font_scale = draw_list->_Data->FontScale; const char* text_end_ellipsis = NULL; ImFontBaked* baked = font->GetFontBaked(font_size); const float ellipsis_width = baked->GetCharAdvance(font->EllipsisChar) * font_scale; // We can now claim the space between pos_max.x and ellipsis_max.x const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f); const float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; // Render text, render ellipsis RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); ImVec4 cpu_fine_clip_rect(pos_min.x, pos_min.y, pos_max.x, pos_max.y); ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y)); font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar, &cpu_fine_clip_rect); } else { RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_full, &text_size, ImVec2(0.0f, 0.0f)); } if (g.LogEnabled) LogRenderedText(&pos_min, text, text_end_full); } // Render a rectangle shaped with optional rounding and borders void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, 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 (borders && border_size > 0.0f) { window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, 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, 0, border_size); window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); } } void ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding) { if (bb.Min.x + 1 >= bb.Max.x) return; ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; RenderRectFilledInRangeH(window->DrawList, bb, col, bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding); } void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags) { ImGuiContext& g = *GImGui; if (id != g.NavId) return; if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw)) return; if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav)) return; // We don't early out on 'window->Flags & ImGuiWindowFlags_NoNavInputs' because it would be inconsistent with // other code directly checking NavCursorVisible. Instead we aim for NavCursorVisible to always be false. ImGuiWindow* window = g.CurrentWindow; if (window->DC.NavHideHighlightOneFrame) return; float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; ImRect display_rect = bb; display_rect.ClipWith(window->ClipRect); const float thickness = 2.0f; if (flags & ImGuiNavRenderCursorFlags_Compact) { window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness); } else { 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, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness); if (!fully_visible) window->DrawList->PopClipRect(); } } void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow) { ImGuiContext& g = *GImGui; if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values. mouse_cursor = ImGuiMouseCursor_Arrow; ImFontAtlas* font_atlas = g.DrawListSharedData.FontAtlas; for (ImGuiViewportP* viewport : g.Viewports) { // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor. ImVec2 offset, size, uv[4]; if (!ImFontAtlasGetMouseCursorTexData(font_atlas, mouse_cursor, &offset, &size, &uv[0], &uv[2])) continue; const ImVec2 pos = base_pos - offset; const float scale = base_scale * viewport->DpiScale; if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale))) continue; ImDrawList* draw_list = GetForegroundDrawList(viewport); ImTextureRef tex_ref = font_atlas->TexRef; draw_list->PushTexture(tex_ref); draw_list->AddImage(tex_ref, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); draw_list->AddImage(tex_ref, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); draw_list->AddImage(tex_ref, pos, pos + size * scale, uv[2], uv[3], col_border); draw_list->AddImage(tex_ref, pos, pos + size * scale, uv[0], uv[1], col_fill); if (mouse_cursor == ImGuiMouseCursor_Wait || mouse_cursor == ImGuiMouseCursor_Progress) { float a_min = ImFmod((float)g.Time * 5.0f, 2.0f * IM_PI); float a_max = a_min + IM_PI * 1.65f; draw_list->PathArcTo(pos + ImVec2(14, -1) * scale, 6.0f * scale, a_min, a_max); draw_list->PathStroke(col_fill, ImDrawFlags_None, 3.0f * scale); } draw_list->PopTexture(); } } //----------------------------------------------------------------------------- // [SECTION] INITIALIZATION, SHUTDOWN //----------------------------------------------------------------------------- // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module ImGuiContext* ImGui::GetCurrentContext() { return GImGui; } void ImGui::SetCurrentContext(ImGuiContext* ctx) { #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. #else GImGui = ctx; #endif } void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) { GImAllocatorAllocFunc = alloc_func; GImAllocatorFreeFunc = free_func; GImAllocatorUserData = user_data; } // This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) { *p_alloc_func = GImAllocatorAllocFunc; *p_free_func = GImAllocatorFreeFunc; *p_user_data = GImAllocatorUserData; } ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) { ImGuiContext* prev_ctx = GetCurrentContext(); ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); SetCurrentContext(ctx); Initialize(); if (prev_ctx != NULL) SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one. return ctx; } void ImGui::DestroyContext(ImGuiContext* ctx) { ImGuiContext* prev_ctx = GetCurrentContext(); if (ctx == NULL) //-V1051 ctx = prev_ctx; SetCurrentContext(ctx); Shutdown(); SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL); IM_DELETE(ctx); } // IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation. static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" }, { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" }, { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" }, { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" }, { ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" }, { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" }, { ImGuiLocKey_WindowingPopup, "(Popup)" }, { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, { ImGuiLocKey_OpenLink_s, "Open '%s'" }, { ImGuiLocKey_CopyLink, "Copy Link###CopyLink" }, { ImGuiLocKey_DockingHideTabBar, "Hide tab bar###HideTabBar" }, { ImGuiLocKey_DockingHoldShiftToDock, "Hold SHIFT to enable Docking window." }, { ImGuiLocKey_DockingDragToUndockOrMoveNode,"Click and drag to move or undock whole node." }, }; ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) { IO.Ctx = this; InputTextState.Ctx = this; Initialized = false; WithinFrameScope = WithinFrameScopeWithImplicitWindow = false; TestEngineHookItems = false; FrameCount = 0; FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1; Time = 0.0f; memset(ContextName, 0, sizeof(ContextName)); ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None; Font = NULL; FontBaked = NULL; FontSize = FontSizeBase = FontBakedScale = CurrentDpiScale = 0.0f; FontRasterizerDensity = 1.0f; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); if (shared_font_atlas == NULL) IO.Fonts->OwnerContext = this; WithinEndChildID = 0; TestEngine = NULL; InputEventsNextMouseSource = ImGuiMouseSource_Mouse; InputEventsNextEventId = 1; WindowsActiveCount = 0; WindowsBorderHoverPadding = 0.0f; CurrentWindow = NULL; HoveredWindow = NULL; HoveredWindowUnderMovingWindow = NULL; HoveredWindowBeforeClear = NULL; MovingWindow = NULL; WheelingWindow = NULL; WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1; WheelingWindowReleaseTimer = 0.0f; DebugDrawIdConflictsId = 0; DebugHookIdInfoId = 0; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdPreviousFrameItemCount = 0; HoveredIdAllowOverlap = false; HoveredIdIsDisabled = false; HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; ItemUnclipByLog = false; ActiveId = 0; ActiveIdIsAlive = 0; ActiveIdTimer = 0.0f; ActiveIdIsJustActivated = false; ActiveIdAllowOverlap = false; ActiveIdNoClearOnFocusLoss = false; ActiveIdHasBeenPressedBefore = false; ActiveIdHasBeenEditedBefore = false; ActiveIdHasBeenEditedThisFrame = false; ActiveIdFromShortcut = false; ActiveIdClickOffset = ImVec2(-1, -1); ActiveIdSource = ImGuiInputSource_None; ActiveIdWindow = NULL; ActiveIdMouseButton = -1; ActiveIdDisabledId = 0; ActiveIdPreviousFrame = 0; memset(&DeactivatedItemData, 0, sizeof(DeactivatedItemData)); memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation)); LastActiveId = 0; LastActiveIdTimer = 0.0f; LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0; ActiveIdUsingNavDirMask = 0x00; ActiveIdUsingAllKeyboardKeys = false; CurrentFocusScopeId = 0; CurrentItemFlags = ImGuiItemFlags_None; DebugShowGroupRects = false; GcCompactAll = false; CurrentViewport = NULL; MouseViewport = MouseLastHoveredViewport = NULL; PlatformLastFocusedViewportId = 0; ViewportCreatedCount = PlatformWindowsCreatedCount = 0; ViewportFocusedStampCount = 0; NavCursorVisible = false; NavHighlightItemUnderNav = false; NavMousePosDirty = false; NavIdIsAlive = false; NavId = 0; NavWindow = NULL; NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0; NavLayer = ImGuiNavLayer_Main; NavIdItemFlags = ImGuiItemFlags_None; NavOpenContextMenuItemId = NavOpenContextMenuWindowId = 0; NavNextActivateId = 0; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavHighlightActivatedId = 0; NavHighlightActivatedTimer = 0.0f; NavInputSource = ImGuiInputSource_Keyboard; NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; NavCursorHideFrames = 0; NavAnyRequest = false; NavInitRequest = false; NavInitRequestFromMove = false; NavMoveSubmitted = false; NavMoveScoringItems = false; NavMoveForwardToNextFrame = false; NavMoveFlags = ImGuiNavMoveFlags_None; NavMoveScrollFlags = ImGuiScrollFlags_None; NavMoveKeyMods = ImGuiMod_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; NavScoringDebugCount = 0; NavTabbingDir = 0; NavTabbingCounter = 0; NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0; NavJustMovedToKeyMods = ImGuiMod_None; NavJustMovedToIsTabbing = false; NavJustMovedToHasSelectionData = false; // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac... // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this.. ConfigNavEnableTabbing = true; ConfigNavWindowingWithGamepad = true; ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab); ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab); NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingInputSource = ImGuiInputSource_None; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; NavWindowingToggleKey = ImGuiKey_None; DimBgRatio = 0.0f; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; DragDropSourceFlags = ImGuiDragDropFlags_None; DragDropSourceFrameCount = -1; DragDropMouseButton = -1; DragDropTargetId = 0; DragDropTargetFullViewport = 0; DragDropAcceptFlagsCurr = DragDropAcceptFlagsPrev = ImGuiDragDropFlags_None; DragDropAcceptIdCurrRectSurface = 0.0f; DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0; DragDropAcceptFrameCount = -1; DragDropHoldJustPressedId = 0; memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); ClipperTempDataStacked = 0; CurrentTable = NULL; TablesTempDataStacked = 0; CurrentTabBar = NULL; CurrentMultiSelect = NULL; MultiSelectTempDataStacked = 0; HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; MouseStationaryTimer = 0.0f; InputTextPasswordFontBackupFlags = ImFontFlags_None; InputTextReactivateId = 0; TempInputId = 0; memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue)); BeginMenuDepth = BeginComboDepth = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditCurrentID = ColorEditSavedID = 0; ColorEditSavedHue = ColorEditSavedSat = 0.0f; ColorEditSavedColor = 0; WindowResizeRelativeMode = false; ScrollbarSeekMode = 0; ScrollbarClickDeltaToGrabCenter = 0.0f; SliderGrabClickOffset = 0.0f; SliderCurrentAccum = 0.0f; SliderCurrentAccumDirty = false; DragCurrentAccumDirty = false; DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; DisabledAlphaBackup = 0.0f; DisabledStackSize = 0; TooltipOverrideCount = 0; TooltipPreviousWindow = NULL; PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission DockNodeWindowMenuHandler = NULL; SettingsLoaded = false; SettingsDirtyTimer = 0.0f; HookIdNext = 0; DemoMarkerCallback = NULL; memset(LocalizationTable, 0, sizeof(LocalizationTable)); LogEnabled = false; LogLineFirstItem = false; LogFlags = ImGuiLogFlags_None; LogWindow = NULL; LogNextPrefix = LogNextSuffix = NULL; LogFile = NULL; LogLinePosY = FLT_MAX; LogDepthRef = 0; LogDepthToExpand = LogDepthToExpandDefault = 2; ErrorCallback = NULL; ErrorCallbackUserData = NULL; ErrorFirst = true; ErrorCountCurrentFrame = 0; StackSizesInBeginForCurrentWindow = NULL; DebugDrawIdConflictsCount = 0; DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY; DebugLocateId = 0; DebugLogSkippedErrors = 0; DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; DebugLogAutoDisableFrames = 0; DebugLocateFrames = 0; DebugBeginReturnValueCullDepth = -1; DebugItemPickerActive = false; DebugItemPickerMouseButton = ImGuiMouseButton_Left; DebugItemPickerBreakId = 0; DebugFlashStyleColorTime = 0.0f; DebugFlashStyleColorIdx = ImGuiCol_COUNT; DebugHoveredDockNode = NULL; // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations DebugBreakInWindow = 0; DebugBreakInTable = 0; DebugBreakInLocateId = false; DebugBreakKeyChord = ImGuiKey_Pause; DebugBreakInShortcutRouting = ImGuiKey_None; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; FramerateSecPerFrameAccum = 0.0f; WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; memset(TempKeychordName, 0, sizeof(TempKeychordName)); } ImGuiContext::~ImGuiContext() { IM_ASSERT(Initialized == false && "Forgot to call DestroyContext()?"); } void ImGui::Initialize() { ImGuiContext& g = *GImGui; IM_ASSERT(!g.Initialized && !g.SettingsLoaded); // Add .ini handle for ImGuiWindow and ImGuiTable types { ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Window"; ini_handler.TypeHash = ImHashStr("Window"); ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; AddSettingsHandler(&ini_handler); } TableSettingsAddSettingsHandler(); // Setup default localization table LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_COUNTOF(GLocalizationEntriesEnUS)); // Setup default ImGuiPlatformIO clipboard/IME handlers. g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl; g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl; g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl; // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; viewport->Idx = 0; viewport->PlatformWindowCreated = true; viewport->Flags = ImGuiViewportFlags_OwnedByApp; g.Viewports.push_back(viewport); g.TempBuffer.resize(1024 * 3 + 1, 0); g.ViewportCreatedCount++; g.PlatformIO.Viewports.push_back(g.Viewports[0]); // Build KeysMayBeCharInput[] lookup table (1 bit per named key) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9) || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual) g.KeysMayBeCharInput.SetBit(key); #ifdef IMGUI_HAS_DOCK // Initialize Docking DockContextInitialize(&g); #endif // Print a debug message when running with debug feature IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS because it is very slow. // DO NOT COMMENT OUT THIS MESSAGE. IT IS DESIGNED TO REMIND YOU THAT IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS SHOULD ONLY BE TEMPORARILY ENABLED. #ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS DebugLog("IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\nMust disable after use! Otherwise Dear ImGui will run slower.\n"); #endif // ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context. // But this link allows us to facilitate/handle a few edge cases better. ImFontAtlas* atlas = g.IO.Fonts; g.DrawListSharedData.Context = &g; RegisterFontAtlas(atlas); g.Initialized = true; } // This function is merely here to free heap allocations. void ImGui::Shutdown() { ImGuiContext& g = *GImGui; IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?"); IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?"); for (ImGuiViewportP* viewport : g.Viewports) { IM_UNUSED(viewport); IM_ASSERT_USER_ERROR(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL, "Backend or app forgot to call DestroyPlatformWindows()?"); } // 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) for (ImFontAtlas* atlas : g.FontAtlases) { UnregisterFontAtlas(atlas); if (atlas->RefCount == 0) { atlas->Locked = false; IM_DELETE(atlas); } } g.DrawListSharedData.TempBuffer.clear(); // Cleanup of other data are conditional on actually having initialized Dear ImGui. if (!g.Initialized) return; // 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) SaveIniSettingsToDisk(g.IO.IniFilename); // Shutdown extensions DockContextShutdown(&g); CallContextHooks(&g, ImGuiContextHookType_Shutdown); // Clear everything else g.Windows.clear_delete(); g.WindowsFocusOrder.clear(); g.WindowsTempSortBuffer.clear(); g.CurrentWindow = NULL; g.CurrentWindowStack.clear(); g.WindowsById.Clear(); g.NavWindow = NULL; g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; g.ActiveIdWindow = NULL; g.MovingWindow = NULL; g.KeysRoutingTable.Clear(); g.ColorStack.clear(); g.StyleVarStack.clear(); g.FontStack.clear(); g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); g.TreeNodeStack.clear(); g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL; g.Viewports.clear_delete(); g.TabBars.Clear(); g.CurrentTabBarStack.clear(); g.ShrinkWidthBuffer.clear(); g.ClipperTempData.clear_destruct(); g.Tables.Clear(); g.TablesTempData.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); g.MultiSelectStorage.Clear(); g.MultiSelectTempData.clear_destruct(); g.ClipboardHandlerData.clear(); g.MenusIdSubmittedThisFrame.clear(); g.InputTextState.ClearFreeMemory(); g.InputTextLineIndex.clear(); g.InputTextDeactivatedState.ClearFreeMemory(); g.SettingsWindows.clear(); g.SettingsHandlers.clear(); if (g.LogFile) { #ifndef IMGUI_DISABLE_TTY_FUNCTIONS if (g.LogFile != stdout) #endif ImFileClose(g.LogFile); g.LogFile = NULL; } g.LogBuffer.clear(); g.DebugLogBuf.clear(); g.DebugLogIndex.clear(); g.Initialized = false; } // When using multiple context it can be helpful to give name a name. // (A) Will be visible in debugger, (B) Will be included in all IMGUI_DEBUG_LOG() calls, (C) Should be <= 15 characters long. void ImGui::SetContextName(ImGuiContext* ctx, const char* name) { ImStrncpy(ctx->ContextName, name, IM_COUNTOF(ctx->ContextName)); } // No specific ordering/dependency support, will see as needed ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) { ImGuiContext& g = *ctx; IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); g.Hooks.push_back(*hook); g.Hooks.back().HookId = ++g.HookIdNext; return g.HookIdNext; } // Deferred removal, avoiding issue with changing vector while iterating it void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) { ImGuiContext& g = *ctx; IM_ASSERT(hook_id != 0); for (ImGuiContextHook& hook : g.Hooks) if (hook.HookId == hook_id) hook.Type = ImGuiContextHookType_PendingRemoval_; } // Call context hooks (used by e.g. test engine) // We assume a small number of hooks so all stored in same array void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) { ImGuiContext& g = *ctx; for (ImGuiContextHook& hook : g.Hooks) if (hook.Type == hook_type) hook.Callback(&g, &hook); } //----------------------------------------------------------------------------- // [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* ctx, const char* name) : DrawListInst(NULL) { memset((void*)this, 0, sizeof(*this)); Ctx = ctx; Name = ImStrdup(name); NameBufLen = (int)ImStrlen(name) + 1; ID = ImHashStr(name); IDStack.push_back(ID); ViewportAllowPlatformMonitorExtend = -1; ViewportPos = ImVec2(FLT_MAX, FLT_MAX); MoveId = GetID("#MOVE"); TabId = GetID("#TAB"); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); AutoPosLastDirection = ImGuiDir_None; AutoFitFramesX = AutoFitFramesY = -1; SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = 0; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); LastFrameActive = -1; LastFrameJustFocused = -1; LastTimeActive = -1.0f; FontRefSize = 0.0f; FontWindowScale = FontWindowScaleParents = 1.0f; SettingsOffset = -1; DockOrder = -1; DrawList = &DrawListInst; DrawList->_OwnerName = Name; DrawList->_SetDrawListSharedData(&Ctx->DrawListSharedData); NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX); IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass(); } ImGuiWindow::~ImGuiWindow() { IM_ASSERT(DrawList == &DrawListInst); IM_DELETE(Name); ColumnsStorage.clear_destruct(); } static void SetCurrentWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; g.CurrentWindow = window; g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL; g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL; if (window) { if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) { ImGuiViewport* viewport = window->Viewport; g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity() } const bool backup_skip_items = window->SkipItems; window->SkipItems = false; ImGui::UpdateCurrentFontSize(0.0f); window->SkipItems = backup_skip_items; ImGui::NavUpdateCurrentWindowIsScrollPushableX(); } } void ImGui::GcCompactTransientMiscBuffers() { ImGuiContext& g = *GImGui; g.ItemFlagsStack.clear(); g.GroupStack.clear(); g.InputTextLineIndex.clear(); g.MultiSelectTempDataStacked = 0; g.MultiSelectTempData.clear_destruct(); TableGcCompactSettings(); for (ImFontAtlas* atlas : g.FontAtlases) atlas->CompactCache(); } // Free up/compact internal window buffers, we can use this when a window becomes unused. // Not freed: // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data) // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost. void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) { window->MemoryCompacted = true; window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity; window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity; window->IDStack.clear(); window->DrawList->_ClearFreeMemory(); window->DC.ChildWindows.clear(); window->DC.ItemWidthStack.clear(); window->DC.TextWrapPosStack.clear(); } void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window) { // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening. // The other buffers tends to amortize much faster. window->MemoryCompacted = false; window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity); window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity); window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0; } void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; // Clear previous active id if (g.ActiveId != 0) { // Store deactivate data ImGuiDeactivatedItemData* deactivated_data = &g.DeactivatedItemData; deactivated_data->ID = g.ActiveId; deactivated_data->ElapseFrame = (g.LastItemData.ID == g.ActiveId) ? g.FrameCount : g.FrameCount + 1; // FIXME: OK to use LastItemData? deactivated_data->HasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore; deactivated_data->IsAlive = (g.ActiveIdIsAlive == g.ActiveId); // This could be written in a more general way (e.g associate a hook to ActiveId), // but since this is currently quite an exception we'll leave it as is. // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID() if (g.InputTextState.ID == g.ActiveId) InputTextDeactivateHook(g.ActiveId); // While most behaved code would make an effort to not steal active id during window move/drag operations, // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) { IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); StopMouseMovingWindow(); } } // Set active id g.ActiveIdIsJustActivated = (g.ActiveId != id); if (g.ActiveIdIsJustActivated) { IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : ""); g.ActiveIdTimer = 0.0f; g.ActiveIdHasBeenPressedBefore = false; g.ActiveIdHasBeenEditedBefore = false; g.ActiveIdMouseButton = -1; if (id != 0) { g.LastActiveId = id; g.LastActiveIdTimer = 0.0f; } } g.ActiveId = id; g.ActiveIdAllowOverlap = false; g.ActiveIdNoClearOnFocusLoss = false; g.ActiveIdWindow = window; g.ActiveIdHasBeenEditedThisFrame = false; g.ActiveIdFromShortcut = false; g.ActiveIdDisabledId = 0; if (id) { g.ActiveIdIsAlive = id; g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None); } // Clear declaration of inputs claimed by the widget // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet) g.ActiveIdUsingNavDirMask = 0x00; g.ActiveIdUsingAllKeyboardKeys = false; } void ImGui::ClearActiveID() { SetActiveID(0, NULL); // g.ActiveId = 0; } void ImGui::SetHoveredID(ImGuiID id) { ImGuiContext& g = *GImGui; g.HoveredId = id; g.HoveredIdAllowOverlap = false; if (id != 0 && g.HoveredIdPreviousFrame != id) g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; } ImGuiID ImGui::GetHoveredID() { ImGuiContext& g = *GImGui; return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame; } void ImGui::MarkItemEdited(ImGuiID id) { // This marking is 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 to fill the data. ImGuiContext& g = *GImGui; if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited) return; if (g.ActiveId == id || g.ActiveId == 0) { // FIXME: Can't we fully rely on LastItemData yet? g.ActiveIdHasBeenEditedThisFrame = true; g.ActiveIdHasBeenEditedBefore = true; if (g.DeactivatedItemData.ID == id) g.DeactivatedItemData.HasBeenEditedBefore = true; } // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343) // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714) // FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction. IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive)); //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; } bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) { // An active popup disable hovering on other windows (apart from its own children) // FIXME-OPT: This could be cached/stored within the window. ImGuiContext& g = *GImGui; if (g.NavWindow) if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree) if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree) { // For the purpose of those flags we differentiate "standard popup" from "modal popup" // NB: The 'else' is important because Modal windows are also Popups. bool want_inhibit = false; if (focused_root_window->Flags & ImGuiWindowFlags_Modal) want_inhibit = true; else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) want_inhibit = true; // Inhibit hover unless the window is within the stack of our modal/popup if (want_inhibit) if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window)) return false; } // Filter by viewport if (window->Viewport != g.MouseViewport) if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree) return false; return true; } static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags) { ImGuiContext& g = *GImGui; if (flags & ImGuiHoveredFlags_DelayNormal) return g.Style.HoverDelayNormal; if (flags & ImGuiHoveredFlags_DelayShort) return g.Style.HoverDelayShort; return 0.0f; } static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags) { // Allow instance flags to override shared flags if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal)) shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal); return user_flags | shared_flags; } // This is roughly matching the behavior of internal-facing ItemHoverable() // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered() // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!"); if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride)) { if (!IsItemFocused()) return false; if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; if (flags & ImGuiHoveredFlags_ForTooltip) flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav); } else { // Test for bounding box overlap, as updated as ItemAdd() ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) return false; if (flags & ImGuiHoveredFlags_ForTooltip) flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse); // Done with rectangle culling so we can perform heavier checks now // Test if we are hovering the right window (our window could be behind another window) // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was // the test that has been running for a long while. if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0) return false; // Test if another item is active (e.g. being dragged) const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && !g.ActiveIdFromShortcut) { // When ActiveId == MoveId it means that either: // - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag) // - the (id == 0) test handles it, however, IsItemHovered() will leak between id==0 items (mostly visible when using _NoMove). // FIXME: May be fixed. // - (2) user clicked a disabled item. UpdateMouseMovingWindowEndFrame() uses ActiveId == MoveId to avoid interference with item logic + sets ActiveIdDisabledId. bool cancel_is_hovered = true; if (g.ActiveId == window->MoveId && (id == 0 || g.ActiveIdDisabledId == id)) cancel_is_hovered = false; // When ActiveId == TabId it means user clicked docking tab for the window. if (g.ActiveId == window->TabId) cancel_is_hovered = false; if (cancel_is_hovered) return false; } // Test if interactions on this window are blocked by an active popup or modal. // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck)) return false; // Test if the item is disabled if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; // Special handling for calling after Begin() which represent the title bar or tab. // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin) // will never be overwritten so we need to detect the case. if (id == window->MoveId && window->WriteAccessed) return false; // Test if using AllowOverlap and overlapped if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0) if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0) if (g.HoveredIdPreviousFrame != g.LastItemData.ID) return false; } // Handle hover delay // (some ideas: https://www.nngroup.com/articles/timing-exposing-content) const float delay = CalcDelayFromHoveredFlags(flags); if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary)) { ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(g.LastItemData.Rect.Min); if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id)) g.HoverItemDelayTimer = 0.0f; g.HoverItemDelayId = hover_delay_id; // When changing hovered item we requires a bit of stationary delay before activating hover timer, // but once unlocked on a given item we also moving. //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); } if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id) return false; if (g.HoverItemDelayTimer < delay) return false; } return true; } // Internal facing ItemHoverable() used when submitting widgets. THIS IS A SUBMISSION NOT A HOVER CHECK. // Returns whether the item was hovered, logic differs slightly from IsItemHovered(). // (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call) // FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28. // If you used this in your legacy/custom widgets code: // - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'. // - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable. bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; // Detect ID conflicts // (this is specifically done here by comparing on hover because it allows us a detection of duplicates that is algorithmically extra cheap, 1 u32 compare per item. No O(log N) lookup whatsoever) #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0) { g.HoveredIdPreviousFrameItemCount++; if (g.DebugDrawIdConflictsId == id) window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); } #endif if (g.HoveredWindow != window) return false; if (!IsMouseHoveringRect(bb.Min, bb.Max)) return false; if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) return false; if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) if (!g.ActiveIdFromShortcut) return false; // We are done with rectangle culling so we can perform heavier checks now. if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdIsDisabled = true; return false; } // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level // hover test in widgets code. We could also decide to split this function is two. if (id != 0) { // Drag source doesn't report as hovered if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) return false; SetHoveredID(id); // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test. if (item_flags & ImGuiItemFlags_AllowOverlap) { g.HoveredIdAllowOverlap = true; if (g.HoveredIdPreviousFrame != id) return false; } // Display shortcut (only works with mouse) // (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip) if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id) if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal)) SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut)); } // When disabled we'll return false but still set HoveredId if (item_flags & ImGuiItemFlags_Disabled) { // Release active id if turning disabled if (g.ActiveId == id && id != 0) ClearActiveID(); g.HoveredIdIsDisabled = true; return false; } #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (id != 0) { // [DEBUG] Item Picker tool! // We perform the check here because reaching is path is rare (1~ time a frame), // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost. if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id) GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); if (g.DebugItemPickerBreakId == id) IM_DEBUG_BREAK(); } #endif if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0) return false; return true; } // FIXME: This is inlined/duplicated in ItemAdd() // FIXME: The id != 0 path is not used by our codebase, may get rid of it? bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!bb.Overlaps(window->ClipRect)) if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId)) if (!g.ItemUnclipByLog) return true; return false; } // This is also inlined in ItemAdd() // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect. void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect) { ImGuiContext& g = *GImGui; g.LastItemData.ID = item_id; g.LastItemData.ItemFlags = item_flags; g.LastItemData.StatusFlags = status_flags; g.LastItemData.Rect = g.LastItemData.NavRect = item_rect; } static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect) { ImGuiContext& g = *GImGui; if (window->DockIsActive) SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.DockTabItemStatusFlags, window->DC.DockTabItemRect); else SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.WindowItemStatusFlags, rect); } static void ImGui::SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect) { ImGuiContext& g = *GImGui; SetLastItemData(window->ChildId, g.CurrentItemFlags, window->DC.ChildItemStatusFlags, rect); } float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) { if (wrap_pos_x < 0.0f) return 0.0f; ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (wrap_pos_x == 0.0f) { // We could decide to setup a default wrapping max point for auto-resizing windows, // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function? //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x); //else wrap_pos_x = window->WorkRect.Max.x; } else if (wrap_pos_x > 0.0f) { wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space } return ImMax(wrap_pos_x - pos.x, 1.0f); } // IM_ALLOC() == ImGui::MemAlloc() void* ImGui::MemAlloc(size_t size) { void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData); #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (ImGuiContext* ctx = GImGui) DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size); #endif return ptr; } // IM_FREE() == ImGui::MemFree() void ImGui::MemFree(void* ptr) { #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (ptr != NULL) if (ImGuiContext* ctx = GImGui) DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1); #endif return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData); } void ImGui::DemoMarker(const char* file, int line, const char* section) { ImGuiContext& g = *GImGui; if (g.DemoMarkerCallback != NULL) g.DemoMarkerCallback(file, line, section); } // We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of "no allocations on idle/repeating frames" void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size) { ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx]; IM_UNUSED(ptr); if (entry->FrameCount != frame_count) { info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_COUNTOF(info->LastEntriesBuf); entry = &info->LastEntriesBuf[info->LastEntriesIdx]; entry->FrameCount = frame_count; entry->AllocCount = entry->FreeCount = 0; } if (size != (size_t)-1) { //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, (int)size, ptr); entry->AllocCount++; info->TotalAllocCount++; } else { //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr); entry->FreeCount++; info->TotalFreeCount++; } } // A conformant backend should return NULL on failure (e.g. clipboard data is not text). const char* ImGui::GetClipboardText() { ImGuiContext& g = *GImGui; return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : NULL; } void ImGui::SetClipboardText(const char* text) { ImGuiContext& g = *GImGui; if (g.PlatformIO.Platform_SetClipboardTextFn != NULL) g.PlatformIO.Platform_SetClipboardTextFn(&g, text); } const char* ImGui::GetVersion() { return IMGUI_VERSION; } ImGuiIO& ImGui::GetIO() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); return GImGui->IO; } // This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856) ImGuiIO& ImGui::GetIO(ImGuiContext* ctx) { IM_ASSERT(ctx != NULL); return ctx->IO; } ImGuiPlatformIO& ImGui::GetPlatformIO() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?"); return GImGui->PlatformIO; } // This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856) ImGuiPlatformIO& ImGui::GetPlatformIO(ImGuiContext* ctx) { IM_ASSERT(ctx != NULL); return ctx->PlatformIO; } // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame() ImDrawData* ImGui::GetDrawData() { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = g.Viewports[0]; return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL; } double ImGui::GetTime() { return GImGui->Time; } int ImGui::GetFrameCount() { return GImGui->FrameCount; } static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name) { // Create the draw list on demand, because they are not frequently used for all viewports ImGuiContext& g = *GImGui; IM_ASSERT(drawlist_no < IM_COUNTOF(viewport->BgFgDrawLists)); ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no]; if (draw_list == NULL) { draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData); draw_list->_OwnerName = drawlist_name; viewport->BgFgDrawLists[drawlist_no] = draw_list; } // Our ImDrawList system requires that there is always a command if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount) { draw_list->_ResetForNewFrame(); draw_list->PushTexture(g.IO.Fonts->TexRef); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount; } return draw_list; } ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport) { if (viewport == NULL) viewport = GImGui->CurrentWindow->Viewport; return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background"); } ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) { if (viewport == NULL) viewport = GImGui->CurrentWindow->Viewport; return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground"); } ImDrawListSharedData* ImGui::GetDrawListSharedData() { return &GImGui->DrawListSharedData; } 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); if (g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = false; g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos; g.ActiveIdNoClearOnFocusLoss = true; SetActiveIdUsingAllKeyboardKeys(); bool can_move_window = true; if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove)) can_move_window = false; if (ImGuiDockNode* node = window->DockNodeAsHost) if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove)) can_move_window = false; if (can_move_window) g.MovingWindow = window; } // We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them. void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock) { ImGuiContext& g = *GImGui; bool can_undock_node = false; if (undock && node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0) { // Can undock if: // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window) // - part of a dockspace node hierarchy: so we can undock the last single visible node too. Undocking from a fixed/central node will create a new node and copy windows. ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) // -V1051 PVS-Studio thinks node should be root_node and is wrong about that. can_undock_node = true; } const bool clicked = IsMouseClicked(0); const bool dragging = IsMouseDragging(0); if (can_undock_node && dragging) DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window) StartMouseMovingWindow(window); } // This is not 100% symmetric with StartMouseMovingWindow(). // We do NOT clear ActiveID, because: // - It would lead to rather confusing recursive code paths. Caller can call ClearActiveID() if desired. // - Some code intentionally cancel moving but keep the ActiveID to lock inputs (e.g. code path taken when clicking a disabled item). void ImGui::StopMouseMovingWindow() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.MovingWindow; // Ref commits 6b7766817, 36055213c for some partial history on checking if viewport != NULL. if (window && window->Viewport) { // Try to merge the window back into the main viewport. // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) UpdateTryMergeWindowIntoHostViewport(window->RootWindowDockTree, g.MouseViewport); // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. if (!IsDragDropPayloadBeingAccepted()) g.MouseViewport = window->Viewport; // Clear the NoInputs window flag set by the Viewport system in AddUpdateViewport() const bool window_can_use_inputs = ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) == false; if (window_can_use_inputs) window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; } g.MovingWindow = NULL; } // Handle mouse moving window // Note: moving window with the navigation keys (Square + d-pad / Ctrl+Tab + Arrows) are processed in NavUpdateWindowing() // FIXME: We don't have strong guarantee that g.MovingWindow stay synced with g.ActiveId == g.MovingWindow->MoveId. // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs, // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other. void ImGui::UpdateMouseMovingWindowNewFrame() { ImGuiContext& g = *GImGui; if (g.MovingWindow != NULL) { // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. KeepAliveID(g.ActiveId); IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree); ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; // When a window stop being submitted while being dragged, it may will its viewport until next Begin() const bool window_disappeared = (!moving_window->WasActive && !moving_window->Active); if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappeared) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) { SetWindowPos(moving_window, pos, ImGuiCond_Always); if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. { moving_window->Viewport->Pos = pos; moving_window->Viewport->UpdateWorkRect(); } } FocusWindow(g.MovingWindow); } else { StopMouseMovingWindow(); ClearActiveID(); } } else { // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others. if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId) { KeepAliveID(g.ActiveId); if (!g.IO.MouseDown[0]) ClearActiveID(); } } } // Initiate focusing and moving window when clicking on empty space or title bar. // Initiate focusing window when clicking on a disabled item. // Handle left-click and right-click focus. void ImGui::UpdateMouseMovingWindowEndFrame() { ImGuiContext& g = *GImGui; if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled)) return; // Unless we just made a window/popup appear if (g.NavWindow && g.NavWindow->Appearing) return; ImGuiWindow* hovered_window = g.HoveredWindow; // Click on empty space to focus window and start moving // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!) if (g.IO.MouseClicked[0]) { // Handle the edge case of a popup being closed while clicking in its empty space. // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. ImGuiWindow* hovered_root = hovered_window ? hovered_window->RootWindow : NULL; const bool is_closed_popup = hovered_root && (hovered_root->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(hovered_root->PopupId, ImGuiPopupFlags_AnyPopupLevel); if (hovered_window != NULL && !is_closed_popup) { StartMouseMovingWindow(hovered_window); //-V595 // FIXME: In principle we might be able to call StopMouseMovingWindow() below. // Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symmetrical, at the later doesn't clear ActiveId. // Cancel moving if clicked outside of title bar if ((hovered_window->BgClickFlags & ImGuiWindowBgClickFlags_Move) == 0) // set by io.ConfigWindowsMoveFromTitleBarOnly if (!(hovered_root->Flags & ImGuiWindowFlags_NoTitleBar) || hovered_root->DockIsActive) if (!hovered_root->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) g.MovingWindow = NULL; // Cancel moving if clicked over an item which was disabled or inhibited by popups // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item) if (g.HoveredIdIsDisabled) { g.MovingWindow = NULL; g.ActiveIdDisabledId = g.HoveredId; } } else if (hovered_window == NULL && g.NavWindow != NULL) { // Clicking on void disable focus FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal); } } // With right mouse button we close popups without changing focus based on where the mouse is aimed // Instead, focus will be restored to the window under the bottom-most closed popup. // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) if (g.IO.MouseClicked[1] && g.HoveredId == 0) { // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); bool hovered_window_above_modal = hovered_window && (modal == NULL || IsWindowAbove(hovered_window, modal)); ClosePopupsOverWindow(hovered_window_above_modal ? hovered_window : modal, true); } } // This is called during NewFrame()->UpdateViewportsNewFrame() only. // Need to keep in sync with SetWindowPos() static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta) { window->Pos += delta; window->ClipRect.Translate(delta); window->OuterRectClipped.Translate(delta); window->InnerRect.Translate(delta); window->DC.CursorPos += delta; window->DC.CursorStartPos += delta; window->DC.CursorMaxPos += delta; window->DC.IdealMaxPos += delta; } static void ScaleWindow(ImGuiWindow* window, float scale) { ImVec2 origin = window->Viewport->Pos; window->Pos = ImFloor((window->Pos - origin) * scale + origin); window->Size = ImTrunc(window->Size * scale); window->SizeFull = ImTrunc(window->SizeFull * scale); window->ContentSize = ImTrunc(window->ContentSize * scale); } static bool IsWindowActiveAndVisible(ImGuiWindow* window) { return window->Active && !window->Hidden; } // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow. g.WindowsBorderHoverPadding = ImMax(ImMax(g.Style.TouchExtraPadding.x, g.Style.TouchExtraPadding.y), g.Style.WindowBorderHoverPadding); // Find the window hovered by mouse: // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. bool clear_hovered_windows = false; FindHoveredWindowEx(mouse_pos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow); IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport); g.HoveredWindowBeforeClear = g.HoveredWindow; // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) // FIXME-MERGE: RootWindowDockTree ? clear_hovered_windows = true; // Disabled mouse hovering (we don't currently clear MousePos, we could) if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) clear_hovered_windows = true; // We track click ownership. When clicked outside of a window the click is owned by the application and // won't report hovering nor request capture even while dragging over our windows afterward. const bool has_open_popup = (g.OpenPopupStack.Size > 0); const bool has_open_modal = (modal_window != NULL); int mouse_earliest_down = -1; bool mouse_any_down = false; for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) { if (io.MouseClicked[i]) { io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup; io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal; } mouse_any_down |= io.MouseDown[i]; if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392) if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down]) mouse_earliest_down = i; } const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down]; const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down]; // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; if (!mouse_avail && !mouse_dragging_extern_payload) clear_hovered_windows = true; if (clear_hovered_windows) g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app) // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag if (g.WantCaptureMouseNextFrame != -1) { io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0); } else { io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup; io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal; } // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app) io.WantCaptureKeyboard = false; if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0) { if ((g.ActiveId != 0) || (modal_window != NULL)) io.WantCaptureKeyboard = true; else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard) io.WantCaptureKeyboard = true; } if (g.WantCaptureKeyboardNextFrame != -1) // Manual override io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } // Called once a frame. Followed by SetCurrentFont() which sets up the remaining data. // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! static void SetupDrawListSharedData() { ImGuiContext& g = *GImGui; ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (ImGuiViewportP* viewport : g.Viewports) virtual_space.Add(viewport->GetMainRect()); g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; if (g.Style.AntiAliasedLines) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines)) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; if (g.Style.AntiAliasedFill) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; g.DrawListSharedData.InitialFringeScale = 1.0f; // FIXME-DPI: Change this for some DPI scaling experiments. } void ImGui::NewFrame() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); ImGuiContext& g = *GImGui; // Remove pending delete hooks before frame start. // This deferred removal avoid issues of removal while iterating the hook vector for (int n = g.Hooks.Size - 1; n >= 0; n--) if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_) g.Hooks.erase(&g.Hooks[n]); CallContextHooks(&g, ImGuiContextHookType_NewFramePre); // Check and assert for various common IO and Configuration mistakes g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame; ErrorCheckNewFrameSanityChecks(); g.ConfigFlagsCurrFrame = g.IO.ConfigFlags; // Load settings on first frame, save settings when modified (after a delay) UpdateSettings(); g.Time += g.IO.DeltaTime; g.FrameCount += 1; g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; g.MenusIdSubmittedThisFrame.resize(0); // Calculate frame-rate for the user, as a purely luxurious feature g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_COUNTOF(g.FramerateSecPerFrame); g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_COUNTOF(g.FramerateSecPerFrame)); g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; // Process input queue (trickle as many events as possible), turn events into writes to IO structure g.InputEventsTrail.resize(0); UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue); // Update viewports (after processing input queue, so io.MouseHoveredViewport is set) UpdateViewportsNewFrame(); // Update texture list (collect destroyed textures, etc.) UpdateTexturesNewFrame(); // Setup current font and draw list shared data SetupDrawListSharedData(); UpdateFontsNewFrame(); g.WithinFrameScope = true; // Mark rendering data as invalid to prevent user who may have a handle on it to use it. for (ImGuiViewportP* viewport : g.Viewports) { viewport->DrawData = NULL; viewport->DrawDataP.Valid = false; } // Drag and drop keep the source ID alive so even if the source disappear our state is consistent if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) KeepAliveID(g.DragDropPayload.SourceId); // [DEBUG] if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding Ctrl g.DebugDrawIdConflictsId = 0; if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1) g.DebugDrawIdConflictsId = g.HoveredIdPreviousFrame; // Update HoveredId data 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.HoveredIdPreviousFrameItemCount = 0; g.HoveredId = 0; g.HoveredIdAllowOverlap = false; g.HoveredIdIsDisabled = false; // Clear ActiveID if the item is not alive anymore. // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd(). // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves. if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId) { IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n"); ClearActiveID(); } // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore) if (g.ActiveId) g.ActiveIdTimer += g.IO.DeltaTime; g.LastActiveIdTimer += g.IO.DeltaTime; g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = 0; g.ActiveIdHasBeenEditedThisFrame = false; g.ActiveIdIsJustActivated = false; if (g.TempInputId != 0 && g.ActiveId != g.TempInputId) g.TempInputId = 0; if (g.InputTextReactivateId != 0 && g.InputTextReactivateId != g.DeactivatedItemData.ID) g.InputTextReactivateId = 0; if (g.ActiveId == 0) { g.ActiveIdUsingNavDirMask = 0x00; g.ActiveIdUsingAllKeyboardKeys = false; } if (g.DeactivatedItemData.ElapseFrame < g.FrameCount) g.DeactivatedItemData.ID = 0; g.DeactivatedItemData.IsAlive = false; // Record when we have been stationary as this state is preserved while over same item. // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values. // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function. if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay) g.HoverItemUnlockedStationaryId = g.HoverItemDelayId; else if (g.HoverItemDelayId == 0) g.HoverItemUnlockedStationaryId = 0; if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay) g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID; else if (g.HoveredWindow == NULL) g.HoverWindowUnlockedStationaryId = 0; // Update hover delay for IsItemHovered() with delays and tooltips g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId; if (g.HoverItemDelayId != 0) { g.HoverItemDelayTimer += g.IO.DeltaTime; g.HoverItemDelayClearTimer = 0.0f; g.HoverItemDelayId = 0; } else if (g.HoverItemDelayTimer > 0.0f) { // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle. g.HoverItemDelayClearTimer += g.IO.DeltaTime; if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. } // Close popups on focus lost (currently wip/opt-in) //if (g.IO.AppFocusLost) // ClosePopupsExceptModals(); // Update keyboard input state UpdateKeyboardInputs(); //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl)); //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift)); //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt)); //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper)); // Drag and drop g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; g.DragDropAcceptIdCurr = 0; g.DragDropAcceptFlagsPrev = g.DragDropAcceptFlagsCurr; g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags_None; g.DragDropAcceptIdCurrRectSurface = FLT_MAX; g.DragDropWithinSource = false; g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; if (g.DragDropActive) { // Also works when g.ActiveId==0 (aka leftover payload in progress, no active id) // You may disable this externally by hijacking the input route: // 'if (GetDragDropPayload() != NULL) { Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive); } // but you will not get a return value from Shortcut() due to ActiveIdUsingAllKeyboardKeys logic. You can however poll IsKeyPressed(ImGuiKey_Escape) afterwards. ImGuiID owner_id = g.ActiveId ? g.ActiveId : ImHashStr("##DragDropCancelHandler"); if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteGlobal, owner_id)) { ClearActiveID(); ClearDragDrop(); } } g.TooltipPreviousWindow = NULL; // Update keyboard/gamepad navigation NavUpdate(); // Update mouse input state UpdateMouseInputs(); // Undocking // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame) DockContextNewFrameUpdateUndocking(&g); // Mark all windows as not visible and compact unused memory. IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; for (ImGuiWindow* window : g.Windows) { window->WasActive = window->Active; window->Active = false; window->WriteAccessed = false; window->BeginCountPreviousFrame = window->BeginCount; window->BeginCount = 0; // Garbage collect transient buffers of recently unused windows if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) GcCompactTransientWindowBuffers(window); } // Find hovered window // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active) UpdateHoveredWindowAndCaptureFlags(g.IO.MousePos); // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) UpdateMouseMovingWindowNewFrame(); // Background darkening/whitening if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f); else g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f); g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; // Platform IME data: reset for the frame g.PlatformImeDataPrev = g.PlatformImeData; g.PlatformImeData.WantVisible = g.PlatformImeData.WantTextInput = false; // Mouse wheel scrolling, scale UpdateMouseWheel(); // Garbage collect transient buffers of recently unused tables for (int i = 0; i < g.TablesLastTimeActive.Size; i++) if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); for (ImGuiTableTempData& table_temp_data : g.TablesTempData) if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time) TableGcCompactTransientBuffers(&table_temp_data); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; // Closing the focused window restore focus to the first active root window in descending z-order if (g.NavWindow && !g.NavWindow->WasActive) FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // 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.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags g.CurrentItemFlags = g.ItemFlagsStack.back(); g.GroupStack.resize(0); // Docking DockContextNewFrameUpdateDocking(&g); // [DEBUG] Update debug features #ifndef IMGUI_DISABLE_DEBUG_TOOLS UpdateDebugToolItemPicker(); UpdateDebugToolItemPathQuery(); UpdateDebugToolFlashStyleColor(); if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0) { g.DebugLocateId = 0; g.DebugBreakInLocateId = false; } if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0) { DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n"); g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags; g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; } #endif // 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 prevents ImGui:: calls from crashing. g.WithinFrameScopeWithImplicitWindow = true; SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); Begin("Debug##Default"); IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true); // Store stack sizes g.ErrorCountCurrentFrame = 0; ErrorRecoveryStoreState(&g.StackSizesInNewFrame); // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack, // allowing to validate correct Begin/End behavior in user code. #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (g.IO.ConfigDebugBeginReturnValueLoop) g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10); else g.DebugBeginReturnValueCullDepth = -1; #endif CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } // FIXME: Add a more explicit sort order in the window structure. static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) { const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs; const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs; if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) return d; if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) return d; return a->BeginOrderWithinParent - b->BeginOrderWithinParent; } static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) { out_sorted_windows->push_back(window); if (window->Active) { int count = window->DC.ChildWindows.Size; ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); for (int i = 0; i < count; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; if (child->Active) AddWindowToSortBuffer(out_sorted_windows, child); } } } static void AddWindowToDrawData(ImGuiWindow* window, int layer) { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = window->Viewport; IM_ASSERT(viewport != NULL); g.IO.MetricsRenderWindows++; if (window->DrawList->_Splitter._Count > 1) window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows. ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (ImGuiWindow* child : window->DC.ChildWindows) if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active AddWindowToDrawData(child, layer); } static inline int GetWindowDisplayLayer(ImGuiWindow* window) { return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; } // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) static inline void AddRootWindowToDrawData(ImGuiWindow* window) { AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder) { int n = builder->Layers[0]->Size; int full_size = n; for (int i = 1; i < IM_COUNTOF(builder->Layers); i++) full_size += builder->Layers[i]->Size; builder->Layers[0]->resize(full_size); for (int layer_n = 1; layer_n < IM_COUNTOF(builder->Layers); layer_n++) { ImVector* layer = builder->Layers[layer_n]; if (layer->empty()) continue; memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*)); n += layer->Size; layer->resize(0); } } static void InitViewportDrawData(ImGuiViewportP* viewport) { ImGuiIO& io = ImGui::GetIO(); ImDrawData* draw_data = &viewport->DrawDataP; viewport->DrawData = draw_data; // Make publicly accessible viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists; viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1; viewport->DrawDataBuilder.Layers[0]->resize(0); viewport->DrawDataBuilder.Layers[1]->resize(0); // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode, // and to allow applications/backends to easily skip rendering. // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure. // This is because the work has been done already, and its wasted! We should fix that and add optimizations for // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline. const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0; draw_data->Valid = true; draw_data->CmdListsCount = 0; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size; draw_data->FramebufferScale = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale : io.DisplayFramebufferScale; draw_data->OwnerViewport = viewport; draw_data->Textures = &ImGui::GetPlatformIO().Textures; } // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. // - When using this function it is sane to ensure that float are perfectly rounded to integer values, // so that e.g. (int)(max.x-min.x) in user's render produce correct result. // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect(): // some frequently called functions which to modify both channels and clipping simultaneously tend to use the // more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds. // - This is analogous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack, // which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts. void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) { ImGuiWindow* window = GetCurrentWindow(); window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); window->ClipRect = window->DrawList->_ClipRectStack.back(); } void ImGui::PopClipRect() { ImGuiWindow* window = GetCurrentWindow(); window->DrawList->PopClipRect(); window->ClipRect = window->DrawList->_ClipRectStack.back(); } static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window) { for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--) if (IsWindowActiveAndVisible(window->DC.ChildWindows[n])) return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]); return window; } static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) return; ImGuiViewportP* viewport = window->Viewport; ImRect viewport_rect = viewport->GetMainRect(); // Draw behind window by moving the draw command at the FRONT of the draw list { // Draw list have been trimmed already, hence the explicit recreation of a draw command if missing. // FIXME: This is a little bit complicated, solely to avoid creating/injecting an extra drawlist in drawdata. ImDrawList* draw_list = window->RootWindowDockTree->DrawList; draw_list->ChannelsMerge(); if (draw_list->CmdBuffer.Size == 0) draw_list->AddDrawCmd(); draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to strictly ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that) ImDrawCmd cmd = draw_list->CmdBuffer.back(); IM_ASSERT(cmd.ElemCount == 0); draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); cmd = draw_list->CmdBuffer.back(); draw_list->CmdBuffer.pop_back(); draw_list->CmdBuffer.push_front(cmd); draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command. draw_list->PopClipRect(); } // Draw over sibling docking nodes in a same docking tree if (window->RootWindow->DockIsActive) { ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; draw_list->ChannelsMerge(); if (draw_list->CmdBuffer.Size == 0) draw_list->AddDrawCmd(); draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false); RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding); draw_list->PopClipRect(); } } ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window) { ImGuiContext& g = *GImGui; ImGuiWindow* bottom_most_visible_window = parent_window; for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; if (window->Flags & ImGuiWindowFlags_ChildWindow) continue; if (!IsWindowWithinBeginStackOf(window, parent_window)) break; if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window)) bottom_most_visible_window = window; } return bottom_most_visible_window; } // Important: AddWindowToDrawData() has not been called yet, meaning DockNodeHost windows needs a DrawList->ChannelsMerge() before usage. // We call ChannelsMerge() lazily here at it is faster that doing a full iteration of g.Windows[] prior to calling RenderDimmedBackgrounds(). static void ImGui::RenderDimmedBackgrounds() { ImGuiContext& g = *GImGui; ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) return; const bool dim_bg_for_modal = (modal_window != NULL); const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); if (!dim_bg_for_modal && !dim_bg_for_window_list) return; ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL }; if (dim_bg_for_modal) { // Draw dimming behind modal or a begin stack child, whichever comes first in draw order. ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window); RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio)); viewports_already_dimmed[0] = modal_window->Viewport; } else if (dim_bg_for_window_list) { // Draw dimming behind Ctrl+Tab target window and behind Ctrl+Tab UI window RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport; if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Active && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport) { RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); viewports_already_dimmed[1] = g.NavWindowingListWindow->Viewport; } // Draw border around Ctrl+Tab target window ImGuiWindow* window = g.NavWindowingTargetAnim; ImGuiViewport* viewport = window->Viewport; float distance = g.FontSize; ImRect bb = window->Rect(); bb.Expand(distance); if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward window->DrawList->ChannelsMerge(); if (window->DrawList->CmdBuffer.Size == 0) window->DrawList->AddDrawCmd(); window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); // FIXME-DPI window->DrawList->PopClipRect(); } // Draw dimming background on _other_ viewports than the ones our windows are in for (ImGuiViewportP* viewport : g.Viewports) { if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1]) continue; if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window)) continue; ImDrawList* draw_list = GetForegroundDrawList(viewport); const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); } } // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. void ImGui::EndFrame() { ImGuiContext& g = *GImGui; IM_ASSERT(g.Initialized); // Don't process EndFrame() multiple times. if (g.FrameCountEnded == g.FrameCount) return; IM_ASSERT_USER_ERROR_RET(g.WithinFrameScope, "Forgot to call ImGui::NewFrame()?"); CallContextHooks(&g, ImGuiContextHookType_EndFramePre); // [EXPERIMENTAL] Recover from errors if (g.IO.ConfigErrorRecovery) ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame); ErrorCheckEndFrameSanityChecks(); ErrorCheckEndFrameFinalizeErrorTooltip(); // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) ImGuiPlatformImeData* ime_data = &g.PlatformImeData; if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) { ImGuiViewport* viewport = FindViewportByID(ime_data->ViewportId); if (viewport == NULL) viewport = GetMainViewport(); IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f) for Viewport 0x%08X\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y, viewport->ID); g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data); } g.WantTextInputNextFrame = ime_data->WantTextInput ? 1 : 0; // Hide and unfocus implicit/fallback "Debug" window if it hasn't been used g.WithinFrameScopeWithImplicitWindow = false; if (g.CurrentWindow && g.CurrentWindow->IsFallbackWindow && g.CurrentWindow->WriteAccessed == false) { g.CurrentWindow->Active = false; if (g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow) FocusWindow(NULL); } End(); // Update navigation: Ctrl+Tab, wrap-around requests NavEndFrame(); // Update docking DockContextEndFrame(&g); SetCurrentViewport(NULL, NULL); // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) if (g.DragDropActive) { bool is_delivered = g.DragDropPayload.Delivery; bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(g.DragDropMouseButton)); if (is_delivered || is_elapsed) ClearDragDrop(); } // Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing. // If you want to handle source item disappearing: instead of submitting your description tooltip // in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot // (e.g. end of your item loop, or before EndFrame) by reading payload data. // In the typical case, the contents of drag tooltip should be possible to infer solely from payload data. if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) { g.DragDropWithinSource = true; SetTooltip("..."); g.DragDropWithinSource = false; } // End frame g.WithinFrameScope = false; g.FrameCountEnded = g.FrameCount; UpdateFontsEndFrame(); // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) UpdateViewportsEndFrame(); // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because children may not exist yet g.WindowsTempSortBuffer.resize(0); g.WindowsTempSortBuffer.reserve(g.Windows.Size); for (ImGuiWindow* window : g.Windows) { if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it continue; AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window); } // 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.WindowsTempSortBuffer.Size); g.Windows.swap(g.WindowsTempSortBuffer); g.IO.MetricsActiveWindows = g.WindowsActiveCount; UpdateTexturesEndFrame(); // Unlock font atlas for (ImFontAtlas* atlas : g.FontAtlases) atlas->Locked = false; // Clear Input data for next frame g.IO.MousePosPrev = g.IO.MousePos; g.IO.AppFocusLost = false; g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; g.IO.InputQueueCharacters.resize(0); CallContextHooks(&g, ImGuiContextHookType_EndFramePost); } // Prepare the data for rendering so you can call GetDrawData() // (As with anything within the ImGui:: namespace this doesn't touch your GPU or graphics API at all: // it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend) void ImGui::Render() { ImGuiContext& g = *GImGui; IM_ASSERT(g.Initialized); if (g.FrameCountEnded != g.FrameCount) EndFrame(); if (g.FrameCountRendered == g.FrameCount) return; g.FrameCountRendered = g.FrameCount; g.IO.MetricsRenderWindows = 0; CallContextHooks(&g, ImGuiContextHookType_RenderPre); // Add background ImDrawList (for each active viewport) for (ImGuiViewportP* viewport : g.Viewports) { InitViewportDrawData(viewport); if (viewport->BgFgDrawLists[0] != NULL) AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } // Draw modal/window whitening backgrounds RenderDimmedBackgrounds(); // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL; windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL); for (ImGuiWindow* window : g.Windows) { IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) AddRootWindowToDrawData(window); } for (int n = 0; n < IM_COUNTOF(windows_to_render_top_most); n++) if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); // Draw software mouse cursor if requested by io.MouseDrawCursor flag if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); // Setup ImDrawData structures for end-user g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; for (ImGuiViewportP* viewport : g.Viewports) { FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder); // Add foreground ImDrawList (for each active viewport) if (viewport->BgFgDrawLists[1] != NULL) AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). ImDrawData* draw_data = &viewport->DrawDataP; IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount); for (ImDrawList* draw_list : draw_data->CmdLists) draw_list->_PopUnusedDrawCmd(); g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; } #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) for (ImFontAtlas* atlas : g.FontAtlases) ImFontAtlasDebugLogTextureRequests(atlas); #endif CallContextHooks(&g, ImGuiContextHookType_RenderPost); } // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. // CalcTextSize("") should return ImVec2(0.0f, g.FontSize) ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) { ImGuiContext& g = *GImGui; const char* text_display_end; if (hide_text_after_double_hash) text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string else text_display_end = text_end; ImFont* font = g.Font; const float font_size = g.FontSize; if (text == text_display_end) return ImVec2(0.0f, font_size); ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); // Round // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out. // FIXME: Investigate using ceilf or e.g. // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html text_size.x = IM_TRUNC(text_size.x + 0.99999f); return text_size; } // Find window given position, search front-to-back // - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow. // - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically // 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. // - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here. void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window) { ImGuiContext& g = *GImGui; ImGuiWindow* hovered_window = NULL; ImGuiWindow* hovered_window_under_moving_window = NULL; // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame) ImGuiViewportP* backup_moving_window_viewport = NULL; if (find_first_and_in_any_viewport == false && g.MovingWindow) { backup_moving_window_viewport = g.MovingWindow->Viewport; g.MovingWindow->Viewport = g.MouseViewport; if (!(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) hovered_window = g.MovingWindow; } ImVec2 padding_regular = g.Style.TouchExtraPadding; ImVec2 padding_for_resize = ImMax(g.Style.TouchExtraPadding, ImVec2(g.Style.WindowBorderHoverPadding, g.Style.WindowBorderHoverPadding)); for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (!window->WasActive || window->Hidden) continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) continue; IM_ASSERT(window->Viewport); if (window->Viewport != g.MouseViewport) continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize; if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding)) continue; // Support for one rectangular hole in any given window // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512) if (window->HitTestHoleSize.x != 0) { ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y); ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y); if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos)) continue; } if (find_first_and_in_any_viewport) { hovered_window = window; break; } else { if (hovered_window == NULL) hovered_window = window; IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)) hovered_window_under_moving_window = window; if (hovered_window && hovered_window_under_moving_window) break; } } *out_hovered_window = hovered_window; if (out_hovered_window_under_moving_window != NULL) *out_hovered_window_under_moving_window = hovered_window_under_moving_window; if (find_first_and_in_any_viewport == false && g.MovingWindow) g.MovingWindow->Viewport = backup_moving_window_viewport; } bool ImGui::IsItemActive() { ImGuiContext& g = *GImGui; if (g.ActiveId) return g.ActiveId == g.LastItemData.ID; return false; } bool ImGui::IsItemActivated() { ImGuiContext& g = *GImGui; if (g.ActiveId) if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID) return true; return false; } bool ImGui::IsItemDeactivated() { ImGuiContext& g = *GImGui; if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated) return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; return g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount; } bool ImGui::IsItemDeactivatedAfterEdit() { ImGuiContext& g = *GImGui; return IsItemDeactivated() && g.DeactivatedItemData.HasBeenEditedBefore; } // == (GetItemID() == GetFocusID() && GetFocusID() != 0) bool ImGui::IsItemFocused() { ImGuiContext& g = *GImGui; if (g.NavId != g.LastItemData.ID || g.NavId == 0) return false; // 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. ImGuiWindow* window = g.CurrentWindow; if (g.LastItemData.ID == window->ID && window->WriteAccessed) return false; return true; } // Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()! // Most widgets have specific reactions based on mouse-up/down state, mouse position etc. bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) { return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); } bool ImGui::IsItemToggledOpen() { ImGuiContext& g = *GImGui; return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; } // Call after a Selectable() or TreeNode() involved in multi-selection. // Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose. // This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block. // (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets // return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.) bool ImGui::IsItemToggledSelection() { ImGuiContext& g = *GImGui; IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; } // IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, // you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! // Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. bool ImGui::IsAnyItemHovered() { ImGuiContext& g = *GImGui; return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; } bool ImGui::IsAnyItemActive() { ImGuiContext& g = *GImGui; return g.ActiveId != 0; } bool ImGui::IsAnyItemFocused() { ImGuiContext& g = *GImGui; return g.NavId != 0 && g.NavCursorVisible; } bool ImGui::IsItemVisible() { ImGuiContext& g = *GImGui; return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0; } bool ImGui::IsItemEdited() { ImGuiContext& g = *GImGui; return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0; } // Allow next item to be overlapped by subsequent items. // This works by requiring HoveredId to match for two subsequent frames, // so if a following items overwrite it our interactions will naturally be disabled. void ImGui::SetNextItemAllowOverlap() { ImGuiContext& g = *GImGui; g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap; } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. // Use SetNextItemAllowOverlap() *before* your item instead of calling this! //void ImGui::SetItemAllowOverlap() //{ // ImGuiContext& g = *GImGui; // ImGuiID id = g.LastItemData.ID; // if (g.HoveredId == id) // g.HoveredIdAllowOverlap = true; // if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id. // g.ActiveIdAllowOverlap = true; //} #endif // This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations. // FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed? void ImGui::SetActiveIdUsingAllKeyboardKeys() { ImGuiContext& g = *GImGui; IM_ASSERT(g.ActiveId != 0); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1; g.ActiveIdUsingAllKeyboardKeys = true; NavMoveRequestCancel(); } ImGuiID ImGui::GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } ImVec2 ImGui::GetItemRectMin() { ImGuiContext& g = *GImGui; return g.LastItemData.Rect.Min; } ImVec2 ImGui::GetItemRectMax() { ImGuiContext& g = *GImGui; return g.LastItemData.Rect.Max; } ImVec2 ImGui::GetItemRectSize() { ImGuiContext& g = *GImGui; return g.LastItemData.Rect.GetSize(); } ImGuiItemFlags ImGui::GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.ItemFlags; } // Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'. // ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details! bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) { ImGuiID id = GetCurrentWindow()->GetID(str_id); return BeginChildEx(str_id, id, size_arg, child_flags, window_flags); } bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) { return BeginChildEx(NULL, id, size_arg, child_flags, window_flags); } bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; IM_ASSERT(id != 0); // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument. const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened; IM_UNUSED(ImGuiChildFlags_SupportedMask_); IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?"); IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!"); if (child_flags & ImGuiChildFlags_AlwaysAutoResize) { IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!"); IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!"); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) { child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; } //if (window_flags & ImGuiWindowFlags_NavFlattened) { child_flags |= ImGuiChildFlags_NavFlattened; } #endif if (child_flags & ImGuiChildFlags_AutoResizeX) child_flags &= ~ImGuiChildFlags_ResizeX; if (child_flags & ImGuiChildFlags_AutoResizeY) child_flags &= ~ImGuiChildFlags_ResizeY; // Set window flags window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking; window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize)) window_flags |= ImGuiWindowFlags_AlwaysAutoResize; if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0) window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; // Special framed style if (child_flags & ImGuiChildFlags_FrameStyle) { PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]); PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding); child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding; window_flags |= ImGuiWindowFlags_NoMove; } // Forward size // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set. // (the alternative would to store conditional flags per axis, which is possible but more code) const ImVec2 size_avail = GetContentRegionAvail(); const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y); ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y); // A SetNextWindowSize() call always has priority (#8020) // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now) // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer. if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0) { if (g.NextWindowData.SizeVal.x > 0.0f) { size.x = g.NextWindowData.SizeVal.x; child_flags &= ~ImGuiChildFlags_ResizeX; } if (g.NextWindowData.SizeVal.y > 0.0f) { size.y = g.NextWindowData.SizeVal.y; child_flags &= ~ImGuiChildFlags_ResizeY; } } SetNextWindowSize(size); // Forward child flags (we allow prior settings to merge but it'll only work for adding flags) if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) g.NextWindowData.ChildFlags |= child_flags; else g.NextWindowData.ChildFlags = child_flags; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags; // 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. // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround. // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it. const char* temp_window_name; /*if (name && parent_window->IDStack.back() == parent_window->ID) ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack else*/ if (name) ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id); else ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id); // Set style const float backup_border_size = g.Style.ChildBorderSize; if ((child_flags & ImGuiChildFlags_Borders) == 0) g.Style.ChildBorderSize = 0.0f; // Begin into window const bool ret = Begin(temp_window_name, NULL, window_flags); // Restore style g.Style.ChildBorderSize = backup_border_size; if (child_flags & ImGuiChildFlags_FrameStyle) { PopStyleVar(3); PopStyleColor(); } ImGuiWindow* child_window = g.CurrentWindow; child_window->ChildId = id; // 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 // Can enter a child if (A) it has navigable items or (B) it can be scrolled. const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id); if (g.ActiveId == temp_id_for_activation) ClearActiveID(); if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY)) { FocusWindow(child_window); NavInitWindow(child_window, false); SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item g.ActiveIdSource = g.NavInputSource; } return ret; } void ImGui::EndChild() { ImGuiContext& g = *GImGui; ImGuiWindow* child_window = g.CurrentWindow; const ImGuiID backup_within_end_child_id = g.WithinEndChildID; IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls g.WithinEndChildID = child_window->ID; ImVec2 child_size = child_window->Size; End(); if (child_window->BeginCount == 1) { ImGuiWindow* parent_window = g.CurrentWindow; ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size); ItemSize(child_size); const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0; if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened) { ItemAdd(bb, child_window->ChildId); RenderNavCursor(bb, child_window->ChildId); // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying) if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow) RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavRenderCursorFlags_Compact); } else { // Not navigable into // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs. // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set. ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav); // But when flattened we directly reach items, adjust active layer mask accordingly if (nav_flattened) parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext; } if (g.HoveredWindow == child_window) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; child_window->DC.ChildItemStatusFlags = g.LastItemData.StatusFlags; //SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); // Not needed, effectively done by ItemAdd() } else { SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); } g.WithinEndChildID = backup_within_end_child_id; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) { window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); window->SetWindowDockAllowFlags = enabled ? (window->SetWindowDockAllowFlags | flags) : (window->SetWindowDockAllowFlags & ~flags); } ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) { ImGuiContext& g = *GImGui; return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); } ImGuiWindow* ImGui::FindWindowByName(const char* name) { ImGuiID id = ImHashStr(name); return FindWindowByID(id); } static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); window->ViewportPos = main_viewport->Pos; if (settings->ViewportId) { window->ViewportId = settings->ViewportId; window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y); } window->Pos = ImTrunc(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y)); if (settings->Size.x > 0 && settings->Size.y > 0) window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y)); window->Collapsed = settings->Collapsed; window->DockId = settings->DockId; window->DockOrder = settings->DockOrder; } static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { // Initial window state with e.g. default/arbitrary window position // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); window->Pos = main_viewport->Pos + ImVec2(60, 60); window->Size = window->SizeFull = ImVec2(0, 0); window->ViewportPos = main_viewport->Pos; window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = window->SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; if (settings != NULL) { SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); ApplyWindowSettings(window, settings); } window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) { window->AutoFitFramesX = window->AutoFitFramesY = 2; window->AutoFitOnlyGrows = false; } else { if (window->Size.x <= 0.0f) window->AutoFitFramesX = 2; if (window->Size.y <= 0.0f) window->AutoFitFramesY = 2; window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } } static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { // Create window the first time //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); ImGuiContext& g = *GImGui; ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); window->Flags = flags; g.WindowsById.SetVoidPtr(window->ID, window); ImGuiWindowSettings* settings = NULL; if (!(flags & ImGuiWindowFlags_NoSavedSettings)) if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0) window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); InitOrLoadWindowSettings(window, settings); if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); return window; } static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window) { return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window; } static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window) { return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window; } static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window) { // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller. // Perhaps should tend further a neater test for this. ImGuiContext& g = *GImGui; ImVec2 size_min; if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) { size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE; size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE; } else { size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE; size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE; } // Reduce artifacts with very small windows ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window); size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); return size_min; } static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired) { ImGuiContext& g = *GImGui; ImVec2 new_size = size_desired; if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint) { // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max. ImRect cr = g.NextWindowData.SizeConstraintRect; new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; if (g.NextWindowData.SizeCallback) { ImGuiSizeCallbackData data; data.UserData = g.NextWindowData.SizeCallbackUserData; data.Pos = window->Pos; data.CurrentSize = window->SizeFull; data.DesiredSize = new_size; g.NextWindowData.SizeCallback(&data); new_size = data.DesiredSize; } new_size.x = IM_TRUNC(new_size.x); new_size.y = IM_TRUNC(new_size.y); } // Minimum size ImVec2 size_min = CalcWindowMinSize(window); return ImMax(new_size, size_min); } static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal) { bool preserve_old_content_sizes = false; if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) preserve_old_content_sizes = true; else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0) preserve_old_content_sizes = true; if (preserve_old_content_sizes) { *content_size_current = window->ContentSize; *content_size_ideal = window->ContentSizeIdeal; return; } content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x); content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y); } static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents, int axis_mask) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x; const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y; ImVec2 size_pad = window->WindowPadding * 2.0f; ImVec2 size_desired; size_desired[ImGuiAxis_X] = (axis_mask & 1) ? size_contents.x + size_pad.x + decoration_w_without_scrollbars : window->Size.x; size_desired[ImGuiAxis_Y] = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y; // Determine maximum window size // Child windows are laid within their parent (unless they are also popups/menus) and thus have no restriction ImVec2 size_max = ImVec2(FLT_MAX, FLT_MAX); if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || (window->Flags & ImGuiWindowFlags_Popup) != 0) { if (!window->ViewportOwned) size_max = ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f; const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) size_max = g.PlatformIO.Monitors[monitor_idx].WorkSize - style.DisplaySafeAreaPadding * 2.0f; } if (window->Flags & ImGuiWindowFlags_Tooltip) { // Tooltip always resize (up to maximum size) return ImMin(size_desired, size_max); } else { ImVec2 size_min = CalcWindowMinSize(window); ImVec2 size_auto_fit = ImClamp(size_desired, ImMin(size_min, size_max), size_max); // 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 = CalcWindowSizeAfterConstraint(window, size_auto_fit); bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; if (will_have_scrollbar_y) size_auto_fit.x += style.ScrollbarSize; return size_auto_fit; } } ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) { ImVec2 size_contents_current; ImVec2 size_contents_ideal; CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal); ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal, ~0); ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit); return size_final; } static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) { if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) return ImGuiCol_PopupBg; if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive) return ImGuiCol_ChildBg; return ImGuiCol_WindowBg; } static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target_arg, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) { ImVec2 corner_target = corner_target_arg; if (window->Flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent { ImGuiWindow* parent_window = window->ParentWindow; ImGuiWindowFlags parent_flags = parent_window->Flags; ImRect limit_rect = parent_window->InnerRect; limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize))); if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)) corner_target.x = ImClamp(corner_target.x, limit_rect.Min.x, limit_rect.Max.x); if (parent_flags & ImGuiWindowFlags_NoScrollbar) corner_target.y = ImClamp(corner_target.y, limit_rect.Min.y, limit_rect.Max.y); } ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right ImVec2 size_expected = pos_max - pos_min; ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected); *out_pos = pos_min; if (corner_norm.x == 0.0f) out_pos->x -= (size_constrained.x - size_expected.x); if (corner_norm.y == 0.0f) out_pos->y -= (size_constrained.y - size_expected.y); *out_size = size_constrained; } // Data for resizing from resize grip / corner struct ImGuiResizeGripDef { ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; }; 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 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused) { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused) }; // Data for resizing from borders struct ImGuiResizeBorderDef { ImVec2 InnerDir; // Normal toward inside ImVec2 SegmentN1, SegmentN2; // End positions, normalized (0,0: upper left) float OuterAngle; // Angle toward outside }; static const ImGuiResizeBorderDef resize_border_def[4] = { { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down }; 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 == ImGuiDir_Left) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } if (border_n == ImGuiDir_Up) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } if (border_n == ImGuiDir_Down) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } IM_ASSERT(0); return ImRect(); } // 0..3: corners (Lower-right, Lower-left, Unused, Unused) ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n) { IM_ASSERT(n >= 0 && n < 4); ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID; id = ImHashStr("#RESIZE", 0, id); id = ImHashData(&n, sizeof(int), id); return id; } // Borders (Left, Right, Up, Down) ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) { IM_ASSERT(dir >= 0 && dir < 4); int n = (int)dir + 4; ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID; id = ImHashStr("#RESIZE", 0, id); id = ImHashData(&n, sizeof(int), id); return id; } // Handle resize for: Resize Grips, Borders, Gamepad // Return true when using auto-fit (double-click on resize grip) static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; if ((flags & ImGuiWindowFlags_NoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) return false; if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && (window->ChildFlags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0) return false; if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window. return false; int ret_auto_fit_mask = 0x00; const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f; const float grip_hover_outer_size = g.WindowsBorderHoverPadding; ImRect clamp_rect = visibility_rect; const bool window_move_from_title_bar = !(window->BgClickFlags & ImGuiWindowBgClickFlags_Move) && !(window->Flags & ImGuiWindowFlags_NoTitleBar); if (window_move_from_title_bar) clamp_rect.Min.y -= window->TitleBarHeight; ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time). // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits. // This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it. // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow. // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold). // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup. const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration); if (clip_with_viewport_rect) window->ClipRect = window->Viewport->GetMainRect(); // Resize grips and borders are on layer 1 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; // Manual resize grips PushID("#RESIZE"); for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window bool hovered, held; ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.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); ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID() ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav); ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE); if (held && g.IO.MouseDoubleClicked[0]) { // Auto-fit when double-clicking ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, ~0); size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); ret_auto_fit_mask = 0x03; // Both axes ClearActiveID(); } else if (held) { // 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 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? clamp_rect.Min.x : -FLT_MAX, (def.CornerPosN.y == 1.0f || (def.CornerPosN.y == 0.0f && window_move_from_title_bar)) ? clamp_rect.Min.y : -FLT_MAX); ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? clamp_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? clamp_rect.Max.y : +FLT_MAX); ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip corner_target = ImClamp(corner_target, clamp_min, clamp_max); CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target); } // Only lower-left grip is visible before hovering/activating const bool resize_grip_visible = held || hovered || (resize_grip_n == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0); if (resize_grip_visible) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } int resize_border_mask = 0x00; if (window->Flags & ImGuiWindowFlags_ChildWindow) resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0); else resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00; for (int border_n = 0; border_n < 4; border_n++) { if ((resize_border_mask & (1 << border_n)) == 0) continue; const ImGuiResizeBorderDef& def = resize_border_def[border_n]; const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, g.WindowsBorderHoverPadding); ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav); ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) hovered = false; if (hovered || held) SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS); if (held && g.IO.MouseDoubleClicked[0]) { // Double-clicking bottom or right border auto-fit on this axis // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases. if (border_n == 1 || border_n == 3) // Right and bottom border { ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, 1 << axis); size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis]; ret_auto_fit_mask |= (1 << axis); hovered = held = false; // So border doesn't show highlighted at new position } ClearActiveID(); } else if (held) { // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop. // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually. // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it! const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false, true)); if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing) { g.WindowResizeBorderExpectedRect = border_rect; g.WindowResizeRelativeMode = false; } if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0) g.WindowResizeRelativeMode = true; const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size); const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis]; const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + g.WindowsBorderHoverPadding; // Match ButtonBehavior() padding above. // Use absolute mode position ImVec2 border_target = window->Pos; border_target[axis] = border_target_abs_mode_for_axis; // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position. bool ignore_resize = false; if (g.WindowResizeRelativeMode) { //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode"); border_target[axis] = border_target_rel_mode_for_axis; if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis)) ignore_resize = true; } // Clamp, apply ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX); ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX); border_target = ImClamp(border_target, clamp_min, clamp_max); if (!ignore_resize) CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); } if (hovered) *border_hovered = border_n; if (held) *border_held = border_n; } PopID(); // Restore nav layer window->DC.NavLayerCurrent = ImGuiNavLayer_Main; // Navigation resize (keyboard/gamepad) // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user. // Not even sure the callback works here. if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window) { ImVec2 nav_resize_dir; if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow); if (g.NavInputSource == ImGuiInputSource_Gamepad) nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown); if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f) { const float NAV_RESIZE_SPEED = 600.0f; const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * GetScale(); g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step; g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size g.NavWindowingToggleLayer = false; g.NavHighlightItemUnderNav = true; resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize); if (accum_floored.x != 0.0f || accum_floored.y != 0.0f) { // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored); g.NavWindowingAccumDeltaSize -= accum_floored; } } } // Apply back modified position/size to window const ImVec2 old_pos = window->Pos; const ImVec2 old_size = window->SizeFull; if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x)) window->Size.x = window->SizeFull.x = size_target.x; if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y)) window->Size.y = window->SizeFull.y = size_target.y; if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x)) window->Pos.x = ImTrunc(pos_target.x); if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y)) window->Pos.y = ImTrunc(pos_target.y); if (old_pos.x != window->Pos.x || old_pos.y != window->Pos.y || old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y) MarkIniSettingsDirty(window); // Recalculate next expected border expected coordinates if (*border_held != -1) g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, g.WindowsBorderHoverPadding); return ret_auto_fit_mask; } static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect) { ImVec2 size_for_clamping = window->Size; const bool move_from_title_bar_only = (window->BgClickFlags & ImGuiWindowBgClickFlags_Move) == 0; if (move_from_title_bar_only && window->DockNodeAsHost) size_for_clamping.y = ImGui::GetFrameHeight(); // Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here. else if (move_from_title_bar_only && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) size_for_clamping.y = window->TitleBarHeight; window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); } static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size) { const ImGuiResizeBorderDef& def = resize_border_def[border_n]; const float rounding = window->WindowRounding; const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f); window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + 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.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size); } static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) { ImGuiContext& g = *GImGui; const float border_size = window->WindowBorderSize; const ImU32 border_col = GetColorU32(ImGuiCol_Border); if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0) window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize); else if (border_size > 0.0f) { if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border. RenderWindowOuterSingleBorder(window, 1, border_col, border_size); if (window->ChildFlags & ImGuiChildFlags_ResizeY) RenderWindowOuterSingleBorder(window, 3, border_col, border_size); } if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1) { const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered; const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered); RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual } if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) { float y = window->Pos.y + window->TitleBarHeight - 1; window->DrawList->AddLine(ImVec2(window->Pos.x + border_size * 0.5f, y), ImVec2(window->Pos.x + window->Size.x - border_size * 0.5f, y), border_col, g.Style.FrameBorderSize); } } // Draw background and borders // Draw and handle scrollbars void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; ImGuiWindowFlags flags = window->Flags; // Ensure that Scrollbar() doesn't read last frame's SkipItems IM_ASSERT(window->BeginCount == 0); window->SkipItems = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; // Draw window + handle manual resize // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame. const float window_rounding = window->WindowRounding; const float window_border_size = window->WindowBorderSize; if (window->Collapsed) { // Title bar only const float backup_border_size = style.FrameBorderSize; g.Style.FrameBorderSize = window->WindowBorderSize; ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); if (window->ViewportOwned) title_bar_col |= IM_COL32_A_MASK; // No alpha (we don't support is_docking_transparent_payload here because simpler and less meaningful, but could with a bit of code shuffle/reuse) RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); g.Style.FrameBorderSize = backup_border_size; } else { // Window background if (!(flags & ImGuiWindowFlags_NoBackground)) { bool is_docking_transparent_payload = false; if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload) if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window) is_docking_transparent_payload = true; ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); if (window->ViewportOwned) { bg_col |= IM_COL32_A_MASK; // No alpha if (is_docking_transparent_payload) window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; } else { // Adjust alpha. For docking bool override_alpha = false; float alpha = 1.0f; if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasBgAlpha) { alpha = g.NextWindowData.BgAlphaVal; override_alpha = true; } if (is_docking_transparent_payload) { alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override? override_alpha = true; } if (override_alpha) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); } // Render, for docked windows and host windows we ensure BG goes before decorations if (window->DockIsActive) window->DockNode->LastBgColor = bg_col; if (flags & ImGuiWindowFlags_DockNodeHost) bg_col = 0; if (bg_col & IM_COL32_A_MASK) { ImRect bg_rect(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size); ImDrawFlags bg_rounding_flags; if (window->DockIsActive) bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, window->DockNode->HostWindow->Rect(), 0.0f); else bg_rounding_flags = (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersBottom; ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList; if (window->DockIsActive) bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); bg_draw_list->AddRectFilled(bg_rect.Min, bg_rect.Max, bg_col, window_rounding, bg_rounding_flags); if (window->DockIsActive) bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); } } if (window->DockIsActive) window->DockNode->IsBgDrawnThisFrame = true; // Draw window shadow if (style.WindowShadowSize > 0.0f && (!(flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_Popup))) if (style.Colors[ImGuiCol_WindowShadow].w > 0.0f) RenderWindowShadow(window); // Title bar // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag, // in order for their pos/size to be matching their undocking state.) if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) { ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); if (window->ViewportOwned) title_bar_col |= IM_COL32_A_MASK; // No alpha window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop); } // Menu bar if (flags & ImGuiWindowFlags_MenuBar) { ImRect menu_bar_rect = window->MenuBarRect(); menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) window->DrawList->AddLine(menu_bar_rect.GetBL() + ImVec2(window_border_size * 0.5f, 0.0f), menu_bar_rect.GetBR() - ImVec2(window_border_size * 0.5f, 0.0f), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock ImGuiDockNode* node = window->DockNode; if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar()) { float unhide_sz_draw = ImTrunc(g.FontSize * 0.70f); float unhide_sz_hit = ImTrunc(g.FontSize * 0.55f); ImVec2 p = node->Pos; ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit)); ImGuiID unhide_id = window->GetID("#UNHIDE"); KeepAliveID(unhide_id); bool hovered, held; if (ButtonBehavior(r, unhide_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren)) node->WantHiddenTabBarToggle = true; else if (held && IsMouseDragging(0)) StartMouseMovingWindowOrNode(window, node, true); // Undock from tab-bar triangle = same as window/collapse menu button // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size.. ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col); } // Scrollbars if (window->ScrollbarX) Scrollbar(ImGuiAxis_X); if (window->ScrollbarY) Scrollbar(ImGuiAxis_Y); // Render resize grips (after their input handling so we don't have a frame of latency) if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize)) { for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImU32 col = resize_grip_col[resize_grip_n]; if ((col & IM_COL32_A_MASK) == 0) continue; const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); const float border_inner = IM_ROUND(window_border_size * 0.5f); window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(border_inner, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, border_inner))); window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, border_inner) : ImVec2(border_inner, resize_grip_draw_size))); window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + border_inner), corner.y + grip.InnerDir.y * (window_rounding + border_inner)), window_rounding, grip.AngleMin12, grip.AngleMax12); window->DrawList->PathFillConvex(col); } } // Borders (for dock node host they will be rendered over after the tab bar) if (handle_borders_and_resize_grips && !window->DockNodeAsHost) RenderWindowOuterBorders(window); } window->DC.NavLayerCurrent = ImGuiNavLayer_Main; } void ImGui::RenderWindowShadow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; float shadow_size = style.WindowShadowSize; ImU32 shadow_col = GetColorU32(ImGuiCol_WindowShadow); ImVec2 shadow_offset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist; window->DrawList->AddShadowRect(window->Pos, window->Pos + window->Size, shadow_col, shadow_size, shadow_offset, ImDrawFlags_ShadowCutOutShapeBackground, window->WindowRounding); } // When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead. // Render title text, collapse button, close button void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; ImGuiWindowFlags flags = window->Flags; const bool has_close_button = (p_open != NULL); const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref? const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; // Layout buttons // FIXME: Would be nice to generalize the subtleties expressed here into reusable code. float pad_l = style.FramePadding.x; float pad_r = style.FramePadding.x; float button_sz = g.FontSize; ImVec2 close_button_pos; ImVec2 collapse_button_pos; if (has_close_button) { close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y); pad_r += button_sz + style.ItemInnerSpacing.x; } if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) { collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y); pad_r += button_sz + style.ItemInnerSpacing.x; } if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left) { collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y); pad_l += button_sz + style.ItemInnerSpacing.x; } // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) if (has_collapse_button) if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL)) window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function // Close button if (has_close_button) { ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; g.CurrentItemFlags |= ImGuiItemFlags_NoFocus; if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) *p_open = false; g.CurrentItemFlags = backup_item_flags; } window->DC.NavLayerCurrent = ImGuiNavLayer_Main; g.CurrentItemFlags = item_flags_backup; // 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 WAY too much messy code.. const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f; const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, // while uncentered title text will still reach edges correctly. if (pad_l > style.FramePadding.x) pad_l += g.Style.ItemInnerSpacing.x; if (pad_r > style.FramePadding.x) pad_r += g.Style.ItemInnerSpacing.x; if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f) { float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x); pad_l = ImMax(pad_l, pad_extend * centerness); pad_r = ImMax(pad_r, pad_extend * centerness); } ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y); if (flags & ImGuiWindowFlags_UnsavedDocument) { ImVec2 marker_pos; marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x); marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; if (marker_pos.x > layout_r.Min.x) { RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_UnsavedMarker)); clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); } } //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); } void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) { window->RootWindowDockTree = parent_window->RootWindowDockTree; if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost)) window->RootWindow = parent_window->RootWindow; } if (parent_window && (flags & ImGuiWindowFlags_Popup)) window->RootWindowPopupTree = parent_window->RootWindowPopupTree; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip))) // FIXME: simply use _NoTitleBar ? window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened) { IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL); window->RootWindowForNav = window->RootWindowForNav->ParentWindow; } } // [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point. // This is designed as a toy/test-bed for void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window) { ImGuiContext& g = *GImGui; window->SkipRefresh = false; if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0) return; if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh) { // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused. if (window->Appearing) // If currently appearing return; if (window->Hidden) // If was hidden (previous frame) return; if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow) if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window)) return; if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow) if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window)) return; window->DrawList = NULL; window->SkipRefresh = true; } } static void SetWindowActiveForSkipRefresh(ImGuiWindow* window) { window->Active = true; for (ImGuiWindow* child : window->DC.ChildWindows) if (!child->Hidden) { child->Active = child->SkipRefresh = true; SetWindowActiveForSkipRefresh(child); } } // Push a new Dear ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). // You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file. // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned. // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required IM_ASSERT(g.WithinFrameScope); // 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 // Find or create ImGuiWindow* window = FindWindowByName(name); const bool window_just_created = (window == NULL); if (window_just_created) window = CreateNewWindow(name, flags); // [DEBUG] Debug break requested by user if (g.DebugBreakInWindow == window->ID) IM_DEBUG_BREAK(); // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow); // Update the Appearing flag (note: the BeginDocked() path may also set this to true later) bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& 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); } // Update Flags, LastFrameActive, BeginOrderXXX fields const bool window_was_appearing = window->Appearing; if (first_begin_of_the_frame) { UpdateWindowInFocusOrderList(window, window_just_created, flags); window->Appearing = window_just_activated_by_user; if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); window->FlagsPreviousFrame = window->Flags; window->Flags = (ImGuiWindowFlags)flags; window->ChildFlags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0; window->LastFrameActive = current_frame; window->LastTimeActive = (float)g.Time; window->BeginOrderWithinParent = 0; window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++); } else { flags = window->Flags; } // Docking // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1) IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasDock) SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond); if (first_begin_of_the_frame) { const bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL); const bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window); const bool dock_node_was_visible = window->DockNodeIsVisible; const bool dock_tab_was_visible = window->DockTabIsVisible; window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false; if (has_dock_node || new_auto_dock_node) { BeginDocked(window, p_open); flags = window->Flags; if (window->DockIsActive) { IM_ASSERT(window->DockNode != NULL); g.NextWindowData.HasFlags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints } // Amend the Appearing flag if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing) { window->Appearing = true; SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); } } } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); // We allow window memory to be compacted so recreate the base stack when needed. if (window->IDStack.Size == 0) window->IDStack.push_back(window->ID); // Add to stack g.CurrentWindow = window; g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1); ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back(); window_stack_data.Window = window; window_stack_data.ParentLastItemDataBackup = g.LastItemData; window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled); window_stack_data.DisabledOverrideReenableAlphaBackup = 0.0f; ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin); g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin; if (flags & ImGuiWindowFlags_ChildMenu) g.BeginMenuDepth++; // Update ->RootWindow and others pointers (before any possible call to FocusWindow) if (first_begin_of_the_frame) { UpdateWindowParentAndRootLinks(window, flags, parent_window); window->ParentWindowInBeginStack = parent_window_in_stack; // Focus route // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack, // Use for e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798) window->ParentWindowForFocusRoute = (window->RootWindow != window) ? parent_window_in_stack : NULL; if (window->ParentWindowForFocusRoute == NULL && window->DockNode != NULL) if (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockedWindowsInFocusRoute) window->ParentWindowForFocusRoute = window->DockNode->HostWindow; // Override with SetNextWindowClass() field or direct call to SetWindowParentWindowForFocusRoute() if (window->WindowClass.FocusRouteParentWindowId != 0) { window->ParentWindowForFocusRoute = FindWindowByID(window->WindowClass.FocusRouteParentWindowId); IM_ASSERT(window->ParentWindowForFocusRoute != 0); // Invalid value for FocusRouteParentWindowId. } // Inherit SetWindowFontScale() from parent until we fix this system... window->FontWindowScaleParents = parent_window ? parent_window->FontWindowScaleParents * parent_window->FontWindowScale : 1.0f; } // Add to focus scope stack PushFocusScope((window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID); window->NavRootFocusScopeId = g.CurrentFocusScopeId; // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[] if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; popup_ref.Window = window; popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent; g.BeginPopupStack.push_back(popup_ref); window->PopupId = popup_ref.PopupId; } // Process SetNextWindow***() calls // (FIXME: Consider splitting the HasXXX flags into X/Y components) bool window_pos_set_by_api = false; bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) { window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0; if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f) { // May be processed on the next frame if this is our first frame and we are measuring size // FIXME: Look into removing the branch so everything can go through this same code path for consistency. window->SetWindowPosVal = g.NextWindowData.PosVal; window->SetWindowPosPivot = g.NextWindowData.PosPivotVal; window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); } else { SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); } } if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) { window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild() g.NextWindowData.SizeVal.x = window->SizeFull.x; if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) g.NextWindowData.SizeVal.y = window->SizeFull.y; SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); } if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) { if (g.NextWindowData.ScrollVal.x >= 0.0f) { window->ScrollTarget.x = g.NextWindowData.ScrollVal.x; window->ScrollTargetCenterRatio.x = 0.0f; } if (g.NextWindowData.ScrollVal.y >= 0.0f) { window->ScrollTarget.y = g.NextWindowData.ScrollVal.y; window->ScrollTargetCenterRatio.y = 0.0f; } } if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasContentSize) window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal; else if (first_begin_of_the_frame) window->ContentSizeExplicit = ImVec2(0.0f, 0.0f); if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowClass) window->WindowClass = g.NextWindowData.WindowClass; if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasCollapsed) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasFocus) FocusWindow(window); if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); // [EXPERIMENTAL] Skip Refresh mode UpdateWindowSkipRefresh(window); // Nested root windows (typically tooltips) override disabled state if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window) BeginDisabledOverrideReenable(); // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindow = NULL; // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame && !window->SkipRefresh) { // Initialize const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); window->Active = true; window->HasCloseButton = (p_open != NULL); window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); window->IDStack.resize(1); window->DrawList->_ResetForNewFrame(); window->DC.CurrentTableIdx = -1; if (flags & ImGuiWindowFlags_DockNodeHost) { window->DrawList->ChannelsSplit(2); window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); // Render decorations on channel 1 as we will render the backgrounds manually later } // Restore buffer capacity when woken from a compacted state, to avoid if (window->MemoryCompacted) GcAwakeTransientWindowBuffers(window); // 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 ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive)) window_title_visible_elsewhere = true; else if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->WasActive && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using Ctrl+Tab window_title_visible_elsewhere = true; else if (flags & ImGuiWindowFlags_ChildMenu) window_title_visible_elsewhere = true; if ((window_title_visible_elsewhere || window_just_activated_by_user) && !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) CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal); // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because // it has a single usage before this code block and may be set below before it is finally checked. if (window->HiddenFramesCanSkipItems > 0) window->HiddenFramesCanSkipItems--; if (window->HiddenFramesCannotSkipItems > 0) window->HiddenFramesCannotSkipItems--; if (window->HiddenFramesForRenderOnly > 0) window->HiddenFramesForRenderOnly--; // Hide new windows for one frame until they calculate their size if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) window->HiddenFramesCannotSkipItems = 1; // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) { window->HiddenFramesCannotSkipItems = 1; if (flags & ImGuiWindowFlags_AlwaysAutoResize) { if (!window_size_x_set_by_api) window->Size.x = window->SizeFull.x = 0.f; if (!window_size_y_set_by_api) window->Size.y = window->SizeFull.y = 0.f; window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f); } } // SELECT VIEWPORT // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes. WindowSelectViewport(window); SetCurrentViewport(window, window->Viewport); SetCurrentWindow(window); flags = window->Flags; // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style. if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow)) window->WindowBorderSize = style.ChildBorderSize; else window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f) window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f; window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f; window->FontRefSize = g.FontSize; // Lock this to discourage calling window->CalcFontSize() outside of current window. // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible. // Those flags will be altered further down in the function depending on more conditions. bool use_current_size_for_scrollbar_x = window_just_created; bool use_current_size_for_scrollbar_y = window_just_created; if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f) use_current_size_for_scrollbar_x = true; if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252 use_current_size_for_scrollbar_y = true; // Collapse window by double-clicking on title bar // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive) { // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), // so verify that we don't have items over the title bar. ImRect title_bar_rect = window->TitleBarRect(); if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max)) if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner) window->WantCollapseToggle = true; if (window->WantCollapseToggle) { window->Collapsed = !window->Collapsed; if (!window->Collapsed) use_current_size_for_scrollbar_y = true; MarkIniSettingsDirty(window); } } else { window->Collapsed = false; } window->WantCollapseToggle = false; // SIZE // Outer Decoration Sizes // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations). const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes; window->DecoOuterSizeX1 = 0.0f; window->DecoOuterSizeX2 = 0.0f; window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight; window->DecoOuterSizeY2 = 0.0f; window->ScrollbarSizes = ImVec2(0.0f, 0.0f); // Calculate auto-fit size, handle automatic resize // - Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. // - We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. // - Auto-fit may only grow window during the first few frames. { const bool size_auto_fit_x_always = !window_size_x_set_by_api && (flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed; const bool size_auto_fit_y_always = !window_size_y_set_by_api && (flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed; const bool size_auto_fit_x_current = !window_size_x_set_by_api && (window->AutoFitFramesX > 0); const bool size_auto_fit_y_current = !window_size_y_set_by_api && (window->AutoFitFramesY > 0); int size_auto_fit_mask = 0; if (size_auto_fit_x_always || size_auto_fit_x_current) size_auto_fit_mask |= (1 << ImGuiAxis_X); if (size_auto_fit_y_always || size_auto_fit_y_current) size_auto_fit_mask |= (1 << ImGuiAxis_Y); const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, size_auto_fit_mask); const ImVec2 old_size = window->SizeFull; if (size_auto_fit_x_always || size_auto_fit_x_current) { if (size_auto_fit_x_always) window->SizeFull.x = size_auto_fit.x; else window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; use_current_size_for_scrollbar_x = true; } if (size_auto_fit_y_always || size_auto_fit_y_current) { if (size_auto_fit_y_always) window->SizeFull.y = size_auto_fit.y; else window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; use_current_size_for_scrollbar_y = true; } if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y) MarkIniSettingsDirty(window); } // Apply minimum/maximum window size constraints and final size window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull); window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; // POSITION // Popup latch its initial position, will position itself when it appears next frame if (window_just_activated_by_user) { window->AutoPosLastDirection = ImGuiDir_None; if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos() window->Pos = g.BeginPopupStack.back().OpenPopupPos; } // Position child window if (flags & ImGuiWindowFlags_ChildWindow) { 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; } const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); if (window_pos_with_pivot) SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) window->Pos = FindBestWindowPosForPopup(window); else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) window->Pos = FindBestWindowPosForPopup(window); else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->Pos = FindBestWindowPosForPopup(window); // Late create viewport if we don't fit within our current host viewport. if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_IsMinimized)) if (!window->Viewport->GetMainRect().Contains(window->Rect())) { // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); // FIXME-DPI //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong SetCurrentViewport(window, window->Viewport); SetCurrentWindow(window); } if (window->ViewportOwned) WindowSyncOwnedViewport(window, parent_window_in_stack); // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. ImRect viewport_rect(window->Viewport->GetMainRect()); ImRect viewport_work_rect(window->Viewport->GetWorkRect()); ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding); // Clamp position/size so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. // FIXME: Similar to code in GetWindowAllowedExtentRect() if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow)) { if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) { ClampWindowPos(window, visibility_rect); } else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0) { if (g.MovingWindow != NULL && window->RootWindowDockTree == g.MovingWindow->RootWindowDockTree) { // While moving windows we allow them to straddle monitors (#7299, #3071) visibility_rect = g.PlatformMonitorsFullWorkRect; } else { // When not moving ensure visible in its monitor // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport. const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(window->Viewport); visibility_rect = ImRect(monitor->WorkPos, monitor->WorkPos + monitor->WorkSize); } visibility_rect.Expand(-visibility_padding); ClampWindowPos(window, visibility_rect); } } window->Pos = ImTrunc(window->Pos); // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) // Large values tend to lead to variety of artifacts and are not recommended. if ((flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive) window->WindowRounding = style.ChildRounding; else if (window->RootWindowDockTree->ViewportOwned) window->WindowRounding = 0.0f; else window->WindowRounding = ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts. //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar)) // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f); // 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_Popup) want_focus = true; else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip)) want_focus = true; } // [Test Engine] Register whole window in the item system (before submitting further decorations) #ifdef IMGUI_ENABLE_TEST_ENGINE if (g.TestEngineHookItems) { IM_ASSERT(window->IDStack.Size == 1); window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself. window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL); IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0); window->IDStack.Size = 1; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; } #endif // Decide if we are going to handle borders and resize grips // 'window->SkipItems' is not updated yet so for child windows we rely on ParentWindow to avoid submitting decorations. (#8815) // Whenever we add support for full decorated child windows we will likely make this logic more general. bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive); if ((flags & ImGuiWindowFlags_ChildWindow) && window->ParentWindow->SkipItems) handle_borders_and_resize_grips = false; // Handle manual resize: Resize Grips, Borders, Gamepad // Child windows can only be resized when they have the flags set. The resize grip allows resizing in both directions, so it should appear only if both flags are set. int border_hovered = -1, border_held = -1; ImU32 resize_grip_col[4] = {}; int resize_grip_count; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) resize_grip_count = ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY)) ? 1 : 0; else resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); if (handle_borders_and_resize_grips && !window->Collapsed) if (int auto_fit_mask = UpdateWindowManualResize(window, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) { if (auto_fit_mask & (1 << ImGuiAxis_X)) use_current_size_for_scrollbar_x = true; if (auto_fit_mask & (1 << ImGuiAxis_Y)) use_current_size_for_scrollbar_y = true; } window->ResizeBorderHovered = (signed char)border_hovered; window->ResizeBorderHeld = (signed char)border_held; // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either) if (window->ViewportOwned) { if (!window->Viewport->PlatformRequestMove) window->Viewport->Pos = window->Pos; if (!window->Viewport->PlatformRequestResize) window->Viewport->Size = window->Size; window->Viewport->UpdateWorkRect(); viewport_rect = window->Viewport->GetMainRect(); } // Save last known viewport position within the window itself (so it can be saved in .ini file and restored) window->ViewportPos = window->Viewport->Pos; // SCROLLBAR VISIBILITY // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). if (!window->Collapsed) { // When reading the current size we need to read it after size constraints have been applied. // Intentionally use previous frame values for InnerRect and ScrollbarSizes. // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet. ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)); ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame; ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; bool scrollbar_x_prev = window->ScrollbarX; //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons? window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); // Track when ScrollbarX visibility keeps toggling, which is a sign of a feedback loop, and stabilize by enforcing visibility (#3285, #8488) // (Feedback loops of this sort can manifest in various situations, but combining horizontal + vertical scrollbar + using a clipper with varying width items is one frequent cause. // The better solution is to, either (1) enforce visibility by using ImGuiWindowFlags_AlwaysHorizontalScrollbar or (2) declare stable contents width with SetNextWindowContentSize(), if you can compute it) window->ScrollbarXStabilizeToggledHistory <<= 1; window->ScrollbarXStabilizeToggledHistory |= (scrollbar_x_prev != window->ScrollbarX) ? 0x01 : 0x00; const bool scrollbar_x_stabilize = (window->ScrollbarXStabilizeToggledHistory != 0) && ImCountSetBits(window->ScrollbarXStabilizeToggledHistory) >= 4; // 4 == half of bits in our U8 history. if (scrollbar_x_stabilize) window->ScrollbarX = true; //if (scrollbar_x_stabilize && !window->ScrollbarXStabilizeEnabled) // IMGUI_DEBUG_LOG("[scroll] Stabilize ScrollbarX for Window '%s'\n", window->Name); window->ScrollbarXStabilizeEnabled = scrollbar_x_stabilize; if (window->ScrollbarX && !window->ScrollbarY) window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); // Amend the partially filled window->DecorationXXX values. window->DecoOuterSizeX2 += window->ScrollbarSizes.x; window->DecoOuterSizeY2 += window->ScrollbarSizes.y; } // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) // Update various regions. Variables they depend on should be set above in this function. // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame. // Outer rectangle // Not affected by window border size. Used by: // - FindHoveredWindow() (w/ extra padding when border resize is enabled) // - Begin() initial clipping rect for drawing window background and borders. // - Begin() clipping whole child const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect; const ImRect outer_rect = window->Rect(); const ImRect title_bar_rect = window->TitleBarRect(); window->OuterRectClipped = outer_rect; if (window->DockIsActive) window->OuterRectClipped.Min.y += window->TitleBarHeight; window->OuterRectClipped.ClipWith(host_rect); // Inner rectangle // Not affected by window border size. Used by: // - InnerClipRect // - ScrollToRectEx() // - NavUpdatePageUpPageDown() // - Scrollbar() window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1; window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1; window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2; window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2; // Inner clipping rectangle. // - Extend a outside of normal work region up to borders. // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space. // - It also makes clipped items be more noticeable. // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312 // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. // Affected by window/frame border size. Used by: // - Begin() initial clip rect float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); // Try to match the fact that our border is drawn centered over the window rectangle, rather than inner. // This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize, // see e.g #7887 #7888, but may do after we move the window border to become an inner border (and then we can remove the 0.5f here). window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f); window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size * 0.5f); window->InnerClipRect.Max.x = ImFloor(window->InnerRect.Max.x - window->WindowBorderSize * 0.5f); window->InnerClipRect.Max.y = ImFloor(window->InnerRect.Max.y - window->WindowBorderSize * 0.5f); window->InnerClipRect.ClipWithFull(host_rect); // SCROLLING // Lock down maximum scrolling // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate // for right/bottom aligned items without creating a scrollbar. window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth()); window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight()); // Apply scrolling window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f; // DRAWING // Setup draw list and outer clipping rectangle IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0); window->DrawList->PushTexture(g.Font->OwnerAtlas->TexRef); PushClipRect(host_rect.Min, host_rect.Max, false); // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible; if (is_undocked_or_docked_visible) { bool render_decorations_in_parent = false; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) { // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here) // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL; bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false; bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0); if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping) render_decorations_in_parent = true; } if (render_decorations_in_parent) window->DrawList = parent_window->DrawList; // Handle title bar, scrollbar, resize grips and resize borders const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode))); RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size); if (render_decorations_in_parent) window->DrawList = &window->DrawListInst; } // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) // Work rectangle. // Affected by window padding and border size. Used by: // - Columns() for right-most edge // - TreeNode(), CollapsingHeader() for right-most edge // - BeginTabBar() for right-most edge const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2))); const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2))); window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y; window->ParentWorkRect = window->WorkRect; // [LEGACY] Content Region // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling. // Used by: // - Mouse wheel scrolling + many other things window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1; window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1; window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2))); window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2))); // Setup drawing context // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x; window->DC.GroupOffset.x = 0.0f; window->DC.ColumnsOffset.x = 0.0f; // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount. // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64. double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x; double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1; window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y); window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y)); window->DC.CursorPos = window->DC.CursorStartPos; window->DC.CursorPosPrevLine = window->DC.CursorPos; window->DC.CursorMaxPos = window->DC.CursorStartPos; window->DC.IdealMaxPos = window->DC.CursorStartPos; window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.IsSameLine = window->DC.IsSetPos = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; window->DC.NavLayersActiveMaskNext = 0x00; window->DC.NavIsScrollPushableX = true; window->DC.NavHideHighlightOneFrame = false; window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f); window->DC.MenuBarAppending = false; window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user); window->DC.TreeDepth = 0; window->DC.TreeHasStackDataDepthMask = window->DC.TreeRecordsClippedNodesY2Mask = 0x00; window->DC.ChildWindows.resize(0); window->DC.StateStorage = &window->StateStorage; window->DC.CurrentColumns = NULL; window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; // Default item width. Make it proportional to window size if window manually resizes const bool is_resizable_window = (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)); if (is_resizable_window) window->DC.ItemWidthDefault = ImTrunc(window->Size.x * 0.65f); else window->DC.ItemWidthDefault = ImTrunc(g.FontSize * 16.0f); window->DC.ItemWidth = window->DC.ItemWidthDefault; window->DC.ItemWidthStack.resize(0); window->DC.TextWrapPos = -1.0f; // Disabled window->DC.TextWrapPosStack.resize(0); if (flags & ImGuiWindowFlags_Modal) window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg)); if (window->AutoFitFramesX > 0) window->AutoFitFramesX--; if (window->AutoFitFramesY > 0) window->AutoFitFramesY--; // Clear SetNextWindowXXX data (can aim to move this higher in the function) g.NextWindowData.ClearFlags(); // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) // We ImGuiFocusRequestFlags_UnlessBelowModal to: // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed. // - Position window behind the modal that is not a begin-parent of this window. if (want_focus) FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal); if (want_focus && window == g.NavWindow) NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls // Close requested by platform window (apply to all windows in this viewport) // FIXME: Investigate removing the 'window->Viewport != GetMainViewport()' test, which seems superfluous. if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) if (window->DockNode == NULL || (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockSpace) == 0) { IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' closed by PlatformRequestClose\n", window->Name); *p_open = false; g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on Alt-F4 so we disable Alt for menu toggle. False positive not an issue. // FIXME-NAV: Try removing. } // Pressing Ctrl+C copy window content into the clipboard // [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope. // [EXPERIMENTAL] Text outputs has many issues. if (g.IO.ConfigWindowsCopyContentsWithCtrlC) if (g.NavWindow && g.NavWindow->RootWindow == window && g.ActiveId == 0 && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C)) LogToClipboard(0); // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open); else if (!(flags & ImGuiWindowFlags_NoTitleBar) && window->DockIsActive) LogText("%.*s\n", (int)(FindRenderedTextEnd(window->Name) - window->Name), window->Name); // Clear hit test shape every frame window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; if (flags & ImGuiWindowFlags_Tooltip) g.TooltipPreviousWindow = window; if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) { // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it. if (g.MovingWindow == window && (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0) BeginDockableDragDropSource(window); // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead. if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking)) if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window) if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost)) BeginDockableDragDropTarget(window); } // Set default BgClickFlags // This is set at the end of this function, so UpdateWindowManualResize()/ClampWindowPos() may use last-frame value if overridden by user code. // FIXME: The general intent is that we will later expose config options to default to enable scrolling + select scrolling mouse button. window->BgClickFlags = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->BgClickFlags : (g.IO.ConfigWindowsMoveFromTitleBarOnly ? ImGuiWindowBgClickFlags_None : ImGuiWindowBgClickFlags_Move); // We fill last item data based on Title Bar/Tab, 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.WindowItemStatusFlags = ImGuiItemStatusFlags_None; window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; SetLastItemDataForWindow(window, title_bar_rect); // [DEBUG] #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId)) DebugLocateItemResolveWithLastItem(); #endif // [Test Engine] Register title bar / tab with MoveId. #ifdef IMGUI_ENABLE_TEST_ENGINE if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) { window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData); window->DC.NavLayerCurrent = ImGuiNavLayer_Main; } #endif } else { // Skip refresh always mark active if (window->SkipRefresh) SetWindowActiveForSkipRefresh(window); // Append SetCurrentViewport(window, window->Viewport); SetCurrentWindow(window); g.NextWindowData.ClearFlags(); SetLastItemDataForWindow(window, window->TitleBarRect()); } if (!(flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh) PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused) window->WriteAccessed = false; window->BeginCount++; // Update visibility if (first_begin_of_the_frame && !window->SkipRefresh) { // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems. // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. // This is analogous to regular windows being hidden from one frame. // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed. if (window->DockIsActive && !window->DockTabIsVisible) { if (window->LastFrameJustFocused == g.FrameCount) window->HiddenFramesCannotSkipItems = 1; else window->HiddenFramesCanSkipItems = 1; } if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) { // 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 || window->DockIsActive); const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); if (!g.LogEnabled && !nav_request) if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) { if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) window->HiddenFramesCannotSkipItems = 1; else window->HiddenFramesCanSkipItems = 1; } // Hide along with parent or if parent is collapsed if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) window->HiddenFramesCanSkipItems = 1; if (parent_window && parent_window->HiddenFramesCannotSkipItems > 0) window->HiddenFramesCannotSkipItems = 1; } // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point) if (style.Alpha <= 0.0f) window->HiddenFramesCanSkipItems = 1; // Update the Hidden flag bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0); // Disable inputs for requested number of frames if (window->DisableInputsFrames > 0) { window->DisableInputsFrames--; window->Flags |= ImGuiWindowFlags_NoInputs; } // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; if (window->Collapsed || !window->Active || hidden_regular) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; // Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value. if (window->SkipItems) window->DC.NavLayersActiveMaskNext = window->DC.NavLayersActiveMask; // Sanity check: there are two spots which can set Appearing = true // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert. if (window->SkipItems && !window->Appearing) IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177 } else if (first_begin_of_the_frame) { // Skip refresh mode window->SkipItems = true; } // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors. // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing) #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (!window->IsFallbackWindow) if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size)) { if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; } if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; } return false; } #endif return !window->SkipItems; } void ImGui::End() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; // Error checking: verify that user hasn't called End() too many times! if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow) { IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!"); return; } ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back(); // Error checking: verify that user doesn't directly call End() on a child window. if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive) IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, "Must call EndChild() and not End()!"); // Close anything that is open if (window->DC.CurrentColumns) EndColumns(); if (!(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh) // Pop inner window clip rectangle PopClipRect(); PopFocusScope(); if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window) EndDisabledOverrideReenable(); if (window->SkipRefresh) { IM_ASSERT(window->DrawList == NULL); window->DrawList = &window->DrawListInst; } // Stop logging if (g.LogWindow == window) // FIXME: add more options for scope of logging LogFinish(); if (window->DC.IsSetPos) ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); // Docking: report contents sizes to parent to allow for auto-resize if (window->DockNode && window->DockTabIsVisible) if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCK host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding; // Pop from window stack g.LastItemData = window_stack_data.ParentLastItemDataBackup; if (window->Flags & ImGuiWindowFlags_ChildMenu) g.BeginMenuDepth--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); // Error handling, state recovery if (g.IO.ConfigErrorRecovery) ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin); g.CurrentWindowStack.pop_back(); SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); if (g.CurrentWindow) SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport); } void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) { ImGuiContext& g = *GImGui; ImGuiItemFlags item_flags = g.CurrentItemFlags; IM_ASSERT(item_flags == g.ItemFlagsStack.back()); if (enabled) item_flags |= option; else item_flags &= ~option; g.CurrentItemFlags = item_flags; g.ItemFlagsStack.push_back(item_flags); } void ImGui::PopItemFlag() { ImGuiContext& g = *GImGui; IM_ASSERT_USER_ERROR_RET(g.ItemFlagsStack.Size > 1, "Calling PopItemFlag() too many times!"); g.ItemFlagsStack.pop_back(); g.CurrentItemFlags = g.ItemFlagsStack.back(); } // BeginDisabled()/EndDisabled() // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. // - Feedback welcome at https://github.com/ocornut/imgui/issues/211 // - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions. // (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls) // - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED. void ImGui::BeginDisabled(bool disabled) { ImGuiContext& g = *GImGui; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; if (!was_disabled && disabled) { g.DisabledAlphaBackup = g.Style.Alpha; g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); } if (was_disabled || disabled) g.CurrentItemFlags |= ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize? g.DisabledStackSize++; } void ImGui::EndDisabled() { ImGuiContext& g = *GImGui; IM_ASSERT_USER_ERROR_RET(g.DisabledStackSize > 0, "Calling EndDisabled() too many times!"); g.DisabledStackSize--; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; //PopItemFlag(); g.ItemFlagsStack.pop_back(); g.CurrentItemFlags = g.ItemFlagsStack.back(); if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0) g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } // Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name. // Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal. // The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled(). void ImGui::BeginDisabledOverrideReenable() { ImGuiContext& g = *GImGui; IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled); g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup = g.Style.Alpha; g.Style.Alpha = g.DisabledAlphaBackup; g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); g.DisabledStackSize++; } void ImGui::EndDisabledOverrideReenable() { ImGuiContext& g = *GImGui; IM_ASSERT(g.DisabledStackSize > 0); g.DisabledStackSize--; g.ItemFlagsStack.pop_back(); g.CurrentItemFlags = g.ItemFlagsStack.back(); g.Style.Alpha = g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup; } // ATTENTION THIS IS IN LEGACY LOCAL SPACE. void ImGui::PushTextWrapPos(float wrap_local_pos_x) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos); window->DC.TextWrapPos = wrap_local_pos_x; } void ImGui::PopTextWrapPos() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT_USER_ERROR_RET(window->DC.TextWrapPosStack.Size > 0, "Calling PopTextWrapPos() too many times!"); window->DC.TextWrapPos = window->DC.TextWrapPosStack.back(); window->DC.TextWrapPosStack.pop_back(); } static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy) { ImGuiWindow* last_window = NULL; while (last_window != window) { last_window = window; window = window->RootWindow; if (popup_hierarchy) window = window->RootWindowPopupTree; if (dock_hierarchy) window = window->RootWindowDockTree; } return window; } bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy) { ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy); if (window_root == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; if (window == window_root) // end of chain return false; window = window->ParentWindow; } return false; } bool ImGui::IsWindowInBeginStack(ImGuiWindow* window) { ImGuiContext& g = *GImGui; for (int n = g.CurrentWindowStack.Size - 1; n >= 0; n--) if (g.CurrentWindowStack[n].Window == window) return true; return false; } bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent) { if (window->RootWindow == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; window = window->ParentWindowInBeginStack; } return false; } bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below); if (display_layer_delta != 0) return display_layer_delta > 0; for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* candidate_window = g.Windows[i]; if (candidate_window == potential_above) return true; if (candidate_window == potential_below) return false; } return false; } // Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. // IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, // you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! // Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { ImGuiContext& g = *GImGui; IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, "Invalid flags for IsWindowHovered()!"); ImGuiWindow* ref_window = g.HoveredWindow; ImGuiWindow* cur_window = g.CurrentWindow; if (ref_window == NULL) return false; if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { IM_ASSERT(cur_window); // Not inside a Begin()/End() const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy); bool result; if (flags & ImGuiHoveredFlags_ChildWindows) result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy); else result = (ref_window == cur_window); if (!result) return false; } if (!IsWindowContentHoverable(ref_window, flags)) return false; if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; // When changing hovered window we requires a bit of stationary delay before activating hover timer. // FIXME: We don't support delay other than stationary one for now, other delay would need a way // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache. // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow. if (flags & ImGuiHoveredFlags_ForTooltip) flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse); if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID) return false; return true; } ImGuiID ImGui::GetWindowDockID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockId; } bool ImGui::IsWindowDocked() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockIsActive; } float ImGui::GetWindowWidth() { ImGuiWindow* window = GImGui->CurrentWindow; return window->Size.x; } float ImGui::GetWindowHeight() { ImGuiWindow* window = GImGui->CurrentWindow; return window->Size.y; } ImVec2 ImGui::GetWindowPos() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; return window->Pos; } 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) return; IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX); // Set const ImVec2 old_pos = window->Pos; window->Pos = ImTrunc(pos); ImVec2 offset = window->Pos - old_pos; if (offset.x == 0.0f && offset.y == 0.0f) return; MarkIniSettingsDirty(window); // FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here. window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. window->DC.IdealMaxPos += offset; window->DC.CursorStartPos += offset; } void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond) { ImGuiWindow* window = GetCurrentWindowRead(); SetWindowPos(window, pos, cond); } void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond) { if (ImGuiWindow* window = FindWindowByName(name)) SetWindowPos(window, pos, cond); } ImVec2 ImGui::GetWindowSize() { ImGuiWindow* window = GetCurrentWindowRead(); return window->Size; } 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) return; IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize) if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0) window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0; if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0) window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0; // Set ImVec2 old_size = window->SizeFull; if (size.x <= 0.0f) window->AutoFitOnlyGrows = false; else window->SizeFull.x = IM_TRUNC(size.x); if (size.y <= 0.0f) window->AutoFitOnlyGrows = false; else window->SizeFull.y = IM_TRUNC(size.y); if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y) MarkIniSettingsDirty(window); } void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond) { SetWindowSize(GImGui->CurrentWindow, size, cond); } void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond) { if (ImGuiWindow* window = FindWindowByName(name)) SetWindowSize(window, size, 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) return; window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); // Queue applying in Begin() if (window->WantCollapseToggle) window->Collapsed ^= 1; window->WantCollapseToggle = (window->Collapsed != collapsed); } void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size) { IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters window->HitTestHoleSize = ImVec2ih(size); window->HitTestHoleOffset = ImVec2ih(pos - window->Pos); } void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window) { window->Hidden = window->SkipItems = true; window->HiddenFramesCanSkipItems = 1; } void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond) { SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); } bool ImGui::IsWindowCollapsed() { ImGuiWindow* window = GetCurrentWindowRead(); return window->Collapsed; } bool ImGui::IsWindowAppearing() { ImGuiWindow* window = GetCurrentWindowRead(); return window->Appearing; } void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond) { if (ImGuiWindow* window = FindWindowByName(name)) SetWindowCollapsed(window, collapsed, cond); } void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot) { ImGuiContext& g = *GImGui; IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasPos; g.NextWindowData.PosVal = pos; g.NextWindowData.PosPivotVal = pivot; g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; g.NextWindowData.PosUndock = true; } void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) { ImGuiContext& g = *GImGui; IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSize; g.NextWindowData.SizeVal = size; g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; } // For each axis: // - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width. // - Use -1 for both min and max of same axis to preserve current size which itself is a constraint. // - See "Demo->Examples->Constrained-resizing window" for examples. void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data) { ImGuiContext& g = *GImGui; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSizeConstraint; g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max); g.NextWindowData.SizeCallback = custom_callback; g.NextWindowData.SizeCallbackUserData = custom_callback_user_data; } // Content size = inner scrollable rectangle, padded with WindowPadding. // SetNextWindowContentSize(ImVec2(100,100)) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. void ImGui::SetNextWindowContentSize(const ImVec2& size) { ImGuiContext& g = *GImGui; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasContentSize; g.NextWindowData.ContentSizeVal = ImTrunc(size); } void ImGui::SetNextWindowScroll(const ImVec2& scroll) { ImGuiContext& g = *GImGui; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasScroll; g.NextWindowData.ScrollVal = scroll; } void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond) { ImGuiContext& g = *GImGui; IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasCollapsed; g.NextWindowData.CollapsedVal = collapsed; g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always; } void ImGui::SetNextWindowBgAlpha(float alpha) { ImGuiContext& g = *GImGui; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasBgAlpha; g.NextWindowData.BgAlphaVal = alpha; } void ImGui::SetNextWindowViewport(ImGuiID id) { ImGuiContext& g = *GImGui; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasViewport; g.NextWindowData.ViewportId = id; } void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond) { ImGuiContext& g = *GImGui; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasDock; g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always; g.NextWindowData.DockId = id; } void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class) { ImGuiContext& g = *GImGui; IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasWindowClass; g.NextWindowData.WindowClass = *window_class; } // This is experimental and meant to be a toy for exploring a future/wider range of features. void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags) { ImGuiContext& g = *GImGui; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasRefreshPolicy; g.NextWindowData.RefreshFlagsVal = flags; } ImDrawList* ImGui::GetWindowDrawList() { ImGuiWindow* window = GetCurrentWindow(); return window->DrawList; } float ImGui::GetWindowDpiScale() { ImGuiContext& g = *GImGui; return g.CurrentDpiScale; } ImGuiViewport* ImGui::GetWindowViewport() { ImGuiContext& g = *GImGui; IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport); return g.CurrentViewport; } ImFont* ImGui::GetFont() { return GImGui->Font; } ImFontBaked* ImGui::GetFontBaked() { return GImGui->FontBaked; } // Get current font size (= height in pixels) of current font, with global scale factors applied. // - Use style.FontSizeBase to get value before global scale factors. // - recap: ImGui::GetFontSize() == style.FontSizeBase * (style.FontScaleMain * style.FontScaleDpi * other_scaling_factors) float ImGui::GetFontSize() { return GImGui->FontSize; } ImVec2 ImGui::GetFontTexUvWhitePixel() { return GImGui->DrawListSharedData.TexUvWhitePixel; } // Prefer using PushFont(NULL, style.FontSizeBase * factor), or use style.FontScaleMain to scale all windows. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS void ImGui::SetWindowFontScale(float scale) { IM_ASSERT(scale > 0.0f); ImGuiWindow* window = GetCurrentWindow(); window->FontWindowScale = scale; UpdateCurrentFontSize(0.0f); } #endif void ImGui::PushFocusScope(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiFocusScopeData data; data.ID = id; data.WindowID = g.CurrentWindow->ID; g.FocusScopeStack.push_back(data); g.CurrentFocusScopeId = id; } void ImGui::PopFocusScope() { ImGuiContext& g = *GImGui; IM_ASSERT_USER_ERROR_RET(g.FocusScopeStack.Size > g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack, "Calling PopFocusScope() too many times!"); g.FocusScopeStack.pop_back(); g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0; } void ImGui::SetNavFocusScope(ImGuiID focus_scope_id) { ImGuiContext& g = *GImGui; g.NavFocusScopeId = focus_scope_id; g.NavFocusRoute.resize(0); // Invalidate if (focus_scope_id == 0) return; IM_ASSERT(g.NavWindow != NULL); // Store current path (in reverse order) if (focus_scope_id == g.CurrentFocusScopeId) { // Top of focus stack contains local focus scopes inside current window for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--) g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]); } else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId) g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID }); else return; // Then follow on manually set ParentWindowForFocusRoute field (#6798) for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute) g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID }); IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3 } // Focus = move navigation cursor, set scrolling, set focus window. void ImGui::FocusItem() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name); if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this? { IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n"); return; } ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; SetNavWindow(window); NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags); NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); } void ImGui::ActivateItemByID(ImGuiID id) { ImGuiContext& g = *GImGui; g.NavNextActivateId = id; g.NavNextActivateFlags = ImGuiActivateFlags_None; } // Note: this will likely be called ActivateItem() once we rework our Focus/Activation system! // But ActivateItem() should function without altering scroll/focus? void ImGui::SetKeyboardFocusHere(int offset) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(offset >= -1); // -1 is allowed but not below IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); // It makes sense in the vast majority of cases to never interrupt a drag and drop. // When we refactor this function into ActivateItem() we may want to make this an option. // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but // is also automatically dropped in the event g.ActiveId is stolen. if (g.DragDropActive || g.MovingWindow != NULL) { IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n"); return; } SetNavWindow(window); ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. if (offset == -1) { NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); } else { g.NavTabbingDir = 1; g.NavTabbingCounter = offset + 1; } } void ImGui::SetItemDefaultFocus() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent) return; g.NavInitRequest = false; NavApplyItemToResult(&g.NavInitResult); NavUpdateAnyRequestFlag(); // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll) if (!window->ClipRect.Contains(g.LastItemData.Rect)) ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None); } void ImGui::SetStateStorage(ImGuiStorage* tree) { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.StateStorage = tree ? tree : &window->StateStorage; } ImGuiStorage* ImGui::GetStateStorage() { ImGuiWindow* window = GImGui->CurrentWindow; return window->DC.StateStorage; } bool ImGui::IsRectVisible(const ImVec2& size) { 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 = GImGui->CurrentWindow; return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); } //----------------------------------------------------------------------------- // [SECTION] FONTS, TEXTURES //----------------------------------------------------------------------------- // Most of the relevant font logic is in imgui_draw.cpp. // Those are high-level support functions. //----------------------------------------------------------------------------- // - UpdateTexturesNewFrame() [Internal] // - UpdateTexturesEndFrame() [Internal] // - UpdateFontsNewFrame() [Internal] // - UpdateFontsEndFrame() [Internal] // - GetDefaultFont() [Internal] // - RegisterUserTexture() [Internal] // - UnregisterUserTexture() [Internal] // - RegisterFontAtlas() [Internal] // - UnregisterFontAtlas() [Internal] // - SetCurrentFont() [Internal] // - UpdateCurrentFontSize() [Internal] // - SetFontRasterizerDensity() [Internal] // - PushFont() // - PopFont() //----------------------------------------------------------------------------- static void ImGui::UpdateTexturesNewFrame() { // Cannot update every atlases based on atlas's FrameCount < g.FrameCount, because an atlas may be shared by multiple contexts with different frame count. ImGuiContext& g = *GImGui; const bool has_textures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0; for (ImFontAtlas* atlas : g.FontAtlases) { if (atlas->OwnerContext == &g) { ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures); } else { // (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it. // Otherwise, calling ImGui::CreateContext() without parameter will create an atlas owned by the context. // (2) If you have multiple font atlases, make sure the 'atlas->RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that. // (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags_RendererHasTextures. IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1); IM_ASSERT(atlas->RendererHasTextures == has_textures); } } } // Build a single texture list static void ImGui::UpdateTexturesEndFrame() { ImGuiContext& g = *GImGui; g.PlatformIO.Textures.resize(0); for (ImFontAtlas* atlas : g.FontAtlases) for (ImTextureData* tex : atlas->TexList) { // We provide this information so backends can decide whether to destroy textures. // This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized. tex->RefCount = (unsigned short)atlas->RefCount; g.PlatformIO.Textures.push_back(tex); } for (ImTextureData* tex : g.UserTextures) g.PlatformIO.Textures.push_back(tex); } void ImGui::UpdateFontsNewFrame() { ImGuiContext& g = *GImGui; if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) for (ImFontAtlas* atlas : g.FontAtlases) atlas->Locked = true; if (g.Style._NextFrameFontSizeBase != 0.0f) { g.Style.FontSizeBase = g.Style._NextFrameFontSizeBase; g.Style._NextFrameFontSizeBase = 0.0f; } // Apply default font size the first time ImFont* font = ImGui::GetDefaultFont(); if (g.Style.FontSizeBase <= 0.0f) g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE_BASE); // Set initial font g.Font = font; g.FontSizeBase = g.Style.FontSizeBase; g.FontSize = 0.0f; ImFontStackData font_stack_data = { font, g.Style.FontSizeBase, g.Style.FontSizeBase }; // <--- Will restore FontSize SetCurrentFont(font_stack_data.Font, font_stack_data.FontSizeBeforeScaling, 0.0f); // <--- but use 0.0f to enable scale g.FontStack.push_back(font_stack_data); IM_ASSERT(g.Font->IsLoaded()); } void ImGui::UpdateFontsEndFrame() { PopFont(); } ImFont* ImGui::GetDefaultFont() { ImGuiContext& g = *GImGui; ImFontAtlas* atlas = g.IO.Fonts; if (atlas->Builder == NULL || atlas->Fonts.Size == 0) ImFontAtlasBuildMain(atlas); return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0]; } // EXPERIMENTAL: DO NOT USE YET. void ImGui::RegisterUserTexture(ImTextureData* tex) { ImGuiContext& g = *GImGui; tex->RefCount++; g.UserTextures.push_back(tex); } void ImGui::UnregisterUserTexture(ImTextureData* tex) { ImGuiContext& g = *GImGui; IM_ASSERT(tex->RefCount > 0); tex->RefCount--; g.UserTextures.find_erase(tex); } void ImGui::RegisterFontAtlas(ImFontAtlas* atlas) { ImGuiContext& g = *GImGui; if (g.FontAtlases.Size == 0) IM_ASSERT(atlas == g.IO.Fonts); atlas->RefCount++; g.FontAtlases.push_back(atlas); ImFontAtlasAddDrawListSharedData(atlas, &g.DrawListSharedData); for (ImTextureData* tex : atlas->TexList) tex->RefCount = (unsigned short)atlas->RefCount; } void ImGui::UnregisterFontAtlas(ImFontAtlas* atlas) { ImGuiContext& g = *GImGui; IM_ASSERT(atlas->RefCount > 0); ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData); g.FontAtlases.find_erase(atlas); atlas->RefCount--; for (ImTextureData* tex : atlas->TexList) tex->RefCount = (unsigned short)atlas->RefCount; } // Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList. // - Whereas ImDrawList::PushTexture()/PopTexture() is not to be used across Begin() calls. // - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did... // - Some code paths never really fully worked with multiple atlas textures. // - The right-ish solution may be to remove _SetTexture() and make AddText/RenderText lazily call PushTexture()/PopTexture() // the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem // because we have a concrete need and a test bed for multiple atlas textures. // FIXME-NEWATLAS-V2: perhaps we can now leverage ImFontAtlasUpdateDrawListsTextures() ? void ImGui::SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling) { ImGuiContext& g = *GImGui; g.Font = font; g.FontSizeBase = font_size_before_scaling; UpdateCurrentFontSize(font_size_after_scaling); if (font != NULL) { IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(font->Scale > 0.0f); #endif ImFontAtlas* atlas = font->OwnerAtlas; g.DrawListSharedData.FontAtlas = atlas; g.DrawListSharedData.Font = font; ImFontAtlasUpdateDrawListsSharedData(atlas); if (g.CurrentWindow != NULL) g.CurrentWindow->DrawList->_SetTexture(atlas->TexRef); } } void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; g.Style.FontSizeBase = g.FontSizeBase; // Restoring is pretty much only used by PopFont() float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f; if (final_size == 0.0f) { final_size = g.FontSizeBase; // Global scale factors final_size *= g.Style.FontScaleMain; // Main global scale factor final_size *= g.Style.FontScaleDpi; // Per-monitor/viewport DPI scale factor (in docking branch: automatically updated when io.ConfigDpiScaleFonts is enabled). // Window scale (mostly obsolete now) if (window != NULL) final_size *= window->FontWindowScale; // Legacy scale factors #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS final_size *= g.IO.FontGlobalScale; // Use style.FontScaleMain instead! if (g.Font != NULL) final_size *= g.Font->Scale; // Was never really useful. #endif } // Round font size // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet. // - We may support it better later and remove this rounding. final_size = GetRoundedFontSize(final_size); final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX); if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)) g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity; g.FontSize = final_size; g.DrawListSharedData.FontSize = g.FontSize; // Early out to avoid hidden window keeping bakes referenced and out of GC reach. // - However this leave a pretty subtle and damning error surface area if g.FontBaked was mismatching. // Probably needs to be reevaluated into e.g. setting g.FontBaked = nullptr to mark it as dirty. // - Note that 'PushFont(); Begin(); End(); PopFont()' from within any collapsed window is not compromised, because Begin() calls SetCurrentWindow()->...->UpdateCurrentSize() if (window != NULL && window->SkipItems) { ImGuiTable* table = g.CurrentTable; const bool allow_early_out = table == NULL || (table->CurrentColumn != -1 && table->Columns[table->CurrentColumn].IsSkipItems == false); // See 8465#issuecomment-2951509561 and #8865. Ideally the SkipItems=true in tables would be amended with extra data. if (allow_early_out) return; } g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size) : NULL; g.FontBakedScale = (g.Font != NULL && window != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f; g.DrawListSharedData.FontScale = g.FontBakedScale; if (g.Font) { ImFontAtlas* atlas = g.Font->OwnerAtlas; g.DrawListSharedData.ShadowRectIds = &atlas->ShadowRectIds[0]; g.DrawListSharedData.ShadowRectUvs = &atlas->ShadowRectUvs[0]; } } // Exposed in case user may want to override setting density. // IMPORTANT: Begin()/End() is overriding density. Be considerate of this you change it. void ImGui::SetFontRasterizerDensity(float rasterizer_density) { ImGuiContext& g = *GImGui; IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures); if (g.FontRasterizerDensity == rasterizer_density) return; g.FontRasterizerDensity = rasterizer_density; UpdateCurrentFontSize(0.0f); } // If you want to scale an existing font size! Read comments in imgui.h! void ImGui::PushFont(ImFont* font, float font_size_base) { ImGuiContext& g = *GImGui; if (font == NULL) // Before 1.92 (June 2025), PushFont(NULL) == PushFont(GetDefaultFont()) font = g.Font; IM_ASSERT(font != NULL); IM_ASSERT(font_size_base >= 0.0f); g.FontStack.push_back({ g.Font, g.FontSizeBase, g.FontSize }); if (font_size_base == 0.0f) font_size_base = g.FontSizeBase; // Keep current font size SetCurrentFont(font, font_size_base, 0.0f); } void ImGui::PopFont() { ImGuiContext& g = *GImGui; IM_ASSERT_USER_ERROR_RET(g.FontStack.Size > 0, "Calling PopFont() too many times!"); ImFontStackData* font_stack_data = &g.FontStack.back(); SetCurrentFont(font_stack_data->Font, font_stack_data->FontSizeBeforeScaling, font_stack_data->FontSizeAfterScaling); g.FontStack.pop_back(); } //----------------------------------------------------------------------------- // [SECTION] ID STACK //----------------------------------------------------------------------------- // This is one of the very rare legacy case where we use ImGuiWindow methods, // it should ideally be flattened at some point but it's been used a lots by widgets. IM_MSVC_RUNTIME_CHECKS_OFF ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *Ctx; if (g.DebugHookIdInfoId == id) ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); #endif return id; } ImGuiID ImGuiWindow::GetID(const void* ptr) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *Ctx; if (g.DebugHookIdInfoId == id) ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); #endif return id; } ImGuiID ImGuiWindow::GetID(int n) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *Ctx; if (g.DebugHookIdInfoId == id) ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); #endif return id; } // This is only used in rare/specific situations to manufacture an ID out of nowhere. // FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those? ImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs) { ImGuiID seed = IDStack.back(); ImVec2 p_rel = ImGui::WindowPosAbsToRel(this, p_abs); ImGuiID id = ImHashData(&p_rel, sizeof(p_rel), seed); return id; } // " ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) { ImGuiID seed = IDStack.back(); ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs); ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); return id; } void ImGui::PushID(const char* str_id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiID id = window->GetID(str_id); window->IDStack.push_back(id); } void ImGui::PushID(const char* str_id_begin, const char* str_id_end) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiID id = window->GetID(str_id_begin, str_id_end); window->IDStack.push_back(id); } void ImGui::PushID(const void* ptr_id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiID id = window->GetID(ptr_id); window->IDStack.push_back(id); } void ImGui::PushID(int int_id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiID id = window->GetID(int_id); window->IDStack.push_back(id); } // Push a given id value ignoring the ID stack as a seed. void ImGui::PushOverrideID(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (g.DebugHookIdInfoId == id) DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); #endif window->IDStack.push_back(id); } // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call // (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level. // for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more) ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) { ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; if (g.DebugHookIdInfoId == id) DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); #endif return id; } ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed) { ImGuiID id = ImHashData(&n, sizeof(n), seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; if (g.DebugHookIdInfoId == id) DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); #endif return id; } void ImGui::PopID() { ImGuiWindow* window = GImGui->CurrentWindow; IM_ASSERT_USER_ERROR_RET(window->IDStack.Size > 1, "Calling PopID() too many times!"); window->IDStack.pop_back(); } ImGuiID ImGui::GetID(const char* str_id) { ImGuiWindow* window = GImGui->CurrentWindow; return window->GetID(str_id); } ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) { ImGuiWindow* window = GImGui->CurrentWindow; return window->GetID(str_id_begin, str_id_end); } ImGuiID ImGui::GetID(const void* ptr_id) { ImGuiWindow* window = GImGui->CurrentWindow; return window->GetID(ptr_id); } ImGuiID ImGui::GetID(int int_id) { ImGuiWindow* window = GImGui->CurrentWindow; return window->GetID(int_id); } IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] INPUTS //----------------------------------------------------------------------------- // - GetModForLRModKey() [Internal] // - FixupKeyChord() [Internal] // - GetKeyData() [Internal] // - GetKeyIndex() [Internal] // - GetKeyName() // - GetKeyChordName() [Internal] // - CalcTypematicRepeatAmount() [Internal] // - GetTypematicRepeatRate() [Internal] // - GetKeyPressedAmount() [Internal] // - GetKeyMagnitude2d() [Internal] //----------------------------------------------------------------------------- // - UpdateKeyRoutingTable() [Internal] // - GetRoutingIdFromOwnerId() [Internal] // - GetShortcutRoutingData() [Internal] // - CalcRoutingScore() [Internal] // - SetShortcutRouting() [Internal] // - TestShortcutRouting() [Internal] //----------------------------------------------------------------------------- // - IsKeyDown() // - IsKeyPressed() // - IsKeyReleased() //----------------------------------------------------------------------------- // - IsMouseDown() // - IsMouseClicked() // - IsMouseReleased() // - IsMouseDoubleClicked() // - GetMouseClickedCount() // - IsMouseHoveringRect() [Internal] // - IsMouseDragPastThreshold() [Internal] // - IsMouseDragging() // - GetMousePos() // - SetMousePos() [Internal] // - GetMousePosOnOpeningCurrentPopup() // - IsMousePosValid() // - IsAnyMouseDown() // - GetMouseDragDelta() // - ResetMouseDragDelta() // - GetMouseCursor() // - SetMouseCursor() //----------------------------------------------------------------------------- // - UpdateAliasKey() // - GetMergedModsFromKeys() // - UpdateKeyboardInputs() // - UpdateMouseInputs() //----------------------------------------------------------------------------- // - LockWheelingWindow [Internal] // - FindBestWheelingWindow [Internal] // - UpdateMouseWheel() [Internal] //----------------------------------------------------------------------------- // - SetNextFrameWantCaptureKeyboard() // - SetNextFrameWantCaptureMouse() //----------------------------------------------------------------------------- // - GetInputSourceName() [Internal] // - DebugPrintInputEvent() [Internal] // - UpdateInputEvents() [Internal] //----------------------------------------------------------------------------- // - GetKeyOwner() [Internal] // - TestKeyOwner() [Internal] // - SetKeyOwner() [Internal] // - SetItemKeyOwner() [Internal] // - Shortcut() [Internal] //----------------------------------------------------------------------------- static ImGuiKeyChord GetModForLRModKey(ImGuiKey key) { if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl) return ImGuiMod_Ctrl; if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift) return ImGuiMod_Shift; if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt) return ImGuiMod_Alt; if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper) return ImGuiMod_Super; return ImGuiMod_None; } ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord) { // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (IsLRModKey(key)) key_chord |= GetModForLRModKey(key); return key_chord; } ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key) { ImGuiContext& g = *ctx; // Special storage location for mods if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); return &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN]; } // Those names are provided for debugging purpose and are not meant to be saved persistently nor compared. static const char* const GKeyNames[] = { "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown", "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape", "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket", "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen", "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply", "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual", "AppBack", "AppForward", "Oem102", "GamepadStart", "GamepadBack", "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown", "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown", "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3", "GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown", "GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown", "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY", "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names. }; IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_COUNTOF(GKeyNames)); const char* ImGui::GetKeyName(ImGuiKey key) { if (key == ImGuiKey_None) return "None"; IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); if (!IsNamedKey(key)) return "Unknown"; return GKeyNames[key - ImGuiKey_NamedKey_BEGIN]; } // Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super. // Lifetime of return value: valid until next call to same function. const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord) { ImGuiContext& g = *GImGui; const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (IsLRModKey(key)) key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift" ImFormatString(g.TempKeychordName, IM_COUNTOF(g.TempKeychordName), "%s%s%s%s%s", (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "", (key_chord & ImGuiMod_Shift) ? "Shift+" : "", (key_chord & ImGuiMod_Alt) ? "Alt+" : "", (key_chord & ImGuiMod_Super) ? "Super+" : "", (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : ""); size_t len; if (key == ImGuiKey_None && key_chord != 0) if ((len = ImStrlen(g.TempKeychordName)) != 0) // Remove trailing '+' g.TempKeychordName[len - 1] = 0; return g.TempKeychordName; } // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) // t1 = current time (e.g.: g.Time) // An event is triggered at: // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) { if (t1 == 0.0f) return 1; if (t0 >= t1) return 0; if (repeat_rate <= 0.0f) return t0 < repeat_delay && t1 >= repeat_delay; const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); const int count = count_t1 - count_t0; return count; } void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate) { ImGuiContext& g = *GImGui; switch (flags & ImGuiInputFlags_RepeatRateMask_) { case ImGuiInputFlags_RepeatRateNavMove: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return; case ImGuiInputFlags_RepeatRateNavTweak: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return; case ImGuiInputFlags_RepeatRateDefault: default: *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return; } } // Return value representing the number of presses in the last time period, for the given repeat rate // (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate) int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate) { ImGuiContext& g = *GImGui; const ImGuiKeyData* key_data = GetKeyData(key); if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) return 0; const float t = key_data->DownDuration; return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); } // Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values). ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down) { return ImVec2( GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue, GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue); } // Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data. // Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D // Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6 // See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation. static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) { ImGuiContext& g = *GImGui; rt->EntriesNext.resize(0); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { const int new_routing_start_idx = rt->EntriesNext.Size; ImGuiKeyRoutingData* routing_entry; for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex) { routing_entry = &rt->Entries[old_routing_idx]; routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore; routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner; routing_entry->RoutingNextScore = 0; if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner) continue; rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer // Apply routing to owner if there's no owner already (RoutingCurr == None at this point) // This is the result of previous frame's SetShortcutRouting() call. if (routing_entry->Mods == g.IO.KeyMods) { ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner) { owner_data->OwnerCurr = routing_entry->RoutingCurr; //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr); } } } // Rewrite linked-list rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1); for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++) rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1); } rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes } // owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope(). static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id) { ImGuiContext& g = *GImGui; return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId; } ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) { // Majority of shortcuts will be Key + any number of Mods // We accept _Single_ mod with ImGuiKey_None. // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl); // Legal // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift); // Legal // - Shortcut(ImGuiMod_Ctrl); // Legal // - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift); // Not legal ImGuiContext& g = *GImGui; ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; ImGuiKeyRoutingData* routing_data; ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); if (key == ImGuiKey_None) key = ConvertSingleModFlagToKey(mods); IM_ASSERT(IsNamedKey(key)); // Get (in the majority of case, the linked list will have one element so this should be 2 reads. // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame). for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex) { routing_data = &rt->Entries[idx]; if (routing_data->Mods == mods) return routing_data; } // Add to linked-list ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size; rt->Entries.push_back(ImGuiKeyRoutingData()); routing_data = &rt->Entries[routing_data_idx]; routing_data->Mods = (ImU16)mods; routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx; return routing_data; } // Current score encoding // - 0: Never route // - 1: ImGuiInputFlags_RouteGlobal (lower priority) // - 100..199: ImGuiInputFlags_RouteFocused (if window in focus-stack) // 200: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused // 300: ImGuiInputFlags_RouteActive or ImGuiInputFlags_RouteFocused (if item active) // 400: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive // - 500..599: ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteOverActive (if window in focus-stack) (higher priority) // 'flags' should include an explicit routing policy static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; if (flags & ImGuiInputFlags_RouteFocused) { // ActiveID gets high priority // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) if (owner_id != 0 && g.ActiveId == owner_id) return 300; // Score based on distance to focused window (lower is better) // Assuming both windows are submitting a routing request, // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match) // - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best) // Assuming only WindowA is submitting a routing request, // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. // This essentially follow the window->ParentWindowForFocusRoute chain. if (focus_scope_id == 0) return 0; for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++) if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id) { if (flags & ImGuiInputFlags_RouteOverActive) // && g.ActiveId != 0 && g.ActiveId != owner_id) return 599 - index_in_focus_path; else return 199 - index_in_focus_path; } return 0; } else if (flags & ImGuiInputFlags_RouteActive) { if (owner_id != 0 && g.ActiveId == owner_id) return 300; return 0; } else if (flags & ImGuiInputFlags_RouteGlobal) { if (flags & ImGuiInputFlags_RouteOverActive) return 400; if (owner_id != 0 && g.ActiveId == owner_id) return 300; if (flags & ImGuiInputFlags_RouteOverFocused) return 200; return 1; } IM_ASSERT(0); return 0; } // - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active // e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be. // - This is also used by UpdateInputEvents() to avoid trickling in the most common case of e.g. pressing ImGuiKey_G also emitting a G character. static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord) { // Mimic 'ignore_char_inputs' logic in InputText() ImGuiContext& g = *GImGui; // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out. ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl)); if (ignore_char_inputs) return false; // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered. ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (key == ImGuiKey_None) return false; return g.KeysMayBeCharInput.TestBit(key); } // Request a desired route for an input chord (key + mods). // Return true if the route is available this frame. // - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state. // (Conceptually this does a "Submit for next frame" + "Test for current frame". // As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.) bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0) flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut() else IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner); if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteUnlessBgFocused)) IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal); if (flags & ImGuiInputFlags_RouteOverActive) IM_ASSERT(flags & (ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteFocused)); // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. key_chord = FixupKeyChord(key_chord); // [DEBUG] Debug break requested by user if (g.DebugBreakInShortcutRouting == key_chord) IM_DEBUG_BREAK(); if (flags & ImGuiInputFlags_RouteUnlessBgFocused) if (g.NavWindow == NULL) return false; // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this? if (flags & ImGuiInputFlags_RouteAlways) { IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id); return true; } // Specific culling when there's an active item. if (g.ActiveId != 0 && g.ActiveId != owner_id) { if (flags & ImGuiInputFlags_RouteActive) return false; // Cull shortcuts with no modifiers when it could generate a character. // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active. // but Shortcut(Ctrl+G) should generally trigger when InputText() is active. // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active. // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined) if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord)) { IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id); return false; } // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys) { ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (key == ImGuiKey_None) key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_)); if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) return false; } } // Where do we evaluate route for? ImGuiID focus_scope_id = g.CurrentFocusScopeId; if (flags & ImGuiInputFlags_RouteFromRootWindow) focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin() const int score = CalcRoutingScore(focus_scope_id, owner_id, flags); IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score); if (score == 0) return false; // Submit routing for NEXT frame (assuming score is sufficient) // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using >= instead of >). ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore); if (score > routing_data->RoutingNextScore) { routing_data->RoutingNext = owner_id; routing_data->RoutingNextScore = (ImU16)score; } // Return routing state for CURRENT frame if (routing_data->RoutingCurr == owner_id) IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n"); return routing_data->RoutingCurr == owner_id; } // Currently unused by core (but used by tests) // Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading. bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id) { const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); key_chord = FixupKeyChord(key_chord); ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry. return routing_data->RoutingCurr == routing_id; } // Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes. // Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87) bool ImGui::IsKeyDown(ImGuiKey key) { return IsKeyDown(key, ImGuiKeyOwner_Any); } bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id) { const ImGuiKeyData* key_data = GetKeyData(key); if (!key_data->Down) return false; if (!TestKeyOwner(key, owner_id)) return false; return true; } bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat) { return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any); } // Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat. bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id) { const ImGuiKeyData* key_data = GetKeyData(key); if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) return false; const float t = key_data->DownDuration; if (t < 0.0f) return false; IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function! if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat flags |= ImGuiInputFlags_Repeat; bool pressed = (t == 0.0f); if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0) { float repeat_delay, repeat_rate; GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate); pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0; if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_)) { // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value. // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code. ImGuiContext& g = *GImGui; double key_pressed_time = g.Time - t + 0.00001f; if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time)) pressed = false; if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time)) pressed = false; if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time)) pressed = false; } } if (!pressed) return false; if (!TestKeyOwner(key, owner_id)) return false; return true; } bool ImGui::IsKeyReleased(ImGuiKey key) { return IsKeyReleased(key, ImGuiKeyOwner_Any); } bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id) { const ImGuiKeyData* key_data = GetKeyData(key); if (key_data->DownDurationPrev < 0.0f || key_data->Down) return false; if (!TestKeyOwner(key, owner_id)) return false; return true; } bool ImGui::IsMouseDown(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array. } bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array. } bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) { return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any); } bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) return false; const float t = g.IO.MouseDownDuration[button]; if (t < 0.0f) return false; IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here. const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0; const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0); if (!pressed) return false; if (!TestKeyOwner(MouseButtonToKey(button), owner_id)) return false; return true; } bool ImGui::IsMouseReleased(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any) } bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id) } // Use if you absolutely need to distinguish single-click from double-click by introducing a delay. // Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test. // This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename. bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]); return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay); } bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); } bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id); } int ImGui::GetMouseClickedCount(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button]; } // Test if mouse cursor is hovering given rectangle // NB- Rectangle is clipped by our current clip setting // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) { ImGuiContext& g = *GImGui; // Clip ImRect rect_clipped(r_min, r_max); if (clip) rect_clipped.ClipWith(g.CurrentWindow->ClipRect); // Hit testing, expanded for touch input if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding)) return false; if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped)) return false; return true; } // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. // [Internal] This doesn't test if the button is pressed bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (lock_threshold < 0.0f) lock_threshold = g.IO.MouseDragThreshold; return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; } bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (!g.IO.MouseDown[button]) return false; return IsMouseDragPastThreshold(button, lock_threshold); } ImVec2 ImGui::GetMousePos() { ImGuiContext& g = *GImGui; return g.IO.MousePos; } // This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well. // It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend. void ImGui::TeleportMousePos(const ImVec2& pos) { ImGuiContext& g = *GImGui; g.IO.MousePos = g.IO.MousePosPrev = pos; g.IO.MouseDelta = ImVec2(0.0f, 0.0f); g.IO.WantSetMousePos = true; //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); } // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() { ImGuiContext& g = *GImGui; 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. bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) { // 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; ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; } // [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid. bool ImGui::IsAnyMouseDown() { ImGuiContext& g = *GImGui; for (int n = 0; n < IM_COUNTOF(g.IO.MouseDown); n++) if (g.IO.MouseDown[n]) return true; return false; } // Return the delta from the initial clicking position while the mouse button is clicked or was just released. // 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(). backends in theory should always keep mouse position valid when dragging even outside the client window. ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (lock_threshold < 0.0f) lock_threshold = g.IO.MouseDragThreshold; if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) return g.IO.MousePos - g.IO.MouseClickedPos[button]; return ImVec2(0.0f, 0.0f); } void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr g.IO.MouseClickedPos[button] = g.IO.MousePos; } // Get desired mouse cursor shape. // Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(), // updated during the frame, and locked in EndFrame()/Render(). // If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you ImGuiMouseCursor ImGui::GetMouseCursor() { ImGuiContext& g = *GImGui; return g.MouseCursor; } // We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value. // Custom cursors may be handled by custom backends. If you are using a standard backend and want to hack in a custom cursor, you may // handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call. void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) { ImGuiContext& g = *GImGui; g.MouseCursor = cursor_type; } static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value) { IM_ASSERT(ImGui::IsAliasKey(key)); ImGuiKeyData* key_data = ImGui::GetKeyData(key); key_data->Down = v; key_data->AnalogValue = analog_value; } // [Internal] Do not use directly static ImGuiKeyChord GetMergedModsFromKeys() { ImGuiKeyChord mods = 0; if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; } if (ImGui::IsKeyDown(ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; } if (ImGui::IsKeyDown(ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; } if (ImGui::IsKeyDown(ImGuiMod_Super)) { mods |= ImGuiMod_Super; } return mods; } static void ImGui::UpdateKeyboardInputs() { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) io.ClearInputKeys(); // Update aliases for (int n = 0; n < ImGuiMouseButton_COUNT; n++) UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f); UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH); UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel); // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values. // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array. // - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array. // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing. const ImGuiKeyChord prev_key_mods = io.KeyMods; io.KeyMods = GetMergedModsFromKeys(); io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0; io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0; io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0; io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0; if (prev_key_mods != io.KeyMods) g.LastKeyModsChangeTime = g.Time; if (prev_key_mods != io.KeyMods && prev_key_mods == 0) g.LastKeyModsChangeFromNoneTime = g.Time; // Clear gamepad data if disabled if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) for (int key = ImGuiKey_Gamepad_BEGIN; key < ImGuiKey_Gamepad_END; key++) { io.KeysData[key - ImGuiKey_NamedKey_BEGIN].Down = false; io.KeysData[key - ImGuiKey_NamedKey_BEGIN].AnalogValue = 0.0f; } // Update keys for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++) { ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN]; key_data->DownDurationPrev = key_data->DownDuration; key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f; if (key_data->DownDuration == 0.0f) { if (IsKeyboardKey((ImGuiKey)key)) g.LastKeyboardKeyPressTime = g.Time; else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper) g.LastKeyboardKeyPressTime = g.Time; } } // Update keys/input owner (named keys only): one entry per key for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN]; ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; owner_data->OwnerCurr = owner_data->OwnerNext; if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp. owner_data->OwnerNext = ImGuiKeyOwner_NoOwner; owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore } // Update key routing (for e.g. shortcuts) UpdateKeyRoutingTable(&g.KeysRoutingTable); } static void ImGui::UpdateMouseInputs() { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; // Mouse Wheel swapping flag // As a standard behavior holding Shift while using Vertical Mouse Wheel triggers Horizontal scroll instead // - We avoid doing it on OSX as it the OS input layer handles this already. // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature. // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source. io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors; // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) if (IsMousePosValid(&io.MousePos)) io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos); // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) io.MouseDelta = io.MousePos - io.MousePosPrev; else io.MouseDelta = ImVec2(0.0f, 0.0f); // Update stationary timer. // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates. const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework. const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold); g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f; //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer); // If mouse moved we re-enable mouse hovering in case it was disabled by keyboard/gamepad. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) g.NavHighlightItemUnderNav = false; for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) { io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; io.MouseClickedCount[i] = 0; // Will be filled below io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; if (io.MouseReleased[i]) io.MouseReleasedTime[i] = g.Time; io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; if (io.MouseClicked[i]) { bool is_repeated_click = false; if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime) { ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist) is_repeated_click = true; } if (is_repeated_click) io.MouseClickedLastCount[i]++; else io.MouseClickedLastCount[i] = 1; io.MouseClickedTime[i] = g.Time; io.MouseClickedPos[i] = io.MousePos; io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; io.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); io.MouseDragMaxDistanceSqr[i] = 0.0f; } else if (io.MouseDown[i]) { // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); io.MouseDragMaxDistanceAbs[i].x = ImMax(io.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); io.MouseDragMaxDistanceAbs[i].y = ImMax(io.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } // We provide io.MouseDoubleClicked[] as a legacy service io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2); // Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation if (io.MouseClicked[i]) g.NavHighlightItemUnderNav = false; } } static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount) { ImGuiContext& g = *GImGui; if (window) g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER); else g.WheelingWindowReleaseTimer = 0.0f; if (g.WheelingWindow == window) return; IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL"); g.WheelingWindow = window; g.WheelingWindowRefMousePos = g.IO.MousePos; if (window == NULL) { g.WheelingWindowStartFrame = -1; g.WheelingAxisAvg = ImVec2(0.0f, 0.0f); } } static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel) { // For each axis, find window in the hierarchy that may want to use scrolling ImGuiContext& g = *GImGui; ImGuiWindow* windows[2] = { NULL, NULL }; for (int axis = 0; axis < 2; axis++) if (wheel[axis] != 0.0f) for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow) { // Bubble up into parent window if: // - a child window doesn't allow any scrolling. // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag. //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP) const bool has_scrolling = (window->ScrollMax[axis] != 0.0f); const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs); //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]); if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits) break; // select this window } if (windows[0] == NULL && windows[1] == NULL) return NULL; // If there's only one window or only one axis then there's no ambiguity if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL) return windows[1] ? windows[1] : windows[0]; // If candidate are different windows we need to decide which one to prioritize // - First frame: only find a winner if one axis is zero. // - Subsequent frames: only find a winner when one is more than the other. if (g.WheelingWindowStartFrame == -1) g.WheelingWindowStartFrame = g.FrameCount; if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y)) { g.WheelingWindowWheelRemainder = wheel; return NULL; } return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1]; } // Called by NewFrame() void ImGui::UpdateMouseWheel() { // Reset the locked window if we move the mouse or after the timer elapses. // FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795) ImGuiContext& g = *GImGui; if (g.WheelingWindow != NULL) { g.WheelingWindowReleaseTimer -= g.IO.DeltaTime; if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) g.WheelingWindowReleaseTimer = 0.0f; if (g.WheelingWindowReleaseTimer <= 0.0f) LockWheelingWindow(NULL, 0.0f); } ImVec2 wheel; wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f; wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f; //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y); ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; if (!mouse_window || mouse_window->Collapsed) return; // Zoom / Scale window // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) { LockWheelingWindow(mouse_window, wheel.y); ImGuiWindow* window = mouse_window; const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); const float scale = new_font_scale / window->FontWindowScale; window->FontWindowScale = new_font_scale; if (window == window->RootWindow) { const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; SetWindowPos(window, window->Pos + offset, 0); window->Size = ImTrunc(window->Size * scale); // FIXME: Legacy-ish code, call SetWindowSize()? window->SizeFull = ImTrunc(window->SizeFull * scale); MarkIniSettingsDirty(window); } return; } if (g.IO.KeyCtrl) return; // Mouse wheel scrolling // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs() if (g.IO.MouseWheelRequestAxisSwap) wheel = ImVec2(wheel.y, 0.0f); // Maintain a rough average of moving magnitude on both axes // FIXME: should by based on wall clock time rather than frame-counter g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30); g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30); // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now. wheel += g.WheelingWindowWheelRemainder; g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f); if (wheel.x == 0.0f && wheel.y == 0.0f) return; // Mouse wheel scrolling: find target and apply // - don't renew lock if axis doesn't apply on the window. // - select a main axis when both axes are being moved. if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel))) if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) { bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f }; if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y]) do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false; if (do_scroll[ImGuiAxis_X]) { LockWheelingWindow(window, wheel.x); float max_step = window->InnerRect.GetWidth() * 0.67f; float scroll_step = ImTrunc(ImMin(2 * window->FontRefSize, max_step)); SetScrollX(window, window->Scroll.x - wheel.x * scroll_step); g.WheelingWindowScrolledFrame = g.FrameCount; } if (do_scroll[ImGuiAxis_Y]) { LockWheelingWindow(window, wheel.y); float max_step = window->InnerRect.GetHeight() * 0.67f; float scroll_step = ImTrunc(ImMin(5 * window->FontRefSize, max_step)); SetScrollY(window, window->Scroll.y - wheel.y * scroll_step); g.WheelingWindowScrolledFrame = g.FrameCount; } } } void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard) { ImGuiContext& g = *GImGui; g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0; } void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse) { ImGuiContext& g = *GImGui; g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0; } #ifndef IMGUI_DISABLE_DEBUG_TOOLS static const char* GetInputSourceName(ImGuiInputSource source) { const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" }; IM_ASSERT(IM_COUNTOF(input_source_names) == ImGuiInputSource_COUNT); if (source < 0 || source >= ImGuiInputSource_COUNT) return "Unknown"; return input_source_names[source]; } static const char* GetMouseSourceName(ImGuiMouseSource source) { const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" }; IM_ASSERT(IM_COUNTOF(mouse_source_names) == ImGuiMouseSource_COUNT); if (source < 0 || source >= ImGuiMouseSource_COUNT) return "Unknown"; return mouse_source_names[source]; } static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) { ImGuiContext& g = *GImGui; char buf[5]; if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO("[io] %s: MouseViewport (0x%08X)\n", prefix, e->MouseViewport.HoveredViewportID); return; } if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } if (e->Type == ImGuiInputEventType_Text) { ImTextCharToUtf8(buf, e->Text.Char); IMGUI_DEBUG_LOG_IO("[io] %s: Text: '%s' (U+%08X)\n", prefix, buf, e->Text.Char); return; } if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } } #endif // Process input queue // We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'. // - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost) // - trickle_fast_inputs = true : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87) void ImGui::UpdateInputEvents(bool trickle_fast_inputs) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; // Only trickle chars<>key when working with InputText() // FIXME: InputText() could parse event trail? // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters) const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1); bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false; int mouse_button_changed = 0x00; ImBitArray key_changed_mask; int event_n = 0; for (; event_n < g.InputEventsQueue.Size; event_n++) { ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; if (e->Type == ImGuiInputEventType_MousePos) { if (g.IO.WantSetMousePos) continue; // Trickling Rule: Stop processing queued events if we already handled a mouse button change ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY); if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted)) break; io.MousePos = event_pos; io.MouseSource = e->MousePos.MouseSource; mouse_moved = true; } else if (e->Type == ImGuiInputEventType_MouseButton) { // Trickling Rule: Stop processing queued events if we got multiple action on the same button const ImGuiMouseButton button = e->MouseButton.Button; IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled)) break; if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover. break; io.MouseDown[button] = e->MouseButton.Down; io.MouseSource = e->MouseButton.MouseSource; mouse_button_changed |= (1 << button); } else if (e->Type == ImGuiInputEventType_MouseWheel) { // Trickling Rule: Stop processing queued events if we got multiple action on the event if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0)) break; io.MouseWheelH += e->MouseWheel.WheelX; io.MouseWheel += e->MouseWheel.WheelY; io.MouseSource = e->MouseWheel.MouseSource; mouse_wheeled = true; } else if (e->Type == ImGuiInputEventType_MouseViewport) { io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID; } else if (e->Type == ImGuiInputEventType_Key) { // Trickling Rule: Stop processing queued events if we got multiple action on the same button if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) continue; ImGuiKey key = e->Key.Key; IM_ASSERT(key != ImGuiKey_None); ImGuiKeyData* key_data = GetKeyData(key); const int key_data_index = (int)(key_data - g.IO.KeysData); if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || mouse_button_changed != 0)) break; const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(GetMergedModsFromKeys() | key); if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input)) break; if (key_data->Down != e->Key.Down) // Analog change only do not trigger this, so it won't block e.g. further mouse pos events testing key_changed. { key_changed = true; key_changed_mask.SetBit(key_data_index); if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input) key_changed_nonchar = true; } key_data->Down = e->Key.Down; key_data->AnalogValue = e->Key.AnalogValue; } else if (e->Type == ImGuiInputEventType_Text) { if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) continue; // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled)) break; if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar) break; unsigned int c = e->Text.Char; io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); if (trickle_interleaved_nonchar_keys_and_text) text_inputted = true; } else if (e->Type == ImGuiInputEventType_Focus) { // We intentionally overwrite this and process in NewFrame(), in order to give a chance // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame. const bool focus_lost = !e->AppFocused.Focused; io.AppFocusLost = focus_lost; } else { IM_ASSERT(0 && "Unknown event!"); } } // Record trail (for domain-specific applications wanting to access a precise trail) //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n); for (int n = 0; n < event_n; n++) g.InputEventsTrail.push_back(g.InputEventsQueue[n]); // [DEBUG] #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO)) for (int n = 0; n < g.InputEventsQueue.Size; n++) DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]); #endif // Remaining events will be processed on the next frame // FIXME-MULTITHREADING: io.AddKeyEvent() etc. calls are mostly thread-safe apart from the fact they push to this // queue which may be resized here. Could potentially rework this to narrow down the section needing a mutex? (#5772) if (event_n == g.InputEventsQueue.Size) g.InputEventsQueue.resize(0); else g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n); // Clear buttons state when focus is lost // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle. // - we clear in EndFrame() and not now in order allow application/user code polling this flag // (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event). if (g.IO.AppFocusLost) { g.IO.ClearInputKeys(); g.IO.ClearInputMouse(); } } ImGuiID ImGui::GetKeyOwner(ImGuiKey key) { if (!IsNamedKeyOrMod(key)) return ImGuiKeyOwner_NoOwner; ImGuiContext& g = *GImGui; ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); ImGuiID owner_id = owner_data->OwnerCurr; if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any) if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) return ImGuiKeyOwner_NoOwner; return owner_id; } // TestKeyOwner(..., ID) : (owner == None || owner == ID) // TestKeyOwner(..., None) : (owner == None) // TestKeyOwner(..., Any) : no owner test // All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags. bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) { if (!IsNamedKeyOrMod(key)) return true; ImGuiContext& g = *GImGui; if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any) if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) return false; ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); if (owner_id == ImGuiKeyOwner_Any) return owner_data->LockThisFrame == false; // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things. // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions. if (owner_data->OwnerCurr != owner_id) { if (owner_data->LockThisFrame) return false; if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner) return false; } return true; } // _LockXXX flags are useful to lock keys away from code which is not input-owner aware. // When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone. // - SetKeyOwner(..., None) : clears owner // - SetKeyOwner(..., Any, !Lock) : illegal (assert) // - SetKeyOwner(..., Any or None, Lock) : set lock void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; IM_ASSERT(IsNamedKeyOrMod(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it) IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function! //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags); ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); owner_data->OwnerCurr = owner_data->OwnerNext = owner_id; // We cannot lock by default as it would likely break lots of legacy code. // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test) owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0; owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease); } // Rarely used helper void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) { if (key_chord & ImGuiMod_Ctrl) { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); } if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); } if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); } if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); } if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); } } // This is more or less equivalent to: // if (IsItemHovered() || IsItemActive()) // SetKeyOwner(key, GetItemID()); // Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times. // More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition. // Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority. void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; ImGuiID id = g.LastItemData.ID; if (id == 0 || (g.HoveredId != id && g.ActiveId != id)) return; if ((flags & ImGuiInputFlags_CondMask_) == 0) flags |= ImGuiInputFlags_CondDefault_; if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive))) { IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function! SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_); } } void ImGui::SetItemKeyOwner(ImGuiKey key) { SetItemKeyOwner(key, ImGuiInputFlags_None); } // This is the only public API until we expose owner_id versions of the API as replacements. bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord) { return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any); } // This is equivalent to comparing KeyMods + doing a IsKeyPressed() bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; key_chord = FixupKeyChord(key_chord); ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); if (g.IO.KeyMods != mods) return false; // Special storage location for mods ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (key == ImGuiKey_None) key = ConvertSingleModFlagToKey(mods); if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id)) return false; return true; } void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut; g.NextItemData.Shortcut = key_chord; g.NextItemData.ShortcutFlags = flags; } // Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData void ImGui::ItemHandleShortcut(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiInputFlags flags = g.NextItemData.ShortcutFlags; IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()! if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) return; if (flags & ImGuiInputFlags_Tooltip) { g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut; g.LastItemData.Shortcut = g.NextItemData.Shortcut; } if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0) return; // FIXME: Generalize Activation queue? g.NavActivateId = id; // Will effectively disable clipping. g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut; //if (g.ActiveId == 0 || g.ActiveId == id) g.NavActivateDownId = g.NavActivatePressedId = id; NavHighlightActivated(id); } bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags) { return Shortcut(key_chord, flags, ImGuiKeyOwner_Any); } bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id); // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any. if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0) flags |= ImGuiInputFlags_RouteFocused; // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default) // Effectively makes Shortcut() always input-owner aware. if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner) owner_id = GetRoutingIdFromOwnerId(owner_id); if (g.CurrentItemFlags & ImGuiItemFlags_Disabled) return false; // Submit route if (!SetShortcutRouting(key_chord, flags, owner_id)) return false; // Default repeat behavior for Shortcut() // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut. if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0) flags |= ImGuiInputFlags_RepeatUntilKeyModsChange; if (!IsKeyChordPressed(key_chord, flags, owner_id)) return false; // Claim mods during the press SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id); IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function! return true; } //----------------------------------------------------------------------------- // [SECTION] ERROR CHECKING, STATE RECOVERY //----------------------------------------------------------------------------- // - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros) // - ErrorCheckUsingSetCursorPosToExtendParentBoundaries() // - ErrorCheckNewFrameSanityChecks() // - ErrorCheckEndFrameSanityChecks() // - ErrorRecoveryStoreState() // - ErrorRecoveryTryToRecoverState() // - ErrorRecoveryTryToRecoverWindowState() // - ErrorLog() //----------------------------------------------------------------------------- // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. // Called by IMGUI_CHECKVERSION(). // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit // If this triggers you have mismatched headers and compiled code versions. // - It could be because of a build issue (using new headers with old compiled code) // - It could be because of mismatched configuration #define, compilation settings, packing pragma etc. // THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI. // Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h). // Otherwise it is possible that different compilation units would see different structure layout. // If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename. bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) { bool error = false; if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); } if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } return !error; } // Until 1.89 (August 2022, IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos()/SetCursorScreenPos() // to extend contents size of our parent container (e.g. window contents size, which is used for auto-resizing // windows, table column contents size used for auto-resizing columns, group size). // This was causing issues and ambiguities and we needed to retire that. // From 1.89, extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED. // // Previously this would make the window content size ~200x200: // Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK ANYMORE // Instead, please submit an item: // Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK // Alternative: // Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK // // The assert below detects when the _last_ call in a window was a SetCursorPos() not followed by an Item, // and with a position that would grow the parent contents size. // // Advanced: // - For reference, old logic was causing issues because it meant that SetCursorScreenPos(GetCursorScreenPos()) // had a side-effect on layout! In particular this caused problem to compute group boundaries. // e.g. BeginGroup() + SomeItem() + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup() would cause the // group to be taller because auto-sizing generally adds padding on bottom and right side. // - While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect. // Using vertical alignment patterns would frequently trigger this sorts of issue. // - See https://github.com/ocornut/imgui/issues/5548 for more details. void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(window->DC.IsSetPos); window->DC.IsSetPos = false; if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y) return; if (window->SkipItems) return; IM_ASSERT_USER_ERROR(0, "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries.\nPlease submit an item e.g. Dummy() afterwards in order to grow window/parent boundaries."); // For reference, the old behavior was essentially: //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); } static void ImGui::ErrorCheckNewFrameSanityChecks() { ImGuiContext& g = *GImGui; // Check user IM_ASSERT macro // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined! // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong! // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct! if (true) IM_ASSERT(1); else IM_ASSERT(0); // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644) // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it. #ifdef __EMSCRIPTEN__ if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0) g.IO.DeltaTime = 0.00001f; #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 || 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.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // 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!"); IM_ASSERT(g.Style.WindowBorderHoverPadding > 0.0f && "Invalid style setting!"); // Required otherwise cannot resize from borders. IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right); IM_ASSERT(g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesNone || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesFull || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesToNodes); // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way. if (g.IO.ConfigErrorRecovery) IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS if (g.IO.FontGlobalScale > 1.0f) IM_ASSERT(g.Style.FontScaleMain == 1.0f && "Since 1.92: use style.FontScaleMain instead of g.IO.FontGlobalScale!"); // Remap legacy names if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) { g.IO.ConfigNavMoveSetMousePos = true; g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos; } if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) { g.IO.ConfigNavCaptureKeyboard = false; g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard; } if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) { g.IO.ConfigDpiScaleFonts = true; g.IO.ConfigFlags &= ~ImGuiConfigFlags_DpiEnableScaleFonts; } if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) { g.IO.ConfigDpiScaleViewports = true; g.IO.ConfigFlags &= ~ImGuiConfigFlags_DpiEnableScaleViewports; } // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024) if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl)) g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); }; if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl)) g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); }; #endif // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0) IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0) IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); // Perform simple checks: multi-viewport and platform windows support if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) { IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference."); IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?"); IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!"); } else { // Disable feature, our backends do not support it g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; } // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs for (ImGuiPlatformMonitor& mon : g.PlatformIO.Monitors) { IM_UNUSED(mon); IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly."); IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them."); IM_ASSERT(mon.DpiScale > 0.0f && mon.DpiScale < 99.0f && "Monitor DpiScale is invalid."); // Typical correct values would be between 1.0f and 4.0f } } } static void ImGui::ErrorCheckEndFrameSanityChecks() { // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), // while still correctly asserting on mid-frame key press events. ImGuiContext& g = *GImGui; const ImGuiKeyChord key_mods = GetMergedModsFromKeys(); IM_UNUSED(g); IM_UNUSED(key_mods); IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); IM_UNUSED(key_mods); IM_ASSERT(g.CurrentWindowStack.Size == 1); IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow); } // Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery. void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out) { ImGuiContext& g = *GImGui; state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size; state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size; state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack! state_out->SizeOfColorStack = (short)g.ColorStack.Size; state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size; state_out->SizeOfFontStack = (short)g.FontStack.Size; state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; state_out->SizeOfGroupStack = (short)g.GroupStack.Size; state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size; state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; state_out->SizeOfDisabledStack = (short)g.DisabledStackSize; } // Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery. // Called by e.g. EndFrame() but may be called for manual recovery. // Attempt to recover full window stack. void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in) { // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" ImGuiContext& g = *GImGui; while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044 { // Recap: // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped. // - Always call a matching End() for each Begin() call, regardless of its return value! // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS. // - We will fix that in a future major update. ImGuiWindow* window = g.CurrentWindow; if (window->Flags & ImGuiWindowFlags_ChildWindow) { if (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) { IM_ASSERT_USER_ERROR(0, "Missing EndTable()"); EndTable(); } else { IM_ASSERT_USER_ERROR(0, "Missing EndChild()"); EndChild(); } } else { IM_ASSERT_USER_ERROR(0, "Missing End()"); End(); } } if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack) ErrorRecoveryTryToRecoverWindowState(state_in); } // Called by e.g. End() but may be called for manual recovery. // Read '// Error Handling [BETA]' block in imgui_internal.h for details. // Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in) { ImGuiContext& g = *GImGui; while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing EndTable()"); EndTable(); } ImGuiWindow* window = g.CurrentWindow; // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()"); EndTabBar(); } while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()"); EndMultiSelect(); } if (window->DC.MenuBarAppending) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing EndMenuBar()"); EndMenuBar(); } while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing TreePop()"); TreePop(); } while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing EndGroup()"); EndGroup(); } IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack); while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing PopID()"); PopID(); } while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()"); if (g.CurrentItemFlags & ImGuiItemFlags_Disabled) EndDisabled(); else { EndDisabledOverrideReenable(); g.CurrentWindowStack.back().DisabledOverrideReenable = false; } } IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack); while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()"); PopStyleColor(); } while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()"); PopItemFlag(); } while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()"); PopStyleVar(); } while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing PopFont()"); PopFont(); } while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()"); PopFocusScope(); } //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack); } bool ImGui::ErrorLog(const char* msg) { ImGuiContext& g = *GImGui; // Output to debug log #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiWindow* window = g.CurrentWindow; if (g.IO.ConfigErrorRecoveryEnableDebugLog) { if (g.ErrorFirst) IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n", g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip); IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg); } g.ErrorFirst = false; // Output to tooltip if (g.IO.ConfigErrorRecoveryEnableTooltip) { if (g.WithinFrameScope && BeginErrorTooltip()) { if (g.ErrorCountCurrentFrame < 20) { Text("In window '%s': %s", window ? window->Name : "NULL", msg); if (window && (!window->IsFallbackWindow || window->WasActive)) GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255)); } if (g.ErrorCountCurrentFrame == 20) Text("(and more errors)"); // EndFrame() will amend debug buttons to this window, after all errors have been submitted. EndErrorTooltip(); } g.ErrorCountCurrentFrame++; } #endif // Output to callback if (g.ErrorCallback != NULL) g.ErrorCallback(&g, g.ErrorCallbackUserData, msg); // Return whether we should assert return g.IO.ConfigErrorRecoveryEnableAssert; } void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() { #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; if (g.DebugDrawIdConflictsId != 0 && g.IO.KeyCtrl == false) g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount; if (g.DebugDrawIdConflictsId != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip()) { Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount); BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!"); BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!"); //BulletText("Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()"); // Not making this too visible for fear of it being abused. BulletText("Set io.ConfigDebugHighlightIdConflicts=false to disable this warning in non-programmers builds."); Separator(); if (g.IO.ConfigDebugHighlightIdConflictsShowItemPicker) { Text("(Hold Ctrl to: use "); SameLine(0.0f, 0.0f); if (SmallButton("Item Picker")) DebugStartItemPicker(); SameLine(0.0f, 0.0f); Text(" to break in item call-stack, or "); } else { Text("(Hold Ctrl to: "); } SameLine(0.0f, 0.0f); TextLinkOpenURL("read FAQ \"About ID Stack System\"", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage"); SameLine(0.0f, 0.0f); Text(")"); EndErrorTooltip(); } if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame { Separator(); Text("(Hold Ctrl to: "); SameLine(0.0f, 0.0f); if (SmallButton("Enable Asserts")) g.IO.ConfigErrorRecoveryEnableAssert = true; //SameLine(); //if (SmallButton("Hide Error Tooltips")) // g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous SameLine(0, 0); Text(")"); EndErrorTooltip(); } #endif } // Pseudo-tooltip. Follow mouse until Ctrl is held. When Ctrl is held we lock position, allowing to click it. bool ImGui::BeginErrorTooltip() { ImGuiContext& g = *GImGui; ImGuiWindow* window = FindWindowByName("##Tooltip_Error"); const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive); PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f)); if (use_locked_pos) SetNextWindowPos(g.ErrorTooltipLockedPos); bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize); PopStyleColor(); if (is_visible && g.CurrentWindow->BeginCount == 1) { SeparatorText("MESSAGE FROM DEAR IMGUI"); BringWindowToDisplayFront(g.CurrentWindow); BringWindowToFocusFront(g.CurrentWindow); g.ErrorTooltipLockedPos = GetWindowPos(); } else if (!is_visible) { End(); } return is_visible; } void ImGui::EndErrorTooltip() { End(); } //----------------------------------------------------------------------------- // [SECTION] ITEM SUBMISSION //----------------------------------------------------------------------------- // - KeepAliveID() // - ItemAdd() //----------------------------------------------------------------------------- // Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID(). void ImGui::KeepAliveID(ImGuiID id) { ImGuiContext& g = *GImGui; if (g.ActiveId == id) g.ActiveIdIsAlive = id; if (g.DeactivatedItemData.ID == id) g.DeactivatedItemData.IsAlive = true; } // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. // THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN) IM_MSVC_RUNTIME_CHECKS_OFF bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; // Set item data // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set) g.LastItemData.ID = id; g.LastItemData.Rect = bb; g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags; g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared. if (id != 0) { KeepAliveID(id); // Directional navigation processing // Runs prior to clipping early-out // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav)) { // FIXME-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test. window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened)) NavProcessItem(); } if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut) ItemHandleShortcut(id); } // Lightweight clear of SetNextItemXXX data. g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None; g.NextItemData.ItemFlags = ImGuiItemFlags_None; #ifdef IMGUI_ENABLE_TEST_ENGINE if (id != 0) IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData); #endif // Clipping test // (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false') // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts) const bool is_rect_visible = bb.Overlaps(window->ClipRect); if (!is_rect_visible) if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId)) if (!g.ItemUnclipByLog) return false; // [DEBUG] #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (id != 0) { if (id == g.DebugLocateId) DebugLocateItemResolveWithLastItem(); // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". // READ THE FAQ: https://dearimgui.com/faq IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); // [DEBUG] Highlight all conflicts WITHOUT needing to hover. THIS WILL SLOW DOWN DEAR IMGUI. DON'T KEEP ACTIVATED. // This will only work for items submitted with ItemAdd(). Some very rare/odd/unrecommended code patterns are calling ButtonBehavior() without ItemAdd(). #ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowDuplicateId) == 0) { int* p_alive = g.DebugDrawIdConflictsAliveCount.GetIntRef(id, -1); // Could halve lookups if we knew ImGuiStorage can store 64-bit, or by storing FrameCount as 30-bits + highlight as 2-bits. But the point is that we should not pretend that this is fast. int* p_highlight = g.DebugDrawIdConflictsHighlightSet.GetIntRef(id, -1); if (*p_alive == g.FrameCount) *p_highlight = g.FrameCount; *p_alive = g.FrameCount; if (*p_highlight >= g.FrameCount - 1) window->DrawList->AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); } #endif } //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] //if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0) // window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG] #endif if (id != 0 && g.DeactivatedItemData.ID == id) g.DeactivatedItemData.ElapseFrame = g.FrameCount; // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) if (is_rect_visible) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible; if (IsMouseHoveringRect(bb.Min, bb.Max)) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; return true; } IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] LAYOUT //----------------------------------------------------------------------------- // - ItemSize() // - SameLine() // - GetCursorScreenPos() // - SetCursorScreenPos() // - GetCursorPos(), GetCursorPosX(), GetCursorPosY() // - SetCursorPos(), SetCursorPosX(), SetCursorPosY() // - GetCursorStartPos() // - Indent() // - Unindent() // - SetNextItemWidth() // - PushItemWidth() // - PushMultiItemsWidths() // - PopItemWidth() // - CalcItemWidth() // - CalcItemSize() // - GetTextLineHeight() // - GetTextLineHeightWithSpacing() // - GetFrameHeight() // - GetFrameHeightWithSpacing() // - GetContentRegionMax() // - GetContentRegionAvail(), // - BeginGroup() // - EndGroup() // Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns. //----------------------------------------------------------------------------- // Advance cursor given item size for layout. // Register minimum needed size so it can extend the bounding box used for auto-fit calculation. // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different. // THIS IS IN THE PERFORMANCE CRITICAL PATH. IM_MSVC_RUNTIME_CHECKS_OFF void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return; // We increase the height in this function to accommodate for baseline offset. // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor, // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect. const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f; const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y; const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y); // Always align ourselves on pixel boundaries //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.x = window->DC.CursorPos.x + size.x; window->DC.CursorPosPrevLine.y = line_y1; window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line 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] window->DC.PrevLineSize.y = line_height; window->DC.CurrLineSize.y = 0.0f; window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y); window->DC.CurrLineTextBaseOffset = 0.0f; window->DC.IsSameLine = window->DC.IsSetPos = false; // Horizontal layout mode if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) SameLine(); } IM_MSVC_RUNTIME_CHECKS_RESTORE // Gets back to previous line and continue with horizontal layout // offset_from_start_x == 0 : follow right after previous item // offset_from_start_x != 0 : align to specified x position (relative to window/group left) // spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0 // spacing_w >= 0 : enforce spacing amount void ImGui::SameLine(float offset_from_start_x, float spacing_w) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return; if (offset_from_start_x != 0.0f) { if (spacing_w < 0.0f) spacing_w = 0.0f; window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x; window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; } else { if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x; window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; } window->DC.CurrLineSize = window->DC.PrevLineSize; window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; window->DC.IsSameLine = true; } ImVec2 ImGui::GetCursorScreenPos() { ImGuiWindow* window = GetCurrentWindowRead(); return window->DC.CursorPos; } void ImGui::SetCursorScreenPos(const ImVec2& pos) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos = pos; //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); window->DC.IsSetPos = true; } // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. // Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'. ImVec2 ImGui::GetCursorPos() { ImGuiWindow* window = GetCurrentWindowRead(); return window->DC.CursorPos - window->Pos + window->Scroll; } float ImGui::GetCursorPosX() { ImGuiWindow* window = GetCurrentWindowRead(); return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; } float ImGui::GetCursorPosY() { ImGuiWindow* window = GetCurrentWindowRead(); return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; } void ImGui::SetCursorPos(const ImVec2& local_pos) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos = window->Pos - window->Scroll + local_pos; //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); window->DC.IsSetPos = true; } void ImGui::SetCursorPosX(float x) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); window->DC.IsSetPos = true; } void ImGui::SetCursorPosY(float y) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); window->DC.IsSetPos = true; } ImVec2 ImGui::GetCursorStartPos() { ImGuiWindow* window = GetCurrentWindowRead(); return window->DC.CursorStartPos - window->Pos; } void ImGui::Indent(float indent_w) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; } void ImGui::Unindent(float indent_w) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; } // Affect large frame+labels widgets only. void ImGui::SetNextItemWidth(float item_width) { ImGuiContext& g = *GImGui; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth; g.NextItemData.Width = item_width; } // FIXME: Remove the == 0.0f behavior? void ImGui::PushItemWidth(float item_width) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width window->DC.ItemWidth = (item_width == 0.0f ? window->DC.ItemWidthDefault : item_width); g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth; } void ImGui::PushMultiItemsWidths(int components, float w_full) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(components > 0); const ImGuiStyle& style = g.Style; window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width float w_items = w_full - style.ItemInnerSpacing.x * (components - 1); float prev_split = w_items; for (int i = components - 1; i > 0; i--) { float next_split = IM_TRUNC(w_items * i / components); window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f)); prev_split = next_split; } window->DC.ItemWidth = ImMax(prev_split, 1.0f); g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth; } void ImGui::PopItemWidth() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->DC.ItemWidthStack.Size <= 0) { IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!"); return; } window->DC.ItemWidth = window->DC.ItemWidthStack.back(); window->DC.ItemWidthStack.pop_back(); } // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(). // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags() float ImGui::CalcItemWidth() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float w; if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth) w = g.NextItemData.Width; else w = window->DC.ItemWidth; if (w < 0.0f) { float region_avail_x = GetContentRegionAvail().x; w = ImMax(1.0f, region_avail_x + w); } w = IM_TRUNC(w); return w; } // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth(). // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical. // Note that only CalcItemWidth() is publicly exposed. // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable) ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h) { ImVec2 avail; if (size.x < 0.0f || size.y < 0.0f) avail = GetContentRegionAvail(); if (size.x == 0.0f) size.x = default_w; else if (size.x < 0.0f) size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting if (size.y == 0.0f) size.y = default_h; else if (size.y < 0.0f) size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting return size; } float ImGui::GetTextLineHeight() { ImGuiContext& g = *GImGui; return g.FontSize; } float ImGui::GetTextLineHeightWithSpacing() { ImGuiContext& g = *GImGui; return g.FontSize + g.Style.ItemSpacing.y; } float ImGui::GetFrameHeight() { ImGuiContext& g = *GImGui; return g.FontSize + g.Style.FramePadding.y * 2.0f; } float ImGui::GetFrameHeightWithSpacing() { ImGuiContext& g = *GImGui; return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; } ImVec2 ImGui::GetContentRegionAvail() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max; return mx - window->DC.CursorPos; } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()! // They are bizarre local-coordinates which don't play well with scrolling. ImVec2 ImGui::GetContentRegionMax() { return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos(); } ImVec2 ImGui::GetWindowContentRegionMin() { ImGuiWindow* window = GImGui->CurrentWindow; return window->ContentRegionRect.Min - window->Pos; } ImVec2 ImGui::GetWindowContentRegionMax() { ImGuiWindow* window = GImGui->CurrentWindow; return window->ContentRegionRect.Max - window->Pos; } #endif // 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.) // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. // FIXME-OPT: Could we safely early out on ->SkipItems? void ImGui::BeginGroup() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; g.GroupStack.resize(g.GroupStack.Size + 1); ImGuiGroupData& group_data = g.GroupStack.back(); group_data.WindowID = window->ID; group_data.BackupCursorPos = window->DC.CursorPos; group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; group_data.BackupIndent = window->DC.Indent; group_data.BackupGroupOffset = window->DC.GroupOffset; group_data.BackupCurrLineSize = window->DC.CurrLineSize; group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; group_data.BackupIsSameLine = window->DC.IsSameLine; group_data.BackupActiveIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedThisFrame; group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive; group_data.EmitItem = true; window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x; window->DC.Indent = window->DC.GroupOffset; window->DC.CursorMaxPos = window->DC.CursorPos; window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); if (g.LogEnabled) g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } void ImGui::EndGroup() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls ImGuiGroupData& group_data = g.GroupStack.back(); IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window? if (window->DC.IsSetPos) ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); // Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543) ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos)); window->DC.CursorPos = group_data.BackupCursorPos; window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine; window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max); window->DC.Indent = group_data.BackupIndent; window->DC.GroupOffset = group_data.BackupGroupOffset; window->DC.CurrLineSize = group_data.BackupCurrLineSize; window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset; window->DC.IsSameLine = group_data.BackupIsSameLine; if (g.LogEnabled) g.LogLinePosY = -FLT_MAX; // To enforce a carriage return if (!group_data.EmitItem) { g.GroupStack.pop_back(); return; } window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // 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()); ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop); // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. // It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. // Also if you grep for LastItemId you'll notice it is only used in that context. // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.) const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; const bool group_contains_deactivated_id = (group_data.BackupDeactivatedIdIsAlive == false) && (g.DeactivatedItemData.IsAlive == true); if (group_contains_curr_active_id) g.LastItemData.ID = g.ActiveId; else if (group_contains_deactivated_id) g.LastItemData.ID = g.DeactivatedItemData.ID; g.LastItemData.Rect = group_bb; // Forward Hovered flag const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0; if (group_contains_curr_hovered_id) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Forward Edited flag if (g.ActiveIdHasBeenEditedThisFrame && !group_data.BackupActiveIdHasBeenEditedThisFrame) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; // Forward Deactivated flag g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated; if (group_contains_deactivated_id) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated; g.GroupStack.pop_back(); if (g.DebugShowGroupRects) window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] } //----------------------------------------------------------------------------- // [SECTION] SCROLLING //----------------------------------------------------------------------------- // Helper to snap on edges when aiming at an item very close to the edge, // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling. // When we refactor the scrolling API this may be configurable with a flag? // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default. static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio) { if (target <= snap_min + snap_threshold) return ImLerp(snap_min, target, center_ratio); if (target >= snap_max - snap_threshold) return ImLerp(target, snap_max, center_ratio); return target; } static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) { ImVec2 scroll = window->Scroll; ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2); for (int axis = 0; axis < 2; axis++) { if (window->ScrollTarget[axis] < FLT_MAX) { float center_ratio = window->ScrollTargetCenterRatio[axis]; float scroll_target = window->ScrollTarget[axis]; if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f) { float snap_min = 0.0f; float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis]; scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio); } scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]); } scroll[axis] = ImRound64(ImMax(scroll[axis], 0.0f)); if (!window->Collapsed && !window->SkipItems) scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]); } return scroll; } void ImGui::ScrollToItem(ImGuiScrollFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ScrollToRectEx(window, g.LastItemData.NavRect, flags); } void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) { ScrollToRectEx(window, item_rect, flags); } // Scroll to keep newly navigated item fully into view ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) { ImGuiContext& g = *GImGui; ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x); scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y); //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG] //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG] // Check that only one behavior is selected per axis IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_)); IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_)); // Defaults ImGuiScrollFlags in_flags = flags; if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX) flags |= ImGuiScrollFlags_KeepVisibleEdgeX; if ((flags & ImGuiScrollFlags_MaskY_) == 0) flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY; const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x; const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y; const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0; const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0; if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) { if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x) SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f); else if (item_rect.Max.x >= scroll_rect.Max.x) SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f); } else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) { if (can_be_fully_visible_x) SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f); else SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f); } if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y) { if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y) SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f); else if (item_rect.Max.y >= scroll_rect.Max.y) SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f); } else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) { if (can_be_fully_visible_y) SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f); else SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f); } ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); ImVec2 delta_scroll = next_scroll - window->Scroll; // Also scroll parent window to keep us into view if necessary if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow)) { // FIXME-SCROLL: May be an option? if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0) in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX; if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0) in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY; delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags); } return delta_scroll; } float ImGui::GetScrollX() { ImGuiWindow* window = GImGui->CurrentWindow; return window->Scroll.x; } float ImGui::GetScrollY() { ImGuiWindow* window = GImGui->CurrentWindow; return window->Scroll.y; } float ImGui::GetScrollMaxX() { ImGuiWindow* window = GImGui->CurrentWindow; return window->ScrollMax.x; } float ImGui::GetScrollMaxY() { ImGuiWindow* window = GImGui->CurrentWindow; return window->ScrollMax.y; } void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x) { window->ScrollTarget.x = scroll_x; window->ScrollTargetCenterRatio.x = 0.0f; window->ScrollTargetEdgeSnapDist.x = 0.0f; } void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y) { window->ScrollTarget.y = scroll_y; window->ScrollTargetCenterRatio.y = 0.0f; window->ScrollTargetEdgeSnapDist.y = 0.0f; } void ImGui::SetScrollX(float scroll_x) { ImGuiContext& g = *GImGui; SetScrollX(g.CurrentWindow, scroll_x); } void ImGui::SetScrollY(float scroll_y) { ImGuiContext& g = *GImGui; SetScrollY(g.CurrentWindow, scroll_y); } // Note that a local position will vary depending on initial scroll value, // This is a little bit confusing so bear with us: // - local_pos = (absolution_pos - window->Pos) // - So local_x/local_y are 0.0f for a position at the upper-left corner of a window, // and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area. // - They mostly exist because of legacy API. // Following the rules above, when trying to work with scrolling code, consider that: // - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect! // - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense // We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) { IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset window->ScrollTargetCenterRatio.x = center_x_ratio; window->ScrollTargetEdgeSnapDist.x = 0.0f; } void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) { IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset window->ScrollTargetCenterRatio.y = center_y_ratio; window->ScrollTargetEdgeSnapDist.y = 0.0f; } void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) { ImGuiContext& g = *GImGui; SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio); } void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio) { ImGuiContext& g = *GImGui; SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio); } // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item. void ImGui::SetScrollHereX(float center_x_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x); float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio); SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos // Tweak: snap on edges when aiming at an item very close to the edge window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x); } // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. void ImGui::SetScrollHereY(float center_y_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y); float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos // Tweak: snap on edges when aiming at an item very close to the edge window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y); } //----------------------------------------------------------------------------- // [SECTION] TOOLTIPS //----------------------------------------------------------------------------- bool ImGui::BeginTooltip() { return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } bool ImGui::BeginItemTooltip() { if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip)) return false; return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget; if (is_dragdrop_tooltip) { // Drag and Drop tooltips are positioning differently than other tooltips: // - offset visibility to increase visibility around mouse. // - never clamp within outer viewport boundary. // We call SetNextWindowPos() to enforce position and disable clamping. // See FindBestWindowPosForPopup() for positioning logic of other tooltips (not drag and drop ones). //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen); if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0) { ImVec2 tooltip_pos = is_touchscreen ? (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * g.Style.MouseCursorScale) : (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * g.Style.MouseCursorScale); ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f); SetNextWindowPos(tooltip_pos, ImGuiCond_None, tooltip_pivot); } SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkerboard has issue with transparent colors :( tooltip_flags |= ImGuiTooltipFlags_OverridePrevious; } // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active && !IsWindowInBeginStack(g.TooltipPreviousWindow)) { //IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name); SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow); g.TooltipOverrideCount++; } const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d"; char window_name[32]; ImFormatString(window_name, IM_COUNTOF(window_name), window_name_template, g.TooltipOverrideCount); ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking; Begin(window_name, NULL, flags | extra_window_flags); // 2023-03-09: Added bool return value to the API, but currently always returning true. // If this ever returns false we need to update BeginDragDropSource() accordingly. //if (!ret) // End(); //return ret; return true; } void ImGui::EndTooltip() { IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls End(); } void ImGui::SetTooltip(const char* fmt, ...) { va_list args; va_start(args, fmt); SetTooltipV(fmt, args); va_end(args); } void ImGui::SetTooltipV(const char* fmt, va_list args) { if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) return; TextV(fmt, args); EndTooltip(); } // Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'. // Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse. void ImGui::SetItemTooltip(const char* fmt, ...) { va_list args; va_start(args, fmt); if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) SetTooltipV(fmt, args); va_end(args); } void ImGui::SetItemTooltipV(const char* fmt, va_list args) { if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) SetTooltipV(fmt, args); } //----------------------------------------------------------------------------- // [SECTION] POPUPS //----------------------------------------------------------------------------- // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; if (popup_flags & ImGuiPopupFlags_AnyPopupId) { // Return true if any popup is open at the current BeginPopup() level of the popup stack // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level. IM_ASSERT(id == 0); if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) return g.OpenPopupStack.Size > 0; else return g.OpenPopupStack.Size > g.BeginPopupStack.Size; } else { if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) { // Return true if the popup is open anywhere in the popup stack for (ImGuiPopupData& popup_data : g.OpenPopupStack) if (popup_data.PopupId == id) return true; return false; } else { // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query) return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id; } } } bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id); if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0) IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally return IsPopupOpen(id, popup_flags); } // Also see FindBlockingModal(NULL) ImGuiWindow* ImGui::GetTopMostPopupModal() { ImGuiContext& g = *GImGui; for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) if (popup->Flags & ImGuiWindowFlags_Modal) return popup; return NULL; } // See Demo->Stacked Modal to confirm what this is for. ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() { ImGuiContext& g = *GImGui; for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup)) return popup; return NULL; } // When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) // should be positioned behind that modal window, unless the window was created inside the modal begin-stack. // In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. // - WindowA // FindBlockingModal() returns Modal1 // - WindowB // .. returns Modal1 // - Modal1 // .. returns Modal2 // - WindowC // .. returns Modal2 // - WindowD // .. returns Modal2 // - Modal2 // .. returns Modal2 // - WindowE // .. returns NULL // Notes: // - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL. // Only difference is here we check for ->Active/WasActive but it may be unnecessary. ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.OpenPopupStack.Size <= 0) return NULL; // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal. for (ImGuiPopupData& popup_data : g.OpenPopupStack) { ImGuiWindow* popup_window = popup_data.Window; if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal)) continue; if (!popup_window->Active && !popup_window->WasActive) // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows. continue; if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click. return popup_window; if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal continue; return popup_window; // Place window right below first block modal } return NULL; } void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiID id = g.CurrentWindow->GetID(str_id); IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id); OpenPopupEx(id, popup_flags); } void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) { OpenPopupEx(id, popup_flags); } // Mark popup as open (toggle toward open state). // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; const int current_stack_size = g.BeginPopupStack.Size; if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup) if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId)) return; ImGuiPopupData 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; popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type). popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); popup_ref.OpenPopupPos = NavCalcPreferredRefPos(ImGuiWindowFlags_Popup); popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id); if (g.OpenPopupStack.Size < current_stack_size + 1) { g.OpenPopupStack.push_back(popup_ref); } else { // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake! // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer. // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified. bool keep_existing = false; if (g.OpenPopupStack[current_stack_size].PopupId == id) if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen)) keep_existing = true; if (keep_existing) { // No reopen g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; } else { // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation) ClosePopupToLevel(current_stack_size, true); g.OpenPopupStack.push_back(popup_ref); } // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). // This is equivalent to what ClosePopupToLevel() does. //if (g.OpenPopupStack[current_stack_size].PopupId == id) // FocusWindow(parent_window); } } // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. // This function closes any popups that are over 'ref_window'. void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; if (g.OpenPopupStack.Size == 0) return; // Don't close our own child popup windows. //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "", restore_focus_to_window_under_popup); int popup_count_to_keep = 0; if (ref_window) { // 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++) { ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep]; if (!popup.Window) continue; IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow) // - Clicking/Focusing Window2 won't close Popup1: // Window -> Popup1 -> Window2(Ref) // - Clicking/focusing Popup1 will close Popup2 and Popup3: // Window -> Popup1(Ref) -> Popup2 -> Popup3 // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree! // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child // We step through every popup from bottom to top to validate their position relative to reference window. bool ref_window_is_descendant_of_popup = false; for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) //if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree) // FIXME-MERGE if (IsWindowWithinBeginStackOf(ref_window, popup_window)) { ref_window_is_descendant_of_popup = true; break; } if (!ref_window_is_descendant_of_popup) break; } } 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_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : ""); ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); } } void ImGui::ClosePopupsExceptModals() { ImGuiContext& g = *GImGui; int popup_count_to_keep; for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--) { ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window; if (!window || (window->Flags & ImGuiWindowFlags_Modal)) break; } 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 ClosePopupToLevel(popup_count_to_keep, true); } void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup); IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) for (int n = remaining; n < g.OpenPopupStack.Size; n++) IMGUI_DEBUG_LOG_POPUP("[popup] - Closing PopupID 0x%08X Window \"%s\"\n", g.OpenPopupStack[n].PopupId, g.OpenPopupStack[n].Window ? g.OpenPopupStack[n].Window->Name : NULL); // Trim open popup stack ImGuiPopupData prev_popup = g.OpenPopupStack[remaining]; g.OpenPopupStack.resize(remaining); // Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case) if (restore_focus_to_window_under_popup && prev_popup.Window) { ImGuiWindow* popup_window = prev_popup.Window; ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow; if (focus_window && !focus_window->WasActive) FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback else FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None); } } // Close the popup we have begin-ed into. void ImGui::CloseCurrentPopup() { ImGuiContext& g = *GImGui; 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; // 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 && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar)) close_parent = true; if (!close_parent) break; popup_idx--; } IMGUI_DEBUG_LOG_POPUP("[popup] 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; } // Attention! BeginPopup() adds default flags when calling BeginPopupEx()! bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; if (!IsPopupOpen(id, ImGuiPopupFlags_None)) { g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values return false; } char name[20]; IM_ASSERT((extra_window_flags & ImGuiWindowFlags_ChildMenu) == 0); // Use BeginPopupMenuEx() ImFormatString(name, IM_COUNTOF(name), "##Popup_%08x", id); // No recycling, so we can close/open during the same frame bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack; return is_open; } bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; if (!IsPopupOpen(id, ImGuiPopupFlags_None)) { g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values return false; } char name[128]; IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu); ImFormatString(name, IM_COUNTOF(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack; return is_open; } bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance { g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values return false; } flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; ImGuiID id = g.CurrentWindow->GetID(str_id); return BeginPopupEx(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 Dear ImGui (and manipulated with e.g. OpenPopup). // - *p_open set back to false in BeginPopupModal() when popup is not open. // - if you set *p_open to false before calling BeginPopupModal(), it will close the popup. bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImGuiID id = window->GetID(name); if (!IsPopupOpen(id, ImGuiPopupFlags_None)) { g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values if (p_open && *p_open) *p_open = false; return false; } // Center modal windows by default for increased visibility // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves) // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0) { const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport? SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); } flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking; 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) ClosePopupToLevel(g.BeginPopupStack.Size, true); return false; } return is_open; } void ImGui::EndPopup() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT_USER_ERROR_RET((window->Flags & ImGuiWindowFlags_Popup) != 0 && g.BeginPopupStack.Size > 0, "Calling EndPopup() in wrong window!"); // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests) if (g.NavWindow == window) NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); // Child-popups don't need to be laid out const ImGuiID backup_within_end_child_id = g.WithinEndChildID; if (window->Flags & ImGuiWindowFlags_ChildWindow) g.WithinEndChildID = window->ID; End(); g.WithinEndChildID = backup_within_end_child_id; } ImGuiMouseButton ImGui::GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags) { #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS if ((flags & ImGuiPopupFlags_InvalidMask_) != 0) // 1,2 --> ImGuiMouseButton_Right, ImGuiMouseButton_Middle return (flags & ImGuiPopupFlags_InvalidMask_); #else IM_ASSERT((flags & ImGuiPopupFlags_InvalidMask_) == 0); #endif if (flags & ImGuiPopupFlags_MouseButtonMask_) return ((flags & ImGuiPopupFlags_MouseButtonMask_) >> ImGuiPopupFlags_MouseButtonShift_) - 1; return ImGuiMouseButton_Right; // Default == 1 } bool ImGui::IsPopupOpenRequestForItem(ImGuiPopupFlags popup_flags, ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) return true; if (g.NavOpenContextMenuItemId == id && (IsItemFocused() || id == g.CurrentWindow->MoveId)) return true; return false; } bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) return true; if (g.NavOpenContextMenuWindowId && g.CurrentWindow->ID) if (IsWindowChildOf(g.NavWindow, g.CurrentWindow, false, false)) // This enable ordering to be used to disambiguate item vs window (#8803) return true; return false; } // Helper to open a popup if mouse button is released over the item // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID)) { ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) OpenPopupEx(id, popup_flags); } } // This is a helper to handle the simplest case of associating one named popup to one given widget. // - To create a popup associated to the last item, you generally want to pass a NULL value to str_id. // - To create a popup with a specific identifier, pass it in str_id. // - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call. // - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id. // - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). // This is essentially the same as: // id = str_id ? GetID(str_id) : GetItemID(); // OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight); // return BeginPopup(id); // Which is essentially the same as: // id = str_id ? GetID(str_id) : GetItemID(); // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) // OpenPopup(id); // return BeginPopup(id); // The main difference being that this is tweaked to avoid computing the ID twice. bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItem ID. Using LastItem ID as a Popup ID won't conflict! IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID)) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!str_id) str_id = "window_context"; ImGuiID id = window->GetID(str_id); if (IsPopupOpenRequestForWindow(popup_flags)) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!str_id) str_id = "void_context"; ImGuiID id = window->GetID(str_id); ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) if (GetTopMostPopupModal() == NULL) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor // information are available, it may represent the entire platform monitor from the frame of reference of the current viewport. // this allows us to have tooltips/popups displayed out of the parent viewport.) 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); //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) { const ImGuiDir dir_preferred_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) { const ImGuiDir dir = (n == -1) ? *last_dir : dir_preferred_order[n]; if (n != -1 && dir == *last_dir) // Already tried this direction? continue; ImVec2 pos; if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left if (!r_outer.Contains(ImRect(pos, pos + size))) continue; *last_dir = dir; return pos; } } // Tooltip and Default popup policy // (Always first try the direction we used on the last frame, if any) if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default) { const ImGuiDir dir_preferred_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) { const ImGuiDir dir = (n == -1) ? *last_dir : dir_preferred_order[n]; if (n != -1 && dir == *last_dir) // Already tried this direction? continue; const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x); const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y); // If there's not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width) if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right)) continue; if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down)) continue; ImVec2 pos; pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; // Clamp top-left corner of popup pos.x = ImMax(pos.x, r_outer.Min.x); pos.y = ImMax(pos.y, r_outer.Min.y); *last_dir = dir; return pos; } } // Fallback when not enough room: *last_dir = ImGuiDir_None; // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. if (policy == ImGuiPopupPositionPolicy_Tooltip) return ref_pos + ImVec2(2, 2); // Otherwise try to keep within display ImVec2 pos = ref_pos; pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); return pos; } // Note that this is used for popups, which can overlap the non work-area of individual viewports. ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImRect r_screen; if (window->ViewportAllowPlatformMonitorExtend >= 0) { // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; r_screen.Min = monitor.WorkPos; r_screen.Max = monitor.WorkPos + monitor.WorkSize; } else { // Use the full viewport area (not work area) for popups r_screen = window->Viewport->GetMainRect(); } ImVec2 padding = g.Style.DisplaySafeAreaPadding; r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); return r_screen; } ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImRect r_outer = GetPopupAllowedExtentRect(window); if (window->Flags & ImGuiWindowFlags_ChildMenu) { // 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. ImGuiWindow* parent_window = window->ParentWindow; 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->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field else r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); } if (window->Flags & ImGuiWindowFlags_Popup) { return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here } if (window->Flags & ImGuiWindowFlags_Tooltip) { // Position tooltip (always follows mouse + clamp within outer boundaries) // FIXME: // - Too many paths. One problem is that FindBestWindowPosForPopupEx() doesn't allow passing a suggested position (so touch screen path doesn't use it by default). // - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position. // - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling // as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin(). IM_ASSERT(g.CurrentWindow == window); const float scale = g.Style.MouseCursorScale; const ImVec2 ref_pos = NavCalcPreferredRefPos(ImGuiWindowFlags_Tooltip); if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource(ImGuiWindowFlags_Tooltip) == ImGuiInputSource_Mouse) { ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size); if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size))) return tooltip_pos; } ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale; ImRect r_avoid; if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos) r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255)); return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); } IM_ASSERT(0); return window->Pos; } //----------------------------------------------------------------------------- // [SECTION] WINDOW FOCUS //---------------------------------------------------------------------------- // - SetWindowFocus() // - SetNextWindowFocus() // - IsWindowFocused() // - UpdateWindowInFocusOrderList() [Internal] // - BringWindowToFocusFront() [Internal] // - BringWindowToDisplayFront() [Internal] // - BringWindowToDisplayBack() [Internal] // - BringWindowToDisplayBehind() [Internal] // - FindWindowDisplayIndex() [Internal] // - FocusWindow() [Internal] // - FocusTopMostWindowUnderOne() [Internal] //----------------------------------------------------------------------------- void ImGui::SetWindowFocus() { FocusWindow(GImGui->CurrentWindow); } void ImGui::SetWindowFocus(const char* name) { if (name) { if (ImGuiWindow* window = FindWindowByName(name)) FocusWindow(window); } else { FocusWindow(NULL); } } void ImGui::SetNextWindowFocus() { ImGuiContext& g = *GImGui; g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasFocus; } // Similar to IsWindowHovered() bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* ref_window = g.NavWindow; ImGuiWindow* cur_window = g.CurrentWindow; if (ref_window == NULL) return false; if (flags & ImGuiFocusedFlags_AnyWindow) return true; IM_ASSERT(cur_window); // Not inside a Begin()/End() const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0; if (flags & ImGuiFocusedFlags_RootWindow) cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy); if (flags & ImGuiFocusedFlags_ChildWindows) return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy); else return ref_window == cur_window; } static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_UNUSED(g); int order = window->FocusOrder; IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking) IM_ASSERT(g.WindowsFocusOrder[order] == window); return order; } static void ImGui::UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) { ImGuiContext& g = *GImGui; const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0); const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; if ((just_created || child_flag_changed) && !new_is_explicit_child) { IM_ASSERT(!g.WindowsFocusOrder.contains(window)); g.WindowsFocusOrder.push_back(window); window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); } else if (!just_created && child_flag_changed && new_is_explicit_child) { IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) g.WindowsFocusOrder[n]->FocusOrder--; g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); window->FocusOrder = -1; } window->IsExplicitChild = new_is_explicit_child; } void ImGui::BringWindowToFocusFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_ASSERT(window == window->RootWindow); const int cur_order = window->FocusOrder; IM_ASSERT(g.WindowsFocusOrder[cur_order] == window); if (g.WindowsFocusOrder.back() == window) return; const int new_order = g.WindowsFocusOrder.Size - 1; for (int n = cur_order; n < new_order; n++) { g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1]; g.WindowsFocusOrder[n]->FocusOrder--; IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n); } g.WindowsFocusOrder[new_order] = window; window->FocusOrder = (short)new_order; } // Note technically focus related but rather adjacent and close to BringWindowToFocusFront() // FIXME-FOCUS: Could opt-in/opt-out enable modal check like in FocusWindow(). void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindow* current_front_window = g.Windows.back(); if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better) return; for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window if (g.Windows[i] == 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::BringWindowToDisplayBack(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.Windows[0] == window) return; for (int i = 0; i < g.Windows.Size; i++) if (g.Windows[i] == window) { memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*)); g.Windows[0] = window; break; } } void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window) { IM_ASSERT(window != NULL && behind_window != NULL); ImGuiContext& g = *GImGui; window = window->RootWindow; behind_window = behind_window->RootWindow; int pos_wnd = FindWindowDisplayIndex(window); int pos_beh = FindWindowDisplayIndex(behind_window); if (pos_wnd < pos_beh) { size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*); memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes); g.Windows[pos_beh - 1] = window; } else { size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*); memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes); g.Windows[pos_beh] = window; } } int ImGui::FindWindowDisplayIndex(ImGuiWindow* window) { ImGuiContext& g = *GImGui; return g.Windows.index_from_ptr(g.Windows.find(window)); } // Moving window to front of display and set focus (which happens to be back of our sorted list) void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags) { ImGuiContext& g = *GImGui; // Modal check? if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case. if (ImGuiWindow* blocking_modal = FindBlockingModal(window)) { // This block would typically be reached in two situations: // - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag. // - User clicking on void or anything behind a modal while a modal is open (window == NULL) IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "", blocking_modal->Name); if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?) ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals return; } // Find last focused child (if any) and focus it instead. if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL) window = NavRestoreLastChildNavWindow(window); // Apply focus if (g.NavWindow != window) { SetNavWindow(window); if (window && g.NavHighlightItemUnderNav) g.NavMousePosDirty = true; g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavLayer = ImGuiNavLayer_Main; SetNavFocusScope(window ? window->NavRootFocusScopeId : 0); g.NavIdIsAlive = false; g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; // Close popups if any ClosePopupsOverWindow(window, false); } // Move the root window to the top of the pile IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL); ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL; ImGuiDockNode* dock_node = window ? window->DockNode : NULL; bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow); // Steal active widgets. Some of the cases it triggers includes: // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window. if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window) if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host) ClearActiveID(); // Passing NULL allow to disable keyboard focus if (!window) return; window->LastFrameJustFocused = g.FrameCount; // Select in dock node // For #2304 we avoid applying focus immediately before the tabbar is visible. //if (dock_node && dock_node->TabBar) // dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId; // Bring to front BringWindowToFocusFront(focus_front_window); if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) BringWindowToDisplayFront(display_front_window); } void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags) { ImGuiContext& g = *GImGui; int start_idx = g.WindowsFocusOrder.Size - 1; if (under_this_window != NULL) { // Aim at root window behind us, if we are in a child window that's our own root (see #4640) int offset = -1; while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow) { under_this_window = under_this_window->ParentWindow; offset = 0; } start_idx = FindWindowFocusIndex(under_this_window) + offset; } for (int i = start_idx; 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) continue; if (filter_viewport != NULL && window->Viewport != filter_viewport) continue; if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { // FIXME-DOCK: When ImGuiFocusRequestFlags_RestoreFocusedChild is set... // This is failing (lagging by one frame) for docked windows. // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B. // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update) // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself? FocusWindow(window, flags); return; } } FocusWindow(NULL, flags); } //----------------------------------------------------------------------------- // [SECTION] KEYBOARD/GAMEPAD NAVIGATION //----------------------------------------------------------------------------- // FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked. // In our terminology those should be interchangeable, yet right now this is super confusing. // Those two functions are merely a legacy artifact, so at minimum naming should be clarified. void ImGui::SetNavCursorVisible(bool visible) { ImGuiContext& g = *GImGui; if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) visible = false; else if (g.IO.ConfigNavCursorVisibleAlways) visible = true; g.NavCursorVisible = visible; } // (was called NavRestoreHighlightAfterMove() before 1.91.4) void ImGui::SetNavCursorVisibleAfterMove() { ImGuiContext& g = *GImGui; if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) g.NavCursorVisible = false; else if (g.NavInputSource == ImGuiInputSource_Keyboard && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) == 0) g.NavCursorVisible = false; else if (g.NavInputSource == ImGuiInputSource_Gamepad && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) g.NavCursorVisible = false; else if (g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = true; g.NavHighlightItemUnderNav = g.NavMousePosDirty = true; } void ImGui::SetNavWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.NavWindow != window) { IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : ""); g.NavWindow = window; g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; } g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); } void ImGui::NavHighlightActivated(ImGuiID id) { ImGuiContext& g = *GImGui; g.NavHighlightActivatedId = id; g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER; } void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis) { ImGuiContext& g = *GImGui; g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX; } void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); g.NavId = id; g.NavLayer = nav_layer; SetNavFocusScope(focus_scope_id); g.NavWindow->NavLastIds[nav_layer] = id; g.NavWindow->NavRectRel[nav_layer] = rect_rel; // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it) NavClearPreferredPosForAxis(ImGuiAxis_X); NavClearPreferredPosForAxis(ImGuiAxis_Y); } void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); if (g.NavWindow != window) SetNavWindow(window); // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid. // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text) const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; g.NavId = id; g.NavLayer = nav_layer; SetNavFocusScope(g.CurrentFocusScopeId); window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); g.NavIdItemFlags = (g.LastItemData.ID == id) ? g.LastItemData.ItemFlags : ImGuiItemFlags_None; if (id == g.ActiveIdIsAlive) g.NavIdIsAlive = true; if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) g.NavHighlightItemUnderNav = true; else if (g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = false; // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it) NavClearPreferredPosForAxis(ImGuiAxis_X); NavClearPreferredPosForAxis(ImGuiAxis_Y); } static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) { if (ImFabs(dx) > ImFabs(dy)) return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; } static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max) { if (cand_max < curr_min) return cand_max - curr_min; if (curr_max < cand_min) return cand_min - curr_max; return 0.0f; } // Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057 static bool ImGui::NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavLayer != window->DC.NavLayerCurrent) return false; // FIXME: Those are not good variables names ImRect cand = nav_bb; // Current item nav rectangle const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) g.NavScoringDebugCount++; // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring if (window->ParentWindow == g.NavWindow) { IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened); if (!window->ClipRect.Overlaps(cand)) return false; cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window } // Compute distance between boxes // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items if (dby != 0.0f && dbx != 0.0f) dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); float dist_box = ImFabs(dbx) + ImFabs(dby); // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance ImGuiDir quadrant; float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; if (dbx != 0.0f || dby != 0.0f) { // For non-overlapping boxes, use distance between boxes // FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail. // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty. dax = dbx; day = dby; dist_axial = dist_box; quadrant = ImGetDirQuadrantFromDelta(dbx, dby); } else if (dcx != 0.0f || dcy != 0.0f) { // For overlapping boxes with different centers, use distance between centers dax = dcx; day = dcy; dist_axial = dist_center; quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); } else { // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } const ImGuiDir move_dir = g.NavMoveDir; #if IMGUI_DEBUG_NAV_SCORING char buf[200]; if (g.IO.KeyCtrl) // Hold Ctrl to preview score in matching quadrant. Ctrl+Arrow to rotate. { if (quadrant == move_dir) { ImFormatString(buf, IM_COUNTOF(buf), "%.0f/%.0f", dist_box, dist_center); ImDrawList* draw_list = GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80)); draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200)); draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf); } } const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max); const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space)); if (debug_hovering || debug_tty) { ImFormatString(buf, IM_COUNTOF(buf), "d-box (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]); if (debug_hovering) { ImDrawList* draw_list = 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 + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200)); draw_list->AddText(cand.Max, ~0U, buf); } if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); } } #endif // Is it in the quadrant we're interested in moving to? bool new_best = false; if (quadrant == move_dir) { // Does it beat the current best candidate? if (dist_box < result->DistBox) { result->DistBox = dist_box; result->DistCenter = dist_center; return true; } if (dist_box == result->DistBox) { // Try using distance between center points to break ties if (dist_center < result->DistCenter) { result->DistCenter = dist_center; new_best = true; } else if (dist_center == result->DistCenter) { // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance new_best = true; } } } // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f)) { result->DistAxial = dist_axial; new_best = true; } return new_best; } static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; result->Window = window; result->ID = g.LastItemData.ID; result->FocusScopeId = g.CurrentFocusScopeId; result->ItemFlags = g.LastItemData.ItemFlags; result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) { IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid); result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData. } } // True when current work location may be scrolled horizontally when moving left / right. // This is generally always true UNLESS within a column. We don't have a vertical equivalent. void ImGui::NavUpdateCurrentWindowIsScrollPushableX() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL); } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) // This is called after LastItemData is set, but NextItemData is also still valid. static void ImGui::NavProcessItem() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImGuiID id = g.LastItemData.ID; const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags; // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816) ImRect nav_bb = g.LastItemData.NavRect; if (window->DC.NavIsScrollPushableX == false) { nav_bb.Min.x = ImClamp(nav_bb.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x); nav_bb.Max.x = ImClamp(nav_bb.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x); } // Process Init Request if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0; if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0) { NavApplyItemToResult(&g.NavInitResult); } if (candidate_for_nav_default_focus) { g.NavInitRequest = false; // Found a match, clear request NavUpdateAnyRequestFlag(); } } // Process Move Request (scoring for navigation) // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0) { if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0) { const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0; if (is_tabbing) { NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags); } else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) { ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; if (NavScoreItem(result, nav_bb)) NavApplyItemToResult(result); // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. const float VISIBLE_RATIO = 0.70f; if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) { const ImRect& r = window->InnerRect; // window->ClipRect if (r.Overlaps(nav_bb)) if (ImClamp(nav_bb.Max.y, r.Min.y, r.Max.y) - ImClamp(nav_bb.Min.y, r.Min.y, r.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) if (NavScoreItem(&g.NavMoveResultLocalVisible, nav_bb)) NavApplyItemToResult(&g.NavMoveResultLocalVisible); } } } } // Update information for currently focused/navigated item if (g.NavId == id) { if (g.NavWindow != window) SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window. g.NavLayer = window->DC.NavLayerCurrent; SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath g.NavFocusScopeId = g.CurrentFocusScopeId; g.NavIdIsAlive = true; g.NavIdItemFlags = item_flags; if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData) { IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid); g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData. } window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) } } // Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest(). // Note that SetKeyboardFocusHere() API calls are considered tabbing requests! // - Case 1: no nav/active id: set result to first eligible item, stop storing. // - Case 2: tab forward: on ref id set counter, on counter elapse store result // - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request // - Case 4: tab backward: store all results, on ref id pick prev, stop storing // - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags) { ImGuiContext& g = *GImGui; if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0) { if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent) return; if (g.NavFocusScopeId != g.CurrentFocusScopeId) return; } // - Can always land on an item when using API call. // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item. // - Tabbing without _NavEnableKeyboard: goes through inputable items only. bool can_stop; if (move_flags & ImGuiNavMoveFlags_FocusApi) can_stop = true; else can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable)); // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows) ImGuiNavItemData* result = &g.NavMoveResultLocal; if (g.NavTabbingDir == +1) { // Tab Forward or SetKeyboardFocusHere() with >= 0 if (can_stop && g.NavTabbingResultFirst.ID == 0) NavApplyItemToResult(&g.NavTabbingResultFirst); if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0) NavMoveRequestResolveWithLastItem(result); else if (g.NavId == id) g.NavTabbingCounter = 1; } else if (g.NavTabbingDir == -1) { // Tab Backward if (g.NavId == id) { if (result->ID) { g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); } } else if (can_stop) { // Keep applying until reaching NavId NavApplyItemToResult(result); } } else if (g.NavTabbingDir == 0) { if (can_stop && g.NavId == id) NavMoveRequestResolveWithLastItem(result); if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init NavApplyItemToResult(&g.NavTabbingResultFirst); } } bool ImGui::NavMoveRequestButNoResultYet() { ImGuiContext& g = *GImGui; return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; } // FIXME: ScoringRect is not set void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name); if (move_flags & ImGuiNavMoveFlags_IsTabbing) move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; g.NavMoveSubmitted = g.NavMoveScoringItems = true; g.NavMoveDir = move_dir; g.NavMoveDirForDebug = move_dir; g.NavMoveClipDir = clip_dir; g.NavMoveFlags = move_flags; g.NavMoveScrollFlags = scroll_flags; g.NavMoveForwardToNextFrame = false; g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods; g.NavMoveResultLocal.Clear(); g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultOther.Clear(); g.NavTabbingCounter = 0; g.NavTabbingResultFirst.Clear(); NavUpdateAnyRequestFlag(); } void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) { ImGuiContext& g = *GImGui; g.NavMoveScoringItems = false; // Ensure request doesn't need more processing NavApplyItemToResult(result); NavUpdateAnyRequestFlag(); } // Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsToParent void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data) { ImGuiContext& g = *GImGui; g.NavMoveScoringItems = false; g.LastItemData.ID = tree_node_data->ID; g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper). g.LastItemData.NavRect = tree_node_data->NavRect; NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult() NavClearPreferredPosForAxis(ImGuiAxis_Y); NavUpdateAnyRequestFlag(); } void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); } // Forward will reuse the move request again on the next frame (generally with modifications done to it) void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavMoveForwardToNextFrame == false); NavMoveRequestCancel(); g.NavMoveForwardToNextFrame = true; g.NavMoveDir = move_dir; g.NavMoveClipDir = clip_dir; g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded; g.NavMoveScrollFlags = scroll_flags; } // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags) { ImGuiContext& g = *GImGui; IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it: // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest(). if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == window->DC.NavLayerCurrent) g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags; } // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). // This way we could find the last focused window among our children. It would be much less confusing this way? static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) { ImGuiWindow* parent = nav_window; while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) parent = parent->ParentWindow; if (parent && parent != nav_window) parent->NavLastChildNavWindow = nav_window; } // Restore the last focused child. // Call when we are expected to land on the Main Layer (0) after FocusWindow() static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) { if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive) return window->NavLastChildNavWindow; if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar) if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar)) return tab->Window; return window; } void ImGui::NavRestoreLayer(ImGuiNavLayer layer) { ImGuiContext& g = *GImGui; if (layer == ImGuiNavLayer_Main) { ImGuiWindow* prev_nav_window = g.NavWindow; g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests? g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; if (prev_nav_window) IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name); } ImGuiWindow* window = g.NavWindow; if (window->NavLastIds[layer] != 0) { SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); } else { g.NavLayer = layer; NavInitWindow(window, true); } } static inline void ImGui::NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); if (g.NavAnyRequest) IM_ASSERT(g.NavWindow != NULL); } // This needs to be called before we submit any widget (aka in or before Begin) void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { // FIXME: ChildWindow test here is wrong for docking ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); if (window->Flags & ImGuiWindowFlags_NoNavInputs) { g.NavId = 0; SetNavFocusScope(window->NavRootFocusScopeId); return; } bool init_for_nav = false; if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) init_for_nav = true; IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); if (init_for_nav) { SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect()); g.NavInitRequest = true; g.NavInitRequestFromMove = false; g.NavInitResult.ID = 0; NavUpdateAnyRequestFlag(); } else { g.NavId = window->NavLastIds[0]; SetNavFocusScope(window->NavRootFocusScopeId); } } // Positioning logic altered slightly for remote activation: for Popup we want to use item rect, for Tooltip we leave things alone. (#9138) // When calling for ImGuiWindowFlags_Popup we use LastItemData. static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource(ImGuiWindowFlags window_type) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; if ((window_type & ImGuiWindowFlags_Popup) && activated_shortcut) return ImGuiInputSource_Keyboard; if (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) return ImGuiInputSource_Mouse; else return ImGuiInputSource_Keyboard; // or Nav in general } static ImVec2 ImGui::NavCalcPreferredRefPos(ImGuiWindowFlags window_type) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; ImGuiInputSource source = NavCalcPreferredRefPosSource(window_type); if (source == ImGuiInputSource_Mouse) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard. // In theory we could move that +1.0f offset in OpenPopupEx() ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos; return ImVec2(p.x + 1.0f, p.y); } else { // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; ImRect ref_rect; if (activated_shortcut && (window_type & ImGuiWindowFlags_Popup)) ref_rect = g.LastItemData.NavRect; else if (window != NULL) ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?) if (window != NULL && window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) { ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); ref_rect.Translate(window->Scroll - next_scroll); } ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight())); if (window != NULL) if (ImGuiViewport* viewport = window->Viewport) pos = ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size); return ImTrunc(pos); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } } float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis) { ImGuiContext& g = *GImGui; float repeat_delay, repeat_rate; GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate); ImGuiKey key_less, key_more; if (g.NavInputSource == ImGuiInputSource_Gamepad) { key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp; key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown; } else { key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow; key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow; } float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate); if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase amount = 0.0f; return amount; } static void ImGui::NavUpdate() { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; io.WantSetMousePos = false; //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard) // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource? const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown }; if (nav_gamepad_active) for (ImGuiKey key : nav_gamepad_keys_to_change_source) if (IsKeyDown(key)) g.NavInputSource = ImGuiInputSource_Gamepad; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow }; if (nav_keyboard_active) for (ImGuiKey key : nav_keyboard_keys_to_change_source) if (IsKeyDown(key)) g.NavInputSource = ImGuiInputSource_Keyboard; // Process navigation init request (select first/default focus) g.NavJustMovedToId = 0; g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0; if (g.NavInitResult.ID != 0) NavInitRequestApplyResult(); g.NavInitRequest = false; g.NavInitRequestFromMove = false; g.NavInitResult.ID = 0; // Process navigation move request if (g.NavMoveSubmitted) NavMoveRequestApplyResult(); g.NavTabbingCounter = 0; g.NavMoveSubmitted = g.NavMoveScoringItems = false; if (g.NavCursorHideFrames > 0) if (--g.NavCursorHideFrames == 0) g.NavCursorVisible = true; // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling) bool set_mouse_pos = false; if (g.NavMousePosDirty && g.NavIdIsAlive) if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow) set_mouse_pos = true; g.NavMousePosDirty = false; IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) NavSaveLastChildNavWindowIntoParent(g.NavWindow); if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) g.NavWindow->NavLastChildNavWindow = NULL; // Update Ctrl+Tab and Windowing features (hold Square to move/resize/etc.) NavUpdateWindowing(); // Set output flags for user application io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL); // Process NavCancel input (to close a popup, get back to parent, clear focus) NavUpdateCancelRequest(); NavUpdateContextMenuRequest(); // Process manual activation request g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0; g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner)); const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner))); const bool input_pressed_keyboard = nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner)); bool input_pressed_gamepad = false; if (activate_down && nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner) && (g.NavIdItemFlags & ImGuiItemFlags_Inputable)) // requires ImGuiItemFlags_Inputable to avoid retriggering regular buttons. if (GetKeyData(ImGuiKey_NavGamepadActivate)->DownDurationPrev < NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY && GetKeyData(ImGuiKey_NavGamepadActivate)->DownDuration >= NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY) input_pressed_gamepad = true; if (g.ActiveId == 0 && activate_pressed) { g.NavActivateId = g.NavId; g.NavActivateFlags = ImGuiActivateFlags_PreferTweak; } if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (input_pressed_keyboard || input_pressed_gamepad)) { g.NavActivateId = g.NavId; g.NavActivateFlags = ImGuiActivateFlags_PreferInput; } if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_pressed_keyboard || input_pressed_gamepad)) // FIXME-NAV: Unsure why input_pressed_xxx (migrated from input_down which was already dubious) g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed_keyboard || input_pressed_gamepad)) { g.NavActivatePressedId = g.NavId; NavHighlightActivated(g.NavId); } } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) g.NavCursorVisible = false; else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0) g.NavCursorVisible = true; if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); // Highlight if (g.NavHighlightActivatedTimer > 0.0f) g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime); if (g.NavHighlightActivatedTimer == 0.0f) g.NavHighlightActivatedId = 0; // Process programmatic activation request // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) if (g.NavNextActivateId != 0) { g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId; g.NavActivateFlags = g.NavNextActivateFlags; } g.NavNextActivateId = 0; // Process move requests NavUpdateCreateMoveRequest(); if (g.NavMoveDir == ImGuiDir_None) NavUpdateCreateTabbingRequest(); NavUpdateAnyRequestFlag(); g.NavIdIsAlive = false; // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; const float scroll_speed = IM_ROUND(window->FontRefSize * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. const ImGuiDir move_dir = g.NavMoveDir; if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None) { if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); } // *Normal* Manual scroll with LStick // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. if (nav_gamepad_active) { const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f; if (scroll_dir.x != 0.0f && window->ScrollbarX) SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor)); if (scroll_dir.y != 0.0f) SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor)); } } // Always prioritize mouse highlight if navigation is disabled if (!nav_keyboard_active && !nav_gamepad_active) { g.NavCursorVisible = false; g.NavHighlightItemUnderNav = set_mouse_pos = false; } // Update mouse position if requested // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) TeleportMousePos(NavCalcPreferredRefPos(ImGuiWindowFlags_Popup)); // [DEBUG] g.NavScoringDebugCount = 0; #if IMGUI_DEBUG_NAV_RECTS if (ImGuiWindow* debug_window = g.NavWindow) { ImDrawList* draw_list = GetForegroundDrawList(debug_window); int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); } //if (1) { ImU32 col = (!debug_window->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 } void ImGui::NavInitRequestApplyResult() { // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) ImGuiContext& g = *GImGui; if (!g.NavWindow) return; ImGuiNavItemData* result = &g.NavInitResult; if (g.NavId != result->ID) { g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId; g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = 0; g.NavJustMovedToIsTabbing = false; g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0; } // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result if (result->SelectionUserData != ImGuiSelectionUserData_Invalid) g.NavLastValidSelectionUserData = result->SelectionUserData; if (g.NavInitRequestFromMove) SetNavCursorVisibleAfterMove(); } // Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags) { // Bias initial rect ImGuiContext& g = *GImGui; const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos; // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias. // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column. // - But each successful move sets new bias on one axis, only cleared when using mouse. if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0) { if (preferred_pos_rel.x == FLT_MAX) preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x; if (preferred_pos_rel.y == FLT_MAX) preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y; } // Apply general bias on the other axis if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX) r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x; else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX) r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y; } void ImGui::NavUpdateCreateMoveRequest() { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; ImGuiWindow* window = g.NavWindow; const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if (g.NavMoveForwardToNextFrame && window != NULL) { // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) // (preserve most state, which were already set by the NavMoveRequestForward() function) IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded); IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); } else { // Initiate directional inputs request g.NavMoveDir = ImGuiDir_None; g.NavMoveFlags = ImGuiNavMoveFlags_None; g.NavMoveScrollFlags = ImGuiScrollFlags_None; if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove; if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; } if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; } if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; } if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; } } g.NavMoveClipDir = g.NavMoveDir; g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); } // Update PageUp/PageDown/Home/End scroll // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? float scoring_page_offset_y = 0.0f; if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) scoring_page_offset_y = NavUpdatePageUpPageDown(); // [DEBUG] Always send a request when holding Ctrl. Hold Ctrl + Arrow change the direction. #if IMGUI_DEBUG_NAV_SCORING //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C)) // g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); if (io.KeyCtrl) { if (g.NavMoveDir == ImGuiDir_None) g.NavMoveDir = g.NavMoveDirForDebug; g.NavMoveClipDir = g.NavMoveDir; g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult; } #endif // Submit g.NavMoveForwardToNextFrame = false; if (g.NavMoveDir != ImGuiDir_None) NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match) if (g.NavMoveSubmitted && g.NavId == 0) { IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "", g.NavLayer); g.NavInitRequest = g.NavInitRequestFromMove = true; g.NavInitResult.ID = 0; if (g.IO.ConfigNavCursorVisibleAuto) // NO check for _NoNavInputs here as we assume MoveRequests cannot be created. g.NavCursorVisible = true; } // When using gamepad, we project the reference nav bounding box into window visible area. // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse). if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded)) { bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0; bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0; ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171) // Otherwise 'inner_rect_rel' would be off on the move result frame. inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll); if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n"); float pad_x = ImMin(inner_rect_rel.GetWidth(), window->FontRefSize * 0.5f); float pad_y = ImMin(inner_rect_rel.GetHeight(), window->FontRefSize * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX; inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX; inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX; inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX; window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel); g.NavId = 0; } } // Prepare scoring rectangle. // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) ImRect scoring_rect; if (window != NULL) { ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); if (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove) { // When we start from a visible location, score visible items and prioritize this result. if (window->InnerRect.Contains(scoring_rect)) g.NavMoveFlags |= ImGuiNavMoveFlags_AlsoScoreVisibleSet; g.NavScoringNoClipRect = scoring_rect; scoring_rect.TranslateY(scoring_page_offset_y); g.NavScoringNoClipRect.Add(scoring_rect); } //GetForegroundDrawList()->AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)); // [DEBUG] Pre-bias if (g.NavMoveSubmitted) NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags); IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem(). //GetForegroundDrawList()->AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)); // [DEBUG] Post-bias //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRectFilled(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(100, 255, 0, 80)); } // [DEBUG] } g.NavScoringRect = scoring_rect; //g.NavScoringNoClipRect.Add(scoring_rect); } void ImGui::NavUpdateCreateTabbingRequest() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; IM_ASSERT(g.NavMoveDir == ImGuiDir_None); if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs) || !g.ConfigNavEnableTabbing) return; const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt; if (!tab_pressed) return; // Initiate tabbing request // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!) // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping. const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if (nav_keyboard_active) g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1; else g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. g.NavTabbingCounter = -1; } // Apply result from previous frame navigation directional move request. Always called from NavUpdate() void ImGui::NavMoveRequestApplyResult() { ImGuiContext& g = *GImGui; #if IMGUI_DEBUG_NAV_SCORING if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times return; #endif // Select which result to use ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; // Tabbing forward wrap if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL) if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) result = &g.NavTabbingResultFirst; // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; if (result == NULL) { if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible; if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0) SetNavCursorVisibleAfterMove(); NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis. IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n"); return; } // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId) result = &g.NavMoveResultLocalVisible; // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) result = &g.NavMoveResultOther; IM_ASSERT(g.NavWindow && result->Window); // Scroll to keep newly navigated item fully into view. if (g.NavLayer == ImGuiNavLayer_Main) { ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) { // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge? float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; SetScrollY(result->Window, scroll_target); } } if (g.NavWindow != result->Window) { IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name); g.NavWindow = result->Window; g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; } // Clear active id unless requested not to // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction, // so this is mostly provided as a gateway for further experiments (see #1418, #2890) if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0) ClearActiveID(); // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior. if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0) { g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId; g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = g.NavMoveKeyMods; g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0; g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0; //IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId); } // Apply new NavID/Focus IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer]; SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); if (result->SelectionUserData != ImGuiSelectionUserData_Invalid) g.NavLastValidSelectionUserData = result->SelectionUserData; // Restore last preferred position for current axis // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..) if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0) { preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis]; g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel; } // Tabbing: Activates Inputable, otherwise only Focus if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0) g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate; // Activate if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) { g.NavNextActivateId = result->ID; g.NavNextActivateFlags = ImGuiActivateFlags_None; if (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) g.NavNextActivateFlags |= ImGuiActivateFlags_FromFocusApi; if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing; } // Make nav cursor visible if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0) SetNavCursorVisibleAfterMove(); } // Process Escape/NavCancel input (to close a popup, get back to parent, clear focus) // FIXME: In order to support e.g. Escape to clear a selection we'll need: // - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it. // - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept static void ImGui::NavUpdateCancelRequest() { ImGuiContext& g = *GImGui; const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner))) return; IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n"); if (g.ActiveId != 0) { ClearActiveID(); } else if (g.NavLayer != ImGuiNavLayer_Main) { // Leave the "menu" layer NavRestoreLayer(ImGuiNavLayer_Main); SetNavCursorVisibleAfterMove(); } else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow) { // Exit child window ImGuiWindow* child_window = g.NavWindow->RootWindowForNav; ImGuiWindow* parent_window = child_window->ParentWindow; IM_ASSERT(child_window->ChildId != 0); FocusWindow(parent_window); SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect())); SetNavCursorVisibleAfterMove(); } else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) { // Close open popup/menu ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); } else { // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were // FIXME-NAV: This should happen on window appearing. if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow) if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) g.NavWindow->NavLastIds[0] = 0; // Clear nav focus if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow) g.NavId = 0; if (g.IO.ConfigNavEscapeClearFocusWindow) FocusWindow(NULL); } } static void ImGui::NavUpdateContextMenuRequest() { ImGuiContext& g = *GImGui; g.NavOpenContextMenuItemId = g.NavOpenContextMenuWindowId = 0; const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if ((!nav_keyboard_active && !nav_gamepad_active) || g.NavWindow == NULL) return; bool request = false; request |= nav_keyboard_active && (IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner) || (IsKeyPressed(ImGuiKey_F10, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner) && g.IO.KeyMods == ImGuiMod_Shift)); request |= nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadContextMenu, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner); if (!request) return; g.NavOpenContextMenuItemId = g.NavId; g.NavOpenContextMenuWindowId = g.NavWindow->ID; // Allow triggering for Begin()..BeginPopupContextItem(). A possible alternative would be to use g.NavLayer == ImGuiNavLayer_Menu. if (g.NavId == g.NavWindow->GetID("#CLOSE") || g.NavId == g.NavWindow->GetID("#COLLAPSE")) g.NavOpenContextMenuItemId = g.NavWindow->MoveId; g.NavInputSource = ImGuiInputSource_Keyboard; SetNavCursorVisibleAfterMove(); } // Handle PageUp/PageDown/Home/End keys // Called from NavUpdateCreateMoveRequest() which will use our output to create a move request // FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference // FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid? static float ImGui::NavUpdatePageUpPageDown() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) return 0.0f; const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner); const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner); const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner); const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner); if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out return 0.0f; if (g.NavLayer != ImGuiNavLayer_Main) NavRestoreLayer(ImGuiNavLayer_Main); if ((window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Main)) == 0 && window->DC.NavWindowHasScrollY) { // Fallback manual-scroll when window has no navigable item if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner)) SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner)) SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); else if (home_pressed) SetScrollY(window, 0.0f); else if (end_pressed) SetScrollY(window, window->ScrollMax.y); } else { ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->FontRefSize * 1.0f + nav_rect_rel.GetHeight()); float nav_scoring_rect_offset_y = 0.0f; if (IsKeyPressed(ImGuiKey_PageUp, true)) { nav_scoring_rect_offset_y = -page_offset_y; g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Up; g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_IsPageMove; // ImGuiNavMoveFlags_AlsoScoreVisibleSet may be added later } else if (IsKeyPressed(ImGuiKey_PageDown, true)) { nav_scoring_rect_offset_y = +page_offset_y; g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Down; g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_IsPageMove; // ImGuiNavMoveFlags_AlsoScoreVisibleSet may be added later } else if (home_pressed) { // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result. // Preserve current horizontal position if we have any. nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f; if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Down; g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; // FIXME-NAV: MoveClipDir left to _None, intentional? } else if (end_pressed) { nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y; if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Up; g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; // FIXME-NAV: MoveClipDir left to _None, intentional? } return nav_scoring_rect_offset_y; } return 0.0f; } static void ImGui::NavEndFrame() { ImGuiContext& g = *GImGui; // Show Ctrl+Tab list window if (g.NavWindowingTarget != NULL) NavUpdateWindowingOverlay(); // Perform wrap-around in menus // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly. // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) NavUpdateCreateWrappingRequest(); } static void ImGui::NavUpdateCreateWrappingRequest() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; bool do_forward = false; ImRect bb_rel = window->NavRectRel[g.NavLayer]; ImGuiDir clip_dir = g.NavMoveDir; const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; // Menu layer does not maintain scrolling / content size (#9178) ImVec2 wrap_size = (g.NavLayer == ImGuiNavLayer_Menu) ? window->Size : window->ContentSize + window->WindowPadding; if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) { bb_rel.Min.x = bb_rel.Max.x = wrap_size.x; if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row clip_dir = ImGuiDir_Up; } do_forward = true; } if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) { bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x; if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row clip_dir = ImGuiDir_Down; } do_forward = true; } if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) { bb_rel.Min.y = bb_rel.Max.y = wrap_size.y; if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column clip_dir = ImGuiDir_Left; } do_forward = true; } if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) { bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y; if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column clip_dir = ImGuiDir_Right; } do_forward = true; } if (!do_forward) return; window->NavRectRel[g.NavLayer] = bb_rel; NavClearPreferredPosForAxis(ImGuiAxis_X); NavClearPreferredPosForAxis(ImGuiAxis_Y); NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } // 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 programmatically. // If you want a window to never be focused, you may use the e.g. NoInputs flag. bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) { return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); } 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.WindowsFocusOrder.Size && i != i_stop; i += dir) if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i])) return g.WindowsFocusOrder[i]; return NULL; } static void NavUpdateWindowingTarget(int focus_change_dir) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindowingTarget); if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) return; const int i_current = ImGui::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.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.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f); } g.NavWindowingToggleLayer = false; } // Apply focus and close overlay static void ImGui::NavUpdateWindowingApplyFocus(ImGuiWindow* apply_focus_window) { // FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow() ? // Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow() ImGuiContext& g = *GImGui; if (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow) { ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL; ClearActiveID(); SetNavCursorVisibleAfterMove(); ClosePopupsOverWindow(apply_focus_window, false); FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild); IM_ASSERT(g.NavWindow != NULL); apply_focus_window = g.NavWindow; if (apply_focus_window->NavLastIds[0] == 0) // FIXME: This is the equivalent of the 'if (g.NavId == 0) { NavInitWindow() }' in DockNodeUpdateTabBar(). NavInitWindow(apply_focus_window, false); // If the window has ONLY a menu layer (no main layer), select it directly // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame, // so Ctrl+Tab where the keys are only held for 1 frame will be able to use correct layers mask since // the target window as already been previewed once. // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases, // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask* // won't be valid. if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu)) g.NavLayer = ImGuiNavLayer_Menu; // Request OS level focus if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus) g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport); } g.NavWindowingTarget = NULL; } // 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; ImGuiIO& io = g.IO; ImGuiWindow* apply_focus_window = NULL; bool apply_toggle_layer = false; ImGuiWindow* modal_window = GetTopMostPopupModal(); bool allow_windowing = (modal_window == NULL); // FIXME: This prevent Ctrl+Tab from being usable with windows that are inside the Begin-stack of that modal. if (!allow_windowing) g.NavWindowingTarget = NULL; // Fade out if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) { g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f); if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) g.NavWindowingTargetAnim = NULL; } // Start Ctrl+Tab or Square+L/R window selection // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab) const ImGuiID owner_id = ImHashStr("##NavUpdateWindowing"); const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id); const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id); const bool start_toggling_with_gamepad = nav_gamepad_active && !g.NavWindowingTarget && Shortcut(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_RouteAlways, owner_id); const bool start_windowing_with_gamepad = allow_windowing && start_toggling_with_gamepad; const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard! bool just_started_windowing_from_null_focus = false; if (start_toggling_with_gamepad) { g.NavWindowingToggleLayer = true; // Gamepad starts toggling layer g.NavWindowingToggleKey = ImGuiKey_NavGamepadMenu; g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Gamepad; } if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = (g.NavWindow && IsWindowNavFocusable(g.NavWindow)) ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { if (start_windowing_with_keyboard || g.ConfigNavWindowingWithGamepad) g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f); g.NavWindowingInputSource = g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; if (g.NavWindow == NULL) just_started_windowing_from_null_focus = true; // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects. if (keyboard_next_window || keyboard_prev_window) SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id); } // Gamepad update if ((g.NavWindowingTarget || g.NavWindowingToggleLayer) && g.NavWindowingInputSource == ImGuiInputSource_Gamepad) { if (g.NavWindowingTarget != NULL) { // Highlight only appears after a brief time holding the button, so that a fast tap on ImGuiKey_NavGamepadMenu (to toggle NavLayer) doesn't add visual noise // However inputs are accepted immediately, so you press ImGuiKey_NavGamepadMenu + L1/R1 fast. g.NavWindowingTimer += io.DeltaTime; g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // Select window to focus const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1); if (focus_change_dir != 0 && !just_started_windowing_from_null_focus) { NavUpdateWindowingTarget(focus_change_dir); g.NavWindowingHighlightAlpha = 1.0f; } } // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most) if (!IsKeyDown(ImGuiKey_NavGamepadMenu)) { g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. if (g.NavWindowingToggleLayer && g.NavWindow) apply_toggle_layer = true; else if (!g.NavWindowingToggleLayer) apply_focus_window = g.NavWindowingTarget; g.NavWindowingTarget = NULL; g.NavWindowingToggleLayer = false; } } // Keyboard: Focus if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_Keyboard) { // Visuals only appears after a brief time after pressing TAB the first time, so that a fast Ctrl+Tab doesn't add visual noise ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_; IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows. g.NavWindowingTimer += io.DeltaTime; g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus) NavUpdateWindowingTarget(keyboard_next_window ? -1 : +1); else if ((io.KeyMods & shared_mods) != shared_mods) apply_focus_window = g.NavWindowingTarget; } // Keyboard: Press and Release Alt to toggle menu layer const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt }; bool windowing_toggle_layer_start = false; if (g.NavWindow != NULL && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) for (ImGuiKey windowing_toggle_key : windowing_toggle_keys) if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner)) { windowing_toggle_layer_start = true; g.NavWindowingToggleLayer = true; g.NavWindowingToggleKey = windowing_toggle_key; g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Keyboard; break; } if (g.NavWindowingToggleLayer && g.NavWindowingInputSource == ImGuiInputSource_Keyboard) { // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370) // We cancel toggling nav layer when other modifiers are pressed. (See #4439) // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). // We cancel toggling nav layer if an owner has claimed the key. if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) g.NavWindowingToggleLayer = false; else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time) g.NavWindowingToggleLayer = false; else if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false) g.NavWindowingToggleLayer = false; // Apply layer toggle on Alt release // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss. if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer) if (g.ActiveId == 0 || g.ActiveIdAllowOverlap) if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev)) apply_toggle_layer = true; if (!IsKeyDown(g.NavWindowingToggleKey)) g.NavWindowingToggleLayer = false; } // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { ImVec2 nav_move_dir; if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow); if (g.NavInputSource == ImGuiInputSource_Gamepad) nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; const float move_step = NAV_MOVE_SPEED * io.DeltaTime * GetScale(); g.NavWindowingAccumDeltaPos += nav_move_dir * move_step; g.NavHighlightItemUnderNav = true; ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos); if (accum_floored.x != 0.0f || accum_floored.y != 0.0f) { ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree; SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always); g.NavWindowingAccumDeltaPos -= accum_floored; } } } // Apply final focus if (apply_focus_window) NavUpdateWindowingApplyFocus(apply_focus_window); // Apply menu/layer toggle if (apply_toggle_layer && g.NavWindow) { ClearActiveID(); // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; while (new_nav_window->ParentWindow && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 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) { ImGuiWindow* old_nav_window = g.NavWindow; FocusWindow(new_nav_window); new_nav_window->NavLastChildNavWindow = old_nav_window; } // Toggle layer const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; if (new_nav_layer != g.NavLayer) { // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?) const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL); if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id) g.NavWindow->NavLastIds[new_nav_layer] = 0; NavRestoreLayer(new_nav_layer); SetNavCursorVisibleAfterMove(); } } } // Window has already passed the IsWindowNavFocusable() static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) { if (window->Flags & ImGuiWindowFlags_Popup) return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup); if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar); if (window->DockNodeAsHost) return "(Dock node)"; // Not normally shown to user. return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled); } // Overlay displayed when using Ctrl+Tab. Called by EndFrame(). void ImGui::NavUpdateWindowingOverlay() { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindowingTarget != NULL); if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) return; const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport(); SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); Begin("##NavWindowingOverlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); g.NavWindowingListWindow = g.CurrentWindow; if (g.ContextName[0] != 0) SeparatorText(g.ContextName); for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) { ImGuiWindow* window = g.WindowsFocusOrder[n]; IM_ASSERT(window != NULL); // Fix static analyzers if (!IsWindowNavFocusable(window)) continue; const char* label = window->Name; if (label == FindRenderedTextEnd(label)) label = GetFallbackWindowNameForWindowingList(window); Selectable(label, g.NavWindowingTarget == window); } End(); PopStyleVar(); } //----------------------------------------------------------------------------- // [SECTION] DRAG AND DROP //----------------------------------------------------------------------------- bool ImGui::IsDragDropActive() { ImGuiContext& g = *GImGui; return g.DragDropActive; } void ImGui::ClearDragDrop() { ImGuiContext& g = *GImGui; if (g.DragDropActive) IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n"); g.DragDropActive = false; g.DragDropPayload.Clear(); g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags_None; g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; g.DragDropAcceptIdCurrRectSurface = FLT_MAX; g.DragDropAcceptFrameCount = -1; g.DragDropPayloadBufHeap.clear(); memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); } bool ImGui::BeginTooltipHidden() { ImGuiContext& g = *GImGui; bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize); SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow); return ret; } // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() // If the item has an identifier: // - This assume/require the item to be activated (typically via ButtonBehavior). // - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button. // - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag. // If the item has no identifier: // - Currently always assume left mouse button. bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button, // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic). ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; bool source_drag_active = false; ImGuiID source_id = 0; ImGuiID source_parent_id = 0; if ((flags & ImGuiDragDropFlags_SourceExtern) == 0) { source_id = g.LastItemData.ID; if (source_id != 0) { // Common path: items with ID if (g.ActiveId != source_id) return false; if (g.ActiveIdMouseButton != -1) mouse_button = g.ActiveIdMouseButton; if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) return false; g.ActiveIdAllowOverlap = false; } else { // Uncommon path: items without ID if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) return false; if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) return false; // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag. if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) { IM_ASSERT(0); return false; } // Magic fallback to handle items with no assigned ID, e.g. Text(), Image() // We build a throwaway ID based on current ID stack + relative AABB of items in window. // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. // Rely on keeping other window->LastItemXXX fields intact. source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); KeepAliveID(source_id); bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags); if (is_hovered && g.IO.MouseClicked[mouse_button]) { SetActiveID(source_id, window); FocusWindow(window); } if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. g.ActiveIdAllowOverlap = is_hovered; } if (g.ActiveId != source_id) return false; source_parent_id = window->IDStack.back(); source_drag_active = IsMouseDragging(mouse_button); // Disable navigation and key inputs while dragging + cancel existing request if any SetActiveIdUsingAllKeyboardKeys(); } else { // When ImGuiDragDropFlags_SourceExtern is set: window = NULL; source_id = ImHashStr("#SourceExtern"); source_drag_active = true; mouse_button = g.IO.MouseDown[0] ? 0 : -1; KeepAliveID(source_id); SetActiveID(source_id, NULL); } IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() if (!source_drag_active) return false; // Activate drag and drop if (!g.DragDropActive) { IM_ASSERT(source_id != 0); ClearDragDrop(); IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\n", source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? " (EXTERN)" : ""); ImGuiPayload& payload = g.DragDropPayload; payload.SourceId = source_id; payload.SourceParentId = source_parent_id; g.DragDropActive = true; g.DragDropSourceFlags = flags; g.DragDropMouseButton = mouse_button; if (payload.SourceId == g.ActiveId) g.ActiveIdNoClearOnFocusLoss = true; } g.DragDropSourceFrameCount = g.FrameCount; g.DragDropWithinSource = true; 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. bool ret; if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlagsPrev & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) ret = BeginTooltipHidden(); else ret = BeginTooltip(); IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddenAndSkipItemsForCurrentFrame(). IM_UNUSED(ret); } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; return true; } void ImGui::EndDragDropSource() { ImGuiContext& g = *GImGui; IM_ASSERT(g.DragDropActive); IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?"); if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) EndTooltip(); // Discard the drag if have not called SetDragDropPayload() if (g.DragDropPayload.DataFrameCount == -1) ClearDragDrop(); g.DragDropWithinSource = false; } // Use 'cond' to choose to submit payload on drag start or every frame bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond) { ImGuiContext& g = *GImGui; ImGuiPayload& payload = g.DragDropPayload; if (cond == 0) cond = ImGuiCond_Always; IM_ASSERT(type != NULL); IM_ASSERT(ImStrlen(type) < IM_COUNTOF(payload.DataType) && "Payload type can be at most 32 characters long"); IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) { // Copy payload ImStrncpy(payload.DataType, type, IM_COUNTOF(payload.DataType)); g.DragDropPayloadBufHeap.resize(0); if (data_size > sizeof(g.DragDropPayloadBufLocal)) { // Store in heap g.DragDropPayloadBufHeap.resize((int)data_size); payload.Data = g.DragDropPayloadBufHeap.Data; memcpy(payload.Data, data, data_size); } else if (data_size > 0) { // Store locally memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); payload.Data = g.DragDropPayloadBufLocal; memcpy(payload.Data, data, data_size); } else { payload.Data = NULL; } payload.DataSize = (int)data_size; } payload.DataFrameCount = g.FrameCount; // Return whether the payload has been accepted return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); } bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; if (!g.DragDropActive) return false; ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree) return false; IM_ASSERT(id != 0); if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) return false; if (window->SkipItems) return false; IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() g.DragDropTargetRect = bb; g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case? g.DragDropTargetId = id; g.DragDropTargetFullViewport = 0; g.DragDropWithinTarget = true; return true; } // Typical usage would be: // if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) // if (ImGui::BeginDragDropTargetViewport(ImGui::GetMainViewport(), NULL)) // But we are leaving the hover test to the caller for maximum flexibility. bool ImGui::BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb) { ImGuiContext& g = *GImGui; if (!g.DragDropActive) return false; ImRect bb = p_bb ? *p_bb : ((ImGuiViewportP*)viewport)->GetWorkRect(); ImGuiID id = viewport->ID; if (g.MouseViewport != viewport || !IsMouseHoveringRect(bb.Min, bb.Max, false) || (id == g.DragDropPayload.SourceId)) return false; IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() g.DragDropTargetRect = bb; g.DragDropTargetClipRect = bb; g.DragDropTargetId = id; g.DragDropTargetFullViewport = id; g.DragDropWithinTarget = true; return true; } // We don't use BeginDragDropTargetCustom() and duplicate its code because: // 1) we use LastItemData's ImGuiItemStatusFlags_HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can. // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case) bool ImGui::BeginDragDropTarget() { ImGuiContext& g = *GImGui; if (!g.DragDropActive) return false; ImGuiWindow* window = g.CurrentWindow; if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems) return false; const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect; ImGuiID id = g.LastItemData.ID; if (id == 0) { id = window->GetIDFromRectangle(display_rect); KeepAliveID(id); } if (g.DragDropPayload.SourceId == id) return false; IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() g.DragDropTargetRect = display_rect; g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect; g.DragDropTargetId = id; g.DragDropWithinTarget = true; return true; } bool ImGui::IsDragDropPayloadBeingAccepted() { ImGuiContext& g = *GImGui; return g.DragDropActive && g.DragDropAcceptIdPrev != 0; } const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags) { ImGuiContext& g = *GImGui; ImGuiPayload& payload = g.DragDropPayload; IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? if (type != NULL && !payload.IsDataType(type)) return NULL; // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints. // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function! const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId); ImRect r = g.DragDropTargetRect; float r_surface = r.GetWidth() * r.GetHeight(); if (r_surface > g.DragDropAcceptIdCurrRectSurface) return NULL; g.DragDropAcceptFlagsCurr = flags; g.DragDropAcceptIdCurr = g.DragDropTargetId; g.DragDropAcceptIdCurrRectSurface = r_surface; //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId); // Render default drop visuals payload.Preview = was_accepted_previously; flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame) const bool draw_target_rect = payload.Preview && !(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); if (draw_target_rect && g.DragDropTargetFullViewport != 0) { ImGuiViewport* viewport = FindViewportByID(g.DragDropTargetFullViewport); IM_ASSERT(viewport != NULL); ImRect bb = g.DragDropTargetRect; bb.Expand(-3.5f); RenderDragDropTargetRectEx(GetForegroundDrawList(viewport), bb); } else if (draw_target_rect) { RenderDragDropTargetRectForItem(r); } g.DragDropAcceptFrameCount = g.FrameCount; if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1) payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount); else payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased() if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery)) return NULL; if (payload.Delivery) IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\n", g.DragDropTargetId); return &payload; } // FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target. void ImGui::RenderDragDropTargetRectForItem(const ImRect& bb) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImRect bb_display = bb; bb_display.ClipWith(g.DragDropTargetClipRect); // Clip THEN expand so we have a way to visualize that target is not entirely visible. bb_display.Expand(g.Style.DragDropTargetPadding); bool push_clip_rect = !window->ClipRect.Contains(bb_display); if (push_clip_rect) window->DrawList->PushClipRectFullScreen(); RenderDragDropTargetRectEx(window->DrawList, bb_display); if (push_clip_rect) window->DrawList->PopClipRect(); } void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb) { ImGuiContext& g = *GImGui; draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), g.Style.DragDropTargetRounding, 0); draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), g.Style.DragDropTargetRounding, 0, g.Style.DragDropTargetBorderSize); } const ImGuiPayload* ImGui::GetDragDropPayload() { ImGuiContext& g = *GImGui; return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL; } void ImGui::EndDragDropTarget() { ImGuiContext& g = *GImGui; IM_ASSERT(g.DragDropActive); IM_ASSERT(g.DragDropWithinTarget); g.DragDropWithinTarget = false; // Clear drag and drop state payload right after delivery if (g.DragDropPayload.Delivery) ClearDragDrop(); } //----------------------------------------------------------------------------- // [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) static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args) { if (g.LogFile) { g.LogBuffer.Buf.resize(0); g.LogBuffer.appendfv(fmt, args); ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile); } else { g.LogBuffer.appendfv(fmt, args); } } void ImGui::LogText(const char* fmt, ...) { ImGuiContext& g = *GImGui; if (!g.LogEnabled) return; va_list args; va_start(args, fmt); LogTextV(g, fmt, args); va_end(args); } void ImGui::LogTextV(const char* fmt, va_list args) { ImGuiContext& g = *GImGui; if (!g.LogEnabled) return; LogTextV(g, fmt, args); } // Internal version that takes a position to decide on newline placement and pad items according to their depth. // We split text into individual lines to add current tree level padding // FIXME: This code is a little complicated perhaps, considering simplifying the whole system. void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const char* prefix = g.LogNextPrefix; const char* suffix = g.LogNextSuffix; g.LogNextPrefix = g.LogNextSuffix = NULL; if (!text_end) text_end = FindRenderedTextEnd(text, text_end); const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + ImMax(g.Style.FramePadding.y, g.Style.ItemSpacing.y) + 1); if (ref_pos) g.LogLinePosY = ref_pos->y; if (log_new_line) { LogText(IM_NEWLINE); g.LogLineFirstItem = true; } if (prefix) LogRenderedText(ref_pos, prefix, prefix + ImStrlen(prefix)); // Calculate end ourself to ensure "##" are included here. // Re-adjust padding if we have popped out of our starting depth if (g.LogDepthRef > window->DC.TreeDepth) g.LogDepthRef = window->DC.TreeDepth; const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); const char* text_remaining = text; for (;;) { // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry. // We don't add a trailing \n yet 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_last_line = (line_end == text_end); if (line_start != line_end || !is_last_line) { const int line_length = (int)(line_end - line_start); const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1; LogText("%*s%.*s", indentation, "", line_length, line_start); g.LogLineFirstItem = false; if (*line_end == '\n') { LogText(IM_NEWLINE); g.LogLineFirstItem = true; } } if (is_last_line) break; text_remaining = line_end + 1; } if (suffix) LogRenderedText(ref_pos, suffix, suffix + ImStrlen(suffix)); } // Start logging/capturing text output void ImGui::LogBegin(ImGuiLogFlags flags, int auto_open_depth) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(g.LogEnabled == false); IM_ASSERT(g.LogFile == NULL && g.LogBuffer.empty()); IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiLogFlags_OutputMask_)); // Check that only 1 type flag is used g.LogEnabled = g.ItemUnclipByLog = true; g.LogFlags = flags; g.LogWindow = window; g.LogNextPrefix = g.LogNextSuffix = NULL; g.LogDepthRef = window->DC.TreeDepth; g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); g.LogLinePosY = FLT_MAX; g.LogLineFirstItem = true; } // Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText) void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix) { ImGuiContext& g = *GImGui; g.LogNextPrefix = prefix; g.LogNextSuffix = suffix; } void ImGui::LogToTTY(int auto_open_depth) { ImGuiContext& g = *GImGui; if (g.LogEnabled) return; IM_UNUSED(auto_open_depth); #ifndef IMGUI_DISABLE_TTY_FUNCTIONS LogBegin(ImGuiLogFlags_OutputTTY, auto_open_depth); g.LogFile = stdout; #endif } // 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; // 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 || !filename[0]) return; ImFileHandle f = ImFileOpen(filename, "ab"); if (!f) { IM_ASSERT(0); return; } LogBegin(ImGuiLogFlags_OutputFile, auto_open_depth); g.LogFile = f; } // Start logging/capturing text output to clipboard void ImGui::LogToClipboard(int auto_open_depth) { ImGuiContext& g = *GImGui; if (g.LogEnabled) return; LogBegin(ImGuiLogFlags_OutputClipboard, auto_open_depth); } void ImGui::LogToBuffer(int auto_open_depth) { ImGuiContext& g = *GImGui; if (g.LogEnabled) return; LogBegin(ImGuiLogFlags_OutputBuffer, auto_open_depth); } void ImGui::LogFinish() { ImGuiContext& g = *GImGui; if (!g.LogEnabled) return; LogText(IM_NEWLINE); switch (g.LogFlags & ImGuiLogFlags_OutputMask_) { case ImGuiLogFlags_OutputTTY: #ifndef IMGUI_DISABLE_TTY_FUNCTIONS fflush(g.LogFile); #endif break; case ImGuiLogFlags_OutputFile: ImFileClose(g.LogFile); break; case ImGuiLogFlags_OutputBuffer: break; case ImGuiLogFlags_OutputClipboard: if (!g.LogBuffer.empty()) SetClipboardText(g.LogBuffer.begin()); break; default: IM_ASSERT(0); break; } g.LogEnabled = g.ItemUnclipByLog = false; g.LogFlags = ImGuiLogFlags_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; PushID("LogButtons"); #ifndef IMGUI_DISABLE_TTY_FUNCTIONS const bool log_to_tty = Button("Log To TTY"); SameLine(); #else const bool log_to_tty = false; #endif const bool log_to_file = Button("Log To File"); SameLine(); const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); PushItemFlag(ImGuiItemFlags_NoTabStop, true); SetNextItemWidth(CalcTextSize("999").x); SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); PopItemFlag(); PopID(); // Start logging at the end of the function so that the buttons don't appear in the log if (log_to_tty) LogToTTY(); if (log_to_file) LogToFile(); if (log_to_clipboard) LogToClipboard(); } //----------------------------------------------------------------------------- // [SECTION] SETTINGS //----------------------------------------------------------------------------- // - UpdateSettings() [Internal] // - MarkIniSettingsDirty() [Internal] // - FindSettingsHandler() [Internal] // - ClearIniSettings() [Internal] // - LoadIniSettingsFromDisk() // - LoadIniSettingsFromMemory() // - SaveIniSettingsToDisk() // - SaveIniSettingsToMemory() //----------------------------------------------------------------------------- // - CreateNewWindowSettings() [Internal] // - FindWindowSettingsByID() [Internal] // - FindWindowSettingsByWindow() [Internal] // - ClearWindowSettings() [Internal] // - WindowSettingsHandler_***() [Internal] //----------------------------------------------------------------------------- // Called by NewFrame() void ImGui::UpdateSettings() { // Load settings on first frame (if not explicitly loaded manually before) ImGuiContext& g = *GImGui; if (!g.SettingsLoaded) { IM_ASSERT(g.SettingsWindows.empty()); if (g.IO.IniFilename) LoadIniSettingsFromDisk(g.IO.IniFilename); g.SettingsLoaded = true; } // Save settings (with a delay after the last modification, so we don't spam disk too much) if (g.SettingsDirtyTimer > 0.0f) { g.SettingsDirtyTimer -= g.IO.DeltaTime; if (g.SettingsDirtyTimer <= 0.0f) { if (g.IO.IniFilename != NULL) SaveIniSettingsToDisk(g.IO.IniFilename); else g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves. g.SettingsDirtyTimer = 0.0f; } } } void ImGui::MarkIniSettingsDirty() { ImGuiContext& g = *GImGui; if (g.SettingsDirtyTimer <= 0.0f) g.SettingsDirtyTimer = g.IO.IniSavingRate; } void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) if (g.SettingsDirtyTimer <= 0.0f) g.SettingsDirtyTimer = g.IO.IniSavingRate; } void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler) { ImGuiContext& g = *GImGui; IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL); g.SettingsHandlers.push_back(*handler); } void ImGui::RemoveSettingsHandler(const char* type_name) { ImGuiContext& g = *GImGui; if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name)) g.SettingsHandlers.erase(handler); } ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) { ImGuiContext& g = *GImGui; const ImGuiID type_hash = ImHashStr(type_name); for (ImGuiSettingsHandler& handler : g.SettingsHandlers) if (handler.TypeHash == type_hash) return &handler; return NULL; } // Clear all settings (windows, tables, docking etc.) void ImGui::ClearIniSettings() { ImGuiContext& g = *GImGui; g.SettingsIniData.clear(); for (ImGuiSettingsHandler& handler : g.SettingsHandlers) if (handler.ClearAllFn != NULL) handler.ClearAllFn(&g, &handler); } void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) { size_t file_data_size = 0; char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); if (!file_data) return; if (file_data_size > 0) LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); IM_FREE(file_data); } // Zero-tolerance, no error reporting, cheap .ini parsing // Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty! void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) { ImGuiContext& g = *GImGui; IM_ASSERT(g.Initialized); //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()"); //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy.. if (ini_size == 0) ini_size = ImStrlen(ini_data); g.SettingsIniData.Buf.resize((int)ini_size + 1); char* const buf = g.SettingsIniData.Buf.Data; char* const buf_end = buf + ini_size; memcpy(buf, ini_data, ini_size); buf_end[0] = 0; // Call pre-read handlers // Some types will clear their data (e.g. dock information) some types will allow merge/override (window) for (ImGuiSettingsHandler& handler : g.SettingsHandlers) if (handler.ReadInitFn != NULL) handler.ReadInitFn(&g, &handler); void* entry_data = NULL; ImGuiSettingsHandler* entry_handler = NULL; char* line_end = NULL; for (char* line = buf; line < buf_end; line = line_end + 1) { // Skip new lines markers, then find end of the line while (*line == '\n' || *line == '\r') line++; line_end = line; 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. line_end[-1] = 0; const char* name_end = line_end - 1; const char* type_start = line + 1; char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']'); const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; if (!type_end || !name_start) continue; *type_end = 0; // Overwrite first ']' name_start++; // Skip second '[' entry_handler = FindSettingsHandler(type_start); entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; } else if (entry_handler != NULL && entry_data != NULL) { // Let type handler parse the line entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); } } g.SettingsLoaded = true; // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary) memcpy(buf, ini_data, ini_size); // Call post-read handlers for (ImGuiSettingsHandler& handler : g.SettingsHandlers) if (handler.ApplyAllFn != NULL) handler.ApplyAllFn(&g, &handler); } void ImGui::SaveIniSettingsToDisk(const char* ini_filename) { ImGuiContext& g = *GImGui; g.SettingsDirtyTimer = 0.0f; if (!ini_filename) return; size_t ini_data_size = 0; const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); ImFileHandle f = ImFileOpen(ini_filename, "wt"); if (!f) return; ImFileWrite(ini_data, sizeof(char), ini_data_size, f); ImFileClose(f); } // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) { ImGuiContext& g = *GImGui; g.SettingsDirtyTimer = 0.0f; g.SettingsIniData.Buf.resize(0); g.SettingsIniData.Buf.push_back(0); for (ImGuiSettingsHandler& handler : g.SettingsHandlers) handler.WriteAllFn(&g, &handler, &g.SettingsIniData); if (out_size) *out_size = (size_t)g.SettingsIniData.size(); return g.SettingsIniData.c_str(); } ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) { ImGuiContext& g = *GImGui; // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier. if (g.IO.ConfigDebugIniSettings == false) name = ImHashSkipUncontributingPrefix(name); const size_t name_len = ImStrlen(name); // Allocate chunk const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1; ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size); IM_PLACEMENT_NEW(settings) ImGuiWindowSettings(); settings->ID = ImHashStr(name, name_len); memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator return settings; } // We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names. // This is called once per window .ini entry + once per newly instantiated window. ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id) { ImGuiContext& g = *GImGui; for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (settings->ID == id && !settings->WantDelete) return settings; return NULL; } // This is faster if you are holding on a Window already as we don't need to perform a search. ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (window->SettingsOffset != -1) return g.SettingsWindows.ptr_from_offset(window->SettingsOffset); return FindWindowSettingsByID(window->ID); } // This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more. void ImGui::ClearWindowSettings(const char* name) { //IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name); ImGuiContext& g = *GImGui; ImGuiWindow* window = FindWindowByName(name); if (window != NULL) { window->Flags |= ImGuiWindowFlags_NoSavedSettings; InitOrLoadWindowSettings(window, NULL); if (window->DockId != 0) DockContextProcessUndockWindow(&g, window, true); } if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name))) settings->WantDelete = true; } static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; for (ImGuiWindow* window : g.Windows) window->SettingsOffset = -1; g.SettingsWindows.clear(); } static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { ImGuiID id = ImHashStr(name); ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id); if (settings) *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry else settings = ImGui::CreateNewWindowSettings(name); settings->ID = id; settings->WantApply = true; return (void*)settings; } static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) { ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; int x, y; int i; ImU32 u1; if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); } else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; } else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; } else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; } } // Apply to existing windows (if any) static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (settings->WantApply) { if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID)) ApplyWindowSettings(window, settings); settings->WantApply = false; } } static void WindowSettingsHandler_WriteAll(ImGuiContext* 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 = *ctx; for (ImGuiWindow* window : g.Windows) { if (window->Flags & ImGuiWindowFlags_NoSavedSettings) continue; ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window); if (!settings) { settings = ImGui::CreateNewWindowSettings(window->Name); window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); settings->Pos = ImVec2ih(window->Pos - window->ViewportPos); settings->Size = ImVec2ih(window->SizeFull); settings->ViewportId = window->ViewportId; settings->ViewportPos = ImVec2ih(window->ViewportPos); IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); settings->DockId = window->DockId; settings->ClassId = window->WindowClass.ClassId; settings->DockOrder = window->DockOrder; settings->Collapsed = window->Collapsed; settings->IsChild = (window->RootWindow != window); // Cannot rely on ImGuiWindowFlags_ChildWindow here as docked windows have this set. settings->WantDelete = false; } // Write to text buffer buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) { if (settings->WantDelete) continue; const char* settings_name = settings->GetName(); buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); if (settings->IsChild) { buf->appendf("IsChild=1\n"); buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); } else { if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) { buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y); buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); } if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); if (settings->Size.x != 0 || settings->Size.y != 0) buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); buf->appendf("Collapsed=%d\n", settings->Collapsed); if (settings->DockId != 0) { //buf->appendf("TabId=0x%08X\n", ImHashStr("#TAB", 4, settings->ID)); // window->TabId: this is not read back but writing it makes "debugging" the .ini data easier. if (settings->DockOrder == -1) buf->appendf("DockId=0x%08X\n", settings->DockId); else buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder); if (settings->ClassId != 0) buf->appendf("ClassId=0x%08X\n", settings->ClassId); } } buf->append("\n"); } } //----------------------------------------------------------------------------- // [SECTION] LOCALIZATION //----------------------------------------------------------------------------- void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count) { ImGuiContext& g = *GImGui; for (int n = 0; n < count; n++) g.LocalizationTable[entries[n].Key] = entries[n].Text; } //----------------------------------------------------------------------------- // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- // - GetMainViewport() // - FindViewportByID() // - FindViewportByPlatformHandle() // - SetCurrentViewport() [Internal] // - SetWindowViewport() [Internal] // - GetWindowAlwaysWantOwnViewport() [Internal] // - UpdateTryMergeWindowIntoHostViewport() [Internal] // - UpdateTryMergeWindowIntoHostViewports() [Internal] // - TranslateWindowsInViewport() [Internal] // - ScaleWindowsInViewport() [Internal] // - FindHoveredViewportFromPlatformWindowStack() [Internal] // - UpdateViewportsNewFrame() [Internal] // - UpdateViewportsEndFrame() [Internal] // - AddUpdateViewport() [Internal] // - WindowSelectViewport() [Internal] // - WindowSyncOwnedViewport() [Internal] // - UpdatePlatformWindows() // - RenderPlatformWindowsDefault() // - FindPlatformMonitorForPos() [Internal] // - FindPlatformMonitorForRect() [Internal] // - UpdateViewportPlatformMonitor() [Internal] // - DestroyPlatformWindow() [Internal] // - DestroyPlatformWindows() //----------------------------------------------------------------------------- void ImGuiPlatformIO::ClearPlatformHandlers() { Platform_GetClipboardTextFn = NULL; Platform_SetClipboardTextFn = NULL; Platform_OpenInShellFn = NULL; Platform_SetImeDataFn = NULL; Platform_ClipboardUserData = Platform_OpenInShellUserData = Platform_ImeUserData = NULL; Platform_CreateWindow = Platform_DestroyWindow = Platform_ShowWindow = NULL; Platform_SetWindowPos = Platform_SetWindowSize = NULL; Platform_GetWindowPos = Platform_GetWindowSize = Platform_GetWindowFramebufferScale = NULL; Platform_SetWindowFocus = NULL; Platform_GetWindowFocus = Platform_GetWindowMinimized = NULL; Platform_SetWindowTitle = NULL; Platform_SetWindowAlpha = NULL; Platform_UpdateWindow = NULL; Platform_RenderWindow = Platform_SwapBuffers = NULL; Platform_GetWindowDpiScale = NULL; Platform_OnChangedViewport = NULL; Platform_GetWindowWorkAreaInsets = NULL; Platform_CreateVkSurface = NULL; } void ImGuiPlatformIO::ClearRendererHandlers() { Renderer_TextureMaxWidth = Renderer_TextureMaxHeight = 0; Renderer_RenderState = NULL; Renderer_CreateWindow = Renderer_DestroyWindow = NULL; Renderer_SetWindowSize = NULL; Renderer_RenderWindow = Renderer_SwapBuffers = NULL; } ImGuiViewport* ImGui::GetMainViewport() { ImGuiContext& g = *GImGui; return g.Viewports[0]; } // FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236) ImGuiViewport* ImGui::FindViewportByID(ImGuiID viewport_id) { ImGuiContext& g = *GImGui; for (ImGuiViewportP* viewport : g.Viewports) if (viewport->ID == viewport_id) return viewport; return NULL; } ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) { ImGuiContext& g = *GImGui; for (ImGuiViewportP* viewport : g.Viewports) if (viewport->PlatformHandle == platform_handle) return viewport; return NULL; } void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; (void)current_window; if (viewport) viewport->LastFrameActive = g.FrameCount; if (g.CurrentViewport == viewport) return; g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f; g.CurrentViewport = viewport; IM_ASSERT(g.CurrentDpiScale > 0.0f && g.CurrentDpiScale < 99.0f); // Typical correct values would be between 1.0f and 4.0f //IMGUI_DEBUG_LOG_VIEWPORT("[viewport] SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); if (g.IO.ConfigDpiScaleFonts) g.Style.FontScaleDpi = g.CurrentDpiScale; // Notify platform layer of viewport changes // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); } void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { // Abandon viewport if (window->ViewportOwned && window->Viewport->Window == window) window->Viewport->Size = ImVec2(0.0f, 0.0f); window->Viewport = viewport; window->ViewportId = viewport->ID; window->ViewportOwned = (viewport->Window == window); } static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) { // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own. ImGuiContext& g = *GImGui; if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge)) if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) if (!window->DockIsActive) if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0) return true; return false; } // Heuristic, see #8948: depends on how backends handle OS-level parenting. // Due to how parent viewport stack is layed out, note that IsViewportAbove(a,b) isn't always the same as !IsViewportAbove(b,a). static bool IsViewportAbove(ImGuiViewportP* potential_above, ImGuiViewportP* potential_below) { // If ImGuiBackendFlags_HasParentViewport if set, ->ParentViewport chain should be accurate. ImGuiContext& g = *GImGui; if (g.IO.BackendFlags & ImGuiBackendFlags_HasParentViewport) { for (ImGuiViewport* v = potential_above; v != NULL && v->ParentViewport; v = v->ParentViewport) if (v->ParentViewport == potential_below) return true; } else { if (potential_above->ParentViewport == potential_below) return true; } if (potential_above->LastFocusedStampCount > potential_below->LastFocusedStampCount) return true; return false; } static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport_dst) { ImGuiContext& g = *GImGui; IM_ASSERT(window == window->RootWindowDockTree); ImGuiViewportP* viewport_src = window->Viewport; // Current viewport if (viewport_src == viewport_dst) return false; if ((viewport_dst->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0) return false; if ((viewport_dst->Flags & ImGuiViewportFlags_IsMinimized) != 0) return false; if (!viewport_dst->GetMainRect().Contains(window->Rect())) return false; if (GetWindowAlwaysWantOwnViewport(window)) return false; for (ImGuiViewportP* viewport_obstructing : g.Viewports) { if (viewport_obstructing == viewport_src || viewport_obstructing == viewport_dst) continue; if (viewport_obstructing->GetMainRect().Overlaps(window->Rect())) if (IsViewportAbove(viewport_obstructing, viewport_dst)) if (viewport_src == NULL || IsViewportAbove(viewport_src, viewport_obstructing)) return false; // viewport_obstructing is between viewport_src and viewport_dst -> Cannot merge. } // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' merge into Viewport 0X%08X\n", window->Name, viewport_dst->ID); if (window->ViewportOwned) for (int n = 0; n < g.Windows.Size; n++) if (g.Windows[n]->Viewport == viewport_src) SetWindowViewport(g.Windows[n], viewport_dst); SetWindowViewport(window, viewport_dst); if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) BringWindowToDisplayFront(window); return true; } // FIXME: handle 0 to N host viewports static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window) { ImGuiContext& g = *GImGui; return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); } // Translate Dear ImGui windows when a Host Viewport has been moved // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size) { ImGuiContext& g = *GImGui; //IMGUI_DEBUG_LOG_VIEWPORT("[viewport] TranslateWindowsInViewport 0x%08X\n", viewport->ID); IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows)); // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently // translate imgui windows from OS-window-local to absolute coordinates or vice-versa. // 2) If it's not going to fit into the new size, keep it at same absolute position. // One problem with this is that most Win32 applications doesn't update their render while dragging, // and so the window will appear to teleport when releasing the mouse. const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable); ImRect test_still_fit_rect(old_pos, old_pos + old_size); ImVec2 delta_pos = new_pos - old_pos; for (ImGuiWindow* window : g.Windows) // FIXME-OPT if (translate_all_windows || (window->Viewport == viewport && (old_size == new_size || test_still_fit_rect.Contains(window->Rect())))) TranslateWindow(window, delta_pos); } // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) { ImGuiContext& g = *GImGui; //IMGUI_DEBUG_LOG_VIEWPORT("[viewport] ScaleWindowsInViewport 0x%08X\n", viewport->ID); if (viewport->Window) { ScaleWindow(viewport->Window, scale); } else { for (ImGuiWindow* window : g.Windows) if (window->Viewport == viewport) ScaleWindow(window, scale); } } // If the backend doesn't support ImGuiBackendFlags_HasMouseHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs for it, we do a search ourselves. // A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. // B) It requires Platform_GetWindowFocus to be implemented by backend. ImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos) { ImGuiContext& g = *GImGui; ImGuiViewportP* best_candidate = NULL; for (ImGuiViewportP* viewport : g.Viewports) if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_IsMinimized)) && viewport->GetMainRect().Contains(mouse_platform_pos)) if (best_candidate == NULL || best_candidate->LastFocusedStampCount < viewport->LastFocusedStampCount) if (viewport->PlatformWindowCreated) best_candidate = viewport; return best_candidate; } // Update viewports and monitor infos // Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info. static void ImGui::UpdateViewportsNewFrame() { ImGuiContext& g = *GImGui; IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) // Update Focused status const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0; if (viewports_enabled) { ImGuiViewportP* focused_viewport = NULL; for (ImGuiViewportP* viewport : g.Viewports) { const bool platform_funcs_available = viewport->PlatformWindowCreated; if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) { bool is_minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); if (is_minimized) viewport->Flags |= ImGuiViewportFlags_IsMinimized; else viewport->Flags &= ~ImGuiViewportFlags_IsMinimized; } // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport. // When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored. if (g.PlatformIO.Platform_GetWindowFocus && platform_funcs_available) { bool is_focused = g.PlatformIO.Platform_GetWindowFocus(viewport); if (is_focused) viewport->Flags |= ImGuiViewportFlags_IsFocused; else viewport->Flags &= ~ImGuiViewportFlags_IsFocused; if (is_focused) focused_viewport = viewport; } } // Focused viewport has changed? if (focused_viewport && g.PlatformLastFocusedViewportId != focused_viewport->ID) { IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Focused viewport changed %08X -> %08X '%s', attempting to apply our focus.\n", g.PlatformLastFocusedViewportId, focused_viewport->ID, focused_viewport->Window ? focused_viewport->Window->Name : "n/a"); const ImGuiViewport* prev_focused_viewport = FindViewportByID(g.PlatformLastFocusedViewportId); const bool prev_focused_has_been_destroyed = (prev_focused_viewport == NULL) || (prev_focused_viewport->PlatformWindowCreated == false); // Store a tag so we can infer z-order easily from all our windows // We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag // will keep the front most stamp instead of losing it back to their parent viewport. if (focused_viewport->LastFocusedStampCount != g.ViewportFocusedStampCount) focused_viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount; g.PlatformLastFocusedViewportId = focused_viewport->ID; // Focus associated dear imgui window // - if focus didn't happen with a click within imgui boundaries, e.g. Clicking platform title bar. (#6299) // - if focus didn't happen because we destroyed another window (#6462) // FIXME: perhaps 'FocusTopMostWindowUnderOne()' can handle the 'focused_window->Window != NULL' case as well. const bool apply_imgui_focus_on_focused_viewport = !IsAnyMouseDown() && !prev_focused_has_been_destroyed; if (apply_imgui_focus_on_focused_viewport && g.IO.ConfigViewportsPlatformFocusSetsImGuiFocus) { focused_viewport->LastFocusedHadNavWindow |= (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport); // Update so a window changing viewport won't lose focus. ImGuiFocusRequestFlags focus_request_flags = ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild; if (focused_viewport->Window != NULL) FocusWindow(focused_viewport->Window, focus_request_flags); else if (focused_viewport->LastFocusedHadNavWindow) FocusTopMostWindowUnderOne(NULL, NULL, focused_viewport, focus_request_flags); // Focus top most in viewport else FocusWindow(NULL, focus_request_flags); // No window had focus last time viewport was focused } } if (focused_viewport) focused_viewport->LastFocusedHadNavWindow = (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport); } // Create/update main viewport with current platform position. // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent. ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); IM_ASSERT(main_viewport->Window == NULL); ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f); ImVec2 main_viewport_size = g.IO.DisplaySize; ImVec2 main_viewport_framebuffer_scale = g.IO.DisplayFramebufferScale; if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_IsMinimized)) { main_viewport_pos = main_viewport->Pos; // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path) main_viewport_size = main_viewport->Size; main_viewport_framebuffer_scale = main_viewport->FramebufferScale; } AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows); g.CurrentDpiScale = 0.0f; g.CurrentViewport = NULL; g.MouseViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; viewport->Idx = n; // Erase unused viewports if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) { DestroyViewport(viewport); n--; continue; } const bool platform_funcs_available = viewport->PlatformWindowCreated; if (viewports_enabled) { // Update Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. if (!(viewport->Flags & ImGuiViewportFlags_IsMinimized) && platform_funcs_available) { // Viewport->WorkPos and WorkSize will be updated below if (viewport->PlatformRequestMove) viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); if (viewport->PlatformRequestResize) viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); if (g.PlatformIO.Platform_GetWindowFramebufferScale != NULL) viewport->FramebufferScale = g.PlatformIO.Platform_GetWindowFramebufferScale(viewport); } } // Update/copy monitor info UpdateViewportPlatformMonitor(viewport); // Lock down space taken by menu bars and status bars + query initial insets from backend // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc. viewport->WorkInsetMin = viewport->BuildWorkInsetMin; viewport->WorkInsetMax = viewport->BuildWorkInsetMax; viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f); if (g.PlatformIO.Platform_GetWindowWorkAreaInsets != NULL && platform_funcs_available) { ImVec4 insets = g.PlatformIO.Platform_GetWindowWorkAreaInsets(viewport); IM_ASSERT(insets.x >= 0.0f && insets.y >= 0.0f && insets.z >= 0.0f && insets.w >= 0.0f); viewport->BuildWorkInsetMin = ImVec2(insets.x, insets.y); viewport->BuildWorkInsetMax = ImVec2(insets.z, insets.w); } viewport->UpdateWorkRect(); // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. viewport->Alpha = 1.0f; // Translate Dear ImGui windows when a Host Viewport has been moved // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos; if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f)) TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos, viewport->LastSize, viewport->Size); // Update DPI scale float new_dpi_scale; if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available) new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); else if (viewport->PlatformMonitor != -1) new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; else new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; IM_ASSERT(new_dpi_scale > 0.0f && new_dpi_scale < 99.0f); // Typical correct values would be between 1.0f and 4.0f if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) { float scale_factor = new_dpi_scale / viewport->DpiScale; if (g.IO.ConfigDpiScaleViewports) ScaleWindowsInViewport(viewport, scale_factor); //if (viewport == GetMainViewport()) // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); // Scale our window moving pivot so that the window will rescale roughly around the mouse position. // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border. // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.) //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) // g.ActiveIdClickOffset = ImTrunc(g.ActiveIdClickOffset * scale_factor); } viewport->DpiScale = new_dpi_scale; } // Update fallback monitor g.PlatformMonitorsFullWorkRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); if (g.PlatformIO.Monitors.Size == 0) { ImGuiPlatformMonitor* monitor = &g.FallbackMonitor; monitor->MainPos = main_viewport->Pos; monitor->MainSize = main_viewport->Size; monitor->WorkPos = main_viewport->WorkPos; monitor->WorkSize = main_viewport->WorkSize; monitor->DpiScale = main_viewport->DpiScale; g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos); g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos + monitor->WorkSize); } else { g.FallbackMonitor = g.PlatformIO.Monitors[0]; } for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors) { g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos); g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos + monitor.WorkSize); } if (!viewports_enabled) { g.MouseViewport = main_viewport; return; } // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set. ImGuiViewportP* viewport_hovered = NULL; if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) { viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL; if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback. } else { // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. // B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order. // C) uses LastFocusedStampCount as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); } if (viewport_hovered != NULL) g.MouseLastHoveredViewport = viewport_hovered; else if (g.MouseLastHoveredViewport == NULL) g.MouseLastHoveredViewport = g.Viewports[0]; // Update mouse reference viewport // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details) if (g.MovingWindow && g.MovingWindow->Viewport) g.MouseViewport = g.MovingWindow->Viewport; else g.MouseViewport = g.MouseLastHoveredViewport; // When dragging something, always refer to the last hovered viewport. // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. // FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags_HasMouseHoveredViewport is set we want to trust when viewport_hovered==NULL and use that. const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) viewport_hovered = g.MouseLastHoveredViewport; if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown()) if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) g.MouseViewport = viewport_hovered; IM_ASSERT(g.MouseViewport != NULL); } // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) static void ImGui::UpdateViewportsEndFrame() { ImGuiContext& g = *GImGui; g.PlatformIO.Viewports.resize(0); for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; viewport->LastPos = viewport->Pos; viewport->LastSize = viewport->Size; if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) if (i > 0) // Always include main viewport in the list continue; if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) continue; if (i > 0) IM_ASSERT(viewport->Window != NULL); g.PlatformIO.Viewports.push_back(viewport); } g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called } // FIXME: We should ideally refactor the system to call this every frame (we currently don't) ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); flags |= ImGuiViewportFlags_IsPlatformWindow; if (window != NULL) { const bool window_can_use_inputs = ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) == false; if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window) flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing; if (!window_can_use_inputs) flags |= ImGuiViewportFlags_NoInputs; if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing) flags |= ImGuiViewportFlags_NoFocusOnAppearing; } ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { // Always update for main viewport as we are already pulling correct platform pos/size (see #4900) ImVec2 prev_pos = viewport->Pos; ImVec2 prev_size = viewport->Size; if (!viewport->PlatformRequestMove || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID) viewport->Pos = pos; if (!viewport->PlatformRequestResize || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID) viewport->Size = size; viewport->Flags = flags | (viewport->Flags & (ImGuiViewportFlags_IsMinimized | ImGuiViewportFlags_IsFocused)); // Preserve existing flags if (prev_pos != viewport->Pos || prev_size != viewport->Size) UpdateViewportPlatformMonitor(viewport); } else { // New viewport viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = id; viewport->Idx = g.Viewports.Size; viewport->Pos = viewport->LastPos = pos; viewport->Size = viewport->LastSize = size; viewport->Flags = flags; UpdateViewportPlatformMonitor(viewport); g.Viewports.push_back(viewport); g.ViewportCreatedCount++; IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Add Viewport %08X '%s'\n", id, window ? window->Name : ""); // We assume the window becomes front-most (even when ImGuiViewportFlags_NoFocusOnAppearing is used). // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available. viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount; // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x); g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y); g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); // Store initial DpiScale before the OS platform window creation, based on expected monitor data. // This is so we can select an appropriate font size on the first frame of our window lifetime viewport->DpiScale = GetViewportPlatformMonitor(viewport)->DpiScale; } viewport->Window = window; viewport->LastFrameActive = g.FrameCount; viewport->UpdateWorkRect(); IM_ASSERT(window == NULL || viewport->ID == window->ID); if (window != NULL) window->ViewportOwned = true; return viewport; } static void ImGui::DestroyViewport(ImGuiViewportP* viewport) { // Clear references to this viewport in windows (window->ViewportId becomes the master data) ImGuiContext& g = *GImGui; for (ImGuiWindow* window : g.Windows) { if (window->Viewport != viewport) continue; window->Viewport = NULL; window->ViewportOwned = false; } if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; // Destroy IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Delete Viewport %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_ASSERT(g.Viewports[viewport->Idx] == viewport); g.Viewports.erase(g.Viewports.Data + viewport->Idx); IM_DELETE(viewport); } // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. static void ImGui::WindowSelectViewport(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; window->ViewportAllowPlatformMonitorExtend = -1; // Restore main viewport if multi-viewport is not supported by the backend ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport(); if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { SetWindowViewport(window, main_viewport); return; } window->ViewportOwned = false; // Appearing popups reset their viewport so they can inherit again if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing) { window->Viewport = NULL; window->ViewportId = 0; } if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasViewport) == 0) { // By default inherit from parent window if (window->Viewport == NULL && window->ParentWindow && (!window->ParentWindow->IsFallbackWindow || window->ParentWindow->WasActive)) window->Viewport = window->ParentWindow->Viewport; // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file if (window->Viewport == NULL && window->ViewportId != 0) { window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId); if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None); } } bool lock_viewport = false; if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasViewport) { // Code explicitly request a viewport window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId); window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. if (window->Viewport && (window->Flags & ImGuiWindowFlags_DockNodeHost) != 0 && window->Viewport->Window != NULL) { window->Viewport->Window = window; window->Viewport->ID = window->ViewportId = window->ID; // Overwrite ID (always owned by node) } lock_viewport = true; } else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) { // Always inherit viewport from parent window if (window->DockNode && window->DockNode->HostWindow) IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport); window->Viewport = window->ParentWindow->Viewport; } else if (window->DockNode && window->DockNode->HostWindow) { // This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame window->Viewport = window->DockNode->HostWindow->Viewport; } else if (flags & ImGuiWindowFlags_Tooltip) { window->Viewport = g.MouseViewport; } else if (GetWindowAlwaysWantOwnViewport(window)) { window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } else if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window && IsMousePosValid()) { if (window->Viewport != NULL && window->Viewport->Window == window) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } else { // Merge into host viewport? // We cannot test window->ViewportOwned as it set lower in the function. // Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212) bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && (g.ActiveId == 0 || g.ActiveIdAllowOverlap)); if (try_to_merge_into_host_viewport) UpdateTryMergeWindowIntoHostViewports(window); } // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport if (window->Viewport == NULL) if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport)) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); // Mark window as allowed to protrude outside of its viewport and into the current monitor if (!lock_viewport) { if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) { // We need to take account of the possibility that mouse may become invalid. // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos; bool use_mouse_ref = (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !g.NavWindow); bool mouse_valid = IsMousePosValid(&mouse_ref); if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid)) window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos(window->Flags)); else window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL) { // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) { // Steal/transfer ownership IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name); window->Viewport->Window = window; window->Viewport->ID = window->ID; window->Viewport->LastNameHash = 0; } else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge? { // New viewport window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); } } else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) { // Regular (non-child, non-popup) windows by default are also allowed to protrude // Child windows are kept contained within their parent. window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } } // Update flags window->ViewportOwned = (window == window->Viewport->Window); window->ViewportId = window->Viewport->ID; // If the OS window has a title bar, hide our imgui title bar //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) // window->Flags |= ImGuiWindowFlags_NoTitleBar; } void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack) { ImGuiContext& g = *GImGui; bool viewport_rect_changed = false; // Synchronize window --> viewport in most situations // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM if (window->Viewport->PlatformRequestMove) { window->Pos = window->Viewport->Pos; MarkIniSettingsDirty(window); } else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0) { viewport_rect_changed = true; window->Viewport->Pos = window->Pos; } if (window->Viewport->PlatformRequestResize) { window->Size = window->SizeFull = window->Viewport->Size; MarkIniSettingsDirty(window); } else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0) { viewport_rect_changed = true; window->Viewport->Size = window->Size; } window->Viewport->UpdateWorkRect(); // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame() // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect. if (viewport_rect_changed) UpdateViewportPlatformMonitor(window->Viewport); // Update common viewport flags const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear; ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear; ImGuiWindowFlags window_flags = window->Flags; const bool is_modal = (window_flags & ImGuiWindowFlags_Modal) != 0; const bool is_short_lived_floating_window = (window_flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; if (window_flags & ImGuiWindowFlags_Tooltip) viewport_flags |= ImGuiViewportFlags_TopMost; if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal) viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window) viewport_flags |= ImGuiViewportFlags_NoDecoration; // Not correct to set modal as topmost because: // - Because other popups can be stacked above a modal (e.g. combo box in a modal) // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost" //if (flags & ImGuiWindowFlags_Modal) // viewport_flags |= ImGuiViewportFlags_TopMost; // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere. if (is_short_lived_floating_window && !is_modal) viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; // We can overwrite viewport flags using ImGuiWindowClass (advanced users) if (window->WindowClass.ViewportFlagsOverrideSet) viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet; if (window->WindowClass.ViewportFlagsOverrideClear) viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear; // We can also tell the backend that clearing the platform window won't be necessary, // as our window background is filling the viewport and we have disabled BgAlpha. // FIXME: Work on support for per-viewport transparency (#2766) if (!(window_flags & ImGuiWindowFlags_NoBackground)) viewport_flags |= ImGuiViewportFlags_NoRendererClear; window->Viewport->Flags = viewport_flags; // Update parent viewport ID // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport()) if (window->WindowClass.ParentViewportId != (ImGuiID)-1) { ImGuiID old_parent_viewport_id = window->Viewport->ParentViewportId; window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; if (window->Viewport->ParentViewportId != old_parent_viewport_id) window->Viewport->ParentViewport = FindViewportByID(window->Viewport->ParentViewportId); } else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive)) { window->Viewport->ParentViewport = parent_window_in_stack->Viewport; window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; } else { window->Viewport->ParentViewport = g.IO.ConfigViewportsNoDefaultParent ? NULL : GetMainViewport(); window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; } } // Called by user at the end of the main loop, after EndFrame() // This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. void ImGui::UpdatePlatformWindows() { ImGuiContext& g = *GImGui; IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); g.FrameCountPlatformEnded = g.FrameCount; if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) return; // Create/resize/destroy platform windows to match each active viewport. // Skip the main viewport (index 0), which is always fully handled by the application! for (int i = 1; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame) bool destroy_platform_window = false; destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); if (destroy_platform_window) { DestroyPlatformWindow(viewport); continue; } // New windows that appears directly in a new viewport won't always have a size on their first frame if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; // Create window const bool is_new_platform_window = (viewport->PlatformWindowCreated == false); if (is_new_platform_window) { IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Create Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); g.PlatformIO.Platform_CreateWindow(viewport); if (g.PlatformIO.Renderer_CreateWindow != NULL) g.PlatformIO.Renderer_CreateWindow(viewport); g.PlatformWindowsCreatedCount++; viewport->LastNameHash = 0; viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?) viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it. viewport->PlatformWindowCreated = true; } // Apply Position and Size (from ImGui to Platform/Renderer backends) if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove) g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize) g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize) g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); viewport->LastPlatformPos = viewport->Pos; viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size; // Update title bar (if it changed) if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window)) { const char* title_begin = window_for_title->Name; char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin); const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin); if (viewport->LastNameHash != title_hash) { char title_end_backup_c = *title_end; *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain. g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); *title_end = title_end_backup_c; viewport->LastNameHash = title_hash; } } // Update alpha (if it changed) if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); viewport->LastAlpha = viewport->Alpha; // Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed. if (g.PlatformIO.Platform_UpdateWindow) g.PlatformIO.Platform_UpdateWindow(viewport); if (is_new_platform_window) { // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late) if (g.FrameCount < 3) viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; // Show window g.PlatformIO.Platform_ShowWindow(viewport); } // Clear request flags viewport->ClearRequestFlags(); } } // This is a default/basic function for performing the rendering/swap of multiple Platform Windows. // Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. // The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: // // ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); // for (int i = 1; i < platform_io.Viewports.Size; i++) // if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) // MyRenderFunction(platform_io.Viewports[i], my_args); // for (int i = 1; i < platform_io.Viewports.Size; i++) // if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) // MySwapBufferFunction(platform_io.Viewports[i], my_args); // void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) { // Skip the main viewport (index 0), which is always fully handled by the application! ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); for (int i = 1; i < platform_io.Viewports.Size; i++) { ImGuiViewport* viewport = platform_io.Viewports[i]; if (viewport->Flags & ImGuiViewportFlags_IsMinimized) continue; if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); } for (int i = 1; i < platform_io.Viewports.Size; i++) { ImGuiViewport* viewport = platform_io.Viewports[i]; if (viewport->Flags & ImGuiViewportFlags_IsMinimized) continue; if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); } } static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) { ImGuiContext& g = *GImGui; for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos)) return monitor_n; } return -1; } // Search for the monitor with the largest intersection area with the given rectangle // We generally try to avoid searching loops but the monitor count should be very small here // FIXME-OPT: We could test the last monitor used for that viewport first, and early static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) { ImGuiContext& g = *GImGui; const int monitor_count = g.PlatformIO.Monitors.Size; if (monitor_count <= 1) return monitor_count - 1; // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position. // This is necessary for tooltips which always resize down to zero at first. const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); int best_monitor_n = 0; // Default to the first monitor as fallback float best_monitor_surface = 0.001f; for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) { const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize); if (monitor_rect.Contains(rect)) return monitor_n; ImRect overlapping_rect = rect; overlapping_rect.ClipWithFull(monitor_rect); float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); if (overlapping_surface < best_monitor_surface) continue; best_monitor_surface = overlapping_surface; best_monitor_n = monitor_n; } return best_monitor_n; } // Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor) static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) { viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect()); } // Return value is always != NULL, but don't hold on it across frames. const ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* viewport_p) { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p; int monitor_idx = viewport->PlatformMonitor; if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) return &g.PlatformIO.Monitors[monitor_idx]; return &g.FallbackMonitor; } void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; if (viewport->PlatformWindowCreated) { IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Destroy Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); if (g.PlatformIO.Renderer_DestroyWindow) g.PlatformIO.Renderer_DestroyWindow(viewport); if (g.PlatformIO.Platform_DestroyWindow) g.PlatformIO.Platform_DestroyWindow(viewport); IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL); // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize() // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public. if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID) viewport->PlatformWindowCreated = false; } else { IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); } viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL; viewport->ClearRequestFlags(); } void ImGui::DestroyPlatformWindows() { // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the backend // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData. // It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling // code to operator a consistent manner. // It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without // crashing if it doesn't have data stored. ImGuiContext& g = *GImGui; for (ImGuiViewportP* viewport : g.Viewports) DestroyPlatformWindow(viewport); } //----------------------------------------------------------------------------- // [SECTION] DOCKING //----------------------------------------------------------------------------- // Docking: Internal Types // Docking: Forward Declarations // Docking: ImGuiDockContext // Docking: ImGuiDockContext Docking/Undocking functions // Docking: ImGuiDockNode // Docking: ImGuiDockNode Tree manipulation functions // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) // Docking: Builder Functions // Docking: Begin/End Support Functions (called from Begin/End) // Docking: Settings //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Typical Docking call flow: (root level is generally public API): //----------------------------------------------------------------------------- // - NewFrame() new dear imgui frame // | DockContextNewFrameUpdateUndocking() - process queued undocking requests // | - DockContextProcessUndockWindow() - process one window undocking request // | - DockContextProcessUndockNode() - process one whole node undocking request // | DockContextNewFrameUpdateUndocking() - process queue docking requests, create floating dock nodes // | - update g.HoveredDockNode - [debug] update node hovered by mouse // | - DockContextProcessDock() - process one docking request // | - DockNodeUpdate() // | - DockNodeUpdateForRootNode() // | - DockNodeUpdateFlagsAndCollapse() // | - DockNodeFindInfo() // | - destroy unused node or tab bar // | - create dock node host window // | - Begin() etc. // | - DockNodeStartMouseMovingWindow() // | - DockNodeTreeUpdatePosSize() // | - DockNodeTreeUpdateSplitter() // | - draw node background // | - DockNodeUpdateTabBar() - create/update tab bar for a docking node // | - DockNodeAddTabBar() // | - DockNodeWindowMenuUpdate() // | - DockNodeCalcTabBarLayout() // | - BeginTabBarEx() // | - TabItemEx() calls // | - EndTabBar() // | - BeginDockableDragDropTarget() // | - DockNodeUpdate() - recurse into child nodes... //----------------------------------------------------------------------------- // - DockSpace() user submit a dockspace into a window // | Begin(Child) - create a child window // | DockNodeUpdate() - call main dock node update function // | End(Child) // | ItemSize() //----------------------------------------------------------------------------- // - Begin() // | BeginDocked() // | BeginDockableDragDropSource() // | BeginDockableDragDropTarget() // | - DockNodePreviewDockRender() //----------------------------------------------------------------------------- // - EndFrame() // | DockContextEndFrame() //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Docking: Internal Types //----------------------------------------------------------------------------- // - ImGuiDockRequestType // - ImGuiDockRequest // - ImGuiDockPreviewData // - ImGuiDockNodeSettings // - ImGuiDockContext //----------------------------------------------------------------------------- enum ImGuiDockRequestType { ImGuiDockRequestType_None = 0, ImGuiDockRequestType_Dock, ImGuiDockRequestType_Undock, ImGuiDockRequestType_Split // Split is the same as Dock but without a DockPayload }; struct ImGuiDockRequest { ImGuiDockRequestType Type; ImGuiWindow* DockTargetWindow; // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL) ImGuiDockNode* DockTargetNode; // Destination/Target Node to dock into ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode), [Optional] ImGuiDir DockSplitDir; float DockSplitRatio; bool DockSplitOuter; ImGuiWindow* UndockTargetWindow; ImGuiDockNode* UndockTargetNode; ImGuiDockRequest() { Type = ImGuiDockRequestType_None; DockTargetWindow = DockPayload = UndockTargetWindow = NULL; DockTargetNode = UndockTargetNode = NULL; DockSplitDir = ImGuiDir_None; DockSplitRatio = 0.5f; DockSplitOuter = false; } }; struct ImGuiDockPreviewData { ImGuiDockNode FutureNode; bool IsDropAllowed; bool IsCenterAvailable; bool IsSidesAvailable; // Hold your breath, grammar freaks.. bool IsSplitDirExplicit; // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window) ImGuiDockNode* SplitNode; ImGuiDir SplitDir; float SplitRatio; ImRect DropRectsDraw[ImGuiDir_COUNT + 1]; // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects() ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; for (int n = 0; n < IM_COUNTOF(DropRectsDraw); n++) DropRectsDraw[n] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); } }; // Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes) struct ImGuiDockNodeSettings { ImGuiID ID; ImGuiID ParentNodeId; ImGuiID ParentWindowId; ImGuiID SelectedTabId; signed char SplitAxis; char Depth; ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_) ImVec2ih Pos; ImVec2ih Size; ImVec2ih SizeRef; ImGuiDockNodeSettings() { memset((void*)this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; } }; //----------------------------------------------------------------------------- // Docking: Forward Declarations //----------------------------------------------------------------------------- namespace ImGui { // ImGuiDockContext static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node); static void DockContextDeleteNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); static ImGuiDockNode* DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window); static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all // ImGuiDockNode static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar); static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); static void DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); static ImGuiWindow* DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id); static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node); static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); static void DockNodeHideHostWindow(ImGuiDockNode* node); static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateForRootNode(ImGuiDockNode* node); static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node); static void DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeAddTabBar(ImGuiDockNode* node); static void DockNodeRemoveTabBar(ImGuiDockNode* node); static void DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar); static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); static void DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } static int DockNodeGetTabOrder(ImGuiWindow* window); // ImGuiDockNode tree manipulations static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node = NULL); static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos); static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); // Settings static void DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id); static void DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count); static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id); static void DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); static void DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); } //----------------------------------------------------------------------------- // Docking: ImGuiDockContext //----------------------------------------------------------------------------- // The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings, // or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active. // At boot time only, we run a simple GC to remove nodes that have no references. // Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures), // we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does). // This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data. //----------------------------------------------------------------------------- // - DockContextInitialize() // - DockContextShutdown() // - DockContextClearNodes() // - DockContextRebuildNodes() // - DockContextNewFrameUpdateUndocking() // - DockContextNewFrameUpdateDocking() // - DockContextEndFrame() // - DockContextFindNodeByID() // - DockContextBindNodeToWindow() // - DockContextGenNodeID() // - DockContextAddNode() // - DockContextRemoveNode() // - ImGuiDockContextPruneNodeData // - DockContextPruneUnusedSettingsNodes() // - DockContextBuildNodesFromSettings() // - DockContextBuildAddWindowsToNodes() //----------------------------------------------------------------------------- void ImGui::DockContextInitialize(ImGuiContext* ctx) { ImGuiContext& g = *ctx; // Add .ini handle for persistent docking data ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Docking"; ini_handler.TypeHash = ImHashStr("Docking"); ini_handler.ClearAllFn = DockSettingsHandler_ClearAll; ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen; ini_handler.ReadLineFn = DockSettingsHandler_ReadLine; ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll; ini_handler.WriteAllFn = DockSettingsHandler_WriteAll; g.SettingsHandlers.push_back(ini_handler); g.DockNodeWindowMenuHandler = &DockNodeWindowMenuHandler_Default; } void ImGui::DockContextShutdown(ImGuiContext* ctx) { ImGuiDockContext* dc = &ctx->DockContext; for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) DockContextDeleteNode(ctx, node); } void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs) { IM_UNUSED(ctx); IM_ASSERT(ctx == GImGui); DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs); DockBuilderRemoveNodeChildNodes(root_id); } // [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch // (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!) void ImGui::DockContextRebuildNodes(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = &ctx->DockContext; IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRebuildNodes\n"); SaveIniSettingsToMemory(); ImGuiID root_id = 0; // Rebuild all DockContextClearNodes(ctx, root_id, false); DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size); DockContextBuildAddWindowsToNodes(ctx, root_id); } // Docking context update function, called by NewFrame() void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = &ctx->DockContext; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) { if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0) DockContextClearNodes(ctx, 0, true); return; } // Setting NoSplit at runtime merges all nodes if (g.IO.ConfigDockingNoSplit) for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (node->IsRootNode() && node->IsSplitNode()) { DockBuilderRemoveNodeChildNodes(node->ID); //dc->WantFullRebuild = true; } // Process full rebuild #if 0 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) dc->WantFullRebuild = true; #endif if (dc->WantFullRebuild) { DockContextRebuildNodes(ctx); dc->WantFullRebuild = false; } // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame) for (ImGuiDockRequest& req : dc->Requests) { if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetWindow) DockContextProcessUndockWindow(ctx, req.UndockTargetWindow); else if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetNode) DockContextProcessUndockNode(ctx, req.UndockTargetNode); } } // Docking context update function, called by NewFrame() void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = &ctx->DockContext; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; // [DEBUG] Store hovered dock node. // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut. // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering. g.DebugHoveredDockNode = NULL; if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow) { if (hovered_window->DockNodeAsHost) g.DebugHoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos); else if (hovered_window->RootWindow->DockNode) g.DebugHoveredDockNode = hovered_window->RootWindow->DockNode; } // Process Docking requests for (ImGuiDockRequest& req : dc->Requests) if (req.Type == ImGuiDockRequestType_Dock) DockContextProcessDock(ctx, &req); dc->Requests.resize(0); // Create windows for each automatic docking nodes // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high) for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (node->IsFloatingNode()) DockNodeUpdate(node); } void ImGui::DockContextEndFrame(ImGuiContext* ctx) { // Draw backgrounds of node missing their window ImGuiContext& g = *ctx; ImGuiDockContext* dc = &g.DockContext; for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame) { ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size); ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), g.Style.DockingSeparatorSize); node->HostWindow->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags); } } ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) { return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id); } ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) { // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used) // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0 // We should poke in ctx->Nodes to find a suitable ID faster. Even more so trivial that ctx->Nodes lookup is already sorted. ImGuiID id = 0x0001; while (DockContextFindNodeByID(ctx, id) != NULL) id++; return id; } static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) { // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window. ImGuiContext& g = *ctx; if (id == 0) id = DockContextGenNodeID(ctx); else IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings! IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextAddNode 0x%08X\n", id); ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); ctx->DockContext.Nodes.SetVoidPtr(node->ID, node); return node; } static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node) { ImGuiContext& g = *ctx; IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRemoveNode 0x%08X\n", node->ID); IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL); IM_ASSERT(node->Windows.Size == 0); if (node->HostWindow) node->HostWindow->DockNodeAsHost = NULL; ImGuiDockNode* parent_node = node->ParentNode; const bool merge = (merge_sibling_into_parent_node && parent_node != NULL); if (merge) { IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node); ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]); DockNodeTreeMerge(&g, parent_node, sibling_node); } else { for (int n = 0; parent_node && n < IM_COUNTOF(parent_node->ChildNodes); n++) if (parent_node->ChildNodes[n] == node) node->ParentNode->ChildNodes[n] = NULL; DockContextDeleteNode(ctx, node); } } // Raw-ish delete static void ImGui::DockContextDeleteNode(ImGuiContext* ctx, ImGuiDockNode* node) { ImGuiDockContext* dc = &ctx->DockContext; if (node->TabBar) IM_DELETE(node->TabBar); node->TabBar = NULL; dc->Nodes.SetVoidPtr(node->ID, NULL); IM_DELETE(node); } static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs) { const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs; const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs; return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a); } // Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here. struct ImGuiDockContextPruneNodeData { int CountWindows, CountChildWindows, CountChildNodes; ImGuiID RootId; ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; } }; // Garbage collect unused nodes (run once at init time) static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = &ctx->DockContext; IM_ASSERT(g.Windows.Size == 0); ImPool pool; pool.Reserve(dc->NodesSettings.Size); // Count child nodes and compute RootID for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; if (pool.GetByKey(settings->ID) != 0) { settings->ID = 0; // Duplicate continue; } ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0; pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID; if (settings->ParentNodeId) pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++; } // Count reference to dock ids from dockspaces // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes() for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; if (settings->ParentWindowId != 0) if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->ParentWindowId)) if (window_settings->DockId) if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId)) data->CountChildNodes++; } // Count reference to dock ids from window settings // We guard against the possibility of an invalid .ini file (RootID may point to a missing node) for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (ImGuiID dock_id = settings->DockId) if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id)) { data->CountWindows++; if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId)) data_root->CountChildWindows++; } // Prune for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID); if (data == NULL || data->CountWindows > 1) continue; ImGuiDockContextPruneNodeData* data_root = (settings->ID == data->RootId) ? data : pool.GetByKey(data->RootId); ImGuiDockContextPruneNodeData* data_parent = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : NULL; bool remove = false; remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window remove |= (data_root == NULL || data_root->CountChildWindows == 0); if (remove) { IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID); DockSettingsRemoveNodeReferences(&settings->ID, 1); settings->ID = 0; } else if (data_parent && data_parent->CountChildNodes == 1) { IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextPruneUnusedSettingsNodes: Merge 0x%08X->0X%08X\n", settings->ID, settings->ParentNodeId); DockSettingsRenameNodeReferences(settings->ID, settings->ParentNodeId); settings->ID = 0; } } } static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count) { // Build nodes ImGuiContext& g = *ctx; IM_UNUSED(g); for (int node_n = 0; node_n < node_settings_count; node_n++) { ImGuiDockNodeSettings* settings = &node_settings_array[node_n]; if (settings->ID == 0) continue; if (DockContextFindNodeByID(ctx, settings->ID) != NULL) { IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextBuildNodesFromSettings: skip duplicate node 0x%08X\n", settings->ID); continue; } ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID); node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL; node->Pos = ImVec2(settings->Pos.x, settings->Pos.y); node->Size = ImVec2(settings->Size.x, settings->Size.y); node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y); node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode; if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL) node->ParentNode->ChildNodes[0] = node; else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) node->ParentNode->ChildNodes[1] = node; node->SelectedTabId = settings->SelectedTabId; node->SplitAxis = (ImGuiAxis)settings->SplitAxis; node->SetLocalFlags(settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. char host_window_title[20]; ImGuiDockNode* root_node = DockNodeGetRootNode(node); node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_COUNTOF(host_window_title))); } } void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id) { // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame) ImGuiContext& g = *ctx; for (ImGuiWindow* window : g.Windows) { if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1) continue; if (window->DockNode != NULL) continue; ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId); IM_ASSERT(node != NULL); // This should have been called after DockContextBuildNodesFromSettings() if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id) DockNodeAddWindow(node, window, true); } } //----------------------------------------------------------------------------- // Docking: ImGuiDockContext Docking/Undocking functions //----------------------------------------------------------------------------- // - DockContextQueueDock() // - DockContextQueueUndockWindow() // - DockContextQueueUndockNode() // - DockContextQueueNotifyRemovedNode() // - DockContextProcessDock() // - DockContextProcessUndockWindow() // - DockContextProcessUndockNode() // - DockContextCalcDropPosForDocking() //----------------------------------------------------------------------------- void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) { IM_ASSERT(target != payload); ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Dock; req.DockTargetWindow = target; req.DockTargetNode = target_node; req.DockPayload = payload; req.DockSplitDir = split_dir; req.DockSplitRatio = split_ratio; req.DockSplitOuter = split_outer; ctx->DockContext.Requests.push_back(req); } void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) { ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Undock; req.UndockTargetWindow = window; ctx->DockContext.Requests.push_back(req); } void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) { ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Undock; req.UndockTargetNode = node; ctx->DockContext.Requests.push_back(req); } void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node) { ImGuiDockContext* dc = &ctx->DockContext; for (ImGuiDockRequest& req : dc->Requests) if (req.DockTargetNode == node) req.Type = ImGuiDockRequestType_None; } void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL)); IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL); ImGuiContext& g = *ctx; IM_UNUSED(g); ImGuiWindow* payload_window = req->DockPayload; // Optional ImGuiWindow* target_window = req->DockTargetWindow; ImGuiDockNode* node = req->DockTargetNode; if (payload_window) IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X target '%s' dock window '%s', split_dir %d\n", node ? node->ID : 0, target_window ? target_window->Name : "NULL", payload_window->Name, req->DockSplitDir); else IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir); // Decide which Tab will be selected at the end of the operation ImGuiID next_selected_id = 0; ImGuiDockNode* payload_node = NULL; if (payload_window) { payload_node = payload_window->DockNodeAsHost; payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later. if (payload_node && payload_node->IsLeafNode()) next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; if (payload_node == NULL) next_selected_id = payload_window->TabId; } // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==. if (node) IM_ASSERT(node->LastFrameAlive <= g.FrameCount); if (node && target_window && node == target_window->DockNodeAsHost) IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode()); // Create new node and add existing window to it if (node == NULL) { node = DockContextAddNode(ctx, 0); node->Pos = target_window->Pos; node->Size = target_window->Size; if (target_window->DockNodeAsHost == NULL) { DockNodeAddWindow(node, target_window, true); node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted; target_window->DockIsActive = true; } } ImGuiDir split_dir = req->DockSplitDir; if (split_dir != ImGuiDir_None) { // Split into two, one side will be our payload node unless we are dropping a loose window const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side const float split_ratio = req->DockSplitRatio; DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here! ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1]; new_node->HostWindow = node->HostWindow; node = new_node; } node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar); if (node != payload_node) { // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!) if (node->Windows.Size > 0 && node->TabBar == NULL) { DockNodeAddTabBar(node); for (int n = 0; n < node->Windows.Size; n++) TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]); } if (payload_node != NULL) { // Transfer full payload node (with 1+ child windows or child nodes) if (payload_node->IsSplitNode()) { if (node->Windows.Size > 0) { // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node. // In this situation, we move the windows of the target node into the currently visible node of the payload. // This allows us to preserve some of the underlying dock tree settings nicely. IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted. ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows; if (visible_node->TabBar) IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); DockNodeMoveWindows(node, visible_node); DockNodeMoveWindows(visible_node, node); DockSettingsRenameNodeReferences(node->ID, visible_node->ID); } if (node->IsCentralNode()) { // Central node property needs to be moved to a leaf node, pick the last focused one. // FIXME-DOCK: If we had to transfer other flags here, what would the policy be? ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId); IM_ASSERT(last_focused_node != NULL); ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node)); last_focused_node->SetLocalFlags(last_focused_node->LocalFlags | ImGuiDockNodeFlags_CentralNode); node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode); last_focused_root_node->CentralNode = last_focused_node; } IM_ASSERT(node->Windows.Size == 0); DockNodeMoveChildNodes(node, payload_node); } else { const ImGuiID payload_dock_id = payload_node->ID; DockNodeMoveWindows(node, payload_node); DockSettingsRenameNodeReferences(payload_dock_id, node->ID); } DockContextRemoveNode(ctx, payload_node, true); } else if (payload_window) { // Transfer single window const ImGuiID payload_dock_id = payload_window->DockId; node->VisibleWindow = payload_window; DockNodeAddWindow(node, payload_window, true); if (payload_dock_id != 0) DockSettingsRenameNodeReferences(payload_dock_id, node->ID); } } else { // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar node->WantHiddenTabBarUpdate = true; } // Update selection immediately if (ImGuiTabBar* tab_bar = node->TabBar) tab_bar->NextSelectedTabId = next_selected_id; MarkIniSettingsDirty(); } // Problem: // Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more // than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic // with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be // due to missing ImGuiBackendFlags_HasMouseCursors backend flag). // Solution: // When undocking a window we currently force its maximum size to 90% of the host viewport or monitor. // Reevaluate this when we implement preserving docked/undocked size ("docking_wip/undocked_size" branch). static ImVec2 FixLargeWindowsWhenUndocking(const ImVec2& size, ImGuiViewport* ref_viewport) { if (ref_viewport == NULL) return size; ImGuiContext& g = *GImGui; ImVec2 max_size = ImTrunc(ref_viewport->WorkSize * 0.90f); if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) { const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ref_viewport); max_size = ImTrunc(monitor->WorkSize * 0.90f); } return ImMin(size, max_size); } void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref) { ImGuiContext& g = *ctx; IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref); if (window->DockNode) DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId); else window->DockId = 0; window->Collapsed = false; window->DockIsActive = false; window->DockNodeIsVisible = window->DockTabIsVisible = false; window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport); MarkIniSettingsDirty(); } void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) { ImGuiContext& g = *ctx; IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockNode node %08X\n", node->ID); IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Windows.Size >= 1); if (node->IsRootNode() || node->IsCentralNode()) { // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); new_node->Pos = node->Pos; new_node->Size = node->Size; new_node->SizeRef = node->SizeRef; DockNodeMoveWindows(new_node, node); DockSettingsRenameNodeReferences(node->ID, new_node->ID); node = new_node; } else { // Otherwise extract our node and merge our sibling back into the parent node. IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; node->ParentNode->ChildNodes[index_in_parent] = NULL; DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]); node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport node->ParentNode = NULL; } for (ImGuiWindow* window : node->Windows) { window->Flags &= ~ImGuiWindowFlags_ChildWindow; if (window->ParentWindow) window->ParentWindow->DC.ChildWindows.find_erase(window); UpdateWindowParentAndRootLinks(window, window->Flags, NULL); } node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode; node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport); node->WantMouseMove = true; MarkIniSettingsDirty(); } // This is mostly used for automation. bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos) { if (target != NULL && target_node == NULL) target_node = target->DockNode; // In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects // (which would be functionally identical) we only show the outer one. Reflect this here. if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None) split_outer = true; ImGuiDockPreviewData split_data; DockNodePreviewDockSetup(target, target_node, payload_window, payload_node, &split_data, false, split_outer); if (split_data.DropRectsDraw[split_dir+1].IsInverted()) return false; *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); return true; } //----------------------------------------------------------------------------- // Docking: ImGuiDockNode //----------------------------------------------------------------------------- // - DockNodeGetTabOrder() // - DockNodeAddWindow() // - DockNodeRemoveWindow() // - DockNodeMoveChildNodes() // - DockNodeMoveWindows() // - DockNodeApplyPosSizeToWindows() // - DockNodeHideHostWindow() // - ImGuiDockNodeFindInfoResults // - DockNodeFindInfo() // - DockNodeFindWindowByID() // - DockNodeUpdateFlagsAndCollapse() // - DockNodeUpdateHasCentralNodeFlag() // - DockNodeUpdateVisibleFlag() // - DockNodeStartMouseMovingWindow() // - DockNodeUpdate() // - DockNodeUpdateWindowMenu() // - DockNodeBeginAmendTabBar() // - DockNodeEndAmendTabBar() // - DockNodeUpdateTabBar() // - DockNodeAddTabBar() // - DockNodeRemoveTabBar() // - DockNodeIsDropAllowedOne() // - DockNodeIsDropAllowed() // - DockNodeCalcTabBarLayout() // - DockNodeCalcSplitRects() // - DockNodeCalcDropRectsAndTestMousePos() // - DockNodePreviewDockSetup() // - DockNodePreviewDockRender() //----------------------------------------------------------------------------- ImGuiDockNode::ImGuiDockNode(ImGuiID id) { ID = id; SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None; ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; SplitAxis = ImGuiAxis_None; State = ImGuiDockNodeState_Unknown; LastBgColor = IM_COL32_WHITE; HostWindow = VisibleWindow = NULL; CentralNode = OnlyNodeWithWindows = NULL; CountNodeWithWindows = 0; LastFrameAlive = LastFrameActive = LastFrameFocused = -1; LastFocusedNodeId = 0; SelectedTabId = 0; WantCloseTabId = 0; RefViewportId = 0; AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; IsBgDrawnThisFrame = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; } ImGuiDockNode::~ImGuiDockNode() { IM_ASSERT(TabBar == NULL); ChildNodes[0] = ChildNodes[1] = NULL; } int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) { ImGuiTabBar* tab_bar = window->DockNode->TabBar; if (tab_bar == NULL) return -1; ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId); return tab ? TabBarGetTabOrder(tab_bar, tab) : -1; } static void DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow* window) { window->Hidden = true; window->HiddenFramesCanSkipItems = window->Active ? 1 : 2; } static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar) { ImGuiContext& g = *GImGui; (void)g; if (window->DockNode) { // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node) IM_ASSERT(window->DockNode->ID != node->ID); DockNodeRemoveWindow(window->DockNode, window, 0); } IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name); // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window, // we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame). // We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin() if (node->HostWindow == NULL && node->Windows.Size == 1 && node->Windows[0]->WasActive == false) DockNodeHideWindowDuringHostWindowCreation(node->Windows[0]); node->Windows.push_back(window); node->WantHiddenTabBarUpdate = true; window->DockNode = node; window->DockId = node->ID; window->DockIsActive = (node->Windows.Size > 1); window->DockTabWantClose = false; // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage. // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. if (node->HostWindow == NULL && node->IsFloatingNode()) { if (node->AuthorityForPos == ImGuiDataAuthority_Auto) node->AuthorityForPos = ImGuiDataAuthority_Window; if (node->AuthorityForSize == ImGuiDataAuthority_Auto) node->AuthorityForSize = ImGuiDataAuthority_Window; if (node->AuthorityForViewport == ImGuiDataAuthority_Auto) node->AuthorityForViewport = ImGuiDataAuthority_Window; } // Add to tab bar if requested if (add_to_tab_bar) { if (node->TabBar == NULL) { DockNodeAddTabBar(node); node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId; // Add existing windows for (int n = 0; n < node->Windows.Size - 1; n++) TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]); } TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window); } DockNodeUpdateVisibleFlag(node); // Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame. if (node->HostWindow) UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow); } static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id) { ImGuiContext& g = *GImGui; IM_ASSERT(window->DockNode == node); //IM_ASSERT(window->RootWindowDockTree == node->HostWindow); //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin() IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID); IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name); window->DockNode = NULL; window->DockIsActive = window->DockTabWantClose = false; window->DockId = save_dock_id; window->Flags &= ~ImGuiWindowFlags_ChildWindow; if (window->ParentWindow) window->ParentWindow->DC.ChildWindows.find_erase(window); UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately if (node->HostWindow && node->HostWindow->ViewportOwned) { // When undocking from a user interaction this will always run in NewFrame() and have not much effect. // But mid-frame, if we clear viewport we need to mark window as hidden as well. window->Viewport = NULL; window->ViewportId = 0; window->ViewportOwned = false; window->Hidden = true; } // Remove window bool erased = false; for (int n = 0; n < node->Windows.Size; n++) if (node->Windows[n] == window) { node->Windows.erase(node->Windows.Data + n); erased = true; break; } if (!erased) IM_ASSERT(erased); if (node->VisibleWindow == window) node->VisibleWindow = NULL; // Remove tab and possibly tab bar node->WantHiddenTabBarUpdate = true; if (node->TabBar) { TabBarRemoveTab(node->TabBar, window->TabId); const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2; if (node->Windows.Size < tab_count_threshold_for_tab_bar) DockNodeRemoveTabBar(node); } if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID) { // Automatic dock node delete themselves if they are not holding at least one tab DockContextRemoveNode(&g, node, true); return; } if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow) { ImGuiWindow* remaining_window = node->Windows[0]; // Note: we used to transport viewport ownership here. remaining_window->Collapsed = node->HostWindow->Collapsed; } // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree DockNodeUpdateVisibleFlag(node); } static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) { IM_ASSERT(dst_node->Windows.Size == 0); dst_node->ChildNodes[0] = src_node->ChildNodes[0]; dst_node->ChildNodes[1] = src_node->ChildNodes[1]; if (dst_node->ChildNodes[0]) dst_node->ChildNodes[0]->ParentNode = dst_node; if (dst_node->ChildNodes[1]) dst_node->ChildNodes[1]->ParentNode = dst_node; dst_node->SplitAxis = src_node->SplitAxis; dst_node->SizeRef = src_node->SizeRef; src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL; } static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) { // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered) IM_ASSERT(src_node && dst_node && dst_node != src_node); ImGuiTabBar* src_tab_bar = src_node->TabBar; if (src_tab_bar != NULL) IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size); // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.) bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL); if (move_tab_bar) { dst_node->TabBar = src_node->TabBar; src_node->TabBar = NULL; } // Tab order is not important here, it is preserved by sorting in DockNodeUpdateTabBar(). for (ImGuiWindow* window : src_node->Windows) { window->DockNode = NULL; window->DockIsActive = false; DockNodeAddWindow(dst_node, window, !move_tab_bar); } src_node->Windows.clear(); if (!move_tab_bar && src_node->TabBar) { if (dst_node->TabBar) dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId; DockNodeRemoveTabBar(src_node); } } static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node) { for (ImGuiWindow* window : node->Windows) { SetWindowPos(window, node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame SetWindowSize(window, node->Size, ImGuiCond_Always); } } static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) { if (node->HostWindow) { if (node->HostWindow->DockNodeAsHost == node) node->HostWindow->DockNodeAsHost = NULL; node->HostWindow = NULL; } if (node->Windows.Size == 1) { node->VisibleWindow = node->Windows[0]; node->Windows[0]->DockIsActive = false; } if (node->TabBar) DockNodeRemoveTabBar(node); } // Search function called once by root node in DockNodeUpdate() struct ImGuiDockNodeTreeInfo { ImGuiDockNode* CentralNode; ImGuiDockNode* FirstNodeWithWindows; int CountNodesWithWindows; //ImGuiWindowClass WindowClassForMerges; ImGuiDockNodeTreeInfo() { memset((void*)this, 0, sizeof(*this)); } }; static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info) { if (node->Windows.Size > 0) { if (info->FirstNodeWithWindows == NULL) info->FirstNodeWithWindows = node; info->CountNodesWithWindows++; } if (node->IsCentralNode()) { IM_ASSERT(info->CentralNode == NULL); // Should be only one IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this."); info->CentralNode = node; } if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL) return; if (node->ChildNodes[0]) DockNodeFindInfo(node->ChildNodes[0], info); if (node->ChildNodes[1]) DockNodeFindInfo(node->ChildNodes[1], info); } static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id) { IM_ASSERT(id != 0); for (ImGuiWindow* window : node->Windows) if (window->ID == id) return window; return NULL; } // - Remove inactive windows/nodes. // - Update visibility flag. static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); // Inherit most flags if (node->ParentNode) node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; // Recurse into children // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) node->HasCentralNodeChild = false; if (node->ChildNodes[0]) DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]); if (node->ChildNodes[1]) DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]); // Remove inactive windows, collapse nodes // Merge node flags overrides stored in windows node->LocalFlagsInWindows = ImGuiDockNodeFlags_None; for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; IM_ASSERT(window->DockNode == node); bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); bool remove = false; remove |= node_was_active && (window->WasActive == false); // Can't use 'window->LastFrameActive + 1 < g.FrameCount'. (see #9151) remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame remove |= (window->DockTabWantClose); if (remove) { window->DockTabWantClose = false; if (node->Windows.Size == 1 && !node->IsCentralNode()) { DockNodeHideHostWindow(node); node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return return; } DockNodeRemoveWindow(node, window, node->ID); window_n--; continue; } // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear; node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet; } node->UpdateMergedFlags(); // Auto-hide tab bar option ImGuiDockNodeFlags node_flags = node->MergedFlags; if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar()) node->WantHiddenTabBarToggle = true; node->WantHiddenTabBarUpdate = false; // Cancel toggling if we know our tab bar is enforced to be hidden at all times if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar)) node->WantHiddenTabBarToggle = false; // Apply toggles at a single point of the frame (here!) if (node->Windows.Size > 1) node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar); else if (node->WantHiddenTabBarToggle) node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar); node->WantHiddenTabBarToggle = false; DockNodeUpdateVisibleFlag(node); } // This is rarely called as DockNodeUpdateForRootNode() generally does it most frames. static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node) { node->HasCentralNodeChild = false; if (node->ChildNodes[0]) DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]); if (node->ChildNodes[1]) DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]); if (node->IsRootNode()) { ImGuiDockNode* mark_node = node->CentralNode; while (mark_node) { mark_node->HasCentralNodeChild = true; mark_node = mark_node->ParentNode; } } } static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode(); is_visible |= (node->Windows.Size > 0); is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); node->IsVisible = is_visible; } static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_ASSERT(node->WantMouseMove == true); StartMouseMovingWindow(window); g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos; g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision. node->WantMouseMove = false; } // Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class. static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) { DockNodeUpdateFlagsAndCollapse(node); // - Setup central node pointers // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing ImGuiDockNodeTreeInfo info; DockNodeFindInfo(node, &info); node->CentralNode = info.CentralNode; node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL; node->CountNodeWithWindows = info.CountNodesWithWindows; if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL) node->LastFocusedNodeId = info.FirstNodeWithWindows->ID; // Copy the window class from of our first window so it can be used for proper dock filtering. // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows) { node->WindowClass = first_node_with_windows->Windows[0]->WindowClass; for (int n = 1; n < first_node_with_windows->Windows.Size; n++) if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false) { node->WindowClass = first_node_with_windows->Windows[n]->WindowClass; break; } } ImGuiDockNode* mark_node = node->CentralNode; while (mark_node) { mark_node->HasCentralNodeChild = true; mark_node = mark_node->ParentNode; } } static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window) { // Remove ourselves from any previous different host window // This can happen if a user mistakenly does (see #4295 for details): // - N+0: DockBuilderAddNode(id, 0) // missing ImGuiDockNodeFlags_DockSpace // - N+1: NewFrame() // will create floating host window for that node // - N+1: DockSpace(id) // requalify node as dockspace, moving host window if (node->HostWindow && node->HostWindow != host_window && node->HostWindow->DockNodeAsHost == node) node->HostWindow->DockNodeAsHost = NULL; host_window->DockNodeAsHost = node; node->HostWindow = host_window; } static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; IM_ASSERT(node->LastFrameActive != g.FrameCount); node->LastFrameAlive = g.FrameCount; node->IsBgDrawnThisFrame = false; node->CentralNode = node->OnlyNodeWithWindows = NULL; if (node->IsRootNode()) DockNodeUpdateForRootNode(node); // Remove tab bar if not needed if (node->TabBar && node->IsNoTabBar()) DockNodeRemoveTabBar(node); // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) bool want_to_hide_host_window = false; if (node->IsFloatingNode()) { if (node->Windows.Size <= 1 && node->IsLeafNode()) if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar)) want_to_hide_host_window = true; if (node->CountNodeWithWindows == 0) want_to_hide_host_window = true; } if (want_to_hide_host_window) { if (node->Windows.Size == 1) { // Floating window pos/size is authoritative ImGuiWindow* single_window = node->Windows[0]; node->Pos = single_window->Pos; node->Size = single_window->SizeFull; node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; // Transfer focus immediately so when we revert to a regular window it is immediately selected if (node->HostWindow && g.NavWindow == node->HostWindow) FocusWindow(single_window); if (node->HostWindow) { IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Node %08X transfer Viewport %08X->%08X to Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, single_window->ID, single_window->Name); single_window->Viewport = node->HostWindow->Viewport; single_window->ViewportId = node->HostWindow->ViewportId; if (node->HostWindow->ViewportOwned) { single_window->Viewport->ID = single_window->ID; single_window->Viewport->Window = single_window; single_window->ViewportOwned = true; } } node->RefViewportId = single_window->ViewportId; } DockNodeHideHostWindow(node); node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; node->WantCloseAll = false; node->WantCloseTabId = 0; node->HasCloseButton = node->HasWindowMenuButton = false; node->LastFrameActive = g.FrameCount; if (node->WantMouseMove && node->Windows.Size == 1) DockNodeStartMouseMovingWindow(node, node->Windows[0]); return; } // In some circumstance we will defer creating the host window (so everything will be kept hidden), // while the expected visible window is resizing itself. // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled, // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up: // N+0: Begin(): window created (with no known size), node is created // N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible // N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code. // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin(). // In reality it isn't very important as user quickly ends up with size data in .ini file. if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode()) { IM_ASSERT(node->Windows.Size > 0); ImGuiWindow* ref_window = NULL; if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them! ref_window = DockNodeFindWindowByID(node, node->SelectedTabId); if (ref_window == NULL) ref_window = node->Windows[0]; if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0) { node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing; return; } } const ImGuiDockNodeFlags node_flags = node->MergedFlags; // Decide if the node will have a close button and a window menu button node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; node->HasCloseButton = false; for (ImGuiWindow* window : node->Windows) { // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call. node->HasCloseButton |= window->HasCloseButton; window->DockIsActive = (node->Windows.Size > 1); } if ((node_flags & ImGuiDockNodeFlags_NoCloseButton) || !g.Style.DockingNodeHasCloseButton) node->HasCloseButton = false; // Bind or create host window ImGuiWindow* host_window = NULL; bool beginned_into_host_window = false; if (node->IsDockSpace()) { // [Explicit root dockspace node] IM_ASSERT(node->HostWindow); host_window = node->HostWindow; } else { // [Automatic root or child nodes] if (node->IsRootNode() && node->IsVisible) { ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL; // Sync Pos if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window) SetNextWindowPos(ref_window->Pos); else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode) SetNextWindowPos(node->Pos); // Sync Size if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window) SetNextWindowSize(ref_window->SizeFull); else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode) SetNextWindowSize(node->Size); // Sync Collapsed if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window) SetNextWindowCollapsed(ref_window->Collapsed); // Sync Viewport if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window) SetNextWindowViewport(ref_window->ViewportId); else if (node->AuthorityForViewport == ImGuiDataAuthority_Window && node->RefViewportId != 0) SetNextWindowViewport(node->RefViewportId); SetNextWindowClass(&node->WindowClass); // Begin into the host window char window_label[20]; DockNodeGetHostWindowTitle(node, window_label, IM_COUNTOF(window_label)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost; window_flags |= ImGuiWindowFlags_NoFocusOnAppearing; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse; window_flags |= ImGuiWindowFlags_NoTitleBar; SetNextWindowBgAlpha(0.0f); // Don't set ImGuiWindowFlags_NoBackground because it disables borders PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); Begin(window_label, NULL, window_flags); PopStyleVar(); beginned_into_host_window = true; host_window = g.CurrentWindow; DockNodeSetupHostWindow(node, host_window); host_window->DC.CursorPos = host_window->Pos; node->Pos = host_window->Pos; node->Size = host_window->Size; // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow) // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags. // One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again. // When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window // during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back // after the dock host window, losing their top-most status. if (node->HostWindow->Appearing) BringWindowToDisplayFront(node->HostWindow); node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto; } else if (node->ParentNode) { node->HostWindow = host_window = node->ParentNode->HostWindow; node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto; } if (node->WantMouseMove && node->HostWindow) DockNodeStartMouseMovingWindow(node, node->HostWindow); } node->RefViewportId = 0; // Clear when we have a host window // Update focused node (the one whose title bar is highlight) within a node tree if (node->IsSplitNode()) IM_ASSERT(node->TabBar == NULL); if (node->IsRootNode()) if (ImGuiWindow* p_window = g.NavWindow ? g.NavWindow->RootWindow : NULL) while (p_window != NULL && p_window->DockNode != NULL) { ImGuiDockNode* p_node = DockNodeGetRootNode(p_window->DockNode); if (p_node == node) { node->LastFocusedNodeId = p_window->DockNode->ID; // Note: not using root node ID! break; } p_window = p_node->HostWindow ? p_node->HostWindow->RootWindow : NULL; } // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace ImGuiDockNode* central_node = node->CentralNode; const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); bool central_node_hole_register_hit_test_hole = central_node_hole; if (central_node_hole) if (const ImGuiPayload* payload = ImGui::GetDragDropPayload()) if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data)) central_node_hole_register_hit_test_hole = false; if (central_node_hole_register_hit_test_hole) { // We add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily. // (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen // covering passthru node we'd have a gap on the edge not covered by the hole) IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode ImGuiDockNode* root_node = DockNodeGetRootNode(central_node); ImRect root_rect(root_node->Pos, root_node->Pos + root_node->Size); ImRect hole_rect(central_node->Pos, central_node->Pos + central_node->Size); if (hole_rect.Min.x > root_rect.Min.x) { hole_rect.Min.x += g.WindowsBorderHoverPadding; } if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= g.WindowsBorderHoverPadding; } if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += g.WindowsBorderHoverPadding; } if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= g.WindowsBorderHoverPadding; } //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255)); if (central_node_hole && !hole_rect.IsInverted()) { SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min); if (host_window->ParentWindow) SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min); } } // Update position/size, process and draw resizing splitters if (node->IsRootNode() && host_window) { DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); PushStyleColor(ImGuiCol_Separator, g.Style.Colors[ImGuiCol_Border]); PushStyleColor(ImGuiCol_SeparatorActive, g.Style.Colors[ImGuiCol_ResizeGripActive]); PushStyleColor(ImGuiCol_SeparatorHovered, g.Style.Colors[ImGuiCol_ResizeGripHovered]); DockNodeTreeUpdateSplitter(node); PopStyleColor(3); } // Draw empty node background (currently can only be the Central Node) if (host_window && node->IsEmpty() && node->IsVisible) { host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg); if (node->LastBgColor != 0) host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor); node->IsBgDrawnThisFrame = true; } // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set. // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; if (render_dockspace_bg && node->IsVisible) { host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); if (central_node_hole) RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f); else host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f); } // Draw and populate Tab Bar if (host_window) host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); if (host_window && node->Windows.Size > 0) { DockNodeUpdateTabBar(node, host_window); } else { node->WantCloseAll = false; node->WantCloseTabId = 0; node->IsFocused = false; } if (node->TabBar && node->TabBar->SelectedTabId) node->SelectedTabId = node->TabBar->SelectedTabId; else if (node->Windows.Size > 0) node->SelectedTabId = node->Windows[0]->TabId; // Draw payload drop target if (host_window && node->IsVisible) if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window)) BeginDockableDragDropTarget(host_window); // We update this after DockNodeUpdateTabBar() node->LastFrameActive = g.FrameCount; // Recurse into children // FIXME-DOCK FIXME-OPT: Should not need to recurse into children if (host_window) { if (node->ChildNodes[0]) DockNodeUpdate(node->ChildNodes[0]); if (node->ChildNodes[1]) DockNodeUpdate(node->ChildNodes[1]); // Render outer borders last (after the tab bar) if (node->IsRootNode()) RenderWindowOuterBorders(host_window); } // End host window if (beginned_into_host_window) //-V1020 End(); } // Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame. static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs) { ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window; ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window; if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder)) return d; return (a->BeginOrderWithinContext - b->BeginOrderWithinContext); } // Default handler for g.DockNodeWindowMenuHandler(): display the list of windows for a given dock-node. // This is exceptionally stored in a function pointer to also user applications to tweak this menu (undocumented) // Custom overrides may want to decorate, group, sort entries. // Please note those are internal structures: if you copy this expect occasional breakage. // (if you don't need to modify the "Tabs.Size == 1" behavior/path it is recommend you call this function in your handler) void ImGui::DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar) { IM_UNUSED(ctx); if (tab_bar->Tabs.Size == 1) { // "Hide tab bar" option. Being one of our rare user-facing string we pull it from a table. if (MenuItem(LocalizeGetMsg(ImGuiLocKey_DockingHideTabBar), NULL, node->IsHiddenTabBar())) node->WantHiddenTabBarToggle = true; } else { // Display a selectable list of windows in this docking node for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; if (tab->Flags & ImGuiTabItemFlags_Button) continue; if (Selectable(TabBarGetTabName(tab_bar, tab), tab->ID == tab_bar->SelectedTabId)) TabBarQueueFocus(tab_bar, tab); SameLine(); Text(" "); } } } static void ImGui::DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar) { // Try to position the menu so it is more likely to stays within the same viewport ImGuiContext& g = *GImGui; if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left) SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f)); else SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); if (BeginPopup("#WindowMenu")) { node->IsFocused = true; g.DockNodeWindowMenuHandler(&g, node, tab_bar); EndPopup(); } } // User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button. bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node) { if (node->TabBar == NULL || node->HostWindow == NULL) return false; if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) return false; if (node->TabBar->ID == 0) return false; Begin(node->HostWindow->Name); PushOverrideID(node->ID); bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags); IM_UNUSED(ret); IM_ASSERT(ret); return true; } void ImGui::DockNodeEndAmendTabBar() { EndTabBar(); PopID(); End(); } static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node) { // CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy) ImGuiContext& g = *GImGui; if (g.NavWindowingTarget) return (g.NavWindowingTarget->DockNode == node); // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window) if (g.NavWindow && root_node->LastFocusedNodeId == node->ID) { // FIXME: This could all be backed in RootWindowForTitleBarHighlight? Probably need to reorganize for both dock nodes + other RootWindowForTitleBarHighlight users (not-node) ImGuiWindow* parent_window = g.NavWindow->RootWindow; while (parent_window->Flags & ImGuiWindowFlags_ChildMenu) parent_window = parent_window->ParentWindow->RootWindow; ImGuiDockNode* start_parent_node = parent_window->DockNodeAsHost ? parent_window->DockNodeAsHost : parent_window->DockNode; for (ImGuiDockNode* parent_node = start_parent_node; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL) if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node) return true; } return false; } // Submit the tab bar corresponding to a dock node and various housekeeping details. static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); node->WantCloseAll = false; node->WantCloseTabId = 0; // Decide if we should use a focused title bar color bool is_focused = false; ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (IsDockNodeTitleBarHighlighted(node, root_node)) is_focused = true; // Hidden tab bar will show a triangle on the upper-left (in Begin) if (node->IsHiddenTabBar() || node->IsNoTabBar()) { node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL; node->IsFocused = is_focused; if (is_focused) node->LastFrameFocused = g.FrameCount; if (node->VisibleWindow) { // Notify root of visible window (used to display title in OS task bar) if (is_focused || root_node->VisibleWindow == NULL) root_node->VisibleWindow = node->VisibleWindow; if (node->TabBar) node->TabBar->VisibleTabId = node->VisibleWindow->TabId; } return; } // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed bool backup_skip_item = host_window->SkipItems; if (!node->IsDockSpace()) { host_window->SkipItems = false; host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; } // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID. // This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs, // as docked windows themselves will override the stack with their own root ID. PushOverrideID(node->ID); ImGuiTabBar* tab_bar = node->TabBar; bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden if (tab_bar == NULL) { DockNodeAddTabBar(node); tab_bar = node->TabBar; } ImGuiID focus_tab_id = 0; node->IsFocused = is_focused; const ImGuiDockNodeFlags node_flags = node->MergedFlags; const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None); // In a dock node, the Collapse Button turns into the Window Menu button. // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes? if (has_window_menu_button && IsPopupOpen("#WindowMenu")) { ImGuiID next_selected_tab_id = tab_bar->NextSelectedTabId; DockNodeWindowMenuUpdate(node, tab_bar); if (tab_bar->NextSelectedTabId != 0 && tab_bar->NextSelectedTabId != next_selected_tab_id) focus_tab_id = tab_bar->NextSelectedTabId; is_focused |= node->IsFocused; } // Layout ImRect title_bar_rect, tab_bar_rect; ImVec2 window_menu_button_pos; ImVec2 close_button_pos; DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos, &close_button_pos); // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value. const int tabs_count_old = tab_bar->Tabs.Size; for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; if (TabBarFindTabByID(tab_bar, window->TabId) == NULL) TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); } // Title bar if (is_focused) node->LastFrameFocused = g.FrameCount; ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), g.Style.DockingSeparatorSize); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags); // Docking/Collapse button if (has_window_menu_button) { if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node) OpenPopup("#WindowMenu"); if (IsItemActive()) focus_tab_id = tab_bar->SelectedTabId; if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal) && g.HoveredIdTimer > 0.5f) SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingDragToUndockOrMoveNode)); } // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value int tabs_unsorted_start = tab_bar->Tabs.Size; for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--) { // FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting? tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted; tabs_unsorted_start = tab_n; } if (tab_bar->Tabs.Size > tabs_unsorted_start) { IMGUI_DEBUG_LOG_DOCKING("[docking] In node 0x%08X: %d new appearing tabs:%s\n", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? " (will sort)" : ""); for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; IM_UNUSED(tab); IMGUI_DEBUG_LOG_DOCKING("[docking] - Tab 0x%08X '%s' Order %d\n", tab->ID, TabBarGetTabName(tab_bar, tab), tab->Window ? tab->Window->DockOrder : -1); } IMGUI_DEBUG_LOG_DOCKING("[docking] SelectedTabId = 0x%08X, NavWindow->TabId = 0x%08X\n", node->SelectedTabId, g.NavWindow ? g.NavWindow->TabId : -1); if (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); } // Apply NavWindow focus back to the tab bar if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node) tab_bar->SelectedTabId = g.NavWindow->RootWindow->TabId; // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId; else if (tab_bar->Tabs.Size > tabs_count_old) tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId; // Begin tab bar ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons); tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode; tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyMixed; // Enforce default policy. Since 1.92.2 this is now reasonable. May expose later if needed. (#8800, #3421) tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline; if (!host_window->Collapsed && is_focused) tab_bar_flags |= ImGuiTabBarFlags_IsFocused; tab_bar->ID = node->ID;// GetID("#TabBar"); tab_bar->SeparatorMinX = node->Pos.x + host_window->WindowBorderSize; // Separator cover the whole node width tab_bar->SeparatorMaxX = node->Pos.x + node->Size.x - host_window->WindowBorderSize; BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags); //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255)); // Backup style colors ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT]; for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]]; // Submit actual tabs node->VisibleWindow = NULL; for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; if (window->LastFrameActive + 1 < g.FrameCount && node_was_active) continue; // FIXME: Not sure if that's still taken/useful, as windows are normally removed in DockNodeUpdateFlagsAndCollapse(). ImGuiTabItemFlags tab_item_flags = 0; tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet; if (window->Flags & ImGuiWindowFlags_UnsavedDocument) tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument; if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; // Apply stored style overrides for the window for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]); // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so) bool tab_open = true; TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); if (!tab_open) node->WantCloseTabId = window->TabId; if (tab_bar->VisibleTabId == window->TabId) node->VisibleWindow = window; // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call window->DC.DockTabItemStatusFlags = g.LastItemData.StatusFlags; window->DC.DockTabItemRect = g.LastItemData.Rect; // Update navigation ID on menu layer if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) host_window->NavLastIds[1] = window->TabId; } // Restore style colors for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n]; // Notify root of visible window (used to display title in OS task bar) if (node->VisibleWindow) if (is_focused || root_node->VisibleWindow == NULL) root_node->VisibleWindow = node->VisibleWindow; // Close button (after VisibleWindow was updated) // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton; const bool close_button_is_visible = node->HasCloseButton; //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one) if (close_button_is_visible) { if (!close_button_is_enabled) { PushItemFlag(ImGuiItemFlags_Disabled, true); PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f)); } if (CloseButton(host_window->GetID("#CLOSE"), close_button_pos)) { node->WantCloseAll = true; for (int n = 0; n < tab_bar->Tabs.Size; n++) TabBarCloseTab(tab_bar, &tab_bar->Tabs[n]); } //if (IsItemActive()) // focus_tab_id = tab_bar->SelectedTabId; if (!close_button_is_enabled) { PopStyleColor(); PopItemFlag(); } } // When clicking on the title bar outside of tabs, we still focus the selected tab for that node // FIXME: TabItems submitted earlier use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) in order to not cover them. ImGuiID title_bar_id = host_window->GetID("#TITLEBAR"); if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id) { // AllowOverlap mode required for appending into dock node tab bar, // otherwise dragging window will steal HoveredId and amended tabs cannot get them. bool held; KeepAliveID(title_bar_id); ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowOverlap); if (g.HoveredId == title_bar_id) { g.LastItemData.ID = title_bar_id; } if (held) { if (IsMouseClicked(0)) focus_tab_id = tab_bar->SelectedTabId; // Forward moving request to selected window if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); // Undock from tab bar empty space } } // Forward focus from host node to selected window //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget) // focus_tab_id = tab_bar->SelectedTabId; // When clicked on a tab we requested focus to the docked child // This overrides the value set by "forward focus from host node to selected window". if (tab_bar->NextSelectedTabId) focus_tab_id = tab_bar->NextSelectedTabId; // Apply navigation focus if (focus_tab_id != 0) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) if (tab->Window) { FocusWindow(tab->Window); if (g.NavId == 0) // only init if FocusWindow() didn't restore anything. NavInitWindow(tab->Window, false); } EndTabBar(); PopID(); // Restore SkipItems flag if (!node->IsDockSpace()) { host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main; host_window->SkipItems = backup_skip_item; } } static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node) { IM_ASSERT(node->TabBar == NULL); node->TabBar = IM_NEW(ImGuiTabBar); } static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node) { if (node->TabBar == NULL) return; IM_DELETE(node->TabBar); node->TabBar = NULL; } static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window) { if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) return false; ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass; ImGuiWindowClass* payload_class = &payload->WindowClass; if (host_class->ClassId != payload_class->ClassId) { bool pass = false; if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0) pass = true; if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0) pass = true; if (!pass) return false; } // Prevent docking any window created above a popup // Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features), // by e.g. adding a 'if (!ImGui::IsWindowWithinBeginStackOf(host_window, popup_window))' test. // But it would requires more work on our end because the dock host windows is technically created in NewFrame() // and our ->ParentXXX and ->RootXXX pointers inside windows are currently mislading or lacking. ImGuiContext& g = *GImGui; for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) if (ImGuiWindow* popup_window = g.OpenPopupStack[i].Window) if (ImGui::IsWindowWithinBeginStackOf(payload, popup_window)) // Payload is created from within a popup begin stack. return false; return true; } static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload) { if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) // FIXME-DOCK: Missing filtering return true; const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1; for (int payload_n = 0; payload_n < payload_count; payload_n++) { ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload; if (DockNodeIsDropAllowedOne(payload, host_window)) return true; } return false; } // window menu button == collapse button when not in a dock node. // FIXME: This is similar to RenderWindowTitleBarContents(), may want to share code. static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f); if (out_title_rect) { *out_title_rect = r; } r.Min.x += style.WindowBorderSize; r.Max.x -= style.WindowBorderSize; float button_sz = g.FontSize; r.Min.x += style.FramePadding.x; r.Max.x -= style.FramePadding.x; ImVec2 window_menu_button_pos = ImVec2(r.Min.x, r.Min.y + style.FramePadding.y); if (node->HasCloseButton) { if (out_close_button_pos) *out_close_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y); r.Max.x -= button_sz + style.ItemInnerSpacing.x; } if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Left) { r.Min.x += button_sz + style.ItemInnerSpacing.x; } else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right) { window_menu_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y); r.Max.x -= button_sz + style.ItemInnerSpacing.x; } if (out_tab_bar_rect) { *out_tab_bar_rect = r; } if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; } } void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired) { ImGuiContext& g = *GImGui; const float dock_spacing = g.Style.ItemInnerSpacing.x; const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; pos_new[axis ^ 1] = pos_old[axis ^ 1]; size_new[axis ^ 1] = size_old[axis ^ 1]; // Distribute size on given axis (with a desired size or equally) const float w_avail = size_old[axis] - dock_spacing; if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f) { size_new[axis] = size_new_desired[axis]; size_old[axis] = IM_TRUNC(w_avail - size_new[axis]); } else { size_new[axis] = IM_TRUNC(w_avail * 0.5f); size_old[axis] = IM_TRUNC(w_avail - size_new[axis]); } // Position each node if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) { pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing; } else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up) { pos_new[axis] = pos_old[axis]; pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing; } } // Retrieve the drop rectangles for a given direction or for the center + perform hit testing. bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos) { ImGuiContext& g = *GImGui; const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight()); const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f)); float hs_w; // Half-size, longer axis float hs_h; // Half-size, smaller axis ImVec2 off; // Distance from edge or center if (outer_docking) { //hs_w = ImTrunc(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f)); //hs_h = ImTrunc(hs_w * 0.15f); //off = ImVec2(ImTrunc(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImTrunc(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h)); hs_w = ImTrunc(hs_for_central_nodes * 1.50f); hs_h = ImTrunc(hs_for_central_nodes * 0.80f); off = ImTrunc(ImVec2(parent.GetWidth() * 0.5f - hs_h, parent.GetHeight() * 0.5f - hs_h)); } else { hs_w = ImTrunc(hs_for_central_nodes); hs_h = ImTrunc(hs_for_central_nodes * 0.90f); off = ImTrunc(ImVec2(hs_w * 2.40f, hs_w * 2.40f)); } ImVec2 c = ImTrunc(parent.GetCenter()); if (dir == ImGuiDir_None) { out_r = ImRect(c.x - hs_w, c.y - hs_w, c.x + hs_w, c.y + hs_w); } else if (dir == ImGuiDir_Up) { out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h); } else if (dir == ImGuiDir_Down) { out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h); } else if (dir == ImGuiDir_Left) { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); } else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); } if (test_mouse_pos == NULL) return false; ImRect hit_r = out_r; if (!outer_docking) { // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides hit_r.Expand(ImTrunc(hs_w * 0.30f)); ImVec2 mouse_delta = (*test_mouse_pos - c); float mouse_delta_len2 = ImLengthSqr(mouse_delta); float r_threshold_center = hs_w * 1.4f; float r_threshold_sides = hs_w * (1.4f + 1.2f); if (mouse_delta_len2 < r_threshold_center * r_threshold_center) return (dir == ImGuiDir_None); if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides) return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y)); } return hit_r.Contains(*test_mouse_pos); } // host_node may be NULL if the window doesn't have a DockNode already. // FIXME-DOCK: This is misnamed since it's also doing the filtering. static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) { ImGuiContext& g = *GImGui; // There is an edge case when docking into a dockspace which only has inactive nodes. // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive. // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference. if (payload_node == NULL) payload_node = payload_window->DockNodeAsHost; ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node; if (ref_node_for_rect) IM_ASSERT(ref_node_for_rect->IsVisible == true); // Filter, figure out where we are allowed to dock ImGuiDockNodeFlags src_node_flags = payload_node ? payload_node->MergedFlags : payload_window->WindowClass.DockNodeFlagsOverrideSet; ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet; data->IsCenterAvailable = true; if (is_outer_docking) data->IsCenterAvailable = false; else if (g.IO.ConfigDockingNoDockingOver) data->IsCenterAvailable = false; else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) data->IsCenterAvailable = false; else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) && host_node->IsCentralNode()) data->IsCenterAvailable = false; else if ((!host_node || !host_node->IsEmpty()) && payload_node && payload_node->IsSplitNode() && (payload_node->OnlyNodeWithWindows == NULL)) // Is _visibly_ split? data->IsCenterAvailable = false; else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther) && (!host_node || !host_node->IsEmpty())) data->IsCenterAvailable = false; else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverEmpty) && host_node && host_node->IsEmpty()) data->IsCenterAvailable = false; data->IsSidesAvailable = true; if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplit) || g.IO.ConfigDockingNoSplit) data->IsSidesAvailable = false; else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode()) data->IsSidesAvailable = false; else if (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther) data->IsSidesAvailable = false; // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split) data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (payload_window->HasCloseButton); data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos; data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size; // Calculate drop shapes geometry for allowed splitting directions IM_ASSERT(ImGuiDir_None == -1); data->SplitNode = host_node; data->SplitDir = ImGuiDir_None; data->IsSplitDirExplicit = false; if (!host_window->Collapsed) for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) { if (dir == ImGuiDir_None && !data->IsCenterAvailable) continue; if (dir != ImGuiDir_None && !data->IsSidesAvailable) continue; if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos)) { data->SplitDir = (ImGuiDir)dir; data->IsSplitDirExplicit = true; } } // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable); if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift) data->IsDropAllowed = false; // Calculate split area data->SplitRatio = 0.0f; if (data->SplitDir != ImGuiDir_None) { ImGuiDir split_dir = data->SplitDir; ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; ImVec2 pos_new, pos_old = data->FutureNode.Pos; ImVec2 size_new, size_old = data->FutureNode.Size; DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, payload_window->Size); // Calculate split ratio so we can pass it down the docking request float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]); data->FutureNode.Pos = pos_new; data->FutureNode.Size = size_new; data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio); } } static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data) { ImGuiContext& g = *GImGui; IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent. // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes. const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload; // In case the two windows involved are on different viewports, we will draw the overlay on each of them. int overlay_draw_lists_count = 0; ImDrawList* overlay_draw_lists[2]; overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport); if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload) overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport); // Draw main preview rectangle const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f); const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f); const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f); const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f); // Display area preview const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0); if (data->IsDropAllowed) { ImRect overlay_rect = data->FutureNode.Rect(); if (data->SplitDir == ImGuiDir_None && can_preview_tabs) overlay_rect.Min.y += GetFrameHeight(); if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable) for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), g.Style.DockingSeparatorSize)); } // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read) if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable) { // Compute target tab bar geometry so we can locate our preview tabs ImRect tab_bar_rect; DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL); ImVec2 tab_pos = tab_bar_rect.Min; if (host_node && host_node->TabBar) { if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar()) tab_pos.x += host_node->TabBar->WidthAllTabs + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. else tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]).x; } else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost)) { tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window).x; // Account for slight offset which will be added when changing from title bar to tab bar } // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) if (root_payload->DockNodeAsHost) IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size); ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL; const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1; for (int payload_n = 0; payload_n < payload_count; payload_n++) { // DockNode's TabBar may have non-window Tabs manually appended by user ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload; if (tab_bar_with_payload && payload_window == NULL) continue; if (!DockNodeIsDropAllowedOne(payload_window, host_window)) continue; // Calculate the tab bounding box for each payload window ImVec2 tab_size = TabItemCalcSize(payload_window); ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y); tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x; const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]); const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabSelected]); const ImU32 overlay_col_unsaved_marker = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_UnsavedMarker]); PushStyleColor(ImGuiCol_Text, overlay_col_text); PushStyleColor(ImGuiCol_UnsavedMarker, overlay_col_unsaved_marker); for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) { ImGuiTabItemFlags tab_flags = (payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0; if (!tab_bar_rect.Contains(tab_bb)) overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max); TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs); TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL); if (!tab_bar_rect.Contains(tab_bb)) overlay_draw_lists[overlay_n]->PopClipRect(); } PopStyleColor(2); } } // Display drop boxes const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding); for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) { if (!data->DropRectsDraw[dir + 1].IsInverted()) { ImRect draw_r = data->DropRectsDraw[dir + 1]; ImRect draw_r_in = draw_r; draw_r_in.Expand(-2.0f); ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop; for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) { ImVec2 center = ImFloor(draw_r_in.GetCenter()); overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding); overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding); if (dir == ImGuiDir_Left || dir == ImGuiDir_Right) overlay_draw_lists[overlay_n]->AddLine(ImVec2(center.x, draw_r_in.Min.y), ImVec2(center.x, draw_r_in.Max.y), overlay_col_lines); if (dir == ImGuiDir_Up || dir == ImGuiDir_Down) overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines); } } // Stop after ImGuiDir_None if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoDockingSplit)) || g.IO.ConfigDockingNoSplit) return; } } //----------------------------------------------------------------------------- // Docking: ImGuiDockNode Tree manipulation functions //----------------------------------------------------------------------------- // - DockNodeTreeSplit() // - DockNodeTreeMerge() // - DockNodeTreeUpdatePosSize() // - DockNodeTreeUpdateSplitterFindTouchingNode() // - DockNodeTreeUpdateSplitter() // - DockNodeTreeFindFallbackLeafNode() // - DockNodeTreeFindNodeByPos() //----------------------------------------------------------------------------- void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) { ImGuiContext& g = *GImGui; IM_ASSERT(split_axis != ImGuiAxis_None); ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0); child_0->ParentNode = parent_node; ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0); child_1->ParentNode = parent_node; ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1; DockNodeMoveChildNodes(child_inheritor, parent_node); parent_node->ChildNodes[0] = child_0; parent_node->ChildNodes[1] = child_1; parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow; parent_node->SplitAxis = split_axis; parent_node->VisibleWindow = NULL; parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode; float size_avail = (parent_node->Size[split_axis] - g.Style.DockingSeparatorSize); size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f); IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting. child_0->SizeRef = child_1->SizeRef = parent_node->Size; child_0->SizeRef[split_axis] = ImTrunc(size_avail * split_ratio); child_1->SizeRef[split_axis] = ImTrunc(size_avail - child_0->SizeRef[split_axis]); DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID); DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node)); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property) child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_; parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; child_0->UpdateMergedFlags(); child_1->UpdateMergedFlags(); parent_node->UpdateMergedFlags(); if (child_inheritor->IsCentralNode()) DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor; } void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) { // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL. ImGuiContext& g = *GImGui; ImGuiDockNode* child_0 = parent_node->ChildNodes[0]; ImGuiDockNode* child_1 = parent_node->ChildNodes[1]; IM_ASSERT(child_0 || child_1); IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1); if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0)) { IM_ASSERT(parent_node->TabBar == NULL); IM_ASSERT(parent_node->Windows.Size == 0); } IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeTreeMerge: 0x%08X + 0x%08X back into parent 0x%08X\n", child_0 ? child_0->ID : 0, child_1 ? child_1->ID : 0, parent_node->ID); ImVec2 backup_last_explicit_size = parent_node->SizeRef; DockNodeMoveChildNodes(parent_node, merge_lead_child); if (child_0) { DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID); } if (child_1) { DockNodeMoveWindows(parent_node, child_1); DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID); } DockNodeApplyPosSizeToWindows(parent_node); parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; parent_node->SizeRef = backup_last_explicit_size; // Flags transfer parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; parent_node->LocalFlagsInWindows = (child_0 ? child_0->LocalFlagsInWindows : 0) | (child_1 ? child_1->LocalFlagsInWindows : 0); // FIXME: Would be more consistent to update from actual windows parent_node->UpdateMergedFlags(); if (child_0) DockContextDeleteNode(ctx, child_0); if (child_1) DockContextDeleteNode(ctx, child_1); } // Update Pos/Size for a node hierarchy (don't affect child Windows yet) // (Depth-first, Pre-Order) void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node) { // During the regular dock node update we write to all nodes. // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away. ImGuiContext& g = *GImGui; const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node; if (write_to_node) { node->Pos = pos; node->Size = size; } if (node->IsLeafNode()) return; ImGuiDockNode* child_0 = node->ChildNodes[0]; ImGuiDockNode* child_1 = node->ChildNodes[1]; ImVec2 child_0_pos = pos, child_1_pos = pos; ImVec2 child_0_size = size, child_1_size = size; const bool child_0_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_0)); const bool child_1_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_1)); const bool child_0_is_or_will_be_visible = child_0->IsVisible || child_0_is_toward_single_node; const bool child_1_is_or_will_be_visible = child_1->IsVisible || child_1_is_toward_single_node; if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible) { const float spacing = g.Style.DockingSeparatorSize; const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; const float size_avail = ImMax(size[axis] - spacing, 0.0f); // Size allocation policy // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows. const float size_min_each = ImTrunc(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing. // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImTrunc() // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge) if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce) { child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]); child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce) { child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]); child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce) { // FIXME-DOCK: We cannot honor the requested size, so apply ratio. // Currently this path will only be taken if code programmatically sets WantLockSizeOnce float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]); child_0_size[axis] = child_0->SizeRef[axis] = ImTrunc(size_avail * split_ratio); child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild) { child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild) { child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); } else { // 4) Otherwise distribute according to the relative ratio of each SizeRef value float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]); child_0_size[axis] = ImMax(size_min_each, ImTrunc(size_avail * split_ratio + 0.5f)); child_1_size[axis] = (size_avail - child_0_size[axis]); } child_1_pos[axis] += spacing + child_0_size[axis]; } if (only_write_to_single_node == NULL) child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false; const bool child_0_recurse = only_write_to_single_node ? child_0_is_toward_single_node : child_0->IsVisible; const bool child_1_recurse = only_write_to_single_node ? child_1_is_toward_single_node : child_1->IsVisible; if (child_0_recurse) DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size); if (child_1_recurse) DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size); } static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector* touching_nodes) { if (node->IsLeafNode()) { touching_nodes->push_back(node); return; } if (node->ChildNodes[0]->IsVisible) if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible) DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes); if (node->ChildNodes[1]->IsVisible) if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible) DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes); } // (Depth-First, Pre-Order) void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) { if (node->IsLeafNode()) return; ImGuiContext& g = *GImGui; ImGuiDockNode* child_0 = node->ChildNodes[0]; ImGuiDockNode* child_1 = node->ChildNodes[1]; if (child_0->IsVisible && child_1->IsVisible) { // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally) const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; IM_ASSERT(axis != ImGuiAxis_None); ImRect bb; bb.Min = child_0->Pos; bb.Max = child_1->Pos; bb.Min[axis] += child_0->Size[axis]; bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); const ImGuiDockNodeFlags merged_flags = child_0->MergedFlags | child_1->MergedFlags; // Merged flags for BOTH childs const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY; if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag)) { ImGuiWindow* window = g.CurrentWindow; window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding); } else { //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. //bb.Max[axis] -= 1; PushID(node->ID); // Find resizing limits by gathering list of nodes that are touching the splitter line. ImVector touching_nodes[2]; float min_size = g.Style.WindowMinSize[axis]; float resize_limits[2]; resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size; resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size; ImGuiID splitter_id = GetID("##Splitter"); if (g.ActiveId == splitter_id) // Only process when splitter is active { DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]); DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]); for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++) resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size); for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++) resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size); // [DEBUG] Render touching nodes & limits /* ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); for (int n = 0; n < 2; n++) { for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++) draw_list->AddRect(touching_nodes[n][touching_node_n]->Pos, touching_nodes[n][touching_node_n]->Pos + touching_nodes[n][touching_node_n]->Size, IM_COL32(0, 255, 0, 255)); if (axis == ImGuiAxis_X) draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); else draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); } */ } // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters float cur_size_0 = child_0->Size[axis]; float cur_size_1 = child_1->Size[axis]; float min_size_0 = resize_limits[0] - child_0->Pos[axis]; float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; ImU32 bg_col = GetColorU32(ImGuiCol_WindowBg); if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, g.WindowsBorderHoverPadding, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER, bg_col)) { if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) { child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0; child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis]; child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1; // Lock the size of every node that is a sibling of the node we are touching // This might be less desirable if we can merge sibling of a same axis into the same parental level. for (int side_n = 0; side_n < 2; side_n++) for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) { ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n]; //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255)); while (touching_node->ParentNode != node) { if (touching_node->ParentNode->SplitAxis == axis) { // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize(). ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n]; node_to_preserve->WantLockSizeOnce = true; //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255)); //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100)); } touching_node = touching_node->ParentNode; } } DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size); DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size); MarkIniSettingsDirty(); } } PopID(); } } if (child_0->IsVisible) DockNodeTreeUpdateSplitter(child_0); if (child_1->IsVisible) DockNodeTreeUpdateSplitter(child_1); } ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node) { if (node->IsLeafNode()) return node; if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0])) return leaf_node; if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1])) return leaf_node; return NULL; } ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos) { if (!node->IsVisible) return NULL; const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE? ImRect r(node->Pos, node->Pos + node->Size); r.Expand(dock_spacing * 0.5f); bool inside = r.Contains(pos); if (!inside) return NULL; if (node->IsLeafNode()) return node; if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos)) return hovered_node; if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos)) return hovered_node; // This means we are hovering over the splitter/spacing of a parent node return node; } //----------------------------------------------------------------------------- // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) //----------------------------------------------------------------------------- // - SetWindowDock() [Internal] // - DockSpace() // - DockSpaceOverViewport() //----------------------------------------------------------------------------- // [Internal] Called via SetNextWindowDockID() void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) { // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowDockAllowFlags & cond) == 0) return; window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); if (window->DockId == dock_id) return; // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot ImGuiContext& g = *GImGui; if (ImGuiDockNode* new_node = DockContextFindNodeByID(&g, dock_id)) if (new_node->IsSplitNode()) { // Policy: Find central node or latest focused node. We first move back to our root node. new_node = DockNodeGetRootNode(new_node); if (new_node->CentralNode) { IM_ASSERT(new_node->CentralNode->IsCentralNode()); dock_id = new_node->CentralNode->ID; } else { dock_id = new_node->LastFocusedNodeId; } } if (window->DockId == dock_id) return; if (window->DockNode) DockNodeRemoveWindow(window->DockNode, window, 0); window->DockId = dock_id; } // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default. // The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. // DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. // When ImGuiDockNodeFlags_KeepAliveOnly is set, nothing is submitted in the current window (function may be called from any location). ImGuiID ImGui::DockSpace(ImGuiID dockspace_id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindowRead(); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return 0; // Early out if parent window is hidden/collapsed // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960. // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true. if (window->SkipItems) flags |= ImGuiDockNodeFlags_KeepAliveOnly; if ((flags & ImGuiDockNodeFlags_KeepAliveOnly) == 0) window = GetCurrentWindow(); // call to set window->WriteAccessed = true; IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0); // Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags! IM_ASSERT((flags & ImGuiDockNodeFlags_CentralNode) == 0); // Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags! (#8145) IM_ASSERT(dockspace_id != 0); ImGuiDockNode* node = DockContextFindNodeByID(&g, dockspace_id); if (node == NULL) { IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X created\n", dockspace_id); node = DockContextAddNode(&g, dockspace_id); node->SetLocalFlags(ImGuiDockNodeFlags_CentralNode); } if (window_class && window_class->ClassId != node->WindowClass.ClassId) IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", dockspace_id, node->WindowClass.ClassId, window_class->ClassId); node->SharedFlags = flags; node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); // When a DockSpace transitioned form implicit to explicit this may be called a second time // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly)) { IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace); return dockspace_id; } node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace); // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible if (flags & ImGuiDockNodeFlags_KeepAliveOnly) { node->LastFrameAlive = g.FrameCount; return dockspace_id; } const ImVec2 content_avail = GetContentRegionAvail(); ImVec2 size = ImTrunc(size_arg); if (size.x <= 0.0f) size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) if (size.y <= 0.0f) size.y = ImMax(content_avail.y + size.y, 4.0f); IM_ASSERT(size.x > 0.0f && size.y > 0.0f); node->Pos = window->DC.CursorPos; node->Size = node->SizeRef = size; SetNextWindowPos(node->Pos); SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; // FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window? // FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented) ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; window_flags |= ImGuiWindowFlags_NoBackground; char title[256]; ImFormatString(title, IM_COUNTOF(title), "%s/DockSpace_%08X", window->Name, dockspace_id); PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); Begin(title, NULL, window_flags); PopStyleVar(); ImGuiWindow* host_window = g.CurrentWindow; DockNodeSetupHostWindow(node, host_window); host_window->ChildId = window->GetID(title); node->OnlyNodeWithWindows = NULL; IM_ASSERT(node->IsRootNode()); // We need to handle the rare case were a central node is missing. // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace. // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split. // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining. // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property, // as it doesn't make sense for an empty dockspace to not have this property. if (node->IsLeafNode() && !node->IsCentralNode()) node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode); // Update the node DockNodeUpdate(node); End(); ImRect bb(node->Pos, node->Pos + size); ItemSize(size); ItemAdd(bb, dockspace_id, NULL, ImGuiItemFlags_NoNav); // Not a nav point (could be, would need to draw the nav rect and replicate/refactor activation from BeginChild(), but seems like CTRL+Tab works better here?) if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && IsWindowChildOf(g.HoveredWindow, host_window, false, true)) // To fullfill IsItemHovered(), similar to EndChild() g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; return dockspace_id; } // Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode! // The limitation with this call is that your window won't have a local menu bar, but you can also use BeginMainMenuBar(). // Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. // If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it. ImGuiID ImGui::DockSpaceOverViewport(ImGuiID dockspace_id, const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) { if (viewport == NULL) viewport = GetMainViewport(); // Submit a window filling the entire viewport SetNextWindowPos(viewport->WorkPos); SetNextWindowSize(viewport->WorkSize); SetNextWindowViewport(viewport->ID); ImGuiWindowFlags host_window_flags = 0; host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) host_window_flags |= ImGuiWindowFlags_NoBackground; // FIXME-OPT: When using ImGuiDockNodeFlags_KeepAliveOnly with DockSpaceOverViewport() we might be able to spare submitting the window, // since DockSpace() with that flag doesn't need a window. We'd only need to compute the default ID accordingly. if (dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly) host_window_flags |= ImGuiWindowFlags_NoMouseInputs; char label[32]; ImFormatString(label, IM_COUNTOF(label), "WindowOverViewport_%08X", viewport->ID); PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); Begin(label, NULL, host_window_flags); PopStyleVar(3); // Submit the dockspace if (dockspace_id == 0) dockspace_id = GetID("DockSpace"); DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class); End(); return dockspace_id; } //----------------------------------------------------------------------------- // Docking: Builder Functions //----------------------------------------------------------------------------- // Very early end-user API to manipulate dock nodes. // Only available in imgui_internal.h. Expect this API to change/break! // It is expected that those functions are all called _before_ the dockspace node submission. //----------------------------------------------------------------------------- // - DockBuilderDockWindow() // - DockBuilderGetNode() // - DockBuilderSetNodePos() // - DockBuilderSetNodeSize() // - DockBuilderAddNode() // - DockBuilderRemoveNode() // - DockBuilderRemoveNodeChildNodes() // - DockBuilderRemoveNodeDockedWindows() // - DockBuilderSplitNode() // - DockBuilderCopyNodeRec() // - DockBuilderCopyNode() // - DockBuilderCopyWindowSettings() // - DockBuilderCopyDockSpace() // - DockBuilderFinish() //----------------------------------------------------------------------------- void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) { // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1) ImGuiContext& g = *GImGui; IM_UNUSED(g); IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderDockWindow '%s' to node 0x%08X\n", window_name, node_id); ImGuiID window_id = ImHashStr(window_name); if (ImGuiWindow* window = FindWindowByID(window_id)) { // Apply to created window ImGuiID prev_node_id = window->DockId; SetWindowDock(window, node_id, ImGuiCond_Always); if (window->DockId != prev_node_id) window->DockOrder = -1; } else { // Apply to settings ImGuiWindowSettings* settings = FindWindowSettingsByID(window_id); if (settings == NULL) settings = CreateNewWindowSettings(window_name); if (settings->DockId != node_id) settings->DockOrder = -1; settings->DockId = node_id; } } ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id) { ImGuiContext& g = *GImGui; return DockContextFindNodeByID(&g, node_id); } void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos) { ImGuiContext& g = *GImGui; ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id); if (node == NULL) return; node->Pos = pos; node->AuthorityForPos = ImGuiDataAuthority_DockNode; } void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) { ImGuiContext& g = *GImGui; ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id); if (node == NULL) return; IM_ASSERT(size.x > 0.0f && size.y > 0.0f); node->Size = node->SizeRef = size; node->AuthorityForSize = ImGuiDataAuthority_DockNode; } // Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node! // - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node. // - Dockspace node: calling DockBuilderSetNodePos() is unnecessary. // - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand! // For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect. // - Use (id == 0) to let the system allocate a node identifier. // - Existing node with a same id will be removed. ImGuiID ImGui::DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags) { ImGuiContext& g = *GImGui; IM_UNUSED(g); IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderAddNode 0x%08X flags=%08X\n", node_id, flags); if (node_id != 0) DockBuilderRemoveNode(node_id); ImGuiDockNode* node = NULL; if (flags & ImGuiDockNodeFlags_DockSpace) { DockSpace(node_id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly); node = DockContextFindNodeByID(&g, node_id); } else { node = DockContextAddNode(&g, node_id); node->SetLocalFlags(flags); } node->LastFrameAlive = g.FrameCount; // Set this otherwise BeginDocked will undock during the same frame. return node->ID; } void ImGui::DockBuilderRemoveNode(ImGuiID node_id) { ImGuiContext& g = *GImGui; IM_UNUSED(g); IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderRemoveNode 0x%08X\n", node_id); ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id); if (node == NULL) return; DockBuilderRemoveNodeDockedWindows(node_id, true); DockBuilderRemoveNodeChildNodes(node_id); // Node may have moved or deleted if e.g. any merge happened node = DockContextFindNodeByID(&g, node_id); if (node == NULL) return; if (node->IsCentralNode() && node->ParentNode) node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode); DockContextRemoveNode(&g, node, true); } // root_id = 0 to remove all, root_id != 0 to remove child of given node. void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) { ImGuiContext& g = *GImGui; ImGuiDockContext* dc = &g.DockContext; ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(&g, root_id) : NULL; if (root_id && root_node == NULL) return; bool has_central_node = false; ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto; ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto; // Process active windows ImVector nodes_to_remove; for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) { bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id); if (want_removal) { if (node->IsCentralNode()) has_central_node = true; if (root_id != 0) DockContextQueueNotifyRemovedNode(&g, node); if (root_node) { DockNodeMoveWindows(root_node, node); DockSettingsRenameNodeReferences(node->ID, root_node->ID); } nodes_to_remove.push_back(node); } } // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge) // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead) if (root_node) { root_node->AuthorityForPos = backup_root_node_authority_for_pos; root_node->AuthorityForSize = backup_root_node_authority_for_size; } // Apply to settings for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (ImGuiID window_settings_dock_id = settings->DockId) for (int n = 0; n < nodes_to_remove.Size; n++) if (nodes_to_remove[n]->ID == window_settings_dock_id) { settings->DockId = root_id; break; } // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes if (nodes_to_remove.Size > 1) ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst); for (int n = 0; n < nodes_to_remove.Size; n++) DockContextRemoveNode(&g, nodes_to_remove[n], false); if (root_id == 0) { dc->Nodes.Clear(); dc->Requests.clear(); } else if (has_central_node) { root_node->CentralNode = root_node; root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode); } } void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs) { // Clear references in settings ImGuiContext& g = *GImGui; if (clear_settings_refs) { for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) { bool want_removal = (root_id == 0) || (settings->DockId == root_id); if (!want_removal && settings->DockId != 0) if (ImGuiDockNode* node = DockContextFindNodeByID(&g, settings->DockId)) if (DockNodeGetRootNode(node)->ID == root_id) want_removal = true; if (want_removal) settings->DockId = 0; } } // Clear references in windows for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); if (want_removal) { const ImGuiID backup_dock_id = window->DockId; IM_UNUSED(backup_dock_id); DockContextProcessUndockWindow(&g, window, clear_settings_refs); if (!clear_settings_refs) IM_ASSERT(window->DockId == backup_dock_id); } } } // If 'out_id_at_dir' or 'out_id_at_opposite_dir' are non NULL, the function will write out the ID of the two new nodes created. // Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set. // FIXME-DOCK: We are not exposing nor using split_outer. ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir) { ImGuiContext& g = *GImGui; IM_ASSERT(split_dir != ImGuiDir_None); IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderSplitNode: node 0x%08X, split_dir %d\n", id, split_dir); ImGuiDockNode* node = DockContextFindNodeByID(&g, id); if (node == NULL) { IM_ASSERT(node != NULL); return 0; } ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Split; req.DockTargetWindow = NULL; req.DockTargetNode = node; req.DockPayload = NULL; req.DockSplitDir = split_dir; req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir); req.DockSplitOuter = false; DockContextProcessDock(&g, &req); ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID; ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID; if (out_id_at_dir) *out_id_at_dir = id_at_dir; if (out_id_at_opposite_dir) *out_id_at_opposite_dir = id_at_opposite_dir; return id_at_dir; } static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector* out_node_remap_pairs) { ImGuiContext& g = *GImGui; ImGuiDockNode* dst_node = ImGui::DockContextAddNode(&g, dst_node_id_if_known); dst_node->SharedFlags = src_node->SharedFlags; dst_node->LocalFlags = src_node->LocalFlags; dst_node->LocalFlagsInWindows = ImGuiDockNodeFlags_None; dst_node->Pos = src_node->Pos; dst_node->Size = src_node->Size; dst_node->SizeRef = src_node->SizeRef; dst_node->SplitAxis = src_node->SplitAxis; dst_node->UpdateMergedFlags(); out_node_remap_pairs->push_back(src_node->ID); out_node_remap_pairs->push_back(dst_node->ID); for (int child_n = 0; child_n < IM_COUNTOF(src_node->ChildNodes); child_n++) if (src_node->ChildNodes[child_n]) { dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs); dst_node->ChildNodes[child_n]->ParentNode = dst_node; } IMGUI_DEBUG_LOG_DOCKING("[docking] Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0); return dst_node; } void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs) { ImGuiContext& g = *GImGui; IM_ASSERT(src_node_id != 0); IM_ASSERT(dst_node_id != 0); IM_ASSERT(out_node_remap_pairs != NULL); DockBuilderRemoveNode(dst_node_id); ImGuiDockNode* src_node = DockContextFindNodeByID(&g, src_node_id); IM_ASSERT(src_node != NULL); out_node_remap_pairs->clear(); DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs); IM_ASSERT((out_node_remap_pairs->Size % 2) == 0); } void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name) { ImGuiWindow* src_window = FindWindowByName(src_name); if (src_window == NULL) return; if (ImGuiWindow* dst_window = FindWindowByName(dst_name)) { dst_window->Pos = src_window->Pos; dst_window->Size = src_window->Size; dst_window->SizeFull = src_window->SizeFull; dst_window->Collapsed = src_window->Collapsed; } else { ImGuiWindowSettings* dst_settings = FindWindowSettingsByID(ImHashStr(dst_name)); if (!dst_settings) dst_settings = CreateNewWindowSettings(dst_name); ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos); if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID) { dst_settings->ViewportPos = window_pos_2ih; dst_settings->ViewportId = src_window->ViewportId; dst_settings->Pos = ImVec2ih(0, 0); } else { dst_settings->Pos = window_pos_2ih; } dst_settings->Size = ImVec2ih(src_window->SizeFull); dst_settings->Collapsed = src_window->Collapsed; } } // FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed. void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs) { ImGuiContext& g = *GImGui; IM_ASSERT(src_dockspace_id != 0); IM_ASSERT(dst_dockspace_id != 0); IM_ASSERT(in_window_remap_pairs != NULL); IM_ASSERT((in_window_remap_pairs->Size % 2) == 0); // Duplicate entire dock // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart, // whereas we could attempt to at least keep them together in a new, same floating node. ImVector node_remap_pairs; DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs); // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes // (The windows associated to src_dockspace_id are staying in place) ImVector src_windows; for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2) { const char* src_window_name = (*in_window_remap_pairs)[remap_window_n]; const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1]; ImGuiID src_window_id = ImHashStr(src_window_name); src_windows.push_back(src_window_id); // Search in the remapping tables ImGuiID src_dock_id = 0; if (ImGuiWindow* src_window = FindWindowByID(src_window_id)) src_dock_id = src_window->DockId; else if (ImGuiWindowSettings* src_window_settings = FindWindowSettingsByID(src_window_id)) src_dock_id = src_window_settings->DockId; ImGuiID dst_dock_id = 0; for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2) if (node_remap_pairs[dock_remap_n] == src_dock_id) { dst_dock_id = node_remap_pairs[dock_remap_n + 1]; //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear break; } if (dst_dock_id != 0) { // Docked windows gets redocked into the new node hierarchy. IMGUI_DEBUG_LOG_DOCKING("[docking] Remap live window '%s' 0x%08X -> '%s' 0x%08X\n", src_window_name, src_dock_id, dst_window_name, dst_dock_id); DockBuilderDockWindow(dst_window_name, dst_dock_id); } else { // Floating windows gets their settings transferred (regardless of whether the new window already exist or not) // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together? IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name); DockBuilderCopyWindowSettings(src_window_name, dst_window_name); } } // Anything else in the source nodes of 'node_remap_pairs' are windows that are not included in the remapping list. // Find those windows and move to them to the cloned dock node. This may be optional? // Dock those are a second step as undocking would invalidate source dock nodes. struct DockRemainingWindowTask { ImGuiWindow* Window; ImGuiID DockId; DockRemainingWindowTask(ImGuiWindow* window, ImGuiID dock_id) { Window = window; DockId = dock_id; } }; ImVector dock_remaining_windows; for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2) if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n]) { ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1]; ImGuiDockNode* node = DockBuilderGetNode(src_dock_id); for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; if (src_windows.contains(window->ID)) continue; // Docked windows gets redocked into the new node hierarchy. IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id); dock_remaining_windows.push_back(DockRemainingWindowTask(window, dst_dock_id)); } } for (const DockRemainingWindowTask& task : dock_remaining_windows) DockBuilderDockWindow(task.Window->Name, task.DockId); } // FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node. void ImGui::DockBuilderFinish(ImGuiID root_id) { ImGuiContext& g = *GImGui; //DockContextRebuild(&g); DockContextBuildAddWindowsToNodes(&g, root_id); } //----------------------------------------------------------------------------- // Docking: Begin/End Support Functions (called from Begin/End) //----------------------------------------------------------------------------- // - GetWindowAlwaysWantOwnTabBar() // - DockContextBindNodeToWindow() // - BeginDocked() // - BeginDockableDragDropSource() // - BeginDockableDragDropTarget() //----------------------------------------------------------------------------- bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar) if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0) if (!window->IsFallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise return true; return false; } static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window) { ImGuiContext& g = *ctx; ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId); IM_ASSERT(window->DockNode == NULL); // We should not be docking into a split node (SetWindowDock should avoid this) if (node && node->IsSplitNode()) { DockContextProcessUndockWindow(ctx, window); return NULL; } // Create node if (node == NULL) { node = DockContextAddNode(ctx, window->DockId); node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; node->LastFrameAlive = g.FrameCount; } // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet, // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node). // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout. // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame. if (!node->IsVisible) { ImGuiDockNode* ancestor_node = node; while (!ancestor_node->IsVisible && ancestor_node->ParentNode) ancestor_node = ancestor_node->ParentNode; IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node)); DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, node); } // Add window to node bool node_was_visible = node->IsVisible; DockNodeAddWindow(node, window, true); node->IsVisible = node_was_visible; // Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see) IM_ASSERT(node == window->DockNode); return node; } static void StoreDockStyleForWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) window->DockStyle.Colors[color_n] = ImGui::ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]); } void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) { ImGuiContext& g = *GImGui; // Specific extra processing for fallback window (#9151), could be in Begin() as well. if (window->IsFallbackWindow && !window->WasActive) { DockNodeHideWindowDuringHostWindowCreation(window); return; } const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window); if (auto_dock_node) { if (window->DockId == 0) { IM_ASSERT(window->DockNode == NULL); window->DockId = DockContextGenNodeID(&g); } } else { // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) bool want_undock = false; want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0; want_undock |= (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock; if (want_undock) { DockContextProcessUndockWindow(&g, window); return; } } // Bind to our dock node ImGuiDockNode* node = window->DockNode; if (node != NULL) IM_ASSERT(window->DockId == node->ID); if (window->DockId != 0 && node == NULL) { node = DockContextBindNodeToWindow(&g, window); if (node == NULL) return; } #if 0 // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode)) { DockContextProcessUndockWindow(ctx, window); return; } #endif // Undock if our dockspace node disappeared // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. if (node->LastFrameAlive < g.FrameCount) { // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking() ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (root_node->LastFrameAlive < g.FrameCount) DockContextProcessUndockWindow(&g, window); else window->DockIsActive = true; return; } // Store style overrides StoreDockStyleForWindow(window); // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window, // and never create neither a host window neither a tab bar. // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) if (node->HostWindow == NULL) { if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing) window->DockIsActive = true; if (node->Windows.Size > 1 && window->Appearing) // Only hide appearing window DockNodeHideWindowDuringHostWindowCreation(window); return; } // We can have zero-sized nodes (e.g. children of a small-size dockspace) IM_ASSERT(node->HostWindow); IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f); node->State = ImGuiDockNodeState_HostWindowVisible; // Undock if we are submitted earlier than the host window if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) { DockContextProcessUndockWindow(&g, window); return; } // Position/Size window SetNextWindowPos(node->Pos); SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() window->DockIsActive = true; window->DockNodeIsVisible = true; window->DockTabIsVisible = false; if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) return; // When the window is selected we mark it as visible. if (node->VisibleWindow == window) window->DockTabIsVisible = true; // Update window flag IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize; window->ChildFlags |= ImGuiChildFlags_AlwaysUseWindowPadding; if (node->IsHiddenTabBar() || node->IsNoTabBar()) window->Flags |= ImGuiWindowFlags_NoTitleBar; else window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! // Save new dock order only if the window has been visible once already // This allows multiple windows to be created in the same frame and have their respective dock orders preserved. if (node->TabBar && window->WasActive) window->DockOrder = (short)DockNodeGetTabOrder(window); if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL) *p_open = false; // Update ChildId to allow returning from Child to Parent with Escape ImGuiWindow* parent_window = window->DockNode->HostWindow; window->ChildId = parent_window->GetID(window->Name); } void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_ASSERT(g.ActiveId == window->MoveId); IM_ASSERT(g.MovingWindow == window); IM_ASSERT(g.CurrentWindow == window); // 0: Hold SHIFT to disable docking, 1: Hold SHIFT to enable docking. if (g.IO.ConfigDockingWithShift != g.IO.KeyShift) { // When ConfigDockingWithShift is set, display a tooltip to increase UI affordance. // We cannot set for HoveredWindowUnderMovingWindow != NULL here, as it is only valid/useful when drag and drop is already active // (because of the 'is_mouse_dragging_with_an_expected_destination' logic in UpdateViewportsNewFrame() function) IM_ASSERT(g.NextWindowData.HasFlags == 0); if (g.IO.ConfigDockingWithShift && g.MouseStationaryTimer >= 1.0f && g.ActiveId >= 1.0f) SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingHoldShiftToDock)); return; } g.LastItemData.ID = window->MoveId; window = window->RootWindowDockTree; IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit ImGuiDragDropFlags drag_drop_flags = ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_PayloadAutoExpire | ImGuiDragDropFlags_PayloadNoCrossContext | ImGuiDragDropFlags_PayloadNoCrossProcess; if (is_drag_docking && BeginDragDropSource(drag_drop_flags)) { SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); EndDragDropSource(); StoreDockStyleForWindow(window); // Store style overrides while dragging (even when not docked) because docking preview may need it. } } void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) { ImGuiContext& g = *GImGui; //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); if (!g.DragDropActive) return; //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!BeginDragDropTargetCustom(window->Rect(), window->ID)) return; // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly) const ImGuiPayload* payload = &g.DragDropPayload; if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data)) { EndDragDropTarget(); return; } ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data; if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) { // Select target node // (Important: we cannot use g.HoveredDockNode here! Because each of our target node have filters based on payload, each candidate drop target will do its own evaluation) bool dock_into_floating_window = false; ImGuiDockNode* node = NULL; if (window->DockNodeAsHost) { // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos(). node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos); // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active) // In this case we need to fallback into any leaf mode, possibly the central node. // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. if (node && node->IsDockSpace() && node->IsRootNode()) node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node); } else { if (window->DockNode) node = window->DockNode; else dock_into_floating_window = true; // Dock into a regular window } const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. const bool do_preview = payload->IsPreview() || payload->IsDelivery(); if (do_preview && (node != NULL || dock_into_floating_window)) { // If we have a non-leaf node it means we are hovering the border of a parent node, in which case only outer markers will appear. ImGuiDockPreviewData split_inner; ImGuiDockPreviewData split_outer; ImGuiDockPreviewData* split_data = &split_inner; if (node && (node->ParentNode || node->IsCentralNode() || !node->IsLeafNode())) if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) { DockNodePreviewDockSetup(window, root_node, payload_window, NULL, &split_outer, is_explicit_target, true); if (split_outer.IsSplitDirExplicit) split_data = &split_outer; } if (!node || node->IsLeafNode()) DockNodePreviewDockSetup(window, node, payload_window, NULL, &split_inner, is_explicit_target, false); if (split_data == &split_outer) split_inner.IsDropAllowed = false; // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes DockNodePreviewDockRender(window, node, payload_window, &split_inner); DockNodePreviewDockRender(window, node, payload_window, &split_outer); // Queue docking request if (split_data->IsDropAllowed && payload->IsDelivery()) DockContextQueueDock(&g, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer); } } EndDragDropTarget(); } //----------------------------------------------------------------------------- // Docking: Settings //----------------------------------------------------------------------------- // - DockSettingsRenameNodeReferences() // - DockSettingsRemoveNodeReferences() // - DockSettingsFindNodeSettings() // - DockSettingsHandler_ApplyAll() // - DockSettingsHandler_ReadOpen() // - DockSettingsHandler_ReadLine() // - DockSettingsHandler_DockNodeToSettings() // - DockSettingsHandler_WriteAll() //----------------------------------------------------------------------------- static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id) { ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_DOCKING("[docking] DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id); for (int window_n = 0; window_n < g.Windows.Size; window_n++) { ImGuiWindow* window = g.Windows[window_n]; if (window->DockId == old_node_id && window->DockNode == NULL) window->DockId = new_node_id; } //// FIXME-OPT: We could remove this loop by storing the index in the map for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (settings->DockId == old_node_id) settings->DockId = new_node_id; } // Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count) { ImGuiContext& g = *GImGui; int found = 0; //// FIXME-OPT: We could remove this loop by storing the index in the map for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) for (int node_n = 0; node_n < node_ids_count; node_n++) if (settings->DockId == node_ids[node_n]) { settings->DockId = 0; settings->DockOrder = -1; if (++found < node_ids_count) break; return; } } static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id) { // FIXME-OPT ImGuiDockContext* dc = &ctx->DockContext; for (int n = 0; n < dc->NodesSettings.Size; n++) if (dc->NodesSettings[n].ID == id) return &dc->NodesSettings[n]; return NULL; } // Clear settings data static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiDockContext* dc = &ctx->DockContext; dc->NodesSettings.clear(); DockContextClearNodes(ctx, 0, true); } // Recreate nodes based on settings data static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { // Prune settings at boot time only ImGuiDockContext* dc = &ctx->DockContext; if (ctx->Windows.Size == 0) DockContextPruneUnusedSettingsNodes(ctx); DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size); DockContextBuildAddWindowsToNodes(ctx, 0); } static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { if (strcmp(name, "Data") != 0) return NULL; return (void*)1; } static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line) { char c = 0; int x = 0, y = 0; int r = 0; // Parsing, e.g. // " DockNode ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 " // " DockNode ID=0x00000002 Parent=0x00000001 " // Important: this code expect currently fields in a fixed order. ImGuiDockNodeSettings node; line = ImStrSkipBlank(line); if (strncmp(line, "DockNode", 8) == 0) { line = ImStrSkipBlank(line + strlen("DockNode")); } else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; } else return; if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1) { line += r; if (node.ParentNodeId == 0) return; } if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; } if (node.ParentNodeId == 0) { if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return; if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return; } else { if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } if (sscanf(line, " NoResize=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; } if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; } if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; } if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; } if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; } if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; } if (sscanf(line, " Selected=0x%08X%n", &node.SelectedTabId,&r) == 1) { line += r; } if (node.ParentNodeId != 0) if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId)) node.Depth = parent_settings->Depth + 1; ctx->DockContext.NodesSettings.push_back(node); } static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth) { ImGuiDockNodeSettings node_settings; IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3))); node_settings.ID = node->ID; node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0; node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0; node_settings.SelectedTabId = node->SelectedTabId; node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None); node_settings.Depth = (char)depth; node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); node_settings.Pos = ImVec2ih(node->Pos); node_settings.Size = ImVec2ih(node->Size); node_settings.SizeRef = ImVec2ih(node->SizeRef); dc->NodesSettings.push_back(node_settings); if (node->ChildNodes[0]) DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1); if (node->ChildNodes[1]) DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1); } static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = &ctx->DockContext; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; // Gather settings data // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer) dc->NodesSettings.resize(0); dc->NodesSettings.reserve(dc->Nodes.Data.Size); for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (node->IsRootNode()) DockSettingsHandler_DockNodeToSettings(dc, node, 0); int max_depth = 0; for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++) max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth); // Write to text buffer buf->appendf("[%s][Data]\n", handler->TypeName); for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++) { const int line_start_pos = buf->size(); (void)line_start_pos; const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n]; buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); if (node_settings->ParentNodeId) { buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y); } else { if (node_settings->ParentWindowId) buf->appendf(" Window=0x%08X", node_settings->ParentWindowId); buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); } if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); if (node_settings->Flags & ImGuiDockNodeFlags_NoResize) buf->appendf(" NoResize=1"); if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode) buf->appendf(" CentralNode=1"); if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar) buf->appendf(" NoTabBar=1"); if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar) buf->appendf(" HiddenTabBar=1"); if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton) buf->appendf(" NoWindowMenuButton=1"); if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton) buf->appendf(" NoCloseButton=1"); if (node_settings->SelectedTabId) buf->appendf(" Selected=0x%08X", node_settings->SelectedTabId); // [DEBUG] Include comments in the .ini file to ease debugging (this makes saving slower!) if (g.IO.ConfigDebugIniSettings) if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) { buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); // Iterate settings so we can give info about windows that didn't exist during the session. int contains_window = 0; for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (settings->DockId == node_settings->ID) { if (contains_window++ == 0) buf->appendf(" ; contains "); buf->appendf("'%s' ", settings->GetName()); } } buf->appendf("\n"); } buf->appendf("\n"); } //----------------------------------------------------------------------------- // [SECTION] PLATFORM DEPENDENT HELPERS //----------------------------------------------------------------------------- // - Default clipboard handlers // - Default shell function handlers // - Default IME handlers //----------------------------------------------------------------------------- #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) #ifdef _MSC_VER #pragma comment(lib, "user32") #pragma comment(lib, "kernel32") #endif // Win32 clipboard implementation // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) { ImGuiContext& g = *ctx; g.ClipboardHandlerData.clear(); if (!::OpenClipboard(NULL)) return NULL; HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); if (wbuf_handle == NULL) { ::CloseClipboard(); return NULL; } if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) { int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); g.ClipboardHandlerData.resize(buf_len); ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); } ::GlobalUnlock(wbuf_handle); ::CloseClipboard(); return g.ClipboardHandlerData.Data; } static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text) { if (!::OpenClipboard(NULL)) return; const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); if (wbuf_handle == NULL) { ::CloseClipboard(); return; } WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); ::GlobalUnlock(wbuf_handle); ::EmptyClipboard(); if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) ::GlobalFree(wbuf_handle); ::CloseClipboard(); } #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) #include // Use old API to avoid need for separate .mm file static PasteboardRef main_clipboard = 0; // OSX clipboard implementation // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text) { if (!main_clipboard) PasteboardCreate(kPasteboardClipboard, &main_clipboard); PasteboardClear(main_clipboard); CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, ImStrlen(text)); if (cf_data) { PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); CFRelease(cf_data); } } static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) { ImGuiContext& g = *ctx; if (!main_clipboard) PasteboardCreate(kPasteboardClipboard, &main_clipboard); PasteboardSynchronize(main_clipboard); ItemCount item_count = 0; PasteboardGetItemCount(main_clipboard, &item_count); for (ItemCount i = 0; i < item_count; i++) { PasteboardItemID item_id = 0; PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); CFArrayRef flavor_type_array = 0; PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) { CFDataRef cf_data; if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) { g.ClipboardHandlerData.clear(); int length = (int)CFDataGetLength(cf_data); g.ClipboardHandlerData.resize(length + 1); CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data); g.ClipboardHandlerData[length] = 0; CFRelease(cf_data); return g.ClipboardHandlerData.Data; } } } return NULL; } #else // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) { ImGuiContext& g = *ctx; return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin(); } static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text) { ImGuiContext& g = *ctx; g.ClipboardHandlerData.clear(); const char* text_end = text + ImStrlen(text); g.ClipboardHandlerData.resize((int)(text_end - text) + 1); memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text)); g.ClipboardHandlerData[(int)(text_end - text)] = 0; } #endif // Default clipboard handlers //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #if defined(__APPLE__) && TARGET_OS_IPHONE #define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #endif #if defined(__3DS__) #define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #endif #if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS) #define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #endif #endif // #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #ifdef _WIN32 #include // ShellExecuteA() #ifdef _MSC_VER #pragma comment(lib, "shell32") #endif static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { const int path_wsize = ::MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); ImVector path_wbuf; path_wbuf.resize(path_wsize); ::MultiByteToWideChar(CP_UTF8, 0, path, -1, path_wbuf.Data, path_wsize); return (INT_PTR)::ShellExecuteW(NULL, L"open", path_wbuf.Data, NULL, NULL, SW_SHOWDEFAULT) > 32; } #else #include #include static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { #if defined(__APPLE__) const char* args[] { "open", "--", path, NULL }; #else const char* args[] { "xdg-open", path, NULL }; #endif pid_t pid = fork(); if (pid < 0) return false; if (!pid) { execvp(args[0], const_cast(args)); exit(-1); } else { int status; waitpid(pid, &status, 0); return WEXITSTATUS(status) == 0; } } #endif #else static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; } #endif // Default shell handlers //----------------------------------------------------------------------------- // Win32 API IME support (for Asian languages, etc.) #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) #include #ifdef _MSC_VER #pragma comment(lib, "imm32") #endif static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) { // Notify OS Input Method Editor of text input position HWND hwnd = (HWND)viewport->PlatformHandleRaw; if (hwnd == 0) return; //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0); if (HIMC himc = ::ImmGetContext(hwnd)) { COMPOSITIONFORM composition_form = {}; composition_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x); composition_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y); composition_form.dwStyle = CFS_FORCE_POSITION; ::ImmSetCompositionWindow(himc, &composition_form); CANDIDATEFORM candidate_form = {}; candidate_form.dwStyle = CFS_CANDIDATEPOS; candidate_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x); candidate_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y); ::ImmSetCandidateWindow(himc, &candidate_form); ::ImmReleaseContext(hwnd, himc); } } #else static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {} #endif // Default IME handlers //----------------------------------------------------------------------------- // [SECTION] METRICS/DEBUGGER WINDOW //----------------------------------------------------------------------------- // - MetricsHelpMarker() [Internal] // - DebugRenderViewportThumbnail() [Internal] // - RenderViewportsThumbnails() [Internal] // - DebugRenderKeyboardPreview() [Internal] // - DebugTextEncoding() // - DebugFlashStyleColorStop() [Internal] // - DebugFlashStyleColor() // - UpdateDebugToolFlashStyleColor() [Internal] // - ShowFontAtlas() [Internal but called by Demo!] // - DebugNodeTexture() [Internal] // - ShowMetricsWindow() // - DebugNodeColumns() [Internal] // - DebugNodeDockNode() [Internal] // - DebugNodeDrawList() [Internal] // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal] // - DebugNodeFont() [Internal] // - DebugNodeFontGlyph() [Internal] // - DebugNodeStorage() [Internal] // - DebugNodeTabBar() [Internal] // - DebugNodeViewport() [Internal] // - DebugNodeWindow() [Internal] // - DebugNodeWindowSettings() [Internal] // - DebugNodeWindowsList() [Internal] // - DebugNodeWindowsListByBeginStackParent() [Internal] // - ShowFontSelector() //----------------------------------------------------------------------------- #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS) // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. static void MetricsHelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } } #endif #ifndef IMGUI_DISABLE_DEBUG_TOOLS void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImVec2 scale = bb.GetSize() / viewport->Size; ImVec2 off = bb.Min - viewport->Pos * scale; float alpha_mul = (viewport->Flags & ImGuiViewportFlags_IsMinimized) ? 0.30f : 1.00f; window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); for (ImGuiWindow* thumb_window : g.Windows) { if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) continue; if (thumb_window->Viewport != viewport) continue; ImRect thumb_r = thumb_window->Rect(); ImRect title_r = thumb_window->TitleBarRect(); thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off + thumb_r.Max * scale)); title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off + ImVec2(title_r.Max.x, title_r.Min.y + title_r.GetHeight() * 3.0f) * scale)); // Exaggerate title bar height thumb_r.ClipWithFull(bb); title_r.ClipWithFull(bb); const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul)); window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul)); window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name)); } draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); } static void RenderViewportsThumbnails() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; // Draw monitor and calculate their boundaries float SCALE = 1.0f / 8.0f; ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors) bb_full.Add(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize)); ImVec2 p = window->DC.CursorPos; ImVec2 off = p - bb_full.Min * SCALE; for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors) { ImRect monitor_draw_bb(off + (monitor.MainPos) * SCALE, off + (monitor.MainPos + monitor.MainSize) * SCALE); window->DrawList->AddRect(monitor_draw_bb.Min, monitor_draw_bb.Max, (g.DebugMetricsConfig.HighlightMonitorIdx == g.PlatformIO.Monitors.index_from_ptr(&monitor)) ? IM_COL32(255, 255, 0, 255) : ImGui::GetColorU32(ImGuiCol_Border), 4.0f); window->DrawList->AddRectFilled(monitor_draw_bb.Min, monitor_draw_bb.Max, ImGui::GetColorU32(ImGuiCol_Border, 0.10f), 4.0f); } // Draw viewports for (ImGuiViewportP* viewport : g.Viewports) { ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE); ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb); } ImGui::Dummy(bb_full.GetSize() * SCALE); } static int IMGUI_CDECL ViewportComparerByLastFocusedStampCount(const void* lhs, const void* rhs) { const ImGuiViewportP* a = *(const ImGuiViewportP* const*)lhs; const ImGuiViewportP* b = *(const ImGuiViewportP* const*)rhs; return b->LastFocusedStampCount - a->LastFocusedStampCount; } // Draw an arbitrary US keyboard layout to visualize translated keys void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) { const float scale = ImGui::GetFontSize() / 13.0f; const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale; const float key_rounding = 3.0f * scale; const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale; const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale; const float key_face_rounding = 2.0f * scale; const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale; const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f); const float key_row_offset = 9.0f * scale; ImVec2 board_min = GetCursorScreenPos(); ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f); ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y); struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; }; const KeyLayoutData keys_to_display[] = { { 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R }, { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F }, { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V } }; // Elements rendered manually via ImDrawList API are not clipped automatically. // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view. Dummy(board_max - board_min); if (!IsItemVisible()) return; draw_list->PushClipRect(board_min, board_max, true); for (int n = 0; n < IM_COUNTOF(keys_to_display); n++) { const KeyLayoutData* key_data = &keys_to_display[n]; ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y); ImVec2 key_max = key_min + key_size; draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding); draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding); ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y); ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y); draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f); draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding); ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y); draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label); if (IsKeyDown(key_data->Key)) draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding); } draw_list->PopClipRect(); } // Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct. void ImGui::DebugTextEncoding(const char* str) { Text("Text: \"%s\"", str); if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable)) return; TableSetupColumn("Offset"); TableSetupColumn("UTF-8"); TableSetupColumn("Glyph"); TableSetupColumn("Codepoint"); TableHeadersRow(); const char* str_end = str + strlen(str); // As we may receive malformed UTF-8, pass an explicit end instead of relying on ImTextCharFromUtf8() assuming enough space. for (const char* p = str; *p != 0; ) { unsigned int c; const int c_utf8_len = ImTextCharFromUtf8(&c, p, str_end); TableNextColumn(); Text("%d", (int)(p - str)); TableNextColumn(); for (int byte_index = 0; byte_index < c_utf8_len; byte_index++) { if (byte_index > 0) SameLine(); Text("0x%02X", (int)(unsigned char)p[byte_index]); } TableNextColumn(); TextUnformatted(p, p + c_utf8_len); if (!GetFont()->IsGlyphInFont((ImWchar)c)) { SameLine(); TextUnformatted("[missing]"); } TableNextColumn(); Text("U+%04X", (int)c); p += c_utf8_len; } EndTable(); } static void DebugFlashStyleColorStop() { ImGuiContext& g = *GImGui; if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT) g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup; g.DebugFlashStyleColorIdx = ImGuiCol_COUNT; } // Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls. void ImGui::DebugFlashStyleColor(ImGuiCol idx) { ImGuiContext& g = *GImGui; DebugFlashStyleColorStop(); g.DebugFlashStyleColorTime = 0.5f; g.DebugFlashStyleColorIdx = idx; g.DebugFlashStyleColorBackup = g.Style.Colors[idx]; } void ImGui::UpdateDebugToolFlashStyleColor() { ImGuiContext& g = *GImGui; if (g.DebugFlashStyleColorTime <= 0.0f) return; ColorConvertHSVtoRGB(ImCos(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z); g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f; if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f) DebugFlashStyleColorStop(); } ImU64 ImGui::DebugTextureIDToU64(ImTextureID tex_id) { ImU64 v = 0; memcpy(&v, &tex_id, ImMin(sizeof(ImU64), sizeof(ImTextureID))); return v; } static const char* FormatTextureRefForDebugDisplay(char* buf, int buf_size, ImTextureRef tex_ref) { char* buf_p = buf; char* buf_end = buf + buf_size; if (tex_ref._TexData != NULL) buf_p += ImFormatString(buf_p, buf_end - buf_p, "#%03d: ", tex_ref._TexData->UniqueID); ImFormatString(buf_p, buf_end - buf_p, "0x%X", ImGui::DebugTextureIDToU64(tex_ref.GetTexID())); return buf; } #ifdef IMGUI_ENABLE_FREETYPE namespace ImGuiFreeType { IMGUI_API const ImFontLoader* GetFontLoader(); IMGUI_API bool DebugEditFontLoaderFlags(unsigned int* p_font_builder_flags); } #endif // [DEBUG] List fonts in a font atlas and display its texture void ImGui::ShowFontAtlas(ImFontAtlas* atlas) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; ImGuiStyle& style = g.Style; BeginDisabled(); CheckboxFlags("io.BackendFlags: RendererHasTextures", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures); EndDisabled(); ShowFontSelector("Font"); //BeginDisabled((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0); if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f")) style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work. SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize()); SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later."); DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f); //BeginDisabled(io.ConfigDpiScaleFonts); DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f); //SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten."); //EndDisabled(); if ((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) { BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!"); BulletText("For instructions, see:"); SameLine(); TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md"); } BulletText("Load a nice font for better results!"); BulletText("Please submit feedback:"); SameLine(); TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465"); BulletText("Read FAQ for more details:"); SameLine(); TextLinkOpenURL("dearimgui.com/faq", "https://www.dearimgui.com/faq/"); //EndDisabled(); SeparatorText("Font List"); ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; Checkbox("Show font preview", &cfg->ShowFontPreview); // Font loaders if (TreeNode("Loader", "Loader: \'%s\'", atlas->FontLoaderName ? atlas->FontLoaderName : "NULL")) { const ImFontLoader* loader_current = atlas->FontLoader; BeginDisabled(!atlas->RendererHasTextures); #ifdef IMGUI_ENABLE_STB_TRUETYPE const ImFontLoader* loader_stbtruetype = ImFontAtlasGetFontLoaderForStbTruetype(); if (RadioButton("stb_truetype", loader_current == loader_stbtruetype)) atlas->SetFontLoader(loader_stbtruetype); #else BeginDisabled(); RadioButton("stb_truetype", false); SetItemTooltip("Requires #define IMGUI_ENABLE_STB_TRUETYPE"); EndDisabled(); #endif SameLine(); #ifdef IMGUI_ENABLE_FREETYPE const ImFontLoader* loader_freetype = ImGuiFreeType::GetFontLoader(); if (RadioButton("FreeType", loader_current == loader_freetype)) atlas->SetFontLoader(loader_freetype); if (loader_current == loader_freetype) { unsigned int loader_flags = atlas->FontLoaderFlags; Text("Shared FreeType Loader Flags: 0x%08X", loader_flags); if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags)) { for (ImFont* font : atlas->Fonts) ImFontAtlasFontDestroyOutput(atlas, font); atlas->FontLoaderFlags = loader_flags; for (ImFont* font : atlas->Fonts) ImFontAtlasFontInitOutput(atlas, font); } } #else BeginDisabled(); RadioButton("FreeType", false); SetItemTooltip("Requires #define IMGUI_ENABLE_FREETYPE + imgui_freetype.cpp."); EndDisabled(); #endif EndDisabled(); TreePop(); } // Font list for (ImFont* font : atlas->Fonts) { PushID(font); DebugNodeFont(font); PopID(); } SeparatorText("Font Atlas"); if (Button("Compact")) atlas->CompactCache(); SameLine(); if (Button("Grow")) ImFontAtlasTextureGrow(atlas); SameLine(); if (Button("Clear All")) ImFontAtlasBuildClear(atlas); SetItemTooltip("Destroy cache and custom rectangles."); for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++) { ImTextureData* tex = atlas->TexList[tex_n]; if (tex_n > 0) SameLine(); Text("Tex: %dx%d", tex->Width, tex->Height); } const int packed_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsPackedSurface); const int discarded_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsDiscardedSurface); Text("Packed rects: %d, area: about %d px ~%dx%d px", atlas->Builder->RectsPackedCount, atlas->Builder->RectsPackedSurface, packed_surface_sqrt, packed_surface_sqrt); Text("incl. Discarded rects: %d, area: about %d px ~%dx%d px", atlas->Builder->RectsDiscardedCount, atlas->Builder->RectsDiscardedSurface, discarded_surface_sqrt, discarded_surface_sqrt); ImFontAtlasRectId highlight_r_id = ImFontAtlasRectId_Invalid; if (TreeNode("Rects Index", "Rects Index (%d)", atlas->Builder->RectsPackedCount)) // <-- Use count of used rectangles { PushStyleVar(ImGuiStyleVar_ImageBorderSize, 1.0f); if (BeginTable("##table", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY, ImVec2(0.0f, GetTextLineHeightWithSpacing() * 12))) { for (const ImFontAtlasRectEntry& entry : atlas->Builder->RectsIndex) if (entry.IsUsed) { ImFontAtlasRectId id = ImFontAtlasRectId_Make(atlas->Builder->RectsIndex.index_from_ptr(&entry), entry.Generation); ImFontAtlasRect r = {}; atlas->GetCustomRect(id, &r); const char* buf; ImFormatStringToTempBuffer(&buf, NULL, "ID:%08X, used:%d, { w:%3d, h:%3d } { x:%4d, y:%4d }", id, entry.IsUsed, r.w, r.h, r.x, r.y); TableNextColumn(); Selectable(buf); if (IsItemHovered()) highlight_r_id = id; TableNextColumn(); Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1); } EndTable(); } PopStyleVar(); TreePop(); } // Texture list // (ensure the last texture always use the same ID, so we can keep it open neatly) ImFontAtlasRect highlight_r; if (highlight_r_id != ImFontAtlasRectId_Invalid) atlas->GetCustomRect(highlight_r_id, &highlight_r); for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++) { if (tex_n == atlas->TexList.Size - 1) SetNextItemOpen(true, ImGuiCond_Once); DebugNodeTexture(atlas->TexList[tex_n], atlas->TexList.Size - 1 - tex_n, (highlight_r_id != ImFontAtlasRectId_Invalid) ? &highlight_r : NULL); } } void ImGui::DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect) { ImGuiContext& g = *GImGui; PushID(int_id); if (TreeNode("", "Texture #%03d (%dx%d pixels)", tex->UniqueID, tex->Width, tex->Height)) { ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; Checkbox("Show used rect", &cfg->ShowTextureUsedRect); PushStyleVar(ImGuiStyleVar_ImageBorderSize, ImMax(1.0f, g.Style.ImageBorderSize)); ImVec2 p = GetCursorScreenPos(); if (tex->WantDestroyNextFrame) Dummy(ImVec2((float)tex->Width, (float)tex->Height)); else ImageWithBg(tex->GetTexRef(), ImVec2((float)tex->Width, (float)tex->Height), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); if (cfg->ShowTextureUsedRect) GetWindowDrawList()->AddRect(ImVec2(p.x + tex->UsedRect.x, p.y + tex->UsedRect.y), ImVec2(p.x + tex->UsedRect.x + tex->UsedRect.w, p.y + tex->UsedRect.y + tex->UsedRect.h), IM_COL32(255, 0, 255, 255)); if (highlight_rect != NULL) { ImRect r_outer(p.x, p.y, p.x + tex->Width, p.y + tex->Height); ImRect r_inner(p.x + highlight_rect->x, p.y + highlight_rect->y, p.x + highlight_rect->x + highlight_rect->w, p.y + highlight_rect->y + highlight_rect->h); RenderRectFilledWithHole(GetWindowDrawList(), r_outer, r_inner, IM_COL32(0, 0, 0, 100), 0.0f); GetWindowDrawList()->AddRect(r_inner.Min - ImVec2(1, 1), r_inner.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255)); } PopStyleVar(); char texref_desc[30]; Text("Status = %s (%d), Format = %s (%d), UseColors = %d", ImTextureDataGetStatusName(tex->Status), tex->Status, ImTextureDataGetFormatName(tex->Format), tex->Format, tex->UseColors); Text("TexRef = %s, BackendUserData = %p", FormatTextureRefForDebugDisplay(texref_desc, IM_COUNTOF(texref_desc), tex->GetTexRef()), tex->BackendUserData); TreePop(); } PopID(); } void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; if (cfg->ShowDebugLog) ShowDebugLogWindow(&cfg->ShowDebugLog); if (cfg->ShowIDStackTool) ShowIDStackToolWindow(&cfg->ShowIDStackTool); if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; } // [DEBUG] Clear debug breaks hooks after exactly one cycle. DebugBreakClearData(); // Basic info Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); if (g.ContextName[0] != 0) { SameLine(); Text("(Context Name: \"%s\")", g.ContextName); } Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount); //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } Separator(); // Debugging enums enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" }; enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" }; if (cfg->ShowWindowsRectsType < 0) cfg->ShowWindowsRectsType = WRT_WorkRect; if (cfg->ShowTablesRectsType < 0) cfg->ShowTablesRectsType = TRT_WorkRect; struct Funcs { static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n) { ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance if (rect_type == TRT_OuterRect) { return table->OuterRect; } else if (rect_type == TRT_InnerRect) { return table->InnerRect; } else if (rect_type == TRT_WorkRect) { return table->WorkRect; } else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; } else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; } else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; } else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); } else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); } else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; } else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); } else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } IM_ASSERT(0); return ImRect(); } static ImRect GetWindowRect(ImGuiWindow* window, int rect_type) { if (rect_type == WRT_OuterRect) { return window->Rect(); } else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; } else if (rect_type == WRT_InnerRect) { return window->InnerRect; } else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } else if (rect_type == WRT_WorkRect) { return window->WorkRect; } else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); } else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; } IM_ASSERT(0); return ImRect(); } }; #ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\nMust disable after use! Otherwise Dear ImGui will run slower.\n"); #endif // Tools if (TreeNode("Tools")) { // Debug Break features // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x); SameLine(); MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive) DebugStartItemPicker(); Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent); SeparatorText("Visualize"); Checkbox("Show Debug Log", &cfg->ShowDebugLog); SameLine(); MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code."); Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool); SameLine(); MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code."); Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); SameLine(); SetNextItemWidth(GetFontSize() * 12); cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count); if (cfg->ShowWindowsRects && g.NavWindow != NULL) { BulletText("'%s':", g.NavWindow->Name); Indent(); for (int rect_n = 0; rect_n < WRT_Count; rect_n++) { ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n); Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]); } Unindent(); } Checkbox("Show tables rectangles", &cfg->ShowTablesRects); SameLine(); SetNextItemWidth(GetFontSize() * 12); cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count); if (cfg->ShowTablesRects && g.NavWindow != NULL) { for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) { ImGuiTable* table = g.Tables.TryGetMapData(table_n); if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) continue; BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name); if (IsItemHovered()) GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); Indent(); char buf[128]; for (int rect_n = 0; rect_n < TRT_Count; rect_n++) { if (rect_n >= TRT_ColumnsRect) { if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect) continue; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImRect r = Funcs::GetTableRect(table, rect_n, column_n); ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); } } else { ImRect r = Funcs::GetTableRect(table, rect_n, -1); ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); } } Unindent(); } } Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data SeparatorText("Validate"); Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop); SameLine(); MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running."); Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer); SameLine(); MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct."); if (cfg->ShowTextEncodingViewer) { static char buf[64] = ""; SetNextItemWidth(-FLT_MIN); InputText("##DebugTextEncodingBuf", buf, IM_COUNTOF(buf)); if (buf[0] != 0) DebugTextEncoding(buf); } TreePop(); } // Windows if (TreeNode("Windows", "Windows (%d)", g.Windows.Size)) { //SetNextItemOpen(true, ImGuiCond_Once); DebugNodeWindowsList(&g.Windows, "By display order"); DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)"); if (TreeNode("By submission order (begin stack)")) { // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship! ImVector& temp_buffer = g.WindowsTempSortBuffer; temp_buffer.resize(0); for (ImGuiWindow* window : g.Windows) if (window->LastFrameActive + 1 >= g.FrameCount) temp_buffer.push_back(window); struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } }; ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder); DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL); TreePop(); } TreePop(); } // DrawLists int drawlist_count = 0; for (ImGuiViewportP* viewport : g.Viewports) drawlist_count += viewport->DrawDataP.CmdLists.Size; if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) { Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); for (ImGuiViewportP* viewport : g.Viewports) { bool viewport_has_drawlist = false; for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists) { if (!viewport_has_drawlist) Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID); viewport_has_drawlist = true; DebugNodeDrawList(NULL, viewport, draw_list, "DrawList"); } } TreePop(); } // Viewports if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size)) { cfg->HighlightMonitorIdx = -1; bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size); SameLine(); MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors."); if (open) { for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) { DebugNodePlatformMonitor(&g.PlatformIO.Monitors[i], "Monitor", i); if (IsItemHovered()) cfg->HighlightMonitorIdx = i; } DebugNodePlatformMonitor(&g.FallbackMonitor, "Fallback", 0); TreePop(); } SetNextItemOpen(true, ImGuiCond_Once); if (TreeNode("Windows Minimap")) { RenderViewportsThumbnails(); TreePop(); } cfg->HighlightViewportID = 0; BulletText("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport ? g.MouseViewport->ID : 0, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); if (TreeNode("Inferred Z order (front-to-back)")) { static ImVector viewports; viewports.resize(g.Viewports.Size); memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes()); if (viewports.Size > 1) ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByLastFocusedStampCount); for (ImGuiViewportP* viewport : viewports) { BulletText("Viewport #%d, ID: 0x%08X, LastFocused = %08d, PlatformFocused = %s, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->LastFocusedStampCount, (g.PlatformIO.Platform_GetWindowFocus && viewport->PlatformWindowCreated) ? (g.PlatformIO.Platform_GetWindowFocus(viewport) ? "1" : "0") : "N/A", viewport->Window ? viewport->Window->Name : "N/A"); if (IsItemHovered()) cfg->HighlightViewportID = viewport->ID; } TreePop(); } for (ImGuiViewportP* viewport : g.Viewports) DebugNodeViewport(viewport); TreePop(); } // Details for Fonts for (ImFontAtlas* atlas : g.FontAtlases) if (TreeNode((void*)atlas, "Fonts (%d), Textures (%d)", atlas->Fonts.Size, atlas->TexList.Size)) { ShowFontAtlas(atlas); TreePop(); } // Details for Popups if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) { for (const ImGuiPopupData& popup_data : g.OpenPopupStack) { // As it's difficult to interact with tree nodes while popups are open, we display everything inline. ImGuiWindow* window = popup_data.Window; BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'", popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "", popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL"); } TreePop(); } // Details for TabBars if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount())) { for (int n = 0; n < g.TabBars.GetMapSize(); n++) if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n)) { PushID(tab_bar); DebugNodeTabBar(tab_bar, "TabBar"); PopID(); } TreePop(); } // Details for Tables if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount())) { for (int n = 0; n < g.Tables.GetMapSize(); n++) if (ImGuiTable* table = g.Tables.TryGetMapData(n)) DebugNodeTable(table); TreePop(); } // Details for InputText if (TreeNode("InputText")) { DebugNodeInputTextState(&g.InputTextState); TreePop(); } // Details for TypingSelect if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0)) { DebugNodeTypingSelectState(&g.TypingSelectState); TreePop(); } // Details for MultiSelect if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount())) { ImGuiBoxSelectState* bs = &g.BoxSelectState; BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive); for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++) if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n)) DebugNodeMultiSelectState(state); TreePop(); } // Details for Docking #ifdef IMGUI_HAS_DOCK if (TreeNode("Docking")) { static bool root_nodes_only = true; ImGuiDockContext* dc = &g.DockContext; Checkbox("List root nodes", &root_nodes_only); Checkbox("Ctrl shows window dock info", &cfg->ShowDockingNodes); if (SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); } SameLine(); if (SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (!root_nodes_only || node->IsRootNode()) DebugNodeDockNode(node, "Node"); TreePop(); } #endif // #ifdef IMGUI_HAS_DOCK // Settings if (TreeNode("Settings")) { if (SmallButton("Clear")) ClearIniSettings(); SameLine(); if (SmallButton("Save to memory")) SaveIniSettingsToMemory(); SameLine(); if (SmallButton("Save to disk")) SaveIniSettingsToDisk(g.IO.IniFilename); SameLine(); if (g.IO.IniFilename) Text("\"%s\"", g.IO.IniFilename); else TextUnformatted(""); Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) { for (ImGuiSettingsHandler& handler : g.SettingsHandlers) BulletText("\"%s\"", handler.TypeName); TreePop(); } if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) { for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) DebugNodeWindowSettings(settings); TreePop(); } if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) { for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) DebugNodeTableSettings(settings); TreePop(); } #ifdef IMGUI_HAS_DOCK if (TreeNode("SettingsDocking", "Settings packed data: Docking")) { ImGuiDockContext* dc = &g.DockContext; Text("In SettingsWindows:"); for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (settings->DockId != 0) BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings->GetName(), settings->DockId, settings->DockOrder); Text("In SettingsNodes:"); for (int n = 0; n < dc->NodesSettings.Size; n++) { ImGuiDockNodeSettings* settings = &dc->NodesSettings[n]; const char* selected_tab_name = NULL; if (settings->SelectedTabId) { if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabId)) selected_tab_name = window->Name; else if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->SelectedTabId)) selected_tab_name = window_settings->GetName(); } BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedTabId, selected_tab_name ? selected_tab_name : settings->SelectedTabId ? "N/A" : ""); } TreePop(); } #endif // #ifdef IMGUI_HAS_DOCK if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size())) { InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly); TreePop(); } TreePop(); } // Settings if (TreeNode("Memory allocations")) { ImGuiDebugAllocInfo* info = &g.DebugAllocInfo; Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount); if (SmallButton("GC now")) { g.GcCompactAll = true; } Text("Recent frames with allocations:"); int buf_size = IM_COUNTOF(info->LastEntriesBuf); for (int n = buf_size - 1; n >= 0; n--) { ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size]; BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount); if (n == 0) { SameLine(); Text("<- %d frames ago", g.FrameCount - entry->FrameCount); } } TreePop(); } if (TreeNode("Inputs")) { Text("KEYBOARD/GAMEPAD/MOUSE KEYS"); { // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END. Indent(); Text("Keys down:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); } Text("Keys pressed:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyPressed(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); } Text("Keys released:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); } Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "Ctrl " : "", io.KeyShift ? "Shift " : "", io.KeyAlt ? "Alt " : "", io.KeySuper ? "Super " : ""); Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. DebugRenderKeyboardPreview(GetWindowDrawList()); Unindent(); } Text("MOUSE STATE"); { Indent(); if (IsMousePosValid()) Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); else Text("Mouse pos: "); Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); int count = IM_COUNTOF(io.MouseDown); Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); } Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); } Text("Mouse wheel: %.1f", io.MouseWheel); Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer); Text("Mouse source: %s", GetMouseSourceName(io.MouseSource)); Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused Unindent(); } Text("MOUSE WHEELING"); { Indent(); Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL"); Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer); Text("WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? "X" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? "Y" : ""); Unindent(); } Text("KEY OWNERS"); { Indent(); if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings)) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner) continue; Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr, owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : ""); DebugLocateItemOnHover(owner_data->OwnerCurr); } EndChild(); Unindent(); } Text("SHORTCUT ROUTING"); SameLine(); MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches."); { Indent(); if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings)) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; ) { ImGuiKeyRoutingData* routing_data = &rt->Entries[idx]; ImGuiKeyChord key_chord = key | routing_data->Mods; Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore); DebugLocateItemOnHover(routing_data->RoutingCurr); if (g.IO.ConfigDebugIsDebuggerPresent) { SameLine(); if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord")) g.DebugBreakInShortcutRouting = key_chord; } idx = routing_data->NextEntryIndex; } } EndChild(); Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); Unindent(); } TreePop(); } if (TreeNode("Internal state")) { Text("WINDOWING"); Indent(); Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : "NULL"); Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode ? g.DebugHoveredDockNode->ID : 0); Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); Unindent(); Text("ITEMS"); Indent(); Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource)); DebugLocateItemOnHover(g.ActiveId); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer); Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); DebugLocateItemOnHover(g.DragDropPayload.SourceId); Unindent(); Text("NAV,FOCUS"); Indent(); Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); DebugLocateItemOnHover(g.NavId); Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource)); Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId); Text("NavActivateFlags: %04X", g.NavActivateFlags); Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", g.NavCursorVisible, g.NavHighlightItemUnderNav); Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); Text("NavFocusRoute[] = "); for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--) { const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n]; SameLine(0.0f, 0.0f); Text("0x%08X/", focus_scope.ID); SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name); } Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); Unindent(); TreePop(); } // Overlay: Display windows Rectangles and Begin Order if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder) { for (ImGuiWindow* window : g.Windows) { if (!window->WasActive) continue; ImDrawList* draw_list = GetForegroundDrawList(window); if (cfg->ShowWindowsRects) { ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); } if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow)) { char buf[32]; ImFormatString(buf, IM_COUNTOF(buf), "%d", window->BeginOrderWithinContext); float font_size = GetFontSize(); draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf); } } } // Overlay: Display Tables Rectangles if (cfg->ShowTablesRects) { for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) { ImGuiTable* table = g.Tables.TryGetMapData(table_n); if (table == NULL || table->LastFrameActive < g.FrameCount - 1) continue; ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow); if (cfg->ShowTablesRectsType >= TRT_ColumnsRect) { for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n); ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255); float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f; draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness); } } else { ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); } } } #ifdef IMGUI_HAS_DOCK // Overlay: Display Docking info if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.DebugHoveredDockNode) { char buf[64] = ""; char* p = buf; ImGuiDockNode* node = g.DebugHoveredDockNode; ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); p += ImFormatString(p, buf + IM_COUNTOF(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); p += ImFormatString(p, buf + IM_COUNTOF(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId); p += ImFormatString(p, buf + IM_COUNTOF(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); p += ImFormatString(p, buf + IM_COUNTOF(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); int depth = DockNodeGetDepth(node); overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255)); ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth; overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255)); overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf); } #endif // #ifdef IMGUI_HAS_DOCK End(); } void ImGui::DebugBreakClearData() { // Those fields are scattered in their respective subsystem to stay in hot-data locations ImGuiContext& g = *GImGui; g.DebugBreakInWindow = 0; g.DebugBreakInTable = 0; g.DebugBreakInShortcutRouting = ImGuiKey_None; } void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location) { if (!BeginItemTooltip()) return; Text("To call IM_DEBUG_BREAK() %s:", description_of_location); Separator(); TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space."); Separator(); TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!"); EndTooltip(); } // Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc. // In order to reduce interferences with the contents we are trying to debug into. bool ImGui::DebugBreakButton(const char* label, const char* description_of_location) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset); ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y); const ImRect bb(pos, pos + size); ItemSize(size, 0.0f); if (!ItemAdd(bb, id)) return false; // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects. bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags); bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id); DebugBreakButtonTooltip(false, description_of_location); ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImVec4 hsv; ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z); ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z); RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding); RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } // [DEBUG] Display contents of Columns void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) { if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) return; BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX); for (ImGuiOldColumnData& column : columns->Columns) BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm)); TreePop(); } static void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* label, bool enabled) { using namespace ImGui; PushID(label); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); Text("%s:", label); if (!enabled) BeginDisabled(); CheckboxFlags("NoResize", p_flags, ImGuiDockNodeFlags_NoResize); CheckboxFlags("NoResizeX", p_flags, ImGuiDockNodeFlags_NoResizeX); CheckboxFlags("NoResizeY",p_flags, ImGuiDockNodeFlags_NoResizeY); CheckboxFlags("NoTabBar", p_flags, ImGuiDockNodeFlags_NoTabBar); CheckboxFlags("HiddenTabBar", p_flags, ImGuiDockNodeFlags_HiddenTabBar); CheckboxFlags("NoWindowMenuButton", p_flags, ImGuiDockNodeFlags_NoWindowMenuButton); CheckboxFlags("NoCloseButton", p_flags, ImGuiDockNodeFlags_NoCloseButton); CheckboxFlags("DockedWindowsInFocusRoute", p_flags, ImGuiDockNodeFlags_DockedWindowsInFocusRoute); CheckboxFlags("NoDocking", p_flags, ImGuiDockNodeFlags_NoDocking); // Multiple flags CheckboxFlags("NoDockingSplit", p_flags, ImGuiDockNodeFlags_NoDockingSplit); CheckboxFlags("NoDockingSplitOther", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther); CheckboxFlags("NoDockingOver", p_flags, ImGuiDockNodeFlags_NoDockingOverMe); CheckboxFlags("NoDockingOverOther", p_flags, ImGuiDockNodeFlags_NoDockingOverOther); CheckboxFlags("NoDockingOverEmpty", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty); CheckboxFlags("NoUndocking", p_flags, ImGuiDockNodeFlags_NoUndocking); if (!enabled) EndDisabled(); PopStyleVar(); PopID(); } // [DEBUG] Display contents of ImDockNode void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) { ImGuiContext& g = *GImGui; const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2); // Submitted with ImGuiDockNodeFlags_KeepAliveOnly const bool is_active = (g.FrameCount - node->LastFrameActive < 2); // Submitted if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } bool open; ImGuiTreeNodeFlags tree_node_flags = node->IsFocused ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None; if (node->Windows.Size > 0) open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); else open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %s (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal split" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical split" : "empty", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); if (!is_alive) { PopStyleColor(); } if (is_active && IsItemHovered()) if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow) GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255)); if (open) { IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); DebugNodeWindow(node->HostWindow, "HostWindow"); DebugNodeWindow(node->VisibleWindow, "VisibleWindow"); BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); BulletText("Misc:%s%s%s%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", node->IsFocused ? " IsFocused" : "", node->WantLockSizeOnce ? " WantLockSizeOnce" : "", node->HasCentralNodeChild ? " HasCentralNodeChild" : ""); if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags)) { if (BeginTable("flags", 4)) { TableNextColumn(); DebugNodeDockNodeFlags(&node->MergedFlags, "MergedFlags", false); TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true); TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlagsInWindows, "LocalFlagsInWindows", false); TableNextColumn(); DebugNodeDockNodeFlags(&node->SharedFlags, "SharedFlags", true); EndTable(); } TreePop(); } if (node->ParentNode) DebugNodeDockNode(node->ParentNode, "ParentNode"); if (node->ChildNodes[0]) DebugNodeDockNode(node->ChildNodes[0], "Child[0]"); if (node->ChildNodes[1]) DebugNodeDockNode(node->ChildNodes[1], "Child[1]"); if (node->TabBar) DebugNodeTabBar(node->TabBar, "TabBar"); DebugNodeWindowsList(&node->Windows, "Windows"); TreePop(); } } // [DEBUG] Display contents of ImDrawList // Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport. void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label) { ImGuiContext& g = *GImGui; ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; int cmd_count = draw_list->CmdBuffer.Size; if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL) cmd_count--; bool node_open = 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, cmd_count); if (draw_list == GetWindowDrawList()) { SameLine(); 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) TreePop(); return; } ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list if (window && IsItemHovered() && fg_draw_list) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; if (window && !window->WasActive) TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!"); for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++) { if (pcmd->UserCallback) { BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); continue; } char texid_desc[30]; FormatTextureRefForDebugDisplay(texid_desc, IM_COUNTOF(texid_desc), pcmd->TexRef); char buf[300]; ImFormatString(buf, IM_COUNTOF(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list) DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes); if (!pcmd_node_open) continue; // Calculate approximate coverage area (touched pixel count) // This will be in pixels squared as long there's no post-scaling happening to the renderer output. const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset; float total_area = 0.0f; for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; ) { ImVec2 triangle[3]; for (int n = 0; n < 3; n++, idx_n++) triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos; total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]); } // Display vertex information summary. Hover to get all triangles drawn in wire-frame ImFormatString(buf, IM_COUNTOF(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); Selectable(buf); if (IsItemHovered() && fg_draw_list) DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false); // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. ImGuiListClipper clipper; clipper.Begin(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, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++) { char* buf_p = buf, * buf_end = buf + IM_COUNTOF(buf); ImVec2 triangle[3]; for (int n = 0; n < 3; n++, idx_i++) { const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i]; triangle[n] = v.pos; buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); } Selectable(buf, false); if (fg_draw_list && IsItemHovered()) { ImDrawListFlags backup_flags = fg_draw_list->Flags; fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); fg_draw_list->Flags = backup_flags; } } TreePop(); } TreePop(); } // [DEBUG] Display mesh/aabb of a ImDrawCmd void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb) { IM_ASSERT(show_mesh || show_aabb); // Draw wire-frame version of all triangles ImRect clip_rect = draw_cmd->ClipRect; ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); ImDrawListFlags backup_flags = out_draw_list->Flags; out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; ) { ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; ImVec2 triangle[3]; for (int n = 0; n < 3; n++, idx_n++) vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos)); if (show_mesh) out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles } // Draw bounding boxes if (show_aabb) { out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles } out_draw_list->Flags = backup_flags; } // [DEBUG] Compute mask of inputs with the same codepoint. static int CalcFontGlyphSrcOverlapMask(ImFontAtlas* atlas, ImFont* font, unsigned int codepoint) { int mask = 0, count = 0; for (int src_n = 0; src_n < font->Sources.Size; src_n++) { ImFontConfig* src = font->Sources[src_n]; if (!(src->FontLoader ? src->FontLoader : atlas->FontLoader)->FontSrcContainsGlyph(atlas, src, (ImWchar)codepoint)) continue; mask |= (1 << src_n); count++; } return count > 1 ? mask : 0; } // [DEBUG] Display details for a single font, called by ShowStyleEditor(). void ImGui::DebugNodeFont(ImFont* font) { ImGuiContext& g = *GImGui; ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; ImFontAtlas* atlas = font->OwnerAtlas; bool opened = TreeNode(font, "Font: \"%s\": %d sources(s)", font->GetDebugName(), font->Sources.Size); // Display preview text if (!opened) Indent(); Indent(); if (cfg->ShowFontPreview) { PushFont(font, 0.0f); Text("The quick brown fox jumps over the lazy dog"); PopFont(); } if (!opened) { Unindent(); Unindent(); return; } if (SmallButton("Set as default")) GetIO().FontDefault = font; SameLine(); BeginDisabled(atlas->Fonts.Size <= 1 || atlas->Locked); if (SmallButton("Remove")) atlas->RemoveFont(font); EndDisabled(); SameLine(); if (SmallButton("Clear bakes")) ImFontAtlasFontDiscardBakes(atlas, font, 0); SameLine(); if (SmallButton("Clear unused")) ImFontAtlasFontDiscardBakes(atlas, font, 2); // Display details #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS SetNextItemWidth(GetFontSize() * 8); DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); /*SameLine(); MetricsHelpMarker( "Note that the default embedded font is NOT meant to be scaled.\n\n" "Font 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 will be rewritten in the future to make scaling more flexible.)");*/ #endif char c_str[5]; ImTextCharToUtf8(c_str, font->FallbackChar); Text("Fallback character: '%s' (U+%04X)", c_str, font->FallbackChar); ImTextCharToUtf8(c_str, font->EllipsisChar); Text("Ellipsis character: '%s' (U+%04X)", c_str, font->EllipsisChar); for (int src_n = 0; src_n < font->Sources.Size; src_n++) { ImFontConfig* src = font->Sources[src_n]; if (TreeNode(src, "Input %d: \'%s\' [%d], Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)", src_n, src->Name, src->FontNo, src->OversampleH, src->OversampleV, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y)) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; Text("Loader: '%s'", loader->Name ? loader->Name : "N/A"); //if (DragFloat("ExtraSizeScale", &src->ExtraSizeScale, 0.01f, 0.10f, 2.0f)) // ImFontAtlasFontRebuildOutput(atlas, font); #ifdef IMGUI_ENABLE_FREETYPE if (loader->Name != NULL && strcmp(loader->Name, "FreeType") == 0) { unsigned int loader_flags = src->FontLoaderFlags; Text("FreeType Loader Flags: 0x%08X", loader_flags); if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags)) { ImFontAtlasFontDestroyOutput(atlas, font); src->FontLoaderFlags = loader_flags; ImFontAtlasFontInitOutput(atlas, font); } } #endif TreePop(); } } if (font->Sources.Size > 1 && TreeNode("Input Glyphs Overlap Detection Tool")) { TextWrapped("- First Input that contains the glyph is used.\n" "- Use ImFontConfig::GlyphExcludeRanges[] to specify ranges to ignore glyph in given Input.\n- Prefer using a small number of ranges as the list is scanned every time a new glyph is loaded,\n - e.g. GlyphExcludeRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };\n- This tool doesn't cache results and is slow, don't keep it open!"); if (BeginTable("table", 2)) { for (unsigned int c = 0; c < 0x10000; c++) if (int overlap_mask = CalcFontGlyphSrcOverlapMask(atlas, font, c)) { unsigned int c_end = c + 1; while (c_end < 0x10000 && CalcFontGlyphSrcOverlapMask(atlas, font, c_end) == overlap_mask) c_end++; if (TableNextColumn() && TreeNode((void*)(intptr_t)c, "U+%04X-U+%04X: %d codepoints in %d inputs", c, c_end - 1, c_end - c, ImCountSetBits(overlap_mask))) { char utf8_buf[5]; for (unsigned int n = c; n < c_end; n++) { ImTextCharToUtf8(utf8_buf, n); BulletText("Codepoint U+%04X (%s)", n, utf8_buf); } TreePop(); } TableNextColumn(); for (int src_n = 0; src_n < font->Sources.Size; src_n++) if (overlap_mask & (1 << src_n)) { Text("%d ", src_n); SameLine(); } c = c_end - 1; } EndTable(); } TreePop(); } // Display all glyphs of the fonts in separate pages of 256 characters for (int baked_n = 0; baked_n < atlas->Builder->BakedPool.Size; baked_n++) { ImFontBaked* baked = &atlas->Builder->BakedPool[baked_n]; if (baked->OwnerFont != font) continue; PushID(baked->BakedId); if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : "")) { if (SmallButton("Load all")) for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++) baked->FindGlyph((ImWchar)base); const int surface_sqrt = (int)ImSqrt((float)baked->MetricsTotalSurface); Text("Ascent: %f, Descent: %f, Ascent-Descent: %f", baked->Ascent, baked->Descent, baked->Ascent - baked->Descent); Text("Texture Area: about %d px ~%dx%d px", baked->MetricsTotalSurface, surface_sqrt, surface_sqrt); for (int src_n = 0; src_n < font->Sources.Size; src_n++) { ImFontConfig* src = font->Sources[src_n]; int oversample_h, oversample_v; ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v); BulletText("Input %d: \'%s\', Oversample: (%d=>%d,%d=>%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", src_n, src->Name, src->OversampleH, oversample_h, src->OversampleV, oversample_v, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y); } DebugNodeFontGlyphsForSrcMask(font, baked, ~0); TreePop(); } PopID(); } TreePop(); Unindent(); } void ImGui::DebugNodeFontGlyphsForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask) { ImDrawList* draw_list = GetWindowDrawList(); const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); const float cell_size = baked->Size * 1; const float cell_spacing = GetStyle().ItemSpacing.y; for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) { // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) if (!(base & 8191) && font->IsGlyphRangeUnused(base, base + 8191)) { base += 8192 - 256; continue; } int count = 0; for (unsigned int n = 0; n < 256; n++) if (const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL) if (src_mask & (1 << glyph->SourceIdx)) count++; if (count <= 0) continue; if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) continue; // Draw a 16x16 grid of glyphs ImVec2 base_pos = GetCursorScreenPos(); for (unsigned int n = 0; n < 256; n++) { // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. 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 = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL; draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); if (!glyph || (src_mask & (1 << glyph->SourceIdx)) == 0) continue; font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip()) { DebugNodeFontGlyph(font, glyph); EndTooltip(); } } Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); TreePop(); } } void ImGui::DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph) { Text("Codepoint: U+%04X", glyph->Codepoint); Separator(); Text("Visible: %d", glyph->Visible); Text("AdvanceX: %.1f", glyph->AdvanceX); Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); if (glyph->PackId >= 0) { ImTextureRect* r = ImFontAtlasPackGetRect(font->OwnerAtlas, glyph->PackId); Text("PackId: 0x%X (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y); } Text("SourceIdx: %d", glyph->SourceIdx); } // [DEBUG] Display contents of ImGuiStorage void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label) { if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes())) return; for (const ImGuiStoragePair& p : storage->Data) { BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer. DebugLocateItemOnHover(p.key); } TreePop(); } // [DEBUG] Display contents of ImGuiTabBar void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) { // 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_COUNTOF(buf); const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab)); } p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } bool open = TreeNode(label, "%s", buf); if (!is_active) { PopStyleColor(); } if (is_active && IsItemHovered()) { ImDrawList* draw_list = GetForegroundDrawList(tab_bar->Window); draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255)); draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); } if (open) { for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; PushID(tab); if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2); if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine(); Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth); PopID(); } TreePop(); } } void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; SetNextItemOpen(true, ImGuiCond_Once); bool open = TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : "N/A"); if (IsItemHovered()) g.DebugMetricsConfig.HighlightViewportID = viewport->ID; if (open) { ImGuiWindowFlags flags = viewport->Flags; BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nFrameBufferScale: (%.2f,%.2f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->FramebufferScale.x, viewport->FramebufferScale.y, viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } } BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags, //(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", // Omitting because it is the standard (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", (flags & ImGuiViewportFlags_IsMinimized) ? " IsMinimized" : "", (flags & ImGuiViewportFlags_IsFocused) ? " IsFocused" : "", (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", (flags & ImGuiViewportFlags_NoTaskBarIcon) ? " NoTaskBarIcon" : "", (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoFocusOnClick) ? " NoFocusOnClick" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : "", (flags & ImGuiViewportFlags_TopMost) ? " TopMost" : "", (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : ""); for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists) DebugNodeDrawList(NULL, viewport, draw_list, "DrawList"); TreePop(); } } void ImGui::DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx) { BulletText("%s %d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)", label, idx, monitor->DpiScale * 100.0f, monitor->MainPos.x, monitor->MainPos.y, monitor->MainPos.x + monitor->MainSize.x, monitor->MainPos.y + monitor->MainSize.y, monitor->MainSize.x, monitor->MainSize.y, monitor->WorkPos.x, monitor->WorkPos.y, monitor->WorkPos.x + monitor->WorkSize.x, monitor->WorkPos.y + monitor->WorkSize.y, monitor->WorkSize.x, monitor->WorkSize.y); } void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) { if (window == NULL) { BulletText("%s: NULL", label); return; } ImGuiContext& g = *GImGui; const bool is_active = window->WasActive; ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None; if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*"); if (!is_active) { PopStyleColor(); } if (IsItemHovered() && is_active) GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!open) return; if (window->MemoryCompacted) TextDisabled("Note: some memory buffers have been compacted/freed."); if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()")) g.DebugBreakInWindow = window->ID; ImGuiWindowFlags flags = window->Flags; DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList"); BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y); 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" : ""); if (flags & ImGuiWindowFlags_ChildWindow) BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags, (window->ChildFlags & ImGuiChildFlags_Borders) ? "Borders " : "", (window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "", (window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "", (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : ""); BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId); BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) { ImRect r = window->NavRectRel[layer]; if (r.Min.x >= r.Max.x && r.Min.y >= r.Max.y) BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]); else BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); DebugLocateItemOnHover(window->NavLastIds[layer]); } const ImVec2* pr = window->NavPreferredScoringPosRel; for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) BulletText("NavPreferredScoringPosRel[%d] = (%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater. BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1); BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible); if (window->DockNode || window->DockNodeAsHost) DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode"); if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } if (window->RootWindowDockTree != window->RootWindow) { DebugNodeWindow(window->RootWindowDockTree, "RootWindowDockTree"); } if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); } if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) { for (ImGuiOldColumns& columns : window->ColumnsStorage) DebugNodeColumns(&columns); TreePop(); } DebugNodeStorage(&window->StateStorage, "Storage"); TreePop(); } void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings) { if (settings->WantDelete) BeginDisabled(); Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed); if (settings->WantDelete) EndDisabled(); } void ImGui::DebugNodeWindowsList(ImVector* windows, const char* label) { if (!TreeNode(label, "%s (%d)", label, windows->Size)) return; for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back { PushID((*windows)[i]); DebugNodeWindow((*windows)[i], "Window"); PopID(); } TreePop(); } // FIXME-OPT: This is technically suboptimal, but it is simpler this way. void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack) { for (int i = 0; i < windows_size; i++) { ImGuiWindow* window = windows[i]; if (window->ParentWindowInBeginStack != parent_in_begin_stack) continue; char buf[20]; ImFormatString(buf, IM_COUNTOF(buf), "[%04d] Window", window->BeginOrderWithinContext); //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name); DebugNodeWindow(window, buf); TreePush(buf); DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window); TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DEBUG LOG WINDOW //----------------------------------------------------------------------------- void ImGui::DebugLog(const char* fmt, ...) { va_list args; va_start(args, fmt); DebugLogV(fmt, args); va_end(args); } void ImGui::DebugLogV(const char* fmt, va_list args) { ImGuiContext& g = *GImGui; const int old_size = g.DebugLogBuf.size(); if (g.ContextName[0] != 0) g.DebugLogBuf.appendf("[%s] [%05d] ", g.ContextName, g.FrameCount); else g.DebugLogBuf.appendf("[%05d] ", g.FrameCount); g.DebugLogBuf.appendfv(fmt, args); g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size()); const char* str = g.DebugLogBuf.begin() + old_size; if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY) IMGUI_DEBUG_PRINTF("%s", str); #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToDebugger) { ::OutputDebugStringA("[imgui] "); ::OutputDebugStringA(str); } #endif #ifdef IMGUI_ENABLE_TEST_ENGINE // IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically const int new_size = g.DebugLogBuf.size(); const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n'); if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine) IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), str); #endif } // FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout. static void SameLineOrWrap(const ImVec2& size) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y); if (window->WorkRect.Contains(ImRect(pos, pos + size))) ImGui::SameLine(); } static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags) { ImGuiContext& g = *GImGui; ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight()); SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout. bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0); if (highlight_errors) ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f)); if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0) { g.DebugLogAutoDisableFrames = 2; g.DebugLogAutoDisableFlags |= flags; } if (highlight_errors) { ImGui::PopStyleColor(); ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors); } else { ImGui::SetItemTooltip("Hold Shift when clicking to enable for 2 frames only (useful for spammy log entries)"); } } void ImGui::ShowDebugLogWindow(bool* p_open) { ImGuiContext& g = *GImGui; if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0) SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver); if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; } ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting; CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags); SetItemTooltip("(except InputRouting which is spammy)"); ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError); ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId); ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper); ShowDebugLogFlag("Docking", ImGuiDebugLogFlags_EventDocking); ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus); ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO); ShowDebugLogFlag("Font", ImGuiDebugLogFlags_EventFont); ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav); ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup); ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); ShowDebugLogFlag("Viewport", ImGuiDebugLogFlags_EventViewport); ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting); if (SmallButton("Clear")) { g.DebugLogBuf.clear(); g.DebugLogIndex.clear(); g.DebugLogSkippedErrors = 0; } SameLine(); if (SmallButton("Copy")) SetClipboardText(g.DebugLogBuf.c_str()); SameLine(); if (SmallButton("Configure Outputs..")) OpenPopup("Outputs"); if (BeginPopup("Outputs")) { CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY); CheckboxFlags("OutputToDebugger", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToDebugger); #ifndef IMGUI_ENABLE_TEST_ENGINE BeginDisabled(); #endif CheckboxFlags("OutputToTestEngine", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine); #ifndef IMGUI_ENABLE_TEST_ENGINE EndDisabled(); #endif EndPopup(); } BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags; g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper; ImGuiListClipper clipper; clipper.Begin(g.DebugLogIndex.size()); while (clipper.Step()) for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no)); g.DebugLogFlags = backup_log_flags; if (GetScrollY() >= GetScrollMaxY()) SetScrollHereY(1.0f); EndChild(); End(); } // Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered. void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end) { TextUnformatted(line_begin, line_end); if (!IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) return; ImGuiContext& g = *GImGui; ImRect text_rect = g.LastItemData.Rect; for (const char* p = line_begin; p <= line_end - 10; p++) { ImGuiID id = 0; if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1 || ImCharIsXdigitA(p[10])) continue; ImVec2 p0 = CalcTextSize(line_begin, p); ImVec2 p1 = CalcTextSize(p, p + 10); g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y)); if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true)) DebugLocateItemOnHover(id); p += 10; } } //----------------------------------------------------------------------------- // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL) //----------------------------------------------------------------------------- // Draw a small cross at current CursorPos in current window's DrawList void ImGui::DebugDrawCursorPos(ImU32 col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImVec2 pos = window->DC.CursorPos; window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f); window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f); } // Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList void ImGui::DebugDrawLineExtents(ImU32 col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float curr_x = window->DC.CursorPos.x; float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y); float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y); window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f); window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f); window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f); } // Draw last item rect in ForegroundDrawList (so it is always visible) void ImGui::DebugDrawItemRect(ImU32 col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } // [DEBUG] Locate item position/rectangle given an ID. static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255); // Green void ImGui::DebugLocateItem(ImGuiID target_id) { ImGuiContext& g = *GImGui; g.DebugLocateId = target_id; g.DebugLocateFrames = 2; g.DebugBreakInLocateId = false; } // FIXME: Doesn't work over through a modal window, because they clear HoveredWindow. void ImGui::DebugLocateItemOnHover(ImGuiID target_id) { if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup)) return; ImGuiContext& g = *GImGui; DebugLocateItem(target_id); GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR); // Can't easily use a context menu here because it will mess with focus, active id etc. if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f) { DebugBreakButtonTooltip(false, "in ItemAdd()"); if (IsKeyChordPressed(g.DebugBreakKeyChord)) g.DebugBreakInLocateId = true; } } void ImGui::DebugLocateItemResolveWithLastItem() { ImGuiContext& g = *GImGui; // [DEBUG] Debug break requested by user if (g.DebugBreakInLocateId) IM_DEBUG_BREAK(); ImGuiLastItemData item_data = g.LastItemData; g.DebugLocateId = 0; ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow); ImRect r = item_data.Rect; r.Expand(3.0f); ImVec2 p1 = g.IO.MousePos; ImVec2 p2 = ImVec2((p1.x < r.Min.x) ? r.Min.x : (p1.x > r.Max.x) ? r.Max.x : p1.x, (p1.y < r.Min.y) ? r.Min.y : (p1.y > r.Max.y) ? r.Max.y : p1.y); draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR); draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR); } void ImGui::DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. void ImGui::UpdateDebugToolItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerBreakId = 0; if (!g.DebugItemPickerActive) return; const ImGuiID hovered_id = g.HoveredIdPreviousFrame; SetMouseCursor(ImGuiMouseCursor_Hand); if (IsKeyPressed(ImGuiKey_Escape)) g.DebugItemPickerActive = false; const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift); if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id) { g.DebugItemPickerBreakId = hovered_id; g.DebugItemPickerActive = false; } for (int mouse_button = 0; mouse_button < 3; mouse_button++) if (change_mapping && IsMouseClicked(mouse_button)) g.DebugItemPickerMouseButton = (ImU8)mouse_button; SetNextWindowBgAlpha(0.70f); if (!BeginTooltip()) return; Text("HoveredId: 0x%08X", hovered_id); Text("Press ESC to abort picking."); const char* mouse_button_names[] = { "Left", "Right", "Middle" }; if (change_mapping) Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button."); else TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]); EndTooltip(); } // Update queries. The steps are: -1: query Stack, >= 0: query each stack item // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time static ImGuiID DebugItemPathQuery_UpdateAndGetHookId(ImGuiDebugItemPathQuery* query, ImGuiID id) { // Update query. Clear hook when no active query if (query->MainID != id) { query->MainID = id; query->Step = -1; query->Complete = false; query->Results.resize(0); query->ResultsDescBuf.resize(0); } query->Active = false; if (id == 0) return 0; // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result) if (query->Step >= 0 && query->Step < query->Results.Size) if (query->Results[query->Step].QuerySuccess || query->Results[query->Step].QueryFrameCount > 2) query->Step++; // Update status and hook query->Complete = (query->Step == query->Results.Size); if (query->Step == -1) { query->Active = true; return id; } else if (query->Step >= 0 && query->Step < query->Results.Size) { query->Results[query->Step].QueryFrameCount++; query->Active = true; return query->Results[query->Step].ID; } return 0; } // [DEBUG] ID Stack Tool: update query. Called by NewFrame() void ImGui::UpdateDebugToolItemPathQuery() { ImGuiContext& g = *GImGui; ImGuiID id = 0; if (g.DebugIDStackTool.LastActiveFrame + 1 == g.FrameCount) id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId; g.DebugHookIdInfoId = DebugItemPathQuery_UpdateAndGetHookId(&g.DebugItemPathQuery, id); } // [DEBUG] ID Stack tool: hooks called by GetID() family functions void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end) { ImGuiContext& g = *GImGui; ImGuiDebugItemPathQuery* query = &g.DebugItemPathQuery; if (query->Active == false) { IM_ASSERT(id == 0); return; } ImGuiWindow* window = g.CurrentWindow; // Step -1: stack query // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget. if (query->Step == -1) { IM_ASSERT(query->Results.Size == 0); query->Step++; query->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); for (int n = 0; n < window->IDStack.Size + 1; n++) query->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; return; } // Step 0+: query for individual level IM_ASSERT(query->Step >= 0); if (query->Step != window->IDStack.Size) return; ImGuiStackLevelInfo* info = &query->Results[query->Step]; IM_ASSERT(info->ID == id && info->QueryFrameCount > 0); if (info->DescOffset == -1) { const char* result = NULL; const char* result_end = NULL; switch (data_type) { case ImGuiDataType_S32: ImFormatStringToTempBuffer(&result, &result_end, "%d", (int)(intptr_t)data_id); break; case ImGuiDataType_String: ImFormatStringToTempBuffer(&result, &result_end, "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id); break; case ImGuiDataType_Pointer: ImFormatStringToTempBuffer(&result, &result_end, "(void*)0x%p", data_id); break; case ImGuiDataType_ID: // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. ImFormatStringToTempBuffer(&result, &result_end, "0x%08X [override]", id); break; default: IM_ASSERT(0); } info->DescOffset = query->ResultsDescBuf.size(); query->ResultsDescBuf.append(result, result_end + 1); // Include zero terminator } info->QuerySuccess = true; if (info->DataType == -1) info->DataType = (ImS8)data_type; } static int DebugItemPathQuery_FormatLevelInfo(ImGuiDebugItemPathQuery* query, int n, bool format_for_ui, char* buf, size_t buf_size) { ImGuiStackLevelInfo* info = &query->Results[n]; ImGuiWindow* window = (info->DescOffset == -1 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL; if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", ImHashSkipUncontributingPrefix(window->Name)); if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", ImHashSkipUncontributingPrefix(&query->ResultsDescBuf.Buf[info->DescOffset])); if (query->Step < query->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. return (*buf = 0); #ifdef IMGUI_ENABLE_TEST_ENGINE if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo() return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", ImHashSkipUncontributingPrefix(label)); #endif return ImFormatString(buf, buf_size, "???"); } static const char* DebugItemPathQuery_GetResultAsPath(ImGuiDebugItemPathQuery* query, bool hex_encode_non_ascii_chars) { ImGuiTextBuffer* buf = &query->ResultPathBuf; buf->resize(0); for (int stack_n = 0; stack_n < query->Results.Size; stack_n++) { char level_desc[256]; DebugItemPathQuery_FormatLevelInfo(query, stack_n, false, level_desc, IM_COUNTOF(level_desc)); buf->append(stack_n == 0 ? "//" : "/"); for (const char* p = level_desc; *p != 0; ) { unsigned int c; const char* p_next = p + ImTextCharFromUtf8(&c, p, NULL); if (c == '/') buf->append("\\"); if (c < 256 || !hex_encode_non_ascii_chars) buf->append(p, p_next); else for (; p < p_next; p++) buf->appendf("\\x%02x", (unsigned char)*p); p = p_next; } } return buf->c_str(); } // ID Stack Tool: Display UI void ImGui::ShowIDStackToolWindow(bool* p_open) { ImGuiContext& g = *GImGui; if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0) SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver); if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; } ImGuiDebugItemPathQuery* query = &g.DebugItemPathQuery; ImGuiIDStackTool* tool = &g.DebugIDStackTool; tool->LastActiveFrame = g.FrameCount; const char* result_path = DebugItemPathQuery_GetResultAsPath(query, tool->OptHexEncodeNonAsciiChars); Text("0x%08X", query->MainID); SameLine(); MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details."); // Ctrl+C to copy path const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime; PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f); Checkbox("Hex-encode non-ASCII", &tool->OptHexEncodeNonAsciiChars); SameLine(); Checkbox("Ctrl+C: copy path", &tool->OptCopyToClipboardOnCtrlC); PopStyleVar(); SameLine(); TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*"); if (tool->OptCopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused)) { tool->CopyToClipboardLastTime = (float)g.Time; SetClipboardText(result_path); } Text("- Path \"%s\"", query->Complete ? result_path : ""); #ifdef IMGUI_ENABLE_TEST_ENGINE Text("- Label \"%s\"", query->MainID ? ImGuiTestEngine_FindItemDebugLabel(&g, query->MainID) : ""); #endif Separator(); // Display decorated stack if (query->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) { const float id_width = CalcTextSize("0xDDDDDDDD").x; TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width); TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch); TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width); TableHeadersRow(); for (int n = 0; n < query->Results.Size; n++) { ImGuiStackLevelInfo* info = &query->Results[n]; TableNextColumn(); Text("0x%08X", (n > 0) ? query->Results[n - 1].ID : 0); TableNextColumn(); DebugItemPathQuery_FormatLevelInfo(query, n, true, g.TempBuffer.Data, g.TempBuffer.Size); TextUnformatted(g.TempBuffer.Data); TableNextColumn(); Text("0x%08X", info->ID); if (n == query->Results.Size - 1) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header)); } EndTable(); } End(); } #else void ImGui::ShowMetricsWindow(bool*) {} void ImGui::ShowFontAtlas(ImFontAtlas*) {} void ImGui::DebugNodeColumns(ImGuiOldColumns*) {} void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {} void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {} void ImGui::DebugNodeFont(ImFont*) {} void ImGui::DebugNodeFontGlyphsForSrcMask(ImFont*, ImFontBaked*, int) {} void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {} void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {} void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {} void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} void ImGui::DebugNodeViewport(ImGuiViewportP*) {} void ImGui::ShowDebugLogWindow(bool*) {} void ImGui::ShowIDStackToolWindow(bool*) {} void ImGui::DebugStartItemPicker() {} void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} #endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS) // Demo helper function to select among loaded fonts. // Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one. void ImGui::ShowFontSelector(const char* label) { ImGuiIO& io = GetIO(); ImFont* font_current = GetFont(); if (BeginCombo(label, font_current->GetDebugName())) { for (ImFont* font : io.Fonts->Fonts) { PushID((void*)font); if (Selectable(font->GetDebugName(), font == font_current, ImGuiSelectableFlags_SelectOnNav)) io.FontDefault = font; if (font == font_current) SetItemDefaultFocus(); PopID(); } EndCombo(); } SameLine(); if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) MetricsHelpMarker( "- Load additional fonts with io.Fonts->AddFontXXX() functions.\n" "- Read FAQ and docs/FONTS.md for more details."); else MetricsHelpMarker( "- Load additional fonts with io.Fonts->AddFontXXX() functions.\n" "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" "- Read FAQ and docs/FONTS.md for more details.\n" "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); } #endif // #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS) //----------------------------------------------------------------------------- // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed. // Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github. #ifdef IMGUI_INCLUDE_IMGUI_USER_INL #include "imgui_user.inl" #endif //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui/source/imgui_demo.cpp ================================================ // dear imgui, v1.92.7 WIP // (demo code) // Help: // - Read FAQ at http://dearimgui.com/faq // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. // - Need help integrating Dear ImGui in your codebase? // - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started // - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. // Read top of imgui.cpp and imgui.h for many details, documentation, comments, links. // Get the latest version at https://github.com/ocornut/imgui // How to easily locate code? // - Use Tools->Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools // - Browse pthom's online imgui_explorer: web version the demo w/ source code browser: https://pthom.github.io/imgui_explorer // - Find a visible string and search for it in the code! //--------------------------------------------------- // PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT! //--------------------------------------------------- // Message to the person tempted to delete this file when integrating Dear ImGui into their codebase: // 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! // Also include Metrics! ItemPicker! DebugLog! and other debug features. // 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 a thorough 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 another situation, whenever you have Dear ImGui available you probably want this to be available for reference. // Thank you, // -Your beloved friend, imgui_demo.cpp (which you won't delete) //-------------------------------------------- // ABOUT THE MEANING OF THE 'static' KEYWORD: //-------------------------------------------- // In this demo code, we frequently use 'static' variables inside functions. // A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function. // Think of "static int n = 0;" as "global int n = 0;" ! // We do this IN THE DEMO because we want: // - to gather code and data in the same place. // - to make the demo source code faster to read, faster to change, smaller in size. // - it is also a convenient way of storing simple UI related information as long as your function // doesn't need to be reentrant or used in multiple threads. // This might be a pattern you will want to use in your code, but most of the data you would be working // with in a complex codebase is likely going to be stored outside your functions. //----------------------------------------- // ABOUT THE CODING STYLE OF OUR DEMO CODE //----------------------------------------- // The Demo code in this file is designed to be easy to copy-and-paste into your application! // Because of this: // - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace. // - We try to declare static variables in the local scope, as close as possible to the code using them. // - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API. // - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided // by imgui.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional // and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h. // Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp. // Navigating this file: // - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. // - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. // - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. // - You can search/grep for all sections listed in the index to find the section. /* Index of this file: // [SECTION] Forward Declarations // [SECTION] Helpers // [SECTION] Demo Window / ShowDemoWindow() // [SECTION] DemoWindowMenuBar() // [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos) // [SECTION] DemoWindowWidgetsBasic() // [SECTION] DemoWindowWidgetsBullets() // [SECTION] DemoWindowWidgetsCollapsingHeaders() // [SECTION] DemoWindowWidgetsComboBoxes() // [SECTION] DemoWindowWidgetsColorAndPickers() // [SECTION] DemoWindowWidgetsDataTypes() // [SECTION] DemoWindowWidgetsDisableBlocks() // [SECTION] DemoWindowWidgetsDragAndDrop() // [SECTION] DemoWindowWidgetsDragsAndSliders() // [SECTION] DemoWindowWidgetsFonts() // [SECTION] DemoWindowWidgetsImages() // [SECTION] DemoWindowWidgetsListBoxes() // [SECTION] DemoWindowWidgetsMultiComponents() // [SECTION] DemoWindowWidgetsPlotting() // [SECTION] DemoWindowWidgetsProgressBars() // [SECTION] DemoWindowWidgetsQueryingStatuses() // [SECTION] DemoWindowWidgetsSelectables() // [SECTION] DemoWindowWidgetsSelectionAndMultiSelect() // [SECTION] DemoWindowWidgetsTabs() // [SECTION] DemoWindowWidgetsText() // [SECTION] DemoWindowWidgetsTextFilter() // [SECTION] DemoWindowWidgetsTextInput() // [SECTION] DemoWindowWidgetsTooltips() // [SECTION] DemoWindowWidgetsTreeNodes() // [SECTION] DemoWindowWidgetsVerticalSliders() // [SECTION] DemoWindowWidgets() // [SECTION] DemoWindowLayout() // [SECTION] DemoWindowPopups() // [SECTION] DemoWindowTables() // [SECTION] DemoWindowInputs() // [SECTION] About Window / ShowAboutWindow() // [SECTION] Style Editor / ShowStyleEditor() // [SECTION] User Guide / ShowUserGuide() // [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: Fullscreen window / ShowExampleAppFullscreen() // [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles() // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() // [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace() // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() // [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser() */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #include "imgui.h" #ifndef IMGUI_DISABLE // System includes #include // toupper #include // INT_MIN, INT_MAX #include // sqrtf, powf, cosf, sinf, floorf, ceilf #include // vsnprintf, sscanf, printf #include // NULL, malloc, free, atoi #include // intptr_t #if !defined(_MSC_VER) || _MSC_VER >= 1800 #include // PRId64/PRIu64, not avail in some MinGW headers. #endif #ifdef __EMSCRIPTEN__ #include // __EMSCRIPTEN_MAJOR__ etc. #endif // 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: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning: 'xx' is deprecated: The POSIX name for this.. // for strdup used in demo code (so user can copy & paste the code) #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type #pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int' #pragma clang diagnostic ignored "-Wformat-security" // 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: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used. #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 #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. #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*' #pragma GCC diagnostic ignored "-Wformat-security" // warning: format string is not a string literal (potentially insecure) #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value #pragma GCC diagnostic ignored "-Wmisleading-indentation" // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub. #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers #endif // Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!) #ifdef _WIN32 #define IM_NEWLINE "\r\n" #else #define IM_NEWLINE "\n" #endif // Helpers #if defined(_MSC_VER) && !defined(snprintf) #define snprintf _snprintf #endif #if defined(_MSC_VER) && !defined(vsnprintf) #define vsnprintf _vsnprintf #endif // Format specifiers for 64-bit values (hasn't been decently standardized before VS2013) #if !defined(PRId64) && defined(_MSC_VER) #define PRId64 "I64d" #define PRIu64 "I64u" #elif !defined(PRId64) #define PRId64 "lld" #define PRIu64 "llu" #endif // Helpers macros // We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste, // but making an exception here as those are largely simplifying code... // In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo. #define IM_MIN(A, B) (((A) < (B)) ? (A) : (B)) #define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B)) #define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V)) // Enforce cdecl calling convention for functions called by the standard library, // in case compilation settings changed the default to e.g. __vectorcall #ifndef IMGUI_CDECL #ifdef _MSC_VER #define IMGUI_CDECL __cdecl #else #define IMGUI_CDECL #endif #endif //----------------------------------------------------------------------------- // [SECTION] Forward Declarations //----------------------------------------------------------------------------- #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Forward Declarations struct ImGuiDemoWindowData; static void ShowExampleAppMainMenuBar(); static void ShowExampleAppAssetsBrowser(bool* p_open); static void ShowExampleAppConsole(bool* p_open); static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleAppDockSpace(bool* p_open); static void ShowExampleAppDocuments(bool* p_open); static void ShowExampleAppLog(bool* p_open); static void ShowExampleAppLayout(bool* p_open); static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data); static void ShowExampleAppSimpleOverlay(bool* p_open); static void ShowExampleAppAutoResize(bool* p_open); static void ShowExampleAppConstrainedResize(bool* p_open); static void ShowExampleAppFullscreen(bool* p_open); static void ShowExampleAppLongText(bool* p_open); static void ShowExampleAppWindowTitles(bool* p_open); static void ShowExampleMenuFile(); // We split the contents of the big ShowDemoWindow() function into smaller functions // (because the link time of very large functions tends to grow non-linearly) static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data); static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data); static void DemoWindowLayout(); static void DemoWindowPopups(); static void DemoWindowTables(); static void DemoWindowColumns(); static void DemoWindowInputs(); // Helper tree functions used by Property Editor & Multi-Select demos struct ExampleTreeNode; static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent); static void ExampleTree_DestroyNode(ExampleTreeNode* node); //----------------------------------------------------------------------------- // [SECTION] Helpers //----------------------------------------------------------------------------- // Helper to display a little (?) mark which shows a tooltip when hovered. // In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md) static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } } static void ShowDockingDisabledMessage() { ImGuiIO& io = ImGui::GetIO(); ImGui::Text("ERROR: Docking is not enabled! See Demo > Configuration."); ImGui::Text("Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or "); ImGui::SameLine(0.0f, 0.0f); if (ImGui::SmallButton("click here")) io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; } // Helper to wire demo markers located in code to an interactive browser (e.g. https://pthom.github.io/imgui_explorer) #if IMGUI_VERSION_NUM >= 19263 namespace ImGui { extern IMGUI_API void DemoMarker(const char* file, int line, const char* section); } #define IMGUI_DEMO_MARKER(section) do { ImGui::DemoMarker("imgui_demo.cpp", __LINE__, section); } while (0) #endif // Sneakily forward declare functions which aren't worth putting in public API yet namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool is_open); } //----------------------------------------------------------------------------- // [SECTION] Demo Window / ShowDemoWindow() //----------------------------------------------------------------------------- // Data to be shared across different functions of the demo. struct ImGuiDemoWindowData { // Examples Apps (accessible from the "Examples" menu) bool ShowMainMenuBar = false; bool ShowAppAssetsBrowser = false; bool ShowAppConsole = false; bool ShowAppCustomRendering = false; bool ShowAppDocuments = false; bool ShowAppDockSpace = false; bool ShowAppLog = false; bool ShowAppLayout = false; bool ShowAppPropertyEditor = false; bool ShowAppSimpleOverlay = false; bool ShowAppAutoResize = false; bool ShowAppConstrainedResize = false; bool ShowAppFullscreen = false; bool ShowAppLongText = false; bool ShowAppWindowTitles = false; // Dear ImGui Tools (accessible from the "Tools" menu) bool ShowMetrics = false; bool ShowDebugLog = false; bool ShowIDStackTool = false; bool ShowStyleEditor = false; bool ShowAbout = false; // Other data bool DisableSections = false; ExampleTreeNode* DemoTree = NULL; ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(DemoTree); } }; // 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) { // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup // Most functions would normally just assert/crash if the context is missing. IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!"); // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. IMGUI_CHECKVERSION(); // Stored data static ImGuiDemoWindowData demo_data; // Examples Apps (accessible from the "Examples" menu) if (demo_data.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); } if (demo_data.ShowAppDockSpace) { ShowExampleAppDockSpace(&demo_data.ShowAppDockSpace); } // Important: Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function) if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); } // ...process the Document app next, as it may also use a DockSpace() if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); } if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); } if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); } if (demo_data.ShowAppLog) { ShowExampleAppLog(&demo_data.ShowAppLog); } if (demo_data.ShowAppLayout) { ShowExampleAppLayout(&demo_data.ShowAppLayout); } if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); } if (demo_data.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(&demo_data.ShowAppSimpleOverlay); } if (demo_data.ShowAppAutoResize) { ShowExampleAppAutoResize(&demo_data.ShowAppAutoResize); } if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo_data.ShowAppConstrainedResize); } if (demo_data.ShowAppFullscreen) { ShowExampleAppFullscreen(&demo_data.ShowAppFullscreen); } if (demo_data.ShowAppLongText) { ShowExampleAppLongText(&demo_data.ShowAppLongText); } if (demo_data.ShowAppWindowTitles) { ShowExampleAppWindowTitles(&demo_data.ShowAppWindowTitles); } // Dear ImGui Tools (accessible from the "Tools" menu) if (demo_data.ShowMetrics) { ImGui::ShowMetricsWindow(&demo_data.ShowMetrics); } if (demo_data.ShowDebugLog) { ImGui::ShowDebugLogWindow(&demo_data.ShowDebugLog); } if (demo_data.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(&demo_data.ShowIDStackTool); } if (demo_data.ShowAbout) { ImGui::ShowAboutWindow(&demo_data.ShowAbout); } if (demo_data.ShowStyleEditor) { ImGui::Begin("Dear ImGui Style Editor", &demo_data.ShowStyleEditor); ImGui::ShowStyleEditor(); ImGui::End(); } // Demonstrate the various window flags. Typically you would just use the default! static bool no_titlebar = false; static bool no_scrollbar = false; static bool no_menu = false; static bool no_move = false; static bool no_resize = false; 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; static bool no_docking = false; static bool unsaved_document = 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_background) window_flags |= ImGuiWindowFlags_NoBackground; if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking; if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument; 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. // We only do it to make the demo applications a little more welcoming, but typically this isn't required. const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); // Main body of the Demo window starts here. if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags)) { // Early out if the window is collapsed, as an optimization. ImGui::End(); return; } // Most framed widgets share a common width settings. Remaining width is used for the label. // The width of the frame may be changed with PushItemWidth() or SetNextItemWidth(). // - Positive value for absolute size, negative value for right-alignment. // - The default value is about GetWindowWidth() * 0.65f. // - See 'Demo->Layout->Widgets Width' for details. // Here we change the frame width based on how much width we want to give to the label. const float label_width_base = ImGui::GetFontSize() * 12; // Some amount of width for label, based on font size. const float label_width_max = ImGui::GetContentRegionAvail().x * 0.40f; // ...but always leave some room for framed widgets. const float label_width = IM_MIN(label_width_base, label_width_max); ImGui::PushItemWidth(-label_width); // Right-align: framed items will leave 'label_width' available for the label. //ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for framed widgets, leaving 60% width for labels. //ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for labels, leaving 60% width for framed widgets. //ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // e.g. Use XXX width for labels, leaving the rest for framed widgets. // Menu Bar DemoWindowMenuBar(&demo_data); ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); ImGui::Spacing(); if (ImGui::CollapsingHeader("Help")) { IMGUI_DEMO_MARKER("Help"); ImGui::SeparatorText("ABOUT THIS DEMO:"); ImGui::BulletText("Sections below are demonstrating many aspects of the library."); ImGui::BulletText("The \"Examples\" menu above leads to more demo contents."); ImGui::BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n" "and Metrics/Debugger (general purpose Dear ImGui debugging tool)."); ImGui::BulletText("Web demo (w/ source code browser): "); ImGui::SameLine(0, 0); ImGui::TextLinkOpenURL("https://pthom.github.io/imgui_explorer"); ImGui::SeparatorText("PROGRAMMER GUIDE:"); ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!"); ImGui::BulletText("See comments in imgui.cpp."); ImGui::BulletText("See example applications in the examples/ folder."); ImGui::BulletText("Read the FAQ at "); ImGui::SameLine(0, 0); ImGui::TextLinkOpenURL("https://www.dearimgui.com/faq/"); ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls."); ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls."); ImGui::SeparatorText("USER GUIDE:"); ImGui::ShowUserGuide(); } if (ImGui::CollapsingHeader("Configuration")) { ImGuiIO& io = ImGui::GetIO(); if (ImGui::TreeNode("Configuration##2")) { IMGUI_DEMO_MARKER("Configuration"); ImGui::SeparatorText("General"); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); ImGui::SameLine(); HelpMarker("Enable keyboard controls."); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); ImGui::SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", &io.ConfigFlags, ImGuiConfigFlags_NoMouse); ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable mouse inputs and interactions."); // The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it: if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) { if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f) { ImGui::SameLine(); ImGui::Text("<>"); } // Prevent both being checked if (ImGui::IsKeyPressed(ImGuiKey_Space) || (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)) io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); ImGui::CheckboxFlags("io.ConfigFlags: NoKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NoKeyboard); ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable keyboard inputs and interactions."); ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue); ImGui::SameLine(); HelpMarker("Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates."); ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. 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::SeparatorText("Keyboard/Gamepad Navigation"); ImGui::Checkbox("io.ConfigNavSwapGamepadButtons", &io.ConfigNavSwapGamepadButtons); ImGui::Checkbox("io.ConfigNavMoveSetMousePos", &io.ConfigNavMoveSetMousePos); ImGui::SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult"); ImGui::Checkbox("io.ConfigNavCaptureKeyboard", &io.ConfigNavCaptureKeyboard); ImGui::Checkbox("io.ConfigNavEscapeClearFocusItem", &io.ConfigNavEscapeClearFocusItem); ImGui::SameLine(); HelpMarker("Pressing Escape clears focused item."); ImGui::Checkbox("io.ConfigNavEscapeClearFocusWindow", &io.ConfigNavEscapeClearFocusWindow); ImGui::SameLine(); HelpMarker("Pressing Escape clears focused window."); ImGui::Checkbox("io.ConfigNavCursorVisibleAuto", &io.ConfigNavCursorVisibleAuto); ImGui::SameLine(); HelpMarker("Using directional navigation key makes the cursor visible. Mouse click hides the cursor."); ImGui::Checkbox("io.ConfigNavCursorVisibleAlways", &io.ConfigNavCursorVisibleAlways); ImGui::SameLine(); HelpMarker("Navigation cursor is always visible."); ImGui::SeparatorText("Docking"); ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable); ImGui::SameLine(); if (io.ConfigDockingWithShift) HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to enable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); else HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGui::Indent(); ImGui::Checkbox("io.ConfigDockingNoSplit", &io.ConfigDockingNoSplit); ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars."); ImGui::Checkbox("io.ConfigDockingNoDockingOver", &io.ConfigDockingNoDockingOver); ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window merging into a same tab-bar, so docking is limited to splitting windows."); ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); ImGui::SameLine(); HelpMarker("Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)"); ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar); ImGui::SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows."); ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload); ImGui::SameLine(); HelpMarker("Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge."); ImGui::Unindent(); } ImGui::SeparatorText("Multi-viewports"); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", &io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); ImGui::SameLine(); HelpMarker("[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details."); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::Indent(); ImGui::Checkbox("io.ConfigViewportsNoAutoMerge", &io.ConfigViewportsNoAutoMerge); ImGui::SameLine(); HelpMarker("Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it."); ImGui::Checkbox("io.ConfigViewportsNoTaskBarIcon", &io.ConfigViewportsNoTaskBarIcon); ImGui::SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)"); ImGui::Checkbox("io.ConfigViewportsNoDecoration", &io.ConfigViewportsNoDecoration); ImGui::SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)"); ImGui::Checkbox("io.ConfigViewportsNoDefaultParent", &io.ConfigViewportsNoDefaultParent); ImGui::SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)"); ImGui::Checkbox("io.ConfigViewportsPlatformFocusSetsImGuiFocus", &io.ConfigViewportsPlatformFocusSetsImGuiFocus); ImGui::SameLine(); HelpMarker("When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change)."); ImGui::Unindent(); } //ImGui::SeparatorText("DPI/Scaling"); //ImGui::Checkbox("io.ConfigDpiScaleFonts", &io.ConfigDpiScaleFonts); //ImGui::SameLine(); HelpMarker("Experimental: Automatically update style.FontScaleDpi when Monitor DPI changes. This will scale fonts but NOT style sizes/padding for now."); //ImGui::Checkbox("io.ConfigDpiScaleViewports", &io.ConfigDpiScaleViewports); //ImGui::SameLine(); HelpMarker("Experimental: Scale Dear ImGui and Platform Windows when Monitor DPI changes."); ImGui::SeparatorText("Windows"); ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback."); ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); ImGui::Checkbox("io.ConfigWindowsCopyContentsWithCtrlC", &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL] ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* Ctrl+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order."); ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage); ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location."); ImGui::SeparatorText("Widgets"); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)."); ImGui::Checkbox("io.ConfigInputTextEnterKeepActive", &io.ConfigInputTextEnterKeepActive); ImGui::SameLine(); HelpMarker("Pressing Enter will reactivate item and select all text (single-line only)."); ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText); ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving)."); ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors); ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors."); ImGui::Text("Also see Style->Rendering for rendering options."); // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling ImGui::SeparatorText("Error Handling"); ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery); ImGui::SameLine(); HelpMarker( "Options to configure how we handle recoverable errors.\n" "- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n" "- You not are not supposed to rely on it in the course of a normal application run.\n" "- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n" "- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call! " "Otherwise it would severely hinder your ability to catch and correct mistakes!"); ImGui::Checkbox("io.ConfigErrorRecoveryEnableAssert", &io.ConfigErrorRecoveryEnableAssert); ImGui::Checkbox("io.ConfigErrorRecoveryEnableDebugLog", &io.ConfigErrorRecoveryEnableDebugLog); ImGui::Checkbox("io.ConfigErrorRecoveryEnableTooltip", &io.ConfigErrorRecoveryEnableTooltip); if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip) io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true; // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools ImGui::SeparatorText("Debug"); ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent); ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application."); ImGui::Checkbox("io.ConfigDebugHighlightIdConflicts", &io.ConfigDebugHighlightIdConflicts); ImGui::SameLine(); HelpMarker("Highlight and show an error message when multiple items have conflicting identifiers."); ImGui::BeginDisabled(); ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); ImGui::EndDisabled(); ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover."); ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop); ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running."); ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss); ImGui::SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data."); ImGui::Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); ImGui::SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)."); ImGui::TreePop(); ImGui::Spacing(); } if (ImGui::TreeNode("Backend Flags")) { IMGUI_DEMO_MARKER("Configuration/Backend Flags"); HelpMarker( "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n" "Here we expose them as read-only fields to avoid breaking interactions with your backend."); // Make a local copy to avoid modifying actual backend flags. // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright? ImGui::BeginDisabled(); ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &io.BackendFlags, ImGuiBackendFlags_HasGamepad); ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos); ImGui::CheckboxFlags("io.BackendFlags: PlatformHasViewports", &io.BackendFlags, ImGuiBackendFlags_PlatformHasViewports); ImGui::CheckboxFlags("io.BackendFlags: HasMouseHoveredViewport",&io.BackendFlags, ImGuiBackendFlags_HasMouseHoveredViewport); ImGui::CheckboxFlags("io.BackendFlags: HasParentViewport", &io.BackendFlags, ImGuiBackendFlags_HasParentViewport); ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset); ImGui::CheckboxFlags("io.BackendFlags: RendererHasTextures", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures); ImGui::CheckboxFlags("io.BackendFlags: RendererHasViewports", &io.BackendFlags, ImGuiBackendFlags_RendererHasViewports); ImGui::EndDisabled(); ImGui::TreePop(); ImGui::Spacing(); } if (ImGui::TreeNode("Style, Fonts")) { IMGUI_DEMO_MARKER("Configuration/Style, Fonts"); ImGui::Checkbox("Style Editor", &demo_data.ShowStyleEditor); ImGui::SameLine(); HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); ImGui::TreePop(); ImGui::Spacing(); } if (ImGui::TreeNode("Capture/Logging")) { IMGUI_DEMO_MARKER("Configuration/Capture, Logging"); HelpMarker( "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.\n" "Try opening any of the contents below in this window and then click one of the \"Log To\" button."); ImGui::LogButtons(); HelpMarker("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("Window options")) { IMGUI_DEMO_MARKER("Window options"); if (ImGui::BeginTable("split", 3)) { ImGui::TableNextColumn(); ImGui::Checkbox("No titlebar", &no_titlebar); ImGui::TableNextColumn(); ImGui::Checkbox("No scrollbar", &no_scrollbar); ImGui::TableNextColumn(); ImGui::Checkbox("No menu", &no_menu); ImGui::TableNextColumn(); ImGui::Checkbox("No move", &no_move); ImGui::TableNextColumn(); ImGui::Checkbox("No resize", &no_resize); ImGui::TableNextColumn(); ImGui::Checkbox("No collapse", &no_collapse); ImGui::TableNextColumn(); ImGui::Checkbox("No close", &no_close); ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav); ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background); ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front); ImGui::TableNextColumn(); ImGui::Checkbox("No docking", &no_docking); ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document); ImGui::EndTable(); } } // All demo contents DemoWindowWidgets(&demo_data); DemoWindowLayout(); DemoWindowPopups(); DemoWindowTables(); DemoWindowInputs(); // End of ShowDemoWindow() ImGui::PopItemWidth(); ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] DemoWindowMenuBar() //----------------------------------------------------------------------------- static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data) { if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Menu")) { IMGUI_DEMO_MARKER("Menu/File"); ShowExampleMenuFile(); ImGui::EndMenu(); } if (ImGui::BeginMenu("Examples")) { IMGUI_DEMO_MARKER("Menu/Examples"); ImGui::MenuItem("Main menu bar", NULL, &demo_data->ShowMainMenuBar); ImGui::SeparatorText("Mini apps"); ImGui::MenuItem("Assets Browser", NULL, &demo_data->ShowAppAssetsBrowser); ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole); ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering); ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments); ImGui::MenuItem("Dockspace", NULL, &demo_data->ShowAppDockSpace); ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog); ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor); ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout); ImGui::MenuItem("Simple overlay", NULL, &demo_data->ShowAppSimpleOverlay); ImGui::SeparatorText("Concepts"); ImGui::MenuItem("Auto-resizing window", NULL, &demo_data->ShowAppAutoResize); ImGui::MenuItem("Constrained-resizing window", NULL, &demo_data->ShowAppConstrainedResize); ImGui::MenuItem("Fullscreen window", NULL, &demo_data->ShowAppFullscreen); ImGui::MenuItem("Long text display", NULL, &demo_data->ShowAppLongText); ImGui::MenuItem("Manipulating window titles", NULL, &demo_data->ShowAppWindowTitles); ImGui::EndMenu(); } //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! if (ImGui::BeginMenu("Tools")) { IMGUI_DEMO_MARKER("Menu/Tools"); ImGuiIO& io = ImGui::GetIO(); #ifndef IMGUI_DISABLE_DEBUG_TOOLS const bool has_debug_tools = true; #else const bool has_debug_tools = false; #endif ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools); if (ImGui::BeginMenu("Debug Options")) { ImGui::BeginDisabled(!has_debug_tools); ImGui::Checkbox("Highlight ID Conflicts", &io.ConfigDebugHighlightIdConflicts); ImGui::EndDisabled(); ImGui::Checkbox("Assert on error recovery", &io.ConfigErrorRecoveryEnableAssert); ImGui::TextDisabled("(see Demo->Configuration for details & more)"); ImGui::EndMenu(); } ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools); ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools); bool is_debugger_present = io.ConfigDebugIsDebuggerPresent; if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools))// && is_debugger_present)) ImGui::DebugStartItemPicker(); if (!is_debugger_present) ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable some extra features to avoid casual users crashing the application."); ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor); ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout); ImGui::EndMenu(); } ImGui::EndMenuBar(); } } //----------------------------------------------------------------------------- // [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos) //----------------------------------------------------------------------------- // Simple representation for a tree // (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.) struct ExampleTreeNode { // Tree structure char Name[28] = ""; int UID = 0; ExampleTreeNode* Parent = NULL; ImVector Childs; int IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily // Leaf Data bool HasData = false; // All leaves have data bool DataMyBool = true; int DataMyInt = 128; ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f); }; // Simple representation of struct metadata/serialization data. // (this is a minimal version of what a typical advanced application may provide) struct ExampleMemberInfo { const char* Name; // Member name ImGuiDataType DataType; // Member type int DataCount; // Member count (1 when scalar) int Offset; // Offset inside parent structure }; // Metadata description of ExampleTreeNode struct. static const ExampleMemberInfo ExampleTreeNodeMemberInfos[] { { "MyName", ImGuiDataType_String, 1, offsetof(ExampleTreeNode, Name) }, { "MyBool", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataMyBool) }, { "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataMyInt) }, { "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataMyVec2) }, }; static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent) { ExampleTreeNode* node = IM_NEW(ExampleTreeNode); snprintf(node->Name, IM_COUNTOF(node->Name), "%s", name); node->UID = uid; node->Parent = parent; node->IndexInParent = parent ? parent->Childs.Size : 0; if (parent) parent->Childs.push_back(node); return node; } static void ExampleTree_DestroyNode(ExampleTreeNode* node) { for (ExampleTreeNode* child_node : node->Childs) ExampleTree_DestroyNode(child_node); IM_DELETE(node); } // Create example tree data // (warning: this can allocates MANY MANY more times than other code in all of Dear ImGui + demo combined) // (a real application managing one million nodes would likely store its tree data differently) static ExampleTreeNode* ExampleTree_CreateDemoTree() { // 20 root nodes -> 211 total nodes, ~261 allocs. // 1000 root nodes -> ~11K total nodes, ~14K allocs. // 10000 root nodes -> ~123K total nodes, ~154K allocs. // 100000 root nodes -> ~1338K total nodes, ~1666K allocs. const int ROOT_ITEMS_COUNT = 20; static const char* category_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" }; const int category_count = IM_COUNTOF(category_names); const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name); char name_buf[NAME_MAX_LEN]; int uid = 0; ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); for (int idx_L0 = 0; idx_L0 < ROOT_ITEMS_COUNT; idx_L0++) { snprintf(name_buf, IM_COUNTOF(name_buf), "%s %d", category_names[idx_L0 / (ROOT_ITEMS_COUNT / category_count)], idx_L0 % (ROOT_ITEMS_COUNT / category_count)); ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); const int number_of_childs = (int)strlen(node_L1->Name); for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) { snprintf(name_buf, IM_COUNTOF(name_buf), "Child %d", idx_L1); ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); node_L2->HasData = true; if (idx_L1 == 0) { snprintf(name_buf, IM_COUNTOF(name_buf), "Sub-child %d", 0); ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); node_L3->HasData = true; } } } return node_L0; } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsBasic() //----------------------------------------------------------------------------- static void DemoWindowWidgetsBasic() { if (ImGui::TreeNode("Basic")) { IMGUI_DEMO_MARKER("Widgets/Basic"); ImGui::SeparatorText("General"); IMGUI_DEMO_MARKER("Widgets/Basic/Button"); static int clicked = 0; if (ImGui::Button("Button")) clicked++; if (clicked & 1) { ImGui::SameLine(); ImGui::Text("Thanks for clicking me!"); } IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox"); static bool check = true; ImGui::Checkbox("checkbox", &check); IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton"); 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); ImGui::AlignTextToFramePadding(); ImGui::TextLinkOpenURL("Hyperlink", "https://github.com/ocornut/imgui/wiki/Error-Handling"); // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)"); 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(); } // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!) // See 'Demo->Layout->Text Baseline Alignment' for details. ImGui::AlignTextToFramePadding(); ImGui::Text("Hold to repeat:"); ImGui::SameLine(); // Arrow buttons with Repeater IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)"); static int counter = 0; float spacing = ImGui::GetStyle().ItemInnerSpacing.x; ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; } ImGui::SameLine(0.0f, spacing); if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; } ImGui::PopItemFlag(); ImGui::SameLine(); ImGui::Text("%d", counter); ImGui::Button("Tooltip"); ImGui::SetItemTooltip("I am a tooltip"); ImGui::LabelText("label", "Value"); ImGui::SeparatorText("Inputs"); { // If you want to use InputText() with std::string or any custom dynamic string type: // - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize. IMGUI_DEMO_MARKER("Widgets/Basic/InputText"); static char str0[128] = "Hello, world!"; ImGui::InputText("input text", str0, IM_COUNTOF(str0)); ImGui::SameLine(); HelpMarker( "USER:\n" "Hold 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 for clipboard.\n" "Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.\n" "Escape to revert.\n\n" "PROGRAMMER:\n" "You 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_COUNTOF(str1)); IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); static int i0 = 123; ImGui::InputInt("input int", &i0); static float f0 = 0.001f; 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(); HelpMarker( "You can input value using the scientific notation,\n" " e.g. \"1e+8\" becomes \"100000000\"."); static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; ImGui::InputFloat3("input float3", vec4a); } ImGui::SeparatorText("Drags"); { IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat"); static int i1 = 50, i2 = 42, i3 = 128; ImGui::DragInt("drag int", &i1, 1); ImGui::SameLine(); HelpMarker( "Click and drag to edit value.\n" "Hold Shift/Alt for faster/slower edit.\n" "Double-Click or Ctrl+Click to input value."); ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp); ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuiSliderFlags_WrapAround); 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::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround); } ImGui::SeparatorText("Sliders"); { IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat"); static int i1 = 0; ImGui::SliderInt("slider int", &i1, -1, 3); 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"); ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic); IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle"); static float angle = 0.0f; ImGui::SliderAngle("slider angle", &angle); // Using the format string to display a name instead of an integer. // Here we completely omit '%d' from the format string, so it'll only display a name. // This technique can also be used with DragInt(). IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)"); enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT }; static int elem = Element_Fire; const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" }; const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown"; ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable Ctrl+Click here. ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer."); } ImGui::SeparatorText("Selectors/Pickers"); { IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4"); 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(); HelpMarker( "Click on the color square to open a color picker.\n" "Click and hold to use drag and drop.\n" "Right-Click on the color square to show options.\n" "Ctrl+Click on individual component to input value.\n"); ImGui::ColorEdit4("color 2", col2); } { // Using the _simplified_ one-liner Combo() api here // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api. IMGUI_DEMO_MARKER("Widgets/Basic/Combo"); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; static int item_current = 0; ImGui::Combo("combo", &item_current, items, IM_COUNTOF(items)); ImGui::SameLine(); HelpMarker( "Using the simplified one-liner Combo API here.\n" "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API."); } { // Using the _simplified_ one-liner ListBox() api here // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api. IMGUI_DEMO_MARKER("Widgets/Basic/ListBox"); const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; static int item_current = 1; ImGui::ListBox("listbox", &item_current, items, IM_COUNTOF(items), 4); ImGui::SameLine(); HelpMarker( "Using the simplified one-liner ListBox API here.\n" "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API."); } // Testing ImGuiOnceUponAFrame helper. //static ImGuiOnceUponAFrame once; //for (int i = 0; i < 5; i++) // if (once) // ImGui::Text("This will be displayed only once."); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsBullets() //----------------------------------------------------------------------------- static void DemoWindowWidgetsBullets() { if (ImGui::TreeNode("Bullets")) { IMGUI_DEMO_MARKER("Widgets/Bullets"); ImGui::BulletText("Bullet point 1"); ImGui::BulletText("Bullet point 2\nOn multiple lines"); if (ImGui::TreeNode("Tree node")) { ImGui::BulletText("Another bullet point"); ImGui::TreePop(); } ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); ImGui::Bullet(); ImGui::SmallButton("Button"); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsCollapsingHeaders() //----------------------------------------------------------------------------- static void DemoWindowWidgetsCollapsingHeaders() { if (ImGui::TreeNode("Collapsing Headers")) { IMGUI_DEMO_MARKER("Widgets/Collapsing Headers"); static bool closable_group = true; ImGui::Checkbox("Show 2nd header", &closable_group); if (ImGui::CollapsingHeader("Header", ImGuiTreeNodeFlags_None)) { ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); for (int i = 0; i < 5; i++) ImGui::Text("Some content %d", i); } if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) { ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); for (int i = 0; i < 5; i++) ImGui::Text("More content %d", i); } /* if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet)) ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); */ ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsColorAndPickers() //----------------------------------------------------------------------------- static void DemoWindowWidgetsColorAndPickers() { if (ImGui::TreeNode("Color/Picker Widgets")) { IMGUI_DEMO_MARKER("Widgets/Color"); static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f); static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None; ImGui::SeparatorText("Options"); ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &base_flags, ImGuiColorEditFlags_NoAlpha); ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", &base_flags, ImGuiColorEditFlags_AlphaOpaque); ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", &base_flags, ImGuiColorEditFlags_AlphaNoBg); ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf); ImGui::CheckboxFlags("ImGuiColorEditFlags_NoOptions", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop); ImGui::CheckboxFlags("ImGuiColorEditFlags_NoColorMarkers", &base_flags, ImGuiColorEditFlags_NoColorMarkers); ImGui::CheckboxFlags("ImGuiColorEditFlags_HDR", &base_flags, ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); ImGui::SeparatorText("Inline color editor"); ImGui::Text("Color widget:"); ImGui::SameLine(); HelpMarker( "Click on the color square to open a color picker.\n" "Ctrl+Click on individual component to input value.\n"); ImGui::ColorEdit3("MyColor##1", (float*)&color, base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); ImGui::Text("Color widget HSV with Alpha:"); ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)"); ImGui::Text("Color widget with Float Display:"); ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)"); ImGui::Text("Color button with Picker:"); ImGui::SameLine(); HelpMarker( "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" "With 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 | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)"); ImGui::Text("Color button with Custom Picker Popup:"); // Generate a 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_COUNTOF(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_init = false; } static ImVec4 backup_color; bool open_popup = ImGui::ColorButton("MyColor##3b", color, base_flags); ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x); open_popup |= ImGui::Button("Palette"); if (open_popup) { ImGui::OpenPopup("mypicker"); backup_color = color; } if (ImGui::BeginPopup("mypicker")) { ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); ImGui::Separator(); ImGui::ColorPicker4("##picker", (float*)&color, base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); ImGui::SameLine(); ImGui::BeginGroup(); // Lock X position 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_COUNTOF(saved_palette); n++) { ImGui::PushID(n); if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip; if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags, 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 specifying the ImGuiColorEditFlags_NoDragDrop flag. if (ImGui::BeginDragDropTarget()) { 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(); } ImGui::PopID(); } ImGui::EndGroup(); ImGui::EndPopup(); } IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)"); ImGui::Text("Color button only:"); static bool no_border = false; ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border); ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker"); ImGui::SeparatorText("Color picker"); static bool ref_color = false; static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f); static int picker_mode = 0; static int display_mode = 0; static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar; ImGui::PushID("Color picker"); ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &color_picker_flags, ImGuiColorEditFlags_NoAlpha); ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaBar", &color_picker_flags, ImGuiColorEditFlags_AlphaBar); ImGui::CheckboxFlags("ImGuiColorEditFlags_NoSidePreview", &color_picker_flags, ImGuiColorEditFlags_NoSidePreview); if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview) { ImGui::SameLine(); ImGui::Checkbox("With Ref Color", &ref_color); if (ref_color) { ImGui::SameLine(); ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | base_flags); } } ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0"); ImGui::SameLine(); HelpMarker("When not specified explicitly, user can right-click the picker to change mode."); ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\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 on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex " "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); ImGuiColorEditFlags flags = base_flags | color_picker_flags; 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("Set defaults in code:"); ImGui::SameLine(); HelpMarker( "SetColorEditOptions() is designed to allow you to set boot-time default.\n" "We 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_DisplayHSV | ImGuiColorEditFlags_PickerHueBar); if (ImGui::Button("Default: Float + HDR + Hue Wheel")) ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); // Always display a small version of both types of pickers // (that's in order to make it more visible in the demo to people who are skimming quickly through it) ImGui::Text("Both types:"); float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f; ImGui::SetNextItemWidth(w); ImGui::ColorPicker3("##MyColor##5", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha); ImGui::SameLine(); ImGui::SetNextItemWidth(w); ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha); ImGui::PopID(); // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0) static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV! 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 RGB##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); ImGui::DragFloat4("Raw HSV values", (float*)&color_hsv, 0.01f, 0.0f, 1.0f); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsComboBoxes() //----------------------------------------------------------------------------- static void DemoWindowWidgetsComboBoxes() { if (ImGui::TreeNode("Combo")) { IMGUI_DEMO_MARKER("Widgets/Combo"); // Combo Boxes are also called "Dropdown" in other systems // Expose flags as checkbox for the demo static ImGuiComboFlags flags = 0; ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft); ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton)) flags &= ~ImGuiComboFlags_NoPreview; // Clear incompatible flags if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview)) flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags if (ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview)) flags &= ~ImGuiComboFlags_NoPreview; // Override default popup height if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightSmall", &flags, ImGuiComboFlags_HeightSmall)) flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall); if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightRegular", &flags, ImGuiComboFlags_HeightRegular)) flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular); if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightLargest", &flags, ImGuiComboFlags_HeightLargest)) flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest); // Using the generic BeginCombo() API, you have full control over how to display the combo contents. // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively // stored in the object itself, etc.) const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; static int item_selected_idx = 0; // Here we store our selection data as an index. // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[]) const char* combo_preview_value = items[item_selected_idx]; if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) { for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (ImGui::Selectable(items[n], is_selected)) item_selected_idx = n; // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) if (is_selected) ImGui::SetItemDefaultFocus(); } ImGui::EndCombo(); } // Show case embedding a filter using a simple trick: displaying the filter inside combo contents. // See https://github.com/ocornut/imgui/issues/718 for advanced/esoteric alternatives. if (ImGui::BeginCombo("combo 2 (w/ filter)", combo_preview_value, flags)) { static ImGuiTextFilter filter; if (ImGui::IsWindowAppearing()) { ImGui::SetKeyboardFocusHere(); filter.Clear(); } ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); filter.Draw("##Filter", -FLT_MIN); for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (filter.PassFilter(items[n])) if (ImGui::Selectable(items[n], is_selected)) item_selected_idx = n; } ImGui::EndCombo(); } ImGui::Spacing(); ImGui::SeparatorText("One-liner variants"); HelpMarker("The Combo() function is not greatly useful apart from cases were you want to embed all options in a single strings.\nFlags above don't apply to this section."); // Simplified one-liner Combo() API, using values packed in a single constant string // This is a convenience for when the selection set is small and known at compile-time. static int item_current_2 = 0; ImGui::Combo("combo 3 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); // Simplified one-liner Combo() using an array of const char* // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control. static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview ImGui::Combo("combo 4 (array)", &item_current_3, items, IM_COUNTOF(items)); // Simplified one-liner Combo() using an accessor function static int item_current_4 = 0; ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_COUNTOF(items)); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsDataTypes() //----------------------------------------------------------------------------- static void DemoWindowWidgetsDataTypes() { if (ImGui::TreeNode("Data Types")) { IMGUI_DEMO_MARKER("Widgets/Data Types"); // DragScalar/InputScalar/SliderScalar functions allow various data types // - signed/unsigned // - 8/16/32/64-bits // - integer/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 pointer. // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each type. // 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); // } // Setup limits (as helper variables so we can take their address, as explained above) // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2. #ifndef LLONG_MIN ImS64 LLONG_MIN = -9223372036854775807LL - 1; 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; 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 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; 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_DEMO_MARKER("Widgets/Data Types/Drags"); ImGui::SeparatorText("Drags"); ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker( "As with every widget in dear imgui, we never modify values unless there is a user interaction.\n" "You 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 s32 hex", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, "0x%08X"); 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"); ImGui::DragScalar("drag float log", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", ImGuiSliderFlags_Logarithmic); ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams"); ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic); IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders"); ImGui::SeparatorText("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"); ImGui::SliderScalar("slider s32 hex", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty, "0x%04X"); 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,"%" PRId64); ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" PRId64); ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" PRId64); ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%" PRIu64 " ms"); ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" PRIu64 " ms"); ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" PRIu64 " ms"); ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", ImGuiSliderFlags_Logarithmic); 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"); ImGui::SliderScalar("slider double low log",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", ImGuiSliderFlags_Logarithmic); ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams"); ImGui::SeparatorText("Sliders (reverse)"); ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d"); ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u"); ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d"); ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u"); ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" PRId64); ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" PRIu64 " ms"); IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs"); static bool inputs_step = true; static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None; ImGui::SeparatorText("Inputs"); ImGui::Checkbox("Show step buttons", &inputs_step); ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); ImGui::CheckboxFlags("ImGuiInputTextFlags_ParseEmptyRefVal", &flags, ImGuiInputTextFlags_ParseEmptyRefVal); ImGui::CheckboxFlags("ImGuiInputTextFlags_DisplayEmptyRefVal", &flags, ImGuiInputTextFlags_DisplayEmptyRefVal); ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d", flags); ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u", flags); ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d", flags); ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u", flags); ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d", flags); ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X", flags); ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u", flags); ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", flags); ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL, NULL, NULL, flags); ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL, NULL, NULL, flags); ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL, NULL, NULL, flags); ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL, NULL, NULL, flags); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsDisableBlocks() //----------------------------------------------------------------------------- static void DemoWindowWidgetsDisableBlocks(ImGuiDemoWindowData* demo_data) { if (ImGui::TreeNode("Disable Blocks")) { IMGUI_DEMO_MARKER("Widgets/Disable Blocks"); ImGui::Checkbox("Disable entire section above", &demo_data->DisableSections); ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across other sections."); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsDragAndDrop() //----------------------------------------------------------------------------- static void DemoWindowWidgetsDragAndDrop() { if (ImGui::TreeNode("Drag and Drop")) { IMGUI_DEMO_MARKER("Widgets/Drag and drop"); if (ImGui::TreeNode("Drag and drop in standard widgets")) { IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets"); // 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 'Demo->Widgets->Color/Picker Widgets->Palette' demo. HelpMarker("You can drag from the color squares."); 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::TreePop(); } if (ImGui::TreeNode("Drag and drop to copy/swap items")) { IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items"); enum Mode { 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_COUNTOF(names); n++) { 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)) { // Set payload to carry the index of our item (could be anything) ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int)); // 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_Copy) { ImGui::Text("Copy %s", names[n]); } 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) { 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::TreePop(); } if (ImGui::TreeNode("Drag to reorder items (simple)")) { IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)"); // FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice. // This code was always slightly faulty but in a way which was not easily noticeable. // Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue. ImGui::PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); // Simple reordering HelpMarker( "We don't use the drag and drop api at all here! " "Instead we query when the item is held but not hovered, and order items accordingly."); static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" }; for (int n = 0; n < IM_COUNTOF(item_names); n++) { const char* item = item_names[n]; ImGui::Selectable(item); if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) { int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1); if (n_next >= 0 && n_next < IM_COUNTOF(item_names)) { item_names[n] = item_names[n_next]; item_names[n_next] = item; ImGui::ResetMouseDragDelta(); } } } ImGui::PopItemFlag(); ImGui::TreePop(); } if (ImGui::TreeNode("Tooltip at target location")) { IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location"); for (int n = 0; n < 2; n++) { // Drop targets ImGui::Button(n ? "drop here##1" : "drop here##0"); if (ImGui::BeginDragDropTarget()) { ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip; if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags)) { IM_UNUSED(payload); ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); ImGui::SetTooltip("Cannot drop here!"); } ImGui::EndDragDropTarget(); } // Drop source static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f }; if (n == 0) ImGui::ColorButton("drag me", col4); } ImGui::TreePop(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsDragsAndSliders() //----------------------------------------------------------------------------- static void DemoWindowWidgetsDragsAndSliders() { if (ImGui::TreeNode("Drag/Slider Flags")) { IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags"); // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same! static ImGuiSliderFlags flags = ImGuiSliderFlags_None; ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp); ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", &flags, ImGuiSliderFlags_ClampOnInput); ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds."); ImGui::CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", &flags, ImGuiSliderFlags_ClampZeroRange); ImGui::SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp."); ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic); ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values)."); ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat); ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits)."); ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput); ImGui::SameLine(); HelpMarker("Disable Ctrl+Click or Enter key allowing to input text directly into the widget."); ImGui::CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", &flags, ImGuiSliderFlags_NoSpeedTweaks); ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic."); ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround); ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)"); ImGui::CheckboxFlags("ImGuiSliderFlags_ColorMarkers", &flags, ImGuiSliderFlags_ColorMarkers); // Drags static float drag_f = 0.5f; static float drag_f4[4]; static int drag_i = 50; ImGui::Text("Underlying float value: %f", drag_f); ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags); ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags); ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags); ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags); //ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags); // To test ClampZeroRange //ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags); ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags); ImGui::DragFloat4("DragFloat4 (0 -> 1)", drag_f4, 0.005f, 0.0f, 1.0f, "%.3f", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers. // Sliders static float slider_f = 0.5f; static float slider_f4[4]; static int slider_i = 50; const ImGuiSliderFlags flags_for_sliders = (flags & ~ImGuiSliderFlags_WrapAround); ImGui::Text("Underlying float value: %f", slider_f); ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags_for_sliders); ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags_for_sliders); ImGui::SliderFloat4("SliderFloat4 (0 -> 1)", slider_f4, 0.0f, 1.0f, "%.3f", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers. ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsFonts() //----------------------------------------------------------------------------- static void DemoWindowWidgetsFonts() { if (ImGui::TreeNode("Fonts")) { IMGUI_DEMO_MARKER("Widgets/Fonts"); ImFontAtlas* atlas = ImGui::GetIO().Fonts; ImGui::ShowFontAtlas(atlas); // FIXME-NEWATLAS: Provide a demo to add/create a procedural font? ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsImages() //----------------------------------------------------------------------------- static void DemoWindowWidgetsImages() { if (ImGui::TreeNode("Images")) { IMGUI_DEMO_MARKER("Widgets/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!"); // Below we are displaying the font texture because it is the only texture we have access to inside the demo! // Read description about ImTextureID/ImTextureRef and FAQ for details about texture identifiers. // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top // of their respective source file to specify what they are using as texture identifier, for example: // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer. // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc. // So with the DirectX11 backend, you call ImGui::Image() with a 'ID3D11ShaderResourceView*' cast to ImTextureID. // - 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. // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer, // it will help you debug issues if you are confused about it. // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples // Grab the current texture identifier used by the font atlas. ImTextureRef my_tex_id = io.Fonts->TexRef; // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it. float my_tex_w = (float)io.Fonts->TexData->Width; float my_tex_h = (float)io.Fonts->TexData->Height; { ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); ImVec2 pos = ImGui::GetCursorScreenPos(); ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize)); ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); if (ImGui::BeginItemTooltip()) { float region_sz = 32.0f; float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; float zoom = 4.0f; 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; } 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; } 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::ImageWithBg(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); ImGui::EndTooltip(); } ImGui::PopStyleVar(); } IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons"); ImGui::TextWrapped("And now some textured buttons.."); static int pressed_count = 0; for (int i = 0; i < 8; i++) { // UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures. // Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation. // Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples ImGui::PushID(i); if (i > 0) ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(i - 1.0f, i - 1.0f)); ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h); // UV coordinates for (32,32) in our texture ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint if (ImGui::ImageButton("", my_tex_id, size, uv0, uv1, bg_col, tint_col)) pressed_count += 1; if (i > 0) ImGui::PopStyleVar(); ImGui::PopID(); ImGui::SameLine(); } ImGui::NewLine(); ImGui::Text("Pressed %d times.", pressed_count); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsListBoxes() //----------------------------------------------------------------------------- static void DemoWindowWidgetsListBoxes() { if (ImGui::TreeNode("List Boxes")) { IMGUI_DEMO_MARKER("Widgets/List Boxes"); // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild() // using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. // You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild() // to always be called (inconsistent with BeginListBox()/EndListBox()). // Using the generic BeginListBox() API, you have full control over how to display the combo contents. // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively // stored in the object itself, etc.) const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; static int item_selected_idx = 0; // Here we store our selected data as an index. static bool item_highlight = false; int item_highlighted_idx = -1; // Here we store our highlighted data as an index. ImGui::Checkbox("Highlight hovered item in second listbox", &item_highlight); if (ImGui::BeginListBox("listbox 1")) { for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (ImGui::Selectable(items[n], is_selected)) item_selected_idx = n; if (item_highlight && ImGui::IsItemHovered()) item_highlighted_idx = n; // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) if (is_selected) ImGui::SetItemDefaultFocus(); } ImGui::EndListBox(); } ImGui::SameLine(); HelpMarker("Here we are sharing selection state between both boxes."); // Custom size: use all width, 5 items tall ImGui::Text("Full-width:"); if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing()))) { for (int n = 0; n < IM_COUNTOF(items); n++) { bool is_selected = (item_selected_idx == n); ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0; if (ImGui::Selectable(items[n], is_selected, flags)) item_selected_idx = n; // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) if (is_selected) ImGui::SetItemDefaultFocus(); } ImGui::EndListBox(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsMultiComponents() //----------------------------------------------------------------------------- static void DemoWindowWidgetsMultiComponents() { if (ImGui::TreeNode("Multi-component Widgets")) { IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets"); static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; static int vec4i[4] = { 1, 5, 100, 255 }; static ImGuiSliderFlags flags = 0; ImGui::CheckboxFlags("ImGuiSliderFlags_ColorMarkers", &flags, ImGuiSliderFlags_ColorMarkers); // Only passing this to Drag/Sliders ImGui::SeparatorText("2-wide"); ImGui::InputFloat2("input float2", vec4f); ImGui::InputInt2("input int2", vec4i); ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags); ImGui::DragInt2("drag int2", vec4i, 1, 0, 255, NULL, flags); ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f, NULL, flags); ImGui::SliderInt2("slider int2", vec4i, 0, 255, NULL, flags); ImGui::SeparatorText("3-wide"); ImGui::InputFloat3("input float3", vec4f); ImGui::InputInt3("input int3", vec4i); ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags); ImGui::DragInt3("drag int3", vec4i, 1, 0, 255, NULL, flags); ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f, NULL, flags); ImGui::SliderInt3("slider int3", vec4i, 0, 255, NULL, flags); ImGui::SeparatorText("4-wide"); ImGui::InputFloat4("input float4", vec4f); ImGui::InputInt4("input int4", vec4i); ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags); ImGui::DragInt4("drag int4", vec4i, 1, 0, 255, NULL, flags); ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f, NULL, flags); ImGui::SliderInt4("slider int4", vec4i, 0, 255, NULL, flags); ImGui::SeparatorText("Ranges"); static float begin = 10, end = 90; static int begin_i = 100, end_i = 1000; ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_AlwaysClamp); ImGui::DragIntRange2("range int", &begin_i, &end_i, 5, 0, 1000, "Min: %d units", "Max: %d units"); ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units"); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsPlotting() //----------------------------------------------------------------------------- static void DemoWindowWidgetsPlotting() { // Plot/Graph widgets are not very good. // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions) if (ImGui::TreeNode("Plotting")) { IMGUI_DEMO_MARKER("Widgets/Plotting"); ImGui::Text("Need better plotting and graphing? Consider using ImPlot:"); ImGui::TextLinkOpenURL("https://github.com/epezent/implot"); ImGui::Separator(); static bool animate = true; ImGui::Checkbox("Animate", &animate); // Plot as lines and plot as histogram static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; ImGui::PlotLines("Frame Times", arr, IM_COUNTOF(arr)); ImGui::PlotHistogram("Histogram", arr, IM_COUNTOF(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!"); // Fill an 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] = {}; static int values_offset = 0; static double refresh_time = 0.0; if (!animate || refresh_time == 0.0) refresh_time = ImGui::GetTime(); while (refresh_time < ImGui::GetTime()) // Create 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_COUNTOF(values); phase += 0.10f * values_offset; refresh_time += 1.0f / 60.0f; } // Plots can display overlay texts // (in this example, we will display an average value) { float average = 0.0f; for (int n = 0; n < IM_COUNTOF(values); n++) average += values[n]; average /= (float)IM_COUNTOF(values); char overlay[32]; sprintf(overlay, "avg %f", average); ImGui::PlotLines("Lines", values, IM_COUNTOF(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); } // Use functions to generate output // FIXME: This is actually VERY 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::SeparatorText("Functions"); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::SameLine(); ImGui::SliderInt("Sample count", &display_count, 1, 400); float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; ImGui::PlotLines("Lines##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); ImGui::PlotHistogram("Histogram##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsProgressBars() //----------------------------------------------------------------------------- static void DemoWindowWidgetsProgressBars() { if (ImGui::TreeNode("Progress Bars")) { IMGUI_DEMO_MARKER("Widgets/Progress Bars"); // Animate a simple progress bar static float progress_accum = 0.0f, progress_dir = 1.0f; progress_accum += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; if (progress_accum >= +1.1f) { progress_accum = +1.1f; progress_dir *= -1.0f; } if (progress_accum <= -0.1f) { progress_accum = -0.1f; progress_dir *= -1.0f; } const float progress = IM_CLAMP(progress_accum, 0.0f, 1.0f); // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,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"); char buf[32]; sprintf(buf, "%d/%d", (int)(progress * 1753), 1753); ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf); // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value. // Adjust the factor if you want to adjust the animation speed. ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(0.0f, 0.0f), "Searching.."); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::Text("Indeterminate"); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsQueryingStatuses() //----------------------------------------------------------------------------- static void DemoWindowWidgetsQueryingStatuses() { if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) { IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)"); // Select an item type const char* item_names[] = { "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat", "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" }; static int item_type = 4; static bool item_disabled = false; ImGui::Combo("Item Type", &item_type, item_names, IM_COUNTOF(item_names), IM_COUNTOF(item_names)); ImGui::SameLine(); HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); ImGui::Checkbox("Item Disabled", &item_disabled); // Submit selected items so we can query their status in the code following it. bool ret = false; static bool b = false; static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; static char str[16] = {}; if (item_disabled) ImGui::BeginDisabled(true); 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) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater) if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_COUNTOF(str)); } // Testing input text (which handles tabbing) if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_COUNTOF(str)); } // Testing input text (which uses a child window) if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) if (item_type == 10) { ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item if (item_type == 11) { ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) if (item_type == 12) { ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node if (item_type == 13) { ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_COUNTOF(items)); } if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_COUNTOF(items), IM_COUNTOF(items)); } bool hovered_delay_none = ImGui::IsItemHovered(); bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary); bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort); bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal); bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary // Display the values of IsItemHovered() and other common item state functions. // Note that the ImGuiHoveredFlags_XXX flags can be combined. // Because BulletText is an item itself and that would affect the output of IsItemXXX functions, // we query every state in a single call to avoid storing them and to simplify the code. ImGui::BulletText( "Return value = %d\n" "IsItemFocused() = %d\n" "IsItemHovered() = %d\n" "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsItemHovered(_AllowWhenOverlappedByItem) = %d\n" "IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n" "IsItemHovered(_AllowWhenDisabled) = %d\n" "IsItemHovered(_RectOnly) = %d\n" "IsItemActive() = %d\n" "IsItemEdited() = %d\n" "IsItemActivated() = %d\n" "IsItemDeactivated() = %d\n" "IsItemDeactivatedAfterEdit() = %d\n" "IsItemVisible() = %d\n" "IsItemClicked() = %d\n" "IsItemToggledOpen() = %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_AllowWhenOverlappedByItem), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), ImGui::IsItemActive(), ImGui::IsItemEdited(), ImGui::IsItemActivated(), ImGui::IsItemDeactivated(), ImGui::IsItemDeactivatedAfterEdit(), ImGui::IsItemVisible(), ImGui::IsItemClicked(), ImGui::IsItemToggledOpen(), ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y, ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y, ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y ); ImGui::BulletText( "with Hovering Delay or Stationary test:\n" "IsItemHovered() = %d\n" "IsItemHovered(_Stationary) = %d\n" "IsItemHovered(_DelayShort) = %d\n" "IsItemHovered(_DelayNormal) = %d\n" "IsItemHovered(_Tooltip) = %d", hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip); if (item_disabled) ImGui::EndDisabled(); char buf[1] = ""; ImGui::InputText("unused", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_ReadOnly); ImGui::SameLine(); HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); ImGui::TreePop(); } if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) { IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)"); static bool embed_all_inside_a_child_window = false; ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Borders); // Testing IsWindowFocused() function with its various flags. ImGui::BulletText( "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); // Testing IsWindowHovered() function with its various flags. ImGui::BulletText( "IsWindowHovered() = %d\n" "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_RootWindow) = %d\n" "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n" "IsWindowHovered(_Stationary) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary)); ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Borders); 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. // This is useful in particular if you want to create a context menu associated to the title bar of a window. // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties). static bool test_window = false; ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); if (test_window) { // FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck. // Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our parent as "don't skip items"? 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(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsSelectables() //----------------------------------------------------------------------------- static void DemoWindowWidgetsSelectables() { //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Selectables")) { IMGUI_DEMO_MARKER("Widgets/Selectables"); // Selectable() has 2 overloads: // - The one taking "bool selected" as a read-only selection information. // When Selectable() has been clicked it 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 many different ways // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc). IMGUI_DEMO_MARKER("Widgets/Selectables/Basic"); if (ImGui::TreeNode("Basic")) { static bool selection[5] = { false, true, false, false }; ImGui::Selectable("1. I am selectable", &selection[0]); ImGui::Selectable("2. I am selectable", &selection[1]); ImGui::Selectable("3. I am selectable", &selection[2]); if (ImGui::Selectable("4. I am double clickable", selection[3], ImGuiSelectableFlags_AllowDoubleClick)) if (ImGui::IsMouseDoubleClicked(0)) selection[3] = !selection[3]; ImGui::TreePop(); } IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line"); if (ImGui::TreeNode("Multiple items on the same line")) { IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple items on the same line"); // - Using SetNextItemAllowOverlap() // - Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically. { static bool selected[3] = {}; ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1"); ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2"); ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3"); } // (2) // - Using ImGuiSelectableFlags_AllowOverlap is a shortcut for calling SetNextItemAllowOverlap() // - No visible label, display contents inside the selectable bounds. // - We don't maintain actual selection in this example to keep things simple. ImGui::Spacing(); { static bool checked[5] = {}; static int selected_n = 0; const float color_marker_w = ImGui::CalcTextSize("x").x; for (int n = 0; n < 5; n++) { ImGui::PushID(n); ImGui::AlignTextToFramePadding(); if (ImGui::Selectable("##selectable", selected_n == n, ImGuiSelectableFlags_AllowOverlap)) selected_n = n; ImGui::SameLine(0, 0); ImGui::Checkbox("##check", &checked[n]); ImGui::SameLine(); ImVec4 color((n & 1) ? 1.0f : 0.2f, (n & 2) ? 1.0f : 0.2f, 0.2f, 1.0f); ImGui::ColorButton("##color", color, ImGuiColorEditFlags_NoTooltip, ImVec2(color_marker_w, 0)); ImGui::SameLine(); ImGui::Text("Some label"); ImGui::PopID(); } } ImGui::TreePop(); } if (ImGui::TreeNode("In Tables")) { IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables"); static bool selected[10] = {}; if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) { for (int i = 0; i < 10; i++) { char label[32]; sprintf(label, "Item %d", i); ImGui::TableNextColumn(); ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap } ImGui::EndTable(); } ImGui::Spacing(); if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) { for (int i = 0; i < 10; i++) { char label[32]; sprintf(label, "Item %d", i); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns); ImGui::TableNextColumn(); ImGui::Text("Some other contents"); ImGui::TableNextColumn(); ImGui::Text("123456"); } ImGui::EndTable(); } ImGui::TreePop(); } if (ImGui::TreeNode("Grid")) { IMGUI_DEMO_MARKER("Widgets/Selectables/Grid"); static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; // Add in a bit of silly fun... const float time = (float)ImGui::GetTime(); const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected... if (winning_state) ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f))); const float size = ImGui::CalcTextSize("Sailor").x; for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++) { if (x > 0) ImGui::SameLine(); ImGui::PushID(y * 4 + x); if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(size, size))) { // Toggle clicked cell + toggle neighbors selected[y][x] ^= 1; if (x > 0) { selected[y][x - 1] ^= 1; } if (x < 3) { selected[y][x + 1] ^= 1; } if (y > 0) { selected[y - 1][x] ^= 1; } if (y < 3) { selected[y + 1][x] ^= 1; } } ImGui::PopID(); } if (winning_state) ImGui::PopStyleVar(); ImGui::TreePop(); } if (ImGui::TreeNode("Alignment")) { IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment"); HelpMarker( "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item " "basis using PushStyleVar(). You'll probably want to always keep your default situation to " "left-align otherwise it becomes difficult to layout multiple items on a same line"); static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true }; const float size = ImGui::CalcTextSize("(1.0,1.0)").x; 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(size, size)); ImGui::PopStyleVar(); } } ImGui::TreePop(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsSelectionAndMultiSelect() //----------------------------------------------------------------------------- // Multi-selection demos // Also read: https://github.com/ocornut/imgui/wiki/Multi-Select //----------------------------------------------------------------------------- static const char* ExampleNames[] = { "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" }; // Extra functions to add deletion support to ImGuiSelectionBasicStorage struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage { // Find which item should be Focused after deletion. // Call _before_ item submission. Return an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it. // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection. // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data. // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility. // - Important: Deletion only works if the underlying ImGuiID for your items are stable: aka not depend on their index, but on e.g. item id/ptr. // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset. int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count) { if (Size == 0) return -1; // If focused item is not selected... const int focused_idx = (int)ms_io->NavIdItem; // Index of currently focused item if (ms_io->NavIdSelected == false) // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx)) { ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even when NavIdSelected==true, but it would take an extra frame to recover RangeSrc when deleting a selected item. return focused_idx; // Request to focus same item after deletion. } // If focused item is selected: land on first unselected item after focused item. for (int idx = focused_idx + 1; idx < items_count; idx++) if (!Contains(GetStorageIdFromIndex(idx))) return idx; // If focused item is selected: otherwise return last unselected item before focused item. for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--) if (!Contains(GetStorageIdFromIndex(idx))) return idx; return -1; } // Rewrite item list (delete items) + update selection. // - Call after EndMultiSelect() // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data. template void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector& items, int item_curr_idx_to_select) { // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection). // If NavId was not part of selection, we will stay on same item. ImVector new_items; new_items.reserve(items.Size - Size); int item_next_idx_to_select = -1; for (int idx = 0; idx < items.Size; idx++) { if (!Contains(GetStorageIdFromIndex(idx))) new_items.push_back(items[idx]); if (item_curr_idx_to_select == idx) item_next_idx_to_select = new_items.Size - 1; } items.swap(new_items); // Update selection Clear(); if (item_next_idx_to_select != -1 && ms_io->NavIdSelected) SetItemSelected(GetStorageIdFromIndex(item_next_idx_to_select), true); } }; // Example: Implement dual list box storage and interface struct ExampleDualListBox { ImVector Items[2]; // ID is index into ExampleName[] ImGuiSelectionBasicStorage Selections[2]; // Store ExampleItemId into selection bool OptKeepSorted = true; void MoveAll(int src, int dst) { IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0)); for (ImGuiID item_id : Items[src]) Items[dst].push_back(item_id); Items[src].clear(); SortItems(dst); Selections[src].Swap(Selections[dst]); Selections[src].Clear(); } void MoveSelected(int src, int dst) { for (int src_n = 0; src_n < Items[src].Size; src_n++) { ImGuiID item_id = Items[src][src_n]; if (!Selections[src].Contains(item_id)) continue; Items[src].erase(&Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap) Items[dst].push_back(item_id); src_n--; } if (OptKeepSorted) SortItems(dst); Selections[src].Swap(Selections[dst]); Selections[src].Clear(); } void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side) { // In this example we store item id in selection (instead of item index) Selections[side].UserData = Items[side].Data; Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; }; Selections[side].ApplyRequests(ms_io); } static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs) { const int* a = (const int*)lhs; const int* b = (const int*)rhs; return *a - *b; } void SortItems(int n) { qsort(Items[n].Data, (size_t)Items[n].Size, sizeof(Items[n][0]), CompareItemsByValue); } void Show() { //if (ImGui::Checkbox("Sorted", &OptKeepSorted) && OptKeepSorted) { SortItems(0); SortItems(1); } if (ImGui::BeginTable("split", 3, ImGuiTableFlags_None)) { ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // Buttons ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Right side ImGui::TableNextRow(); int request_move_selected = -1; int request_move_all = -1; float child_height_0 = 0.0f; for (int side = 0; side < 2; side++) { // FIXME-MULTISELECT: Dual List Box: Add context menus // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues. ImVector& items = Items[side]; ImGuiSelectionBasicStorage& selection = Selections[side]; ImGui::TableSetColumnIndex((side == 0) ? 0 : 2); ImGui::Text("%s (%d)", (side == 0) ? "Available" : "Basket", items.Size); // Submit scrolling range to avoid glitches on moving/deletion const float items_height = ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); bool child_visible; if (side == 0) { // Left child is resizable ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), ImVec2(FLT_MAX, FLT_MAX)); child_visible = ImGui::BeginChild("0", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY); child_height_0 = ImGui::GetWindowSize().y; } else { // Right child use same height as left one child_visible = ImGui::BeginChild("1", ImVec2(-FLT_MIN, child_height_0), ImGuiChildFlags_FrameStyle); } if (child_visible) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); ApplySelectionRequests(ms_io, side); for (int item_n = 0; item_n < items.Size; item_n++) { ImGuiID item_id = items[item_n]; bool item_is_selected = selection.Contains(item_id); ImGui::SetNextItemSelectionUserData(item_n); ImGui::Selectable(ExampleNames[item_id], item_is_selected, ImGuiSelectableFlags_AllowDoubleClick); if (ImGui::IsItemFocused()) { // FIXME-MULTISELECT: Dual List Box: Transfer focus if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)) request_move_selected = side; if (ImGui::IsMouseDoubleClicked(0)) // FIXME-MULTISELECT: Double-click on multi-selection? request_move_selected = side; } } ms_io = ImGui::EndMultiSelect(); ApplySelectionRequests(ms_io, side); } ImGui::EndChild(); } // Buttons columns ImGui::TableSetColumnIndex(1); ImGui::NewLine(); //ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f }; ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() }; // (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized) if (ImGui::Button(">>", button_sz)) request_move_all = 0; if (ImGui::Button(">", button_sz)) request_move_selected = 0; if (ImGui::Button("<", button_sz)) request_move_selected = 1; if (ImGui::Button("<<", button_sz)) request_move_all = 1; // Process requests if (request_move_all != -1) MoveAll(request_move_all, request_move_all ^ 1); if (request_move_selected != -1) MoveSelected(request_move_selected, request_move_selected ^ 1); // FIXME-MULTISELECT: Support action from outside /* if (OptKeepSorted == false) { ImGui::NewLine(); if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {} if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {} } */ ImGui::EndTable(); } } }; static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_data) { if (ImGui::TreeNode("Selection State & Multi-Select")) { IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select"); HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); ImGui::BulletText("Wiki page:"); ImGui::SameLine(); ImGui::TextLinkOpenURL("imgui/wiki/Multi-Select", "https://github.com/ocornut/imgui/wiki/Multi-Select"); // Without any fancy API: manage single-selection yourself. if (ImGui::TreeNode("Single-Select")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select"); 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(); } // Demonstrate implementation a most-basic form of multi-selection manually // This doesn't support the Shift modifier which requires BeginMultiSelect()! if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)"); 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++) { 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; // Toggle current item } } ImGui::TreePop(); } // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API. // Shift+Click w/ Ctrl and other standard features are supported. // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement. if (ImGui::TreeNode("Multi-Select")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select"); ImGui::Text("Supported features:"); ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space)."); ImGui::BulletText("Ctrl modifier to preserve and toggle selection."); ImGui::BulletText("Shift modifier for range selection."); ImGui::BulletText("Ctrl+A to select all."); ImGui::BulletText("Escape to clear selection."); ImGui::BulletText("Click and drag to box-select."); ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen."); // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection const int ITEMS_COUNT = 50; static ImGuiSelectionBasicStorage selection; ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); // The BeginChild() has no purpose for selection logic, other that offering a scrolling region. if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); selection.ApplyRequests(ms_io); for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); } ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io); } ImGui::EndChild(); ImGui::TreePop(); } // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect() if (ImGui::TreeNode("Multi-Select (with clipper)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)"); // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection static ImGuiSelectionBasicStorage selection; ImGui::Text("Added features:"); ImGui::BulletText("Using ImGuiListClipper."); const int ITEMS_COUNT = 10000; ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); selection.ApplyRequests(ms_io); ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); if (ms_io->RangeSrcItem != -1) clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. while (clipper.Step()) { for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { char label[64]; sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); } } ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io); } ImGui::EndChild(); ImGui::TreePop(); } // Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API. // In order to support Deletion without any glitches you need to: // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling. // - (2) Items needs to have persistent ID Stack identifier = ID needs to not depends on their index. PushID(index) = KO. PushID(item_id) = OK. This is in order to focus items reliably after a selection. // - (3) BeginXXXX process // - (4) Focus process // - (5) EndXXXX process if (ImGui::TreeNode("Multi-Select (with deletion)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)"); // Storing items data separately from selection data. // (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items) // Use a custom selection.Adapter: store item identifier in Selection (instead of index) static ImVector items; static ExampleSelectionWithDeletion selection; selection.UserData = (void*)&items; selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector* p_items = (ImVector*)self->UserData; return (*p_items)[idx]; }; // Index -> ID ImGui::Text("Added features:"); ImGui::BulletText("Dynamic list with Delete key support."); ImGui::Text("Selection size: %d/%d", selection.Size, items.Size); // Initialize default list with 50 items + button to add/remove items. static ImGuiID items_next_id = 0; if (items_next_id == 0) for (ImGuiID n = 0; n < 50; n++) items.push_back(items_next_id++); if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } } ImGui::SameLine(); if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetItemSelected(items.back(), false); items.pop_back(); } } // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion const float items_height = ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); selection.ApplyRequests(ms_io); const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0); const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; for (int n = 0; n < items.Size; n++) { const ImGuiID item_id = items[n]; char label[64]; sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains(item_id); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); if (item_curr_idx_to_focus == n) ImGui::SetKeyboardFocusHere(-1); } // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io); if (want_delete) selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); } ImGui::EndChild(); ImGui::TreePop(); } // Implement a Dual List Box (#6648) if (ImGui::TreeNode("Multi-Select (dual list box)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)"); // Init default state static ExampleDualListBox dlb; if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0) for (int item_id = 0; item_id < IM_COUNTOF(ExampleNames); item_id++) dlb.Items[0].push_back((ImGuiID)item_id); // Show dlb.Show(); ImGui::TreePop(); } // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect() if (ImGui::TreeNode("Multi-Select (in a table)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)"); static ImGuiSelectionBasicStorage selection; const int ITEMS_COUNT = 10000; ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter, ImVec2(0.0f, ImGui::GetFontSize() * 20))) { ImGui::TableSetupColumn("Object"); ImGui::TableSetupColumn("Action"); ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableHeadersRow(); ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); selection.ApplyRequests(ms_io); ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); if (ms_io->RangeSrcItem != -1) clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. while (clipper.Step()) { for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushID(n); char label[64]; sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); ImGui::TableNextColumn(); ImGui::SmallButton("hello"); ImGui::PopID(); } } ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io); ImGui::EndTable(); } ImGui::TreePop(); } if (ImGui::TreeNode("Multi-Select (checkboxes)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)"); ImGui::Text("In a list of checkboxes (not selectable):"); ImGui::BulletText("Using _NoAutoSelect + _NoAutoClear flags."); ImGui::BulletText("Shift+Click to check multiple boxes."); ImGui::BulletText("Shift+Keyboard to copy current value to other boxes."); // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper. static bool items[20] = {}; static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_COUNTOF(items)); ImGuiSelectionExternalStorage storage_wrapper; storage_wrapper.UserData = (void*)items; storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; }; storage_wrapper.ApplyRequests(ms_io); for (int n = 0; n < 20; n++) { char label[32]; sprintf(label, "Item %d", n); ImGui::SetNextItemSelectionUserData(n); ImGui::Checkbox(label, &items[n]); } ms_io = ImGui::EndMultiSelect(); storage_wrapper.ApplyRequests(ms_io); } ImGui::EndChild(); ImGui::TreePop(); } // Demonstrate individual selection scopes in same window if (ImGui::TreeNode("Multi-Select (multiple scopes)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)"); // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection const int SCOPES_COUNT = 3; const int ITEMS_COUNT = 8; // Per scope static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT]; // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window. static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid; if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) flags &= ~ImGuiMultiSelectFlags_ScopeRect; if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) flags &= ~ImGuiMultiSelectFlags_ScopeWindow; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) { ImGui::PushID(selection_scope_n); ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n]; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size, ITEMS_COUNT); selection->ApplyRequests(ms_io); ImGui::SeparatorText("Selection scope"); ImGui::Text("Selection size: %d/%d", selection->Size, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection->Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); } // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); selection->ApplyRequests(ms_io); ImGui::PopID(); } ImGui::TreePop(); } // See ShowExampleAppAssetsBrowser() if (ImGui::TreeNode("Multi-Select (tiled assets browser)")) { ImGui::Checkbox("Assets Browser", &demo_data->ShowAppAssetsBrowser); ImGui::Text("(also access from 'Examples->Assets Browser' in menu)"); ImGui::TreePop(); } // Demonstrate supporting multiple-selection in a tree. // - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly! // This showcase how SetNextItemSelectionUserData() never assume indices! // - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request. // We want this interpolation to match what the user sees: in visible order, skipping closed nodes. // This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper. // - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you // are more likely to build an array mapping sequential indices to visible tree nodes, since your // filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier. // - Consider this a prototype: we are working toward simplifying some of it. if (ImGui::TreeNode("Multi-Select (trees)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)"); HelpMarker( "This is rather advanced and experimental. If you are getting started with multi-select, " "please don't start by looking at how to use it for a tree!\n\n" "Future versions will try to simplify and formalize some of this."); struct ExampleTreeFuncs { static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection) { ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Enable pressing left to jump to parent if (node->Childs.Size == 0) tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf; if (selection->Contains((ImGuiID)node->UID)) tree_node_flags |= ImGuiTreeNodeFlags_Selected; // Using SetNextItemStorageID() to specify storage id, so we can easily peek into // the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions. ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node); ImGui::SetNextItemStorageID((ImGuiID)node->UID); if (ImGui::TreeNodeEx(node->Name, tree_node_flags)) { for (ExampleTreeNode* child : node->Childs) DrawNode(child, selection); ImGui::TreePop(); } else if (ImGui::IsItemToggledOpen()) { TreeCloseAndUnselectChildNodes(node, selection); } } // When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected. // FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node // features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc. static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0) { // Recursive close (the test for depth == 0 is because we call this on a node that was just closed!) int unselected_count = selection->Contains((ImGuiID)node->UID) ? 1 : 0; if (depth == 0 || ImGui::TreeNodeGetOpen((ImGuiID)node->UID)) { for (ExampleTreeNode* child : node->Childs) unselected_count += TreeCloseAndUnselectChildNodes(child, selection, depth + 1); ImGui::TreeNodeSetOpen((ImGuiID)node->UID, false); } // Select root node if any of its child was selected, otherwise unselect selection->SetItemSelected((ImGuiID)node->UID, (depth == 0 && unselected_count > 0)); return unselected_count; } // Apply multi-selection requests static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection) { for (ImGuiSelectionRequest& req : ms_io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) { if (req.Selected) TreeSetAllInOpenNodes(tree, selection, req.Selected); else selection->Clear(); } else if (req.Type == ImGuiSelectionRequestType_SetRange) { ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem; ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem; for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(node, last_node)) selection->SetItemSelected((ImGuiID)node->UID, req.Selected); } } } static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected) { if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme selection->SetItemSelected((ImGuiID)node->UID, selected); if (node->Parent == NULL || ImGui::TreeNodeGetOpen((ImGuiID)node->UID)) for (ExampleTreeNode* child : node->Childs) TreeSetAllInOpenNodes(child, selection, selected); } // Interpolate in *user-visible order* AND only *over opened nodes*. // If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler. // Here the tricks are that: // - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion. // this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)' // which would only be called when crossing from child to a parent, aka not too much. // - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack, // making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location. static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node) { // Reached last node if (curr_node == last_node) return NULL; // Recurse into childs. Query storage to tell if the node is open. if (curr_node->Childs.Size > 0 && ImGui::TreeNodeGetOpen((ImGuiID)curr_node->UID)) return curr_node->Childs[0]; // Next sibling, then into our own parent while (curr_node->Parent != NULL) { if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size) return curr_node->Parent->Childs[curr_node->IndexInParent + 1]; curr_node = curr_node->Parent; } return NULL; } }; // ExampleTreeFuncs static ImGuiSelectionBasicStorage selection; if (demo_data->DemoTree == NULL) demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once ImGui::Text("Selection size: %d", selection.Size); if (ImGui::BeginChild("##Tree", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ExampleTreeNode* tree = demo_data->DemoTree; ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, -1); ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection); for (ExampleTreeNode* node : tree->Childs) ExampleTreeFuncs::DrawNode(node, &selection); ms_io = ImGui::EndMultiSelect(); ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection); } ImGui::EndChild(); ImGui::TreePop(); } // Advanced demonstration of BeginMultiSelect() // - Showcase clipping. // - Showcase deletion. // - Showcase basic drag and drop. // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing). // - Showcase using inside a table. //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Multi-Select (advanced)")) { IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)"); // Options enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; static bool use_clipper = true; static bool use_deletion = true; static bool use_drag_drop = true; static bool show_in_table = false; static bool show_color_button = true; static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; static WidgetType widget_type = WidgetType_Selectable; if (ImGui::TreeNode("Options")) { if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } ImGui::SameLine(); HelpMarker("TreeNode() is technically supported but... using this correctly is more complicated (you need some sort of linear/random access to your tree, which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this.\n\nFor now the tree demo is actually a little bit meaningless because it is an empty tree with only root nodes."); ImGui::Checkbox("Enable clipper", &use_clipper); ImGui::Checkbox("Enable deletion", &use_deletion); ImGui::Checkbox("Enable drag & drop", &use_drag_drop); ImGui::Checkbox("Show in a table", &show_in_table); ImGui::Checkbox("Show color button", &show_color_button); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectOnRightClick", &flags, ImGuiMultiSelectFlags_NoSelectOnRightClick); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) flags &= ~ImGuiMultiSelectFlags_ScopeRect; if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) flags &= ~ImGuiMultiSelectFlags_ScopeWindow; if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick)) flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease; if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) flags &= ~ImGuiMultiSelectFlags_SelectOnClick; ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); ImGui::TreePop(); } // Initialize default list with 1000 items. // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection static ImVector items; static int items_next_id = 0; if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } } static ExampleSelectionWithDeletion selection; static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu ImGui::Text("Selection size: %d/%d", selection.Size, items.Size); const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f); ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); selection.ApplyRequests(ms_io); const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu; const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; request_deletion_from_menu = false; if (show_in_table) { if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f)); ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); //ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f); } ImGuiListClipper clipper; if (use_clipper) { clipper.Begin(items.Size); if (item_curr_idx_to_focus != -1) clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped. if (ms_io->RangeSrcItem != -1) clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. } while (!use_clipper || clipper.Step()) { const int item_begin = use_clipper ? clipper.DisplayStart : 0; const int item_end = use_clipper ? clipper.DisplayEnd : items.Size; for (int n = item_begin; n < item_end; n++) { if (show_in_table) ImGui::TableNextColumn(); const int item_id = items[n]; const char* item_category = ExampleNames[item_id % IM_COUNTOF(ExampleNames)]; char label[64]; sprintf(label, "Object %05d: %s", item_id, item_category); // IMPORTANT: for deletion refocus to work we need object ID to be stable, // aka not depend on their index in the list. Here we use our persistent item_id // instead of index to build a unique ID that will persist. // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion). ImGui::PushID(item_id); // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part // of the selection scope doesn't erroneously alter our selection. if (show_color_button) { ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz); ImGui::SameLine(); } // Submit item bool item_is_selected = selection.Contains((ImGuiID)n); bool item_is_open = false; ImGui::SetNextItemSelectionUserData(n); if (widget_type == WidgetType_Selectable) { ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None); } else if (widget_type == WidgetType_TreeNode) { ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; if (item_is_selected) tree_node_flags |= ImGuiTreeNodeFlags_Selected; item_is_open = ImGui::TreeNodeEx(label, tree_node_flags); } // Focus (for after deletion) if (item_curr_idx_to_focus == n) ImGui::SetKeyboardFocusHere(-1); // Drag and Drop if (use_drag_drop && ImGui::BeginDragDropSource()) { // Create payload with full selection OR single unselected item. // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease) if (ImGui::GetDragDropPayload() == NULL) { ImVector payload_items; void* it = NULL; ImGuiID id = 0; if (!item_is_selected) payload_items.push_back(item_id); else while (selection.GetNextSelectedItem(&it, &id)) payload_items.push_back((int)id); ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); } // Display payload content in tooltip const ImGuiPayload* payload = ImGui::GetDragDropPayload(); const int* payload_items = (int*)payload->Data; const int payload_count = (int)payload->DataSize / (int)sizeof(int); if (payload_count == 1) ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_COUNTOF(ExampleNames)]); else ImGui::Text("Dragging %d objects", payload_count); ImGui::EndDragDropSource(); } if (widget_type == WidgetType_TreeNode && item_is_open) ImGui::TreePop(); // Right-click: context menu if (ImGui::BeginPopupContextItem()) { ImGui::BeginDisabled(!use_deletion || selection.Size == 0); sprintf(label, "Delete %d item(s)###DeleteSelected", selection.Size); if (ImGui::Selectable(label)) request_deletion_from_menu = true; ImGui::EndDisabled(); ImGui::Selectable("Close"); ImGui::EndPopup(); } // Demo content within a table if (show_in_table) { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::InputText("##NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleVar(); } ImGui::PopID(); } if (!use_clipper) break; } if (show_in_table) { ImGui::EndTable(); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); } // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io); if (want_delete) selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); } ImGui::EndChild(); ImGui::TreePop(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsTabs() //----------------------------------------------------------------------------- static void EditTabBarFittingPolicyFlags(ImGuiTabBarFlags* p_flags) { if ((*p_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) *p_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyMixed", p_flags, ImGuiTabBarFlags_FittingPolicyMixed)) *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyMixed); if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyShrink", p_flags, ImGuiTabBarFlags_FittingPolicyShrink)) *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyShrink); if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", p_flags, ImGuiTabBarFlags_FittingPolicyScroll)) *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); } static void DemoWindowWidgetsTabs() { if (ImGui::TreeNode("Tabs")) { IMGUI_DEMO_MARKER("Widgets/Tabs"); if (ImGui::TreeNode("Basic")) { IMGUI_DEMO_MARKER("Widgets/Tabs/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")) { IMGUI_DEMO_MARKER("Widgets/Tabs/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", &tab_bar_flags, ImGuiTabBarFlags_Reorderable); ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", &tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs); ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton); ImGui::CheckboxFlags("ImGuiTabBarFlags_DrawSelectedOverline", &tab_bar_flags, ImGuiTabBarFlags_DrawSelectedOverline); EditTabBarFittingPolicyFlags(&tab_bar_flags); // Tab Bar ImGui::AlignTextToFramePadding(); ImGui::Text("Opened:"); 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_COUNTOF(opened); n++) { 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_COUNTOF(opened); n++) if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None)) { 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(); } if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags")) { IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags"); static ImVector active_tabs; static int next_tab_id = 0; if (next_tab_id == 0) // Initialize with some default tabs for (int i = 0; i < 3; i++) active_tabs.push_back(next_tab_id++); // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together. // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags... // but they tend to make more sense together) static bool show_leading_button = true; static bool show_trailing_button = true; ImGui::Checkbox("Show Leading TabItemButton()", &show_leading_button); ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button); // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyShrink; EditTabBarFittingPolicyFlags(&tab_bar_flags); if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) { // Demo a Leading TabItemButton(): click the "?" button to open a menu if (show_leading_button) if (ImGui::TabItemButton("?", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip)) ImGui::OpenPopup("MyHelpMenu"); if (ImGui::BeginPopup("MyHelpMenu")) { ImGui::Selectable("Hello!"); ImGui::EndPopup(); } // Demo Trailing Tabs: click the "+" button to add a new tab. // (In your app you may want to use a font icon instead of the "+") // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end. if (show_trailing_button) if (ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) active_tabs.push_back(next_tab_id++); // Add new tab // Submit our regular tabs for (int n = 0; n < active_tabs.Size; ) { bool open = true; char name[16]; snprintf(name, IM_COUNTOF(name), "%04d", active_tabs[n]); if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None)) { ImGui::Text("This is the %s tab!", name); ImGui::EndTabItem(); } if (!open) active_tabs.erase(active_tabs.Data + n); else n++; } ImGui::EndTabBar(); } ImGui::Separator(); ImGui::TreePop(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsText() //----------------------------------------------------------------------------- static void DemoWindowWidgetsText() { if (ImGui::TreeNode("Text")) { IMGUI_DEMO_MARKER("Widgets/Text"); if (ImGui::TreeNode("Colorful Text")) { IMGUI_DEMO_MARKER("Widgets/Text/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(); HelpMarker("The TextDisabled color is stored in ImGuiStyle."); ImGui::TreePop(); } if (ImGui::TreeNode("Font Size")) { IMGUI_DEMO_MARKER("Widgets/Text/Font Size"); ImGuiStyle& style = ImGui::GetStyle(); const float global_scale = style.FontScaleMain * style.FontScaleDpi; ImGui::Text("style.FontScaleMain = %0.2f", style.FontScaleMain); ImGui::Text("style.FontScaleDpi = %0.2f", style.FontScaleDpi); ImGui::Text("global_scale = ~%0.2f", global_scale); // This is not technically accurate as internal scales may apply, but conceptually let's pretend it is. ImGui::Text("FontSize = %0.2f", ImGui::GetFontSize()); ImGui::SeparatorText(""); static float custom_size = 16.0f; ImGui::SliderFloat("custom_size", &custom_size, 10.0f, 100.0f, "%.0f"); ImGui::Text("ImGui::PushFont(nullptr, custom_size);"); ImGui::PushFont(NULL, custom_size); ImGui::Text("FontSize = %.2f (== %.2f * global_scale)", ImGui::GetFontSize(), custom_size); ImGui::PopFont(); ImGui::SeparatorText(""); static float custom_scale = 1.0f; ImGui::SliderFloat("custom_scale", &custom_scale, 0.5f, 4.0f, "%.2f"); ImGui::Text("ImGui::PushFont(nullptr, style.FontSizeBase * custom_scale);"); ImGui::PushFont(NULL, style.FontSizeBase * custom_scale); ImGui::Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), custom_scale); ImGui::PopFont(); ImGui::SeparatorText(""); for (float scaling = 0.5f; scaling <= 4.0f; scaling += 0.5f) { ImGui::PushFont(NULL, style.FontSizeBase * scaling); ImGui::Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), scaling); ImGui::PopFont(); } ImGui::TreePop(); } if (ImGui::TreeNode("Word Wrapping")) { IMGUI_DEMO_MARKER("Widgets/Text/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"); ImDrawList* draw_list = ImGui::GetWindowDrawList(); for (int n = 0; n < 2; n++) { ImGui::Text("Test paragraph %d:", n); ImVec2 pos = ImGui::GetCursorScreenPos(); ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y); ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()); ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); if (n == 0) ImGui::Text("The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width); else ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh"); // Draw actual text bounding box, following by marker of our expected limit (should not overlap!) draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255)); draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255)); ImGui::PopTextWrapPos(); } ImGui::TreePop(); } if (ImGui::TreeNode("UTF-8 Text")) { IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text"); // UTF-8 test with Japanese characters // (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md 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. in Visual Studio, you // can save your source files 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 appear if the font was loaded with the appropriate CJK character ranges. " "Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. " "Read docs/FONTS.md for details."); ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); 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_COUNTOF(buf)); ImGui::TreePop(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsTextFilter() //----------------------------------------------------------------------------- static void DemoWindowWidgetsTextFilter() { if (ImGui::TreeNode("Text Filter")) { IMGUI_DEMO_MARKER("Widgets/Text Filter"); // Helper class to easy setup a text filter. // You may want to implement a more feature-full filtering scheme in your own application. HelpMarker("Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings."); static ImGuiTextFilter filter; ImGui::Text("Filter usage:\n" " \"\" display all lines\n" " \"xxx\" display lines containing \"xxx\"\n" " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n" " \"-xxx\" hide lines containing \"xxx\""); filter.Draw(); const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; for (int i = 0; i < IM_COUNTOF(lines); i++) if (filter.PassFilter(lines[i])) ImGui::BulletText("%s", lines[i]); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsTextInput() //----------------------------------------------------------------------------- static void DemoWindowWidgetsTextInput() { // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. if (ImGui::TreeNode("Text Input")) { IMGUI_DEMO_MARKER("Widgets/Text Input"); if (ImGui::TreeNode("Multi-line Text Input")) { IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input"); // WE ARE USING A FIXED-SIZE BUFFER FOR SIMPLICITY HERE. // If you want to use InputText() with std::string or any custom dynamic string type: // - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize. 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 because we don't want to include in here)"); ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap); ImGui::SameLine(); HelpMarker("Feature is currently in Beta. Please read comments in imgui.h"); ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput); ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets."); ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine); ImGui::InputTextMultiline("##source", text, IM_COUNTOF(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); } if (ImGui::TreeNode("Filtered Text Input")) { IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input"); struct TextFilters { // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback) static int FilterCasingSwap(ImGuiInputTextCallbackData* data) { if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase return 0; } // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out) static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_COUNTOF(buf1)); static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CharsDecimal); static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_COUNTOF(buf4), ImGuiInputTextFlags_CharsUppercase); static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_COUNTOF(buf5), ImGuiInputTextFlags_CharsNoBlank); static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_COUNTOF(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters. static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_COUNTOF(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters. ImGui::TreePop(); } if (ImGui::TreeNode("Password Input")) { IMGUI_DEMO_MARKER("Widgets/Text Input/Password input"); static char password[64] = "password123"; ImGui::InputText("password", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password); ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); ImGui::InputTextWithHint("password (w/ hint)", "", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password); ImGui::InputText("password (clear)", password, IM_COUNTOF(password)); ImGui::TreePop(); } if (ImGui::TreeNode("Completion, History, Edit Callbacks")) { IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks"); struct Funcs { static int MyCallback(ImGuiInputTextCallbackData* data) { if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion) { data->InsertChars(data->CursorPos, ".."); } else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory) { if (data->EventKey == ImGuiKey_UpArrow) { data->DeleteChars(0, data->BufTextLen); data->InsertChars(0, "Pressed Up!"); data->SelectAll(); } else if (data->EventKey == ImGuiKey_DownArrow) { data->DeleteChars(0, data->BufTextLen); data->InsertChars(0, "Pressed Down!"); data->SelectAll(); } } else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit) { // Toggle casing of first character char c = data->Buf[0]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32; data->BufDirty = true; // Increment a counter int* p_int = (int*)data->UserData; *p_int = *p_int + 1; } return 0; } }; static char buf1[64]; ImGui::InputText("Completion", buf1, IM_COUNTOF(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); ImGui::SameLine(); HelpMarker( "Here we append \"..\" each time Tab is pressed. " "See 'Examples>Console' for a more meaningful demonstration of using this callback."); static char buf2[64]; ImGui::InputText("History", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); ImGui::SameLine(); HelpMarker( "Here we replace and select text each time Up/Down are pressed. " "See 'Examples>Console' for a more meaningful demonstration of using this callback."); static char buf3[64]; static int edit_count = 0; ImGui::InputText("Edit", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); ImGui::SameLine(); HelpMarker( "Here we toggle the casing of the first character on every edit + count edits."); ImGui::SameLine(); ImGui::Text("(%d)", edit_count); ImGui::TreePop(); } if (ImGui::TreeNode("Resize Callback")) { IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback"); // To wire InputText() with std::string or any other custom string type, // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string. HelpMarker( "Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n" "See 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; } // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace. // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)' 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 ImGuiInputTextFlags flags = ImGuiInputTextFlags_None; ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap); static ImVector my_str; if (my_str.empty()) my_str.push_back(0); Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity()); ImGui::TreePop(); } if (ImGui::TreeNode("Eliding, Alignment")) { IMGUI_DEMO_MARKER("Widgets/Text Input/Eliding, Alignment"); static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp"; static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft; ImGui::CheckboxFlags("ImGuiInputTextFlags_ElideLeft", &flags, ImGuiInputTextFlags_ElideLeft); ImGui::InputText("Path", buf1, IM_COUNTOF(buf1), flags); ImGui::TreePop(); } if (ImGui::TreeNode("Miscellaneous")) { IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous"); static char buf1[16]; static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll; ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll); ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo); ImGui::InputText("Hello", buf1, IM_COUNTOF(buf1), flags); ImGui::TreePop(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsTooltips() //----------------------------------------------------------------------------- static void DemoWindowWidgetsTooltips() { if (ImGui::TreeNode("Tooltips")) { IMGUI_DEMO_MARKER("Widgets/Tooltips"); // Tooltips are windows following the mouse. They do not take focus away. ImGui::SeparatorText("General"); // Typical use cases: // - Short-form (text only): SetItemTooltip("Hello"); // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); } // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); } // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); } HelpMarker( "Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n" "We provide a helper SetItemTooltip() function to perform the two with standards flags."); ImVec2 sz = ImVec2(-FLT_MIN, 0.0f); ImGui::Button("Basic", sz); ImGui::SetItemTooltip("I am a tooltip"); ImGui::Button("Fancy", sz); if (ImGui::BeginItemTooltip()) { 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_COUNTOF(arr)); ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); ImGui::EndTooltip(); } ImGui::SeparatorText("Always On"); // Showcase NOT relying on a IsItemHovered() to emit a tooltip. // Here the tooltip is always emitted when 'always_on == true'. static int always_on = 0; ImGui::RadioButton("Off", &always_on, 0); ImGui::SameLine(); ImGui::RadioButton("Always On (Simple)", &always_on, 1); ImGui::SameLine(); ImGui::RadioButton("Always On (Advanced)", &always_on, 2); if (always_on == 1) ImGui::SetTooltip("I am following you around."); else if (always_on == 2 && ImGui::BeginTooltip()) { ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f)); ImGui::EndTooltip(); } ImGui::SeparatorText("Custom"); HelpMarker( "Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize " "tooltip activation details across your application. You may however decide to use custom " "flags for a specific tooltip instance."); // The following examples are passed for documentation purpose but may not be useful to most users. // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used. // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary. ImGui::Button("Manual", sz); if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ImGui::SetTooltip("I am a manually emitted tooltip."); ImGui::Button("DelayNone", sz); if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone)) ImGui::SetTooltip("I am a tooltip with no delay."); ImGui::Button("DelayShort", sz); if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay)) ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort); ImGui::Button("DelayLong", sz); if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui::GetStyle().HoverDelayNormal); ImGui::Button("Stationary", sz); if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary)) ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating."); // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav', // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag. ImGui::BeginDisabled(); ImGui::Button("Disabled item", sz); if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ImGui::SetTooltip("I am a a tooltip for a disabled item."); ImGui::EndDisabled(); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsTreeNodes() //----------------------------------------------------------------------------- static void DemoWindowWidgetsTreeNodes() { if (ImGui::TreeNode("Tree Nodes")) { IMGUI_DEMO_MARKER("Widgets/Tree Nodes"); // See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree. if (ImGui::TreeNode("Basic trees")) { IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees"); for (int i = 0; i < 5; i++) { // Use SetNextItemOpen() so set the default state of a node to be open. We could // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing! if (i == 0) ImGui::SetNextItemOpen(true, ImGuiCond_Once); // Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict. // An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)', // aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine. ImGui::PushID(i); if (ImGui::TreeNode("", "Child %d", i)) { ImGui::Text("blah blah"); ImGui::SameLine(); if (ImGui::SmallButton("button")) {} ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } if (ImGui::TreeNode("Hierarchy lines")) { IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines"); static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen; HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags"); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes); if (ImGui::TreeNodeEx("Parent", base_flags)) { if (ImGui::TreeNodeEx("Child 1", base_flags)) { ImGui::Button("Button for Child 1"); ImGui::TreePop(); } if (ImGui::TreeNodeEx("Child 2", base_flags)) { ImGui::Button("Button for Child 2"); ImGui::TreePop(); } ImGui::Text("Remaining contents"); ImGui::Text("Remaining contents"); ImGui::TreePop(); } ImGui::TreePop(); } if (ImGui::TreeNode("Clipping Large Trees")) { IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Clipping Large Trees"); ImGui::TextWrapped( "- Using ImGuiListClipper with trees is a less easy than on arrays or grids.\n" "- Refer to 'Demo->Examples->Property Editor' for an example of how to do that.\n" "- Discuss in #3823"); ImGui::TreePop(); } if (ImGui::TreeNode("Advanced, with Selectable nodes")) { IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes"); HelpMarker( "This is a more typical looking tree with selectable nodes.\n" "Click to select, Ctrl+Click to toggle, click on arrows or double-click to open."); static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; static bool align_label_with_current_x_position = false; static bool test_drag_and_drop = false; ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &base_flags, ImGuiTreeNodeFlags_SpanLabelWidth); ImGui::SameLine(); HelpMarker("Reduce hit area to the text label and a bit of margin."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)"); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_FramePadding", &base_flags, ImGuiTreeNodeFlags_FramePadding); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsToParent", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsToParent); HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags"); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes); ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position); ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop); ImGui::Text("Hello!"); if (align_label_with_current_x_position) ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); // 'selection_mask' is dumb representation of what may be user-side selection state. // You may retain selection state inside or outside your objects in whatever format you see fit. // 'node_clicked' is 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. static int selection_mask = (1 << 2); int node_clicked = -1; for (int i = 0; i < 6; i++) { // Disable the default "open on single-click behavior" + set Selected flag according to our selection. // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. ImGuiTreeNodeFlags node_flags = base_flags; const bool is_selected = (selection_mask & (1 << i)) != 0; if (is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; if (i < 3) { // Items 0..2 are Tree Node bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { ImGui::SetDragDropPayload("_TREENODE", NULL, 0); ImGui::Text("This is a drag and drop source"); ImGui::EndDragDropSource(); } if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth)) { // Item 2 has an additional inline button to help demonstrate SpanLabelWidth. ImGui::SameLine(); if (ImGui::SmallButton("button")) {} } if (node_open) { ImGui::BulletText("Blah blah\nBlah Blah"); ImGui::SameLine(); ImGui::SmallButton("Button"); ImGui::TreePop(); } } else { // Items 3..5 are Tree Leaves // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { ImGui::SetDragDropPayload("_TREENODE", NULL, 0); ImGui::Text("This is a drag and drop source"); ImGui::EndDragDropSource(); } } } if (node_clicked != -1) { // 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, may want to preserve selection when clicking on item that is part of the selection selection_mask = (1 << node_clicked); // Click to single-select } if (align_label_with_current_x_position) ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ImGui::TreePop(); } ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsVerticalSliders() //----------------------------------------------------------------------------- static void DemoWindowWidgetsVerticalSliders() { if (ImGui::TreeNode("Vertical Sliders")) { IMGUI_DEMO_MARKER("Widgets/Vertical Sliders"); 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 (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(); 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, (float)(int)((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::PopID(); 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(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgets() //----------------------------------------------------------------------------- static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data) { //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (!ImGui::CollapsingHeader("Widgets")) return; // IMGUI_DEMO_MARKER("Widgets"); const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the "Disabled" section at the bottom if (disable_all) ImGui::BeginDisabled(); DemoWindowWidgetsBasic(); DemoWindowWidgetsBullets(); DemoWindowWidgetsCollapsingHeaders(); DemoWindowWidgetsComboBoxes(); DemoWindowWidgetsColorAndPickers(); DemoWindowWidgetsDataTypes(); if (disable_all) ImGui::EndDisabled(); DemoWindowWidgetsDisableBlocks(demo_data); if (disable_all) ImGui::BeginDisabled(); DemoWindowWidgetsDragAndDrop(); DemoWindowWidgetsDragsAndSliders(); DemoWindowWidgetsFonts(); DemoWindowWidgetsImages(); DemoWindowWidgetsListBoxes(); DemoWindowWidgetsMultiComponents(); DemoWindowWidgetsPlotting(); DemoWindowWidgetsProgressBars(); DemoWindowWidgetsQueryingStatuses(); DemoWindowWidgetsSelectables(); DemoWindowWidgetsSelectionAndMultiSelect(demo_data); DemoWindowWidgetsTabs(); DemoWindowWidgetsText(); DemoWindowWidgetsTextFilter(); DemoWindowWidgetsTextInput(); DemoWindowWidgetsTooltips(); DemoWindowWidgetsTreeNodes(); DemoWindowWidgetsVerticalSliders(); if (disable_all) ImGui::EndDisabled(); } //----------------------------------------------------------------------------- // [SECTION] DemoWindowLayout() //----------------------------------------------------------------------------- static void DemoWindowLayout() { if (!ImGui::CollapsingHeader("Layout & Scrolling")) return; if (ImGui::TreeNode("Child windows")) { IMGUI_DEMO_MARKER("Layout/Child windows"); ImGui::SeparatorText("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); ImGui::Checkbox("Disable Menu", &disable_menu); // Child 1: no border, enable horizontal scrollbar { ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; if (disable_mouse_wheel) window_flags |= ImGuiWindowFlags_NoScrollWithMouse; ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), ImGuiChildFlags_None, window_flags); for (int i = 0; i < 100; i++) ImGui::Text("%04d: scrollable region", i); ImGui::EndChild(); } ImGui::SameLine(); // Child 2: rounded border { ImGuiWindowFlags window_flags = ImGuiWindowFlags_None; if (disable_mouse_wheel) window_flags |= ImGuiWindowFlags_NoScrollWithMouse; if (!disable_menu) window_flags |= ImGuiWindowFlags_MenuBar; ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Borders, window_flags); if (!disable_menu && ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Menu")) { ShowExampleMenuFile(); ImGui::EndMenu(); } ImGui::EndMenuBar(); } if (ImGui::BeginTable("split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings)) { for (int i = 0; i < 100; i++) { char buf[32]; sprintf(buf, "%03d", i); ImGui::TableNextColumn(); ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); } ImGui::EndTable(); } ImGui::EndChild(); ImGui::PopStyleVar(); } // Child 3: manual-resize ImGui::SeparatorText("Manual-resize"); { HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents."); //if (ImGui::Button("Set Height to 200")) // ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) for (int n = 0; n < 10; n++) ImGui::Text("Line %04d", n); ImGui::PopStyleColor(); ImGui::EndChild(); } // Child 4: auto-resizing height with a limit ImGui::SeparatorText("Auto-resize with constraints"); { static int draw_lines = 3; static int max_height_in_lines = 10; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("Lines Count", &draw_lines, 0.2f); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines)); if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY)) for (int n = 0; n < draw_lines; n++) ImGui::Text("Line %04d", n); ImGui::EndChild(); } ImGui::SeparatorText("Misc/Advanced"); // Demonstrate a few extra things // - Changing ImGuiCol_ChildBg (which is transparent black in default styles) // - Using SetCursorPos() to position child window (the child window is an item from the POV of 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 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details. { static int offset_x = 0; static bool override_bg_color = true; static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); ImGui::Checkbox("Override ChildBg color", &override_bg_color); ImGui::CheckboxFlags("ImGuiChildFlags_Borders", &child_flags, ImGuiChildFlags_Borders); ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding); ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX); ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY); ImGui::CheckboxFlags("ImGuiChildFlags_FrameStyle", &child_flags, ImGuiChildFlags_FrameStyle); ImGui::SameLine(); HelpMarker("Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding."); if (child_flags & ImGuiChildFlags_FrameStyle) override_bg_color = false; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x); if (override_bg_color) ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100)); ImGui::BeginChild("Red", ImVec2(200, 100), child_flags, ImGuiWindowFlags_None); if (override_bg_color) ImGui::PopStyleColor(); for (int n = 0; n < 50; n++) ImGui::Text("Some test %d", n); ImGui::EndChild(); bool child_is_hovered = ImGui::IsItemHovered(); ImVec2 child_rect_min = ImGui::GetItemRectMin(); ImVec2 child_rect_max = ImGui::GetItemRectMax(); ImGui::Text("Hovered: %d", child_is_hovered); 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(); } if (ImGui::TreeNode("Widgets Width")) { IMGUI_DEMO_MARKER("Layout/Widgets Width"); static float f = 0.0f; static bool show_indented_items = true; ImGui::Checkbox("Show indented items", &show_indented_items); // Use SetNextItemWidth() to set the width of a single upcoming item. // Use PushItemWidth()/PopItemWidth() to set the width of a group of items. // In real code use you'll probably want to choose width values that are proportional to your font size // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc. ImGui::Text("SetNextItemWidth/PushItemWidth(100)"); ImGui::SameLine(); HelpMarker("Fixed width."); ImGui::PushItemWidth(100); ImGui::DragFloat("float##1b", &f); if (show_indented_items) { ImGui::Indent(); ImGui::DragFloat("float (indented)##1b", &f); ImGui::Unindent(); } ImGui::PopItemWidth(); ImGui::Text("SetNextItemWidth/PushItemWidth(-100)"); ImGui::SameLine(); HelpMarker("Align to right edge minus 100"); ImGui::PushItemWidth(-100); ImGui::DragFloat("float##2a", &f); if (show_indented_items) { ImGui::Indent(); ImGui::DragFloat("float (indented)##2b", &f); ImGui::Unindent(); } ImGui::PopItemWidth(); ImGui::Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)"); ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); ImGui::DragFloat("float##3a", &f); if (show_indented_items) { ImGui::Indent(); ImGui::DragFloat("float (indented)##3b", &f); ImGui::Unindent(); } ImGui::PopItemWidth(); ImGui::Text("SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)"); ImGui::SameLine(); HelpMarker("Align to right edge minus half"); ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); ImGui::DragFloat("float##4a", &f); if (show_indented_items) { ImGui::Indent(); ImGui::DragFloat("float (indented)##4b", &f); ImGui::Unindent(); } ImGui::PopItemWidth(); ImGui::Text("SetNextItemWidth/PushItemWidth(-Min(GetContentRegionAvail().x * 0.40f, GetFontSize() * 12))"); ImGui::PushItemWidth(-IM_MIN(ImGui::GetFontSize() * 12, ImGui::GetContentRegionAvail().x * 0.40f)); ImGui::DragFloat("float##5a", &f); if (show_indented_items) { ImGui::Indent(); ImGui::DragFloat("float (indented)##5b", &f); ImGui::Unindent(); } ImGui::PopItemWidth(); // Demonstrate using PushItemWidth to surround three items. // Calling SetNextItemWidth() before each of them would have the same effect. ImGui::Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)"); ImGui::SameLine(); HelpMarker("Align to right edge"); ImGui::PushItemWidth(-FLT_MIN); ImGui::DragFloat("##float6a", &f); if (show_indented_items) { ImGui::Indent(); ImGui::DragFloat("float (indented)##6b", &f); ImGui::Unindent(); } ImGui::PopItemWidth(); ImGui::TreePop(); } if (ImGui::TreeNode("Basic Horizontal Layout")) { IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout"); ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); // Text IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine"); 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_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)"); 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 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)"); 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(ImGui::CalcTextSize("AAAAAAA").x); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; static int item = -1; ImGui::Combo("Combo", &item, items, IM_COUNTOF(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::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_COUNTOF(items)); ImGui::PopID(); //ImGui::SetItemTooltip("ListBox %d hovered", i); } ImGui::PopItemWidth(); // Dummy IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/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_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping"); ImGui::Text("Manual wrapping:"); ImGuiStyle& style = ImGui::GetStyle(); int buttons_count = 20; float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().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::TreePop(); } if (ImGui::TreeNode("Groups")) { IMGUI_DEMO_MARKER("Layout/Groups"); HelpMarker( "BeginGroup() basically locks the horizontal position for new line. " "EndGroup() bundles the whole group so that you can use \"item\" functions such as " "IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group."); 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(); ImGui::SetItemTooltip("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_COUNTOF(values), 0, NULL, 0.0f, 1.0f, size); 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::Button("LEVERAGE\nBUZZWORD", size); ImGui::SameLine(); if (ImGui::BeginListBox("List", size)) { ImGui::Selectable("Selected", true); ImGui::Selectable("Not Selected", false); ImGui::EndListBox(); } ImGui::TreePop(); } if (ImGui::TreeNode("Text Baseline Alignment")) { IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment"); { ImGui::BulletText("Text baseline:"); ImGui::SameLine(); 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 use less vertical space than lines with framed widgets."); ImGui::Indent(); ImGui::Text("KO Blahblah"); ImGui::SameLine(); ImGui::Button("Some framed item"); ImGui::SameLine(); HelpMarker("Baseline of button will look misaligned with text.."); // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets. // (because we don't know what's coming after the Text() statement, we need to move the text baseline // down by FramePadding.y ahead of time) ImGui::AlignTextToFramePadding(); ImGui::Text("OK Blahblah"); ImGui::SameLine(); ImGui::Button("Some framed item##2"); ImGui::SameLine(); HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y"); // SmallButton() uses the same vertical padding as Text ImGui::Button("TEST##1"); ImGui::SameLine(); ImGui::Text("TEST"); ImGui::SameLine(); ImGui::SmallButton("TEST##2"); // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets. ImGui::AlignTextToFramePadding(); ImGui::Text("Text aligned to framed item"); ImGui::SameLine(); ImGui::Button("Item##1"); ImGui::SameLine(); ImGui::Text("Item"); ImGui::SameLine(); ImGui::SmallButton("Item##2"); ImGui::SameLine(); ImGui::Button("Item##3"); ImGui::Unindent(); } ImGui::Spacing(); { ImGui::BulletText("Multi-line text:"); ImGui::Indent(); 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::Unindent(); } ImGui::Spacing(); { ImGui::BulletText("Misc items:"); ImGui::Indent(); // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button. ImGui::Button("80x80", ImVec2(80, 80)); ImGui::SameLine(); ImGui::Button("50x50", ImVec2(50, 50)); ImGui::SameLine(); ImGui::Button("Button()"); ImGui::SameLine(); ImGui::SmallButton("SmallButton()"); // Tree // (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline) const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; ImGui::Button("Button##1"); // Will make line higher ImGui::SameLine(0.0f, spacing); if (ImGui::TreeNodeEx("Node##1", ImGuiTreeNodeFlags_DrawLinesNone)) { // Placeholder tree data for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } const float padding = (float)(int)(ImGui::GetFontSize() * 1.20f); // Large padding ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, padding); ImGui::Button("Button##2"); ImGui::PopStyleVar(); ImGui::SameLine(0.0f, spacing); if (ImGui::TreeNodeEx("Node##2", ImGuiTreeNodeFlags_DrawLinesNone)) ImGui::TreePop(); // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. // Otherwise you can use SmallButton() (smaller fit). ImGui::AlignTextToFramePadding(); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add // other contents "inside" the node. bool node_open = ImGui::TreeNode("Node##3"); ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##3"); if (node_open) { // Placeholder tree data for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Bullet ImGui::Button("Button##4"); ImGui::SameLine(0.0f, spacing); ImGui::BulletText("Bullet text"); ImGui::AlignTextToFramePadding(); ImGui::BulletText("Node"); ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##5"); ImGui::Unindent(); } ImGui::TreePop(); } if (ImGui::TreeNode("Scrolling")) { IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical"); // Vertical scroll functions HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position."); static int track_item = 50; static bool enable_track = true; static bool enable_extra_decorations = false; static float scroll_to_off_px = 0.0f; static float scroll_to_pos_px = 200.0f; ImGui::Checkbox("Decoration", &enable_extra_decorations); ImGui::PushItemWidth(ImGui::GetFontSize() * 10); enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); ImGui::SameLine(); ImGui::Checkbox("Track", &enable_track); bool scroll_to_off = ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); ImGui::SameLine(); scroll_to_off |= ImGui::Button("Scroll Offset"); bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); ImGui::SameLine(); scroll_to_pos |= ImGui::Button("Scroll To Pos"); ImGui::PopItemWidth(); if (scroll_to_off || scroll_to_pos) enable_track = false; ImGuiStyle& style = ImGui::GetStyle(); float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5; if (child_w < 1.0f) child_w = 1.0f; ImGui::PushID("##VerticalScrolling"); for (int i = 0; i < 5; i++) { if (i > 0) ImGui::SameLine(); ImGui::BeginGroup(); const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" }; ImGui::TextUnformatted(names[i]); const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0; const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Borders, child_flags); if (ImGui::BeginMenuBar()) { ImGui::TextUnformatted("abc"); ImGui::EndMenuBar(); } if (scroll_to_off) ImGui::SetScrollY(scroll_to_off_px); if (scroll_to_pos) ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f); if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items { for (int item = 0; item < 100; item++) { if (enable_track && item == track_item) { ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom } else { ImGui::Text("Item %d", item); } } } float scroll_y = ImGui::GetScrollY(); float scroll_max_y = ImGui::GetScrollMaxY(); ImGui::EndChild(); ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y); ImGui::EndGroup(); } ImGui::PopID(); // Horizontal scroll functions IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal"); ImGui::Spacing(); HelpMarker( "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n" "Because the clipping rectangle of most window hides half worth of WindowPadding on the " "left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the " "equivalent SetScrollFromPosY(+1) wouldn't."); ImGui::PushID("##HorizontalScrolling"); for (int i = 0; i < 5; i++) { float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f; ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0); ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Borders, child_flags); if (scroll_to_off) ImGui::SetScrollX(scroll_to_off_px); if (scroll_to_pos) ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f); if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items { for (int item = 0; item < 100; item++) { if (item > 0) ImGui::SameLine(); if (enable_track && item == track_item) { ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right } else { ImGui::Text("Item %d", item); } } } float scroll_x = ImGui::GetScrollX(); float scroll_max_x = ImGui::GetScrollMaxX(); ImGui::EndChild(); ImGui::SameLine(); const char* names[] = { "Left", "25%", "Center", "75%", "Right" }; ImGui::Text("%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x); ImGui::Spacing(); } ImGui::PopID(); // Miscellaneous Horizontal Scrolling Demo IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)"); HelpMarker( "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n" "You may want to also explicitly specify content width by using 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)); ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30); ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Borders, 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(); float 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) { // 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::BeginChild("scrolling"); ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); ImGui::EndChild(); } ImGui::Spacing(); static bool show_horizontal_contents_size_demo_window = false; ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window); if (show_horizontal_contents_size_demo_window) { static bool show_h_scrollbar = true; static bool show_button = true; static bool show_tree_nodes = true; static bool show_text_wrapped = false; static bool show_columns = true; static bool show_tab_bar = true; static bool show_child = false; static bool explicit_content_size = false; static float contents_size_x = 300.0f; if (explicit_content_size) ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f)); ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0); IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window"); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0)); HelpMarker( "Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n" "Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); ImGui::Checkbox("H-scrollbar", &show_h_scrollbar); ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten) ImGui::Checkbox("Tree nodes", &show_tree_nodes); // Will grow contents size and display highlight over full width ImGui::Checkbox("Text wrapped", &show_text_wrapped);// Will grow and use contents size ImGui::Checkbox("Columns", &show_columns); // Will use contents size ImGui::Checkbox("Tab bar", &show_tab_bar); // Will use contents size ImGui::Checkbox("Child", &show_child); // Will grow and use contents size ImGui::Checkbox("Explicit content size", &explicit_content_size); ImGui::Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY()); if (explicit_content_size) { ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::CalcTextSize("123456").x); ImGui::DragFloat("##csx", &contents_size_x); ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE); ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE); ImGui::Dummy(ImVec2(0, 10)); } ImGui::PopStyleVar(2); ImGui::Separator(); if (show_button) { ImGui::Button("this is a 300-wide button", ImVec2(300, 0)); } if (show_tree_nodes) { bool open = true; if (ImGui::TreeNode("this is a tree node")) { if (ImGui::TreeNode("another one of those tree node...")) { ImGui::Text("Some tree contents"); ImGui::TreePop(); } ImGui::TreePop(); } ImGui::CollapsingHeader("CollapsingHeader", &open); } if (show_text_wrapped) { ImGui::TextWrapped("This text should automatically wrap on the edge of the work rectangle."); } if (show_columns) { ImGui::Text("Tables:"); if (ImGui::BeginTable("table", 4, ImGuiTableFlags_Borders)) { for (int n = 0; n < 4; n++) { ImGui::TableNextColumn(); ImGui::Text("Width %.2f", ImGui::GetContentRegionAvail().x); } ImGui::EndTable(); } ImGui::Text("Columns:"); ImGui::Columns(4); for (int n = 0; n < 4; n++) { ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); ImGui::NextColumn(); } ImGui::Columns(1); } if (show_tab_bar && ImGui::BeginTabBar("Hello")) { if (ImGui::BeginTabItem("OneOneOne")) { ImGui::EndTabItem(); } if (ImGui::BeginTabItem("TwoTwoTwo")) { ImGui::EndTabItem(); } if (ImGui::BeginTabItem("ThreeThreeThree")) { ImGui::EndTabItem(); } if (ImGui::BeginTabItem("FourFourFour")) { ImGui::EndTabItem(); } ImGui::EndTabBar(); } if (show_child) { ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Borders); ImGui::EndChild(); } ImGui::End(); } ImGui::TreePop(); } if (ImGui::TreeNode("Text Clipping")) { IMGUI_DEMO_MARKER("Layout/Text Clipping"); static ImVec2 size(100.0f, 100.0f); static ImVec2 offset(30.0f, 30.0f); ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f"); ImGui::TextWrapped("(Click and drag to scroll)"); HelpMarker( "(Left) Using ImGui::PushClipRect():\n" "Will alter ImGui hit-testing logic + ImDrawList rendering.\n" "(use this if you want your clipping rectangle to affect interactions)\n\n" "(Center) Using ImDrawList::PushClipRect():\n" "Will alter ImDrawList rendering only.\n" "(use this as a shortcut if you are only using ImDrawList calls)\n\n" "(Right) Using ImDrawList::AddText() with a fine ClipRect:\n" "Will alter only this specific ImDrawList::AddText() rendering.\n" "This is often used internally to avoid altering the clipping rectangle and minimize draw calls."); for (int n = 0; n < 3; n++) { if (n > 0) ImGui::SameLine(); ImGui::PushID(n); ImGui::InvisibleButton("##canvas", size); if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { offset.x += ImGui::GetIO().MouseDelta.x; offset.y += ImGui::GetIO().MouseDelta.y; } ImGui::PopID(); if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped. continue; const ImVec2 p0 = ImGui::GetItemRectMin(); const ImVec2 p1 = ImGui::GetItemRectMax(); const char* text_str = "Line 1 hello\nLine 2 clip me!"; const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y); ImDrawList* draw_list = ImGui::GetWindowDrawList(); switch (n) { case 0: ImGui::PushClipRect(p0, p1, true); draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); draw_list->AddText(text_pos, IM_COL32_WHITE, text_str); ImGui::PopClipRect(); break; case 1: draw_list->PushClipRect(p0, p1, true); draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); draw_list->AddText(text_pos, IM_COL32_WHITE, text_str); draw_list->PopClipRect(); break; case 2: ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert. draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect); break; } } ImGui::TreePop(); } if (ImGui::TreeNode("Overlap Mode")) { IMGUI_DEMO_MARKER("Layout/Overlap Mode"); static bool enable_allow_overlap = true; HelpMarker( "Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n" "By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. " "Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state."); ImGui::Checkbox("Enable AllowOverlap", &enable_allow_overlap); ImVec2 button1_pos = ImGui::GetCursorScreenPos(); ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f); if (enable_allow_overlap) ImGui::SetNextItemAllowOverlap(); ImGui::Button("Button 1", ImVec2(80, 80)); ImGui::SetCursorScreenPos(button2_pos); ImGui::Button("Button 2", ImVec2(80, 80)); // This is typically used with width-spanning items. // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.) if (enable_allow_overlap) ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Some Selectable", false); ImGui::SameLine(); ImGui::SmallButton("++"); ImGui::TreePop(); } } //----------------------------------------------------------------------------- // [SECTION] DemoWindowPopups() //----------------------------------------------------------------------------- static void DemoWindowPopups() { if (!ImGui::CollapsingHeader("Popups & Modal windows")) return; // 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 Dear 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. // 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_DEMO_MARKER("Popups/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::SeparatorText("Aquarium"); for (int i = 0; i < IM_COUNTOF(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_COUNTOF(names); i++) ImGui::MenuItem(names[i], "", &toggles[i]); if (ImGui::BeginMenu("Sub-menu")) { ImGui::MenuItem("Click me"); ImGui::EndMenu(); } ImGui::Separator(); ImGui::Text("Tooltip here"); ImGui::SetItemTooltip("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_COUNTOF(names); i++) ImGui::MenuItem(names[i], "", &toggles[i]); if (ImGui::BeginMenu("Sub-menu")) { ImGui::MenuItem("Click me"); if (ImGui::Button("Stacked Popup")) ImGui::OpenPopup("another popup"); if (ImGui::BeginPopup("another popup")) { ImGui::Text("I am the last one here."); ImGui::EndPopup(); } ImGui::EndMenu(); } ImGui::EndPopup(); } ImGui::EndPopup(); } // Call the more complete ShowExampleMenuFile which we use in various places of this demo if (ImGui::Button("With a menu..")) ImGui::OpenPopup("my_file_popup"); if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar)) { if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) { ShowExampleMenuFile(); ImGui::EndMenu(); } if (ImGui::BeginMenu("Edit")) { ImGui::MenuItem("Dummy"); ImGui::EndMenu(); } ImGui::EndMenuBar(); } ImGui::Text("Hello from popup!"); ImGui::Button("This is a dummy button.."); ImGui::EndPopup(); } ImGui::TreePop(); } if (ImGui::TreeNode("Context menus")) { IMGUI_DEMO_MARKER("Popups/Context menus"); HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier."); // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: // if (id == 0) // id = GetItemID(); // Use last item id // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) // OpenPopup(id); // return BeginPopup(id); // For advanced uses you may want to replicate and customize this code. // See more details in BeginPopupContextItem(). // Example 1 // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(), // and BeginPopupContextItem() will use the last item ID as the popup ID. { const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" }; static int selected = -1; for (int n = 0; n < 5; n++) { if (ImGui::Selectable(names[n], selected == n)) selected = n; if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id { selected = n; ImGui::Text("This is a popup for \"%s\"!", names[n]); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } ImGui::SetItemTooltip("Right-click to open popup"); } } // Example 2 // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem(). // Using an explicit identifier is also convenient if you want to activate the popups from different locations. { HelpMarker("Text() elements don't have stable identifiers so we need to provide one."); static float value = 0.5f; ImGui::Text("Value = %.3f <-- (1) right-click this text", value); if (ImGui::BeginPopupContextItem("my popup")) { if (ImGui::Selectable("Set to zero")) value = 0.0f; if (ImGui::Selectable("Set to PI")) value = 3.1415f; ImGui::SetNextItemWidth(-FLT_MIN); ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); ImGui::EndPopup(); } // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup. // Here we make it that right-clicking this other text element opens the same popup as above. // The popup itself will be submitted by the code above. ImGui::Text("(2) Or right-click this text"); ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight); // Back to square one: manually open the same popup. if (ImGui::Button("(3) Or click this button")) ImGui::OpenPopup("my popup"); } // Example 3 // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID), // we need to make sure your item identifier is stable. // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ). { HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator."); 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()) { ImGui::Text("Edit name:"); ImGui::InputText("##edit", name, IM_COUNTOF(name)); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); } ImGui::TreePop(); } if (ImGui::TreeNode("Modals")) { IMGUI_DEMO_MARKER("Popups/Modals"); ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside."); if (ImGui::Button("Delete..")) ImGui::OpenPopup("Delete?"); // Always center this window when appearing ImVec2 center = ImGui::GetMainViewport()->GetCenter(); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!"); ImGui::Separator(); //static int unused_i = 0; //ImGui::Combo("Combo", &unused_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(); 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", NULL, ImGuiWindowFlags_MenuBar)) { if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Some 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; static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; 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"); // 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 unused_open = true; if (ImGui::BeginPopupModal("Stacked 2", &unused_open)) { ImGui::Text("Hello from Stacked The Second!"); ImGui::ColorEdit4("Color", color); // Allow opening another nested popup if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } ImGui::TreePop(); } if (ImGui::TreeNode("Menus inside a regular window")) { IMGUI_DEMO_MARKER("Popups/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(); ImGui::MenuItem("Menu item", "Ctrl+M"); if (ImGui::BeginMenu("Menu inside a regular window")) { ShowExampleMenuFile(); ImGui::EndMenu(); } ImGui::Separator(); ImGui::TreePop(); } } // Dummy data structure that we use for the Table demo. // (pre-C++11 doesn't allow us to instantiate ImVector template if this structure is defined inside the demo function) namespace { // We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code. // This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID. // But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex) // If you don't use sorting, you will generally never care about giving column an ID! enum MyItemColumnID { MyItemColumnID_ID, MyItemColumnID_Name, MyItemColumnID_Action, MyItemColumnID_Quantity, MyItemColumnID_Description }; struct MyItem { int ID; const char* Name; int Quantity; // We have a problem which is affecting _only this demo_ and should not affect your code: // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(), // however qsort doesn't allow passing user data to comparing function. // As a workaround, we are storing the sort specs in a static/global for the comparing function to access. // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global. // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called // very often by the sorting algorithm it would be a little wasteful. static const ImGuiTableSortSpecs* s_current_sort_specs; static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count) { s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function. if (items_count > 1) qsort(items, (size_t)items_count, sizeof(items[0]), MyItem::CompareWithSortSpecs); s_current_sort_specs = NULL; } // Compare function to be used by qsort() static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs) { const MyItem* a = (const MyItem*)lhs; const MyItem* b = (const MyItem*)rhs; for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) { // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn() // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler! const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n]; int delta = 0; switch (sort_spec->ColumnUserID) { case MyItemColumnID_ID: delta = (a->ID - b->ID); break; case MyItemColumnID_Name: delta = (strcmp(a->Name, b->Name)); break; case MyItemColumnID_Quantity: delta = (a->Quantity - b->Quantity); break; case MyItemColumnID_Description: delta = (strcmp(a->Name, b->Name)); break; default: IM_ASSERT(0); break; } if (delta > 0) return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1; if (delta < 0) return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; } // qsort() is instable so always return a way to differentiate items. // Your own compare function may want to avoid fallback on implicit sort specs. // e.g. a Name compare if it wasn't already part of the sort specs. return a->ID - b->ID; } }; const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL; } // Make the UI compact because there are so many fields static void PushStyleCompact() { ImGuiStyle& style = ImGui::GetStyle(); ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, (float)(int)(style.FramePadding.y * 0.60f)); ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, (float)(int)(style.ItemSpacing.y * 0.60f)); } static void PopStyleCompact() { ImGui::PopStyleVar(2); } // Show a combo box with a choice of sizing policies static void EditTableSizingFlags(ImGuiTableFlags* p_flags) { struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; }; static const EnumDesc policies[] = { { ImGuiTableFlags_None, "Default", "Use default sizing policy:\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n- ImGuiTableFlags_SizingStretchSame otherwise." }, { ImGuiTableFlags_SizingFixedFit, "ImGuiTableFlags_SizingFixedFit", "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." }, { ImGuiTableFlags_SizingFixedSame, "ImGuiTableFlags_SizingFixedSame", "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." }, { ImGuiTableFlags_SizingStretchProp, "ImGuiTableFlags_SizingStretchProp", "Columns default to _WidthStretch with weights proportional to their widths." }, { ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame", "Columns default to _WidthStretch with same weights." } }; int idx; for (idx = 0; idx < IM_COUNTOF(policies); idx++) if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_)) break; const char* preview_text = (idx < IM_COUNTOF(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : ""; if (ImGui::BeginCombo("Sizing Policy", preview_text)) { for (int n = 0; n < IM_COUNTOF(policies); n++) if (ImGui::Selectable(policies[n].Name, idx == n)) *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value; ImGui::EndCombo(); } ImGui::SameLine(); ImGui::TextDisabled("(?)"); if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); for (int m = 0; m < IM_COUNTOF(policies); m++) { ImGui::Separator(); ImGui::Text("%s:", policies[m].Name); ImGui::Separator(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f); ImGui::TextUnformatted(policies[m].Tooltip); } ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } } static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) { ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker("Master disable flag (also hide from context menu)"); ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide); ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort); if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch)) *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch); if (ImGui::CheckboxFlags("_WidthFixed", p_flags, ImGuiTableColumnFlags_WidthFixed)) *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed); ImGui::CheckboxFlags("_NoResize", p_flags, ImGuiTableColumnFlags_NoResize); ImGui::CheckboxFlags("_NoReorder", p_flags, ImGuiTableColumnFlags_NoReorder); ImGui::CheckboxFlags("_NoHide", p_flags, ImGuiTableColumnFlags_NoHide); ImGui::CheckboxFlags("_NoClip", p_flags, ImGuiTableColumnFlags_NoClip); ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort); ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending); ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending); ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel); ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth); ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending); ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending); ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0"); ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0"); ImGui::CheckboxFlags("_AngledHeader", p_flags, ImGuiTableColumnFlags_AngledHeader); } static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) { ImGui::CheckboxFlags("_IsEnabled", &flags, ImGuiTableColumnFlags_IsEnabled); ImGui::CheckboxFlags("_IsVisible", &flags, ImGuiTableColumnFlags_IsVisible); ImGui::CheckboxFlags("_IsSorted", &flags, ImGuiTableColumnFlags_IsSorted); ImGui::CheckboxFlags("_IsHovered", &flags, ImGuiTableColumnFlags_IsHovered); } //----------------------------------------------------------------------------- // [SECTION] DemoWindowTables() //----------------------------------------------------------------------------- static void DemoWindowTables() { //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (!ImGui::CollapsingHeader("Tables & Columns")) return; // Using those as a base value to create width/height that are factor of the size of our font const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); ImGui::PushID("Tables"); int open_action = -1; if (ImGui::Button("Expand all")) open_action = 1; ImGui::SameLine(); if (ImGui::Button("Collapse all")) open_action = 0; ImGui::SameLine(); // Options static bool disable_indent = false; ImGui::Checkbox("Disable tree indentation", &disable_indent); ImGui::SameLine(); HelpMarker("Disable the indenting of tree nodes so demo tables can use the full window width."); ImGui::Separator(); if (disable_indent) ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f); // About Styling of tables // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs. // There are however a few settings that a shared and part of the ImGuiStyle structure: // style.CellPadding // Padding within each cell // style.Colors[ImGuiCol_TableHeaderBg] // Table header background // style.Colors[ImGuiCol_TableBorderStrong] // Table outer and header borders // style.Colors[ImGuiCol_TableBorderLight] // Table inner borders // style.Colors[ImGuiCol_TableRowBg] // Table row background when ImGuiTableFlags_RowBg is enabled (even rows) // style.Colors[ImGuiCol_TableRowBgAlt] // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows) // Demos if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Basic")) { IMGUI_DEMO_MARKER("Tables/Basic"); // Here we will showcase three different ways to output a table. // They are very simple variations of a same thing! // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column. // In many situations, this is the most flexible and easy to use pattern. HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop."); if (ImGui::BeginTable("table1", 3)) { for (int row = 0; row < 4; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("Row %d Column %d", row, column); } } ImGui::EndTable(); } // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex(). // This is generally more convenient when you have code manually submitting the contents of each column. HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually."); if (ImGui::BeginTable("table2", 3)) { for (int row = 0; row < 4; row++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Row %d", row); ImGui::TableNextColumn(); ImGui::Text("Some contents"); ImGui::TableNextColumn(); ImGui::Text("123.456"); } ImGui::EndTable(); } // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(), // as TableNextColumn() will automatically wrap around and create new rows as needed. // This is generally more convenient when your cells all contains the same type of data. HelpMarker( "Only using TableNextColumn(), which tends to be convenient for tables where every cell contains " "the same type of contents.\n This is also more similar to the old NextColumn() function of the " "Columns API, and provided to facilitate the Columns->Tables API transition."); if (ImGui::BeginTable("table3", 3)) { for (int item = 0; item < 14; item++) { ImGui::TableNextColumn(); ImGui::Text("Item %d", item); } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Borders, background")) { IMGUI_DEMO_MARKER("Tables/Borders, background"); // Expose a few Borders related flags interactively enum ContentsType { CT_Text, CT_FillButton }; static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; static bool display_headers = false; static int contents_type = CT_Text; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders); ImGui::SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH"); ImGui::Indent(); ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); ImGui::Indent(); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); ImGui::Unindent(); ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); ImGui::Indent(); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); ImGui::Unindent(); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags, ImGuiTableFlags_BordersInner); ImGui::Unindent(); ImGui::AlignTextToFramePadding(); ImGui::Text("Cell contents:"); ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text); ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton); ImGui::Checkbox("Display headers", &display_headers); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)"); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags)) { // Display headers so we can inspect their interaction with borders // (Headers are not the main purpose of this section of the demo, so we are not elaborating on them now. See other sections for details) if (display_headers) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); ImGui::TableHeadersRow(); } for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); char buf[32]; sprintf(buf, "Hello %d,%d", column, row); if (contents_type == CT_Text) ImGui::TextUnformatted(buf); else if (contents_type == CT_FillButton) ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Resizable, stretch")) { IMGUI_DEMO_MARKER("Tables/Resizable, stretch"); // By default, if we don't enable ScrollX the sizing policy for each column is "Stretch" // All columns maintain a sizing weight, and they will occupy all available width. static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); ImGui::SameLine(); HelpMarker( "Using the _Resizable flag automatically enables the _BordersInnerV flag as well, " "this is why the resize borders are still showing when unchecking this."); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags)) { for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("Hello %d,%d", column, row); } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Resizable, fixed")) { IMGUI_DEMO_MARKER("Tables/Resizable, fixed"); // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set) // So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small) // If there is not enough available width to fit all columns, they will however be resized down. // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings HelpMarker( "Using _Resizable + _SizingFixedFit flags.\n" "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n" "Double-click a column border to auto-fit the column to its contents."); PushStyleCompact(); static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags)) { for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("Hello %d,%d", column, row); } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Resizable, mixed")) { IMGUI_DEMO_MARKER("Tables/Resizable, mixed"); HelpMarker( "Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n" "When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch."); static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; if (ImGui::BeginTable("table1", 3, flags)) { ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthStretch); ImGui::TableHeadersRow(); for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row); } } ImGui::EndTable(); } if (ImGui::BeginTable("table2", 6, flags)) { ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide); ImGui::TableSetupColumn("DDD", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("EEE", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("FFF", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide); ImGui::TableHeadersRow(); for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 6; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row); } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Reorderable, hideable, with headers")) { IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers"); HelpMarker( "Click and drag column headers to reorder columns.\n\n" "Right-click on a header to open a context menu."); static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable); ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags)) { // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column. // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.) ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); ImGui::TableHeadersRow(); for (int row = 0; row < 6; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("Hello %d,%d", column, row); } } ImGui::EndTable(); } // Use outer_size.x == 0.0f instead of default to make the table as tight as possible // (only valid when no scrolling and no stretch column) if (ImGui::BeginTable("table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f))) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); ImGui::TableHeadersRow(); for (int row = 0; row < 6; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("Fixed %d,%d", column, row); } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Padding")) { IMGUI_DEMO_MARKER("Tables/Padding"); // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding. // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding. HelpMarker( "We often want outer padding activated when any using features which makes the edges of a column visible:\n" "e.g.:\n" "- BorderOuterV\n" "- any form of row selection\n" "Because of this, activating BorderOuterV sets the default to PadOuterX. " "Using PadOuterX or NoPadOuterX you can override the default.\n\n" "Actual padding values are using style.CellPadding.\n\n" "In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding."); static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags1, ImGuiTableFlags_PadOuterX); ImGui::SameLine(); HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)"); ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags1, ImGuiTableFlags_NoPadOuterX); ImGui::SameLine(); HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)"); ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags1, ImGuiTableFlags_NoPadInnerX); ImGui::SameLine(); HelpMarker("Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)"); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags1, ImGuiTableFlags_BordersOuterV); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags1, ImGuiTableFlags_BordersInnerV); static bool show_headers = false; ImGui::Checkbox("show_headers", &show_headers); PopStyleCompact(); if (ImGui::BeginTable("table_padding", 3, flags1)) { if (show_headers) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); ImGui::TableHeadersRow(); } for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); if (row == 0) { ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); } else { char buf[32]; sprintf(buf, "Hello %d,%d", column, row); ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); } //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) // ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255)); } } ImGui::EndTable(); } // Second example: set style.CellPadding to (0.0) or a custom value. // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one... HelpMarker("Setting style.CellPadding to (0,0) or a custom value."); static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; static ImVec2 cell_padding(0.0f, 0.0f); static bool show_widget_frame_bg = true; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders); ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags2, ImGuiTableFlags_BordersH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags2, ImGuiTableFlags_BordersV); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags2, ImGuiTableFlags_BordersInner); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags2, ImGuiTableFlags_BordersOuter); ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags2, ImGuiTableFlags_RowBg); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags2, ImGuiTableFlags_Resizable); ImGui::Checkbox("show_widget_frame_bg", &show_widget_frame_bg); ImGui::SliderFloat2("CellPadding", &cell_padding.x, 0.0f, 10.0f, "%.0f"); PopStyleCompact(); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding); if (ImGui::BeginTable("table_padding_2", 3, flags2)) { static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells static bool init = true; if (!show_widget_frame_bg) ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); for (int cell = 0; cell < 3 * 5; cell++) { ImGui::TableNextColumn(); if (init) strcpy(text_bufs[cell], "edit me"); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushID(cell); ImGui::InputText("##cell", text_bufs[cell], IM_COUNTOF(text_bufs[cell])); ImGui::PopID(); } if (!show_widget_frame_bg) ImGui::PopStyleColor(); init = false; ImGui::EndTable(); } ImGui::PopStyleVar(); ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Sizing policies")) { IMGUI_DEMO_MARKER("Tables/Explicit widths"); static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags1, ImGuiTableFlags_NoHostExtendX); PopStyleCompact(); static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame }; for (int table_n = 0; table_n < 4; table_n++) { ImGui::PushID(table_n); ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30); EditTableSizingFlags(&sizing_policy_flags[table_n]); // To make it easier to understand the different sizing policy, // For each policy: we display one table where the columns have equal contents width, // and one where the columns have different contents width. if (ImGui::BeginTable("table1", 3, sizing_policy_flags[table_n] | flags1)) { for (int row = 0; row < 3; row++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Oh dear"); ImGui::TableNextColumn(); ImGui::Text("Oh dear"); ImGui::TableNextColumn(); ImGui::Text("Oh dear"); } ImGui::EndTable(); } if (ImGui::BeginTable("table2", 3, sizing_policy_flags[table_n] | flags1)) { for (int row = 0; row < 3; row++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("AAAA"); ImGui::TableNextColumn(); ImGui::Text("BBBBBBBB"); ImGui::TableNextColumn(); ImGui::Text("CCCCCCCCCCCC"); } ImGui::EndTable(); } ImGui::PopID(); } ImGui::Spacing(); ImGui::TextUnformatted("Advanced"); ImGui::SameLine(); HelpMarker( "This section allows you to interact and see the effect of various sizing policies " "depending on whether Scroll is enabled and the contents of your columns."); enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText }; static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable; static int contents_type = CT_ShowWidth; static int column_count = 3; PushStyleCompact(); ImGui::PushID("Advanced"); ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); EditTableSizingFlags(&flags); ImGui::Combo("Contents", &contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0"); if (contents_type == CT_FillButton) { ImGui::SameLine(); HelpMarker( "Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop " "where contents width can feed into auto-column width can feed into contents width."); } ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth."); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); ImGui::PopItemWidth(); ImGui::PopID(); PopStyleCompact(); if (ImGui::BeginTable("table2", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7))) { for (int cell = 0; cell < 10 * column_count; cell++) { ImGui::TableNextColumn(); int column = ImGui::TableGetColumnIndex(); int row = ImGui::TableGetRowIndex(); ImGui::PushID(cell); char label[32]; static char text_buf[32] = ""; sprintf(label, "Hello %d,%d", column, row); switch (contents_type) { case CT_ShortText: ImGui::TextUnformatted(label); break; case CT_LongText: ImGui::Text("Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break; case CT_ShowWidth: ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break; case CT_Button: ImGui::Button(label); break; case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_COUNTOF(text_buf)); break; } ImGui::PopID(); } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Vertical scrolling, with clipping")) { IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping"); HelpMarker( "Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n" "We also demonstrate using ImGuiListClipper to virtualize the submission of many items."); static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); PopStyleCompact(); // When using ScrollX or ScrollY we need to specify a size for our table container! // Otherwise by default the table will fit all available space, like a BeginChild() call. ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8); if (ImGui::BeginTable("table_scrolly", 3, flags, outer_size)) { ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None); ImGui::TableSetupColumn("Two", ImGuiTableColumnFlags_None); ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None); ImGui::TableHeadersRow(); // Demonstrate using clipper for large vertical lists ImGuiListClipper clipper; clipper.Begin(1000); while (clipper.Step()) { for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("Hello %d,%d", column, row); } } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Horizontal scrolling")) { IMGUI_DEMO_MARKER("Tables/Horizontal scrolling"); HelpMarker( "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, " "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n" "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, " "because the container window won't automatically extend vertically to fix contents " "(this may be improved in future versions)."); static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; static int freeze_cols = 1; static int freeze_rows = 1; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); PopStyleCompact(); // When using ScrollX or ScrollY we need to specify a size for our table container! // Otherwise by default the table will fit all available space, like a BeginChild() call. ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8); if (ImGui::BeginTable("table_scrollx", 7, flags, outer_size)) { ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); ImGui::TableSetupColumn("Line #", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze() ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); ImGui::TableSetupColumn("Four"); ImGui::TableSetupColumn("Five"); ImGui::TableSetupColumn("Six"); ImGui::TableHeadersRow(); for (int row = 0; row < 20; row++) { ImGui::TableNextRow(); for (int column = 0; column < 7; column++) { // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement. // Because here we know that: // - A) all our columns are contributing the same to row height // - B) column 0 is always visible, // We only always submit this one column and can skip others. // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags(). if (!ImGui::TableSetColumnIndex(column) && column > 0) continue; if (column == 0) ImGui::Text("Line %d", row); else ImGui::Text("Hello world %d,%d", column, row); } } ImGui::EndTable(); } ImGui::Spacing(); ImGui::TextUnformatted("Stretch + ScrollX"); ImGui::SameLine(); HelpMarker( "Showcase using Stretch columns + ScrollX together: " "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n" "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns " "along with ScrollX doesn't make sense."); static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; static float inner_width = 1000.0f; PushStyleCompact(); ImGui::PushID("flags3"); ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX); ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f"); ImGui::PopItemWidth(); ImGui::PopID(); PopStyleCompact(); if (ImGui::BeginTable("table2", 7, flags2, outer_size, inner_width)) { for (int cell = 0; cell < 20 * 7; cell++) { ImGui::TableNextColumn(); ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex()); } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Columns flags")) { IMGUI_DEMO_MARKER("Tables/Columns flags"); // Create a first table just to show all the options/flags we want to make visible in our example! const int column_count = 3; const char* column_names[column_count] = { "One", "Two", "Three" }; static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide }; static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags() if (ImGui::BeginTable("table_columns_flags_checkboxes", column_count, ImGuiTableFlags_None)) { PushStyleCompact(); for (int column = 0; column < column_count; column++) { ImGui::TableNextColumn(); ImGui::PushID(column); ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns ImGui::Text("'%s'", column_names[column]); ImGui::Spacing(); ImGui::Text("Input flags:"); EditTableColumnsFlags(&column_flags[column]); ImGui::Spacing(); ImGui::Text("Output flags:"); ImGui::BeginDisabled(); ShowTableColumnsStatusFlags(column_flags_out[column]); ImGui::EndDisabled(); ImGui::PopID(); } PopStyleCompact(); ImGui::EndTable(); } // Create the real table we care about for the example! // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above, // otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible // + resizing the parent window down). const ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable; ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9); if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size)) { bool has_angled_header = false; for (int column = 0; column < column_count; column++) { has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0; ImGui::TableSetupColumn(column_names[column], column_flags[column]); } if (has_angled_header) ImGui::TableAngledHeadersRow(); ImGui::TableHeadersRow(); for (int column = 0; column < column_count; column++) column_flags_out[column] = ImGui::TableGetColumnFlags(column); float indent_step = (float)((int)TEXT_BASE_WIDTH / 2); for (int row = 0; row < 8; row++) { // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags. ImGui::Indent(indent_step); ImGui::TableNextRow(); for (int column = 0; column < column_count; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column)); } } ImGui::Unindent(indent_step * 8.0f); ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Columns widths")) { IMGUI_DEMO_MARKER("Tables/Columns widths"); HelpMarker("Using TableSetupColumn() to setup default width."); static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags1, ImGuiTableFlags_NoBordersInBodyUntilResize); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags1)) { // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed. ImGui::TableSetupColumn("one", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f ImGui::TableSetupColumn("two", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f ImGui::TableSetupColumn("three", ImGuiTableColumnFlags_WidthFixed); // Default to auto ImGui::TableHeadersRow(); for (int row = 0; row < 4; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableSetColumnIndex(column); if (row == 0) ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x); else ImGui::Text("Hello %d,%d", column, row); } } ImGui::EndTable(); } HelpMarker( "Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, " "fixed columns with set width may still be shrunk down if there's not enough space in the host."); static ImGuiTableFlags flags2 = ImGuiTableFlags_None; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags2, ImGuiTableFlags_NoKeepColumnsVisible); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags2, ImGuiTableFlags_BordersInnerV); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags2, ImGuiTableFlags_BordersOuterV); PopStyleCompact(); if (ImGui::BeginTable("table2", 4, flags2)) { // We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns // will default to ImGuiTableColumnFlags_WidthFixed. ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 4; column++) { ImGui::TableSetColumnIndex(column); if (row == 0) ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x); else ImGui::Text("Hello %d,%d", column, row); } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Nested tables")) { IMGUI_DEMO_MARKER("Tables/Nested tables"); HelpMarker("This demonstrates embedding a table into another table cell."); if (ImGui::BeginTable("table_nested1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { ImGui::TableSetupColumn("A0"); ImGui::TableSetupColumn("A1"); ImGui::TableHeadersRow(); ImGui::TableNextColumn(); ImGui::Text("A0 Row 0"); { float rows_height = (TEXT_BASE_HEIGHT * 2.0f) + (ImGui::GetStyle().CellPadding.y * 2.0f); if (ImGui::BeginTable("table_nested2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { ImGui::TableSetupColumn("B0"); ImGui::TableSetupColumn("B1"); ImGui::TableHeadersRow(); ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height); ImGui::TableNextColumn(); ImGui::Text("B0 Row 0"); ImGui::TableNextColumn(); ImGui::Text("B1 Row 0"); ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height); ImGui::TableNextColumn(); ImGui::Text("B0 Row 1"); ImGui::TableNextColumn(); ImGui::Text("B1 Row 1"); ImGui::EndTable(); } } ImGui::TableNextColumn(); ImGui::Text("A1 Row 0"); ImGui::TableNextColumn(); ImGui::Text("A0 Row 1"); ImGui::TableNextColumn(); ImGui::Text("A1 Row 1"); ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Row height")) { IMGUI_DEMO_MARKER("Tables/Row height"); HelpMarker( "You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, " "so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n" "We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row."); if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_Borders)) { for (int row = 0; row < 8; row++) { float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row + ImGui::GetStyle().CellPadding.y * 2.0f); ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height); ImGui::TableNextColumn(); ImGui::Text("min_row_height = %.2f", min_row_height); } ImGui::EndTable(); } HelpMarker( "Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n" "Please note that Tables Row Height is not the same thing as Current Line Height, " "as a table cell may contains multiple lines."); if (ImGui::BeginTable("table_share_lineheight", 2, ImGuiTableFlags_Borders)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::ColorButton("##1", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40)); ImGui::TableNextColumn(); ImGui::Text("Line 1"); ImGui::Text("Line 2"); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::ColorButton("##2", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40)); ImGui::TableNextColumn(); ImGui::SameLine(0.0f, 0.0f); // Reuse line height from previous column ImGui::Text("Line 1, with SameLine(0,0)"); ImGui::Text("Line 2"); ImGui::EndTable(); } HelpMarker("Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table."); if (ImGui::BeginTable("table_changing_cellpadding_y", 1, ImGuiTableFlags_Borders)) { ImGuiStyle& style = ImGui::GetStyle(); for (int row = 0; row < 8; row++) { if ((row % 3) == 2) ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding, 20.0f); ImGui::TableNextRow(ImGuiTableRowFlags_None); ImGui::TableNextColumn(); ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y); if ((row % 3) == 2) ImGui::PopStyleVar(); } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Outer size")) { IMGUI_DEMO_MARKER("Tables/Outer size"); // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY // Important to that note how the two flags have slightly different behaviors! ImGui::Text("Using NoHostExtendX and NoHostExtendY:"); PushStyleCompact(); static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX; ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used."); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible."); PopStyleCompact(); ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f); if (ImGui::BeginTable("table1", 3, flags, outer_size)) { for (int row = 0; row < 10; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableNextColumn(); ImGui::Text("Cell %d,%d", column, row); } } ImGui::EndTable(); } ImGui::SameLine(); ImGui::Text("Hello!"); ImGui::Spacing(); ImGui::Text("Using explicit size:"); if (ImGui::BeginTable("table2", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) { for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { ImGui::TableNextColumn(); ImGui::Text("Cell %d,%d", column, row); } } ImGui::EndTable(); } ImGui::SameLine(); if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) { const float rows_height = TEXT_BASE_HEIGHT * 1.5f + ImGui::GetStyle().CellPadding.y * 2.0f; for (int row = 0; row < 3; row++) { ImGui::TableNextRow(0, rows_height); for (int column = 0; column < 3; column++) { ImGui::TableNextColumn(); ImGui::Text("Cell %d,%d", column, row); } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Background color")) { IMGUI_DEMO_MARKER("Tables/Background color"); static ImGuiTableFlags flags = ImGuiTableFlags_RowBg; static int row_bg_type = 1; static int row_bg_target = 1; static int cell_bg_type = 1; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders); ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); ImGui::SameLine(); HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style."); ImGui::Combo("row bg type", (int*)&row_bg_type, "None\0Red\0Gradient\0"); ImGui::Combo("row bg target", (int*)&row_bg_target, "RowBg0\0RowBg1\0"); ImGui::SameLine(); HelpMarker("Target RowBg0 to override the alternating odd/even colors,\nTarget RowBg1 to blend with them."); ImGui::Combo("cell bg type", (int*)&cell_bg_type, "None\0Blue\0"); ImGui::SameLine(); HelpMarker("We are colorizing cells to B1->C2 here."); IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2); IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1); IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1); PopStyleCompact(); if (ImGui::BeginTable("table1", 5, flags)) { for (int row = 0; row < 6; row++) { ImGui::TableNextRow(); // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)' // We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag. if (row_bg_type != 0) { ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient? ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color); } // Fill cells for (int column = 0; column < 5; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("%c%c", 'A' + row, '0' + column); // Change background of Cells B1->C2 // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)' // (the CellBg color will be blended over the RowBg and ColumnBg colors) // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop. if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1) { ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f)); ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color); } } } ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Tree view")) { IMGUI_DEMO_MARKER("Tables/Tree view"); static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DrawLinesFull; ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanFullWidth); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanLabelWidth); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_LabelSpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_LabelSpanAllColumns); ImGui::SameLine(); HelpMarker("Useful if you know that you aren't displaying contents in other columns"); HelpMarker("See \"Columns flags\" section to configure how indentation is applied to individual columns."); if (ImGui::BeginTable("3ways", 3, table_flags)) { // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); ImGui::TableHeadersRow(); // Simple storage to output a dummy file-system. struct MyTreeNode { const char* Name; const char* Type; int Size; int ChildIdx; int ChildCount; static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes) { ImGui::TableNextRow(); ImGui::TableNextColumn(); const bool is_folder = (node->ChildCount > 0); ImGuiTreeNodeFlags node_flags = tree_node_flags_base; if (node != &all_nodes[0]) node_flags &= ~ImGuiTreeNodeFlags_LabelSpanAllColumns; // Only demonstrate this on the root node. if (is_folder) { bool open = ImGui::TreeNodeEx(node->Name, node_flags); if ((node_flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) == 0) { ImGui::TableNextColumn(); ImGui::TextDisabled("--"); ImGui::TableNextColumn(); ImGui::TextUnformatted(node->Type); } if (open) { for (int child_n = 0; child_n < node->ChildCount; child_n++) DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes); ImGui::TreePop(); } } else { ImGui::TreeNodeEx(node->Name, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen); ImGui::TableNextColumn(); ImGui::Text("%d", node->Size); ImGui::TableNextColumn(); ImGui::TextUnformatted(node->Type); } } }; static const MyTreeNode nodes[] = { { "Root with Long Name", "Folder", -1, 1, 3 }, // 0 { "Music", "Folder", -1, 4, 2 }, // 1 { "Textures", "Folder", -1, 6, 3 }, // 2 { "desktop.ini", "System file", 1024, -1,-1 }, // 3 { "File1_a.wav", "Audio file", 123000, -1,-1 }, // 4 { "File1_b.wav", "Audio file", 456000, -1,-1 }, // 5 { "Image001.png", "Image file", 203128, -1,-1 }, // 6 { "Copy of Image001.png", "Image file", 203256, -1,-1 }, // 7 { "Copy of Image001 (Final2).png","Image file", 203512, -1,-1 }, // 8 }; MyTreeNode::DisplayNode(&nodes[0], nodes); ImGui::EndTable(); } ImGui::TreePop(); } if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Item width")) { IMGUI_DEMO_MARKER("Tables/Item width"); HelpMarker( "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n" "Note that on auto-resizing non-resizable fixed columns, querying the content width for " "e.g. right-alignment doesn't make sense."); if (ImGui::BeginTable("table_item_width", 3, ImGuiTableFlags_Borders)) { ImGui::TableSetupColumn("small"); ImGui::TableSetupColumn("half"); ImGui::TableSetupColumn("right-align"); ImGui::TableHeadersRow(); for (int row = 0; row < 3; row++) { ImGui::TableNextRow(); if (row == 0) { // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient) ImGui::TableSetColumnIndex(0); ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small ImGui::TableSetColumnIndex(1); ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); ImGui::TableSetColumnIndex(2); ImGui::PushItemWidth(-FLT_MIN); // Right-aligned } // Draw our contents static float dummy_f = 0.0f; ImGui::PushID(row); ImGui::TableSetColumnIndex(0); ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f); ImGui::TableSetColumnIndex(1); ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f); ImGui::TableSetColumnIndex(2); ImGui::SliderFloat("##float2", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned ImGui::PopID(); } ImGui::EndTable(); } ImGui::TreePop(); } // Demonstrate using TableHeader() calls instead of TableHeadersRow() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Custom headers")) { IMGUI_DEMO_MARKER("Tables/Custom headers"); const int COLUMNS_COUNT = 3; if (ImGui::BeginTable("table_custom_headers", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { ImGui::TableSetupColumn("Apricot"); ImGui::TableSetupColumn("Banana"); ImGui::TableSetupColumn("Cherry"); // Dummy entire-column selection storage // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox. static bool column_selected[3] = {}; // Instead of calling TableHeadersRow() we'll submit custom headers ourselves. // (A different approach is also possible: // - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call. // - Call TableHeadersRow() normally. This will submit TableHeader() with no name. // - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().) ImGui::TableNextRow(ImGuiTableRowFlags_Headers); for (int column = 0; column < COLUMNS_COUNT; column++) { ImGui::TableSetColumnIndex(column); const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn() ImGui::PushID(column); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::Checkbox("##checkall", &column_selected[column]); ImGui::PopStyleVar(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::TableHeader(column_name); ImGui::PopID(); } // Submit table contents for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); for (int column = 0; column < 3; column++) { char buf[32]; sprintf(buf, "Cell %d,%d", column, row); ImGui::TableSetColumnIndex(column); ImGui::Selectable(buf, column_selected[column]); } } ImGui::EndTable(); } ImGui::TreePop(); } // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Angled headers")) { IMGUI_DEMO_MARKER("Tables/Angled headers"); const char* column_names[] = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" }; const int columns_count = IM_COUNTOF(column_names); const int rows_count = 12; static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn; static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed; static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage static int frozen_cols = 1; static int frozen_rows = 2; ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX); ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("_Sortable", &table_flags, ImGuiTableFlags_Sortable); ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody); ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2); ImGui::CheckboxFlags("Disable header contributing to column width", &column_flags, ImGuiTableColumnFlags_NoHeaderWidth); if (ImGui::TreeNode("Style settings")) { ImGui::SameLine(); HelpMarker("Giving access to some ImGuiStyle value in this demo for convenience."); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::SliderAngle("style.TableAngledHeadersAngle", &ImGui::GetStyle().TableAngledHeadersAngle, -50.0f, +50.0f); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::SliderFloat2("style.TableAngledHeadersTextAlign", (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::TreePop(); } if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12))) { ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder); for (int n = 1; n < columns_count; n++) ImGui::TableSetupColumn(column_names[n], column_flags); ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows); ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag. ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions. for (int row = 0; row < rows_count; row++) { ImGui::PushID(row); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); ImGui::Text("Track %d", row); for (int column = 1; column < columns_count; column++) if (ImGui::TableSetColumnIndex(column)) { ImGui::PushID(column); ImGui::Checkbox("", &bools[row * columns_count + column]); ImGui::PopID(); } ImGui::PopID(); } ImGui::EndTable(); } ImGui::TreePop(); } // Demonstrate creating custom context menus inside columns, // while playing it nice with context menus provided by TableHeadersRow()/TableHeader() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Context menus")) { IMGUI_DEMO_MARKER("Tables/Context menus"); HelpMarker( "By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n" "Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags1, ImGuiTableFlags_ContextMenuInBody); PopStyleCompact(); // Context Menus: first example // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu. // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set) const int COLUMNS_COUNT = 3; if (ImGui::BeginTable("table_context_menu", COLUMNS_COUNT, flags1)) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu. ImGui::TableHeadersRow(); // Submit dummy contents for (int row = 0; row < 4; row++) { ImGui::TableNextRow(); for (int column = 0; column < COLUMNS_COUNT; column++) { ImGui::TableSetColumnIndex(column); ImGui::Text("Cell %d,%d", column, row); } } ImGui::EndTable(); } // Context Menus: second example // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. // [2.2] Right-click on the ".." to open a custom popup // [2.3] Right-click in columns to open another custom popup HelpMarker( "Demonstrate mixing table context menu (over header), item context button (over button) " "and custom per-column context menu (over column body)."); ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2)) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. ImGui::TableHeadersRow(); for (int row = 0; row < 4; row++) { ImGui::TableNextRow(); for (int column = 0; column < COLUMNS_COUNT; column++) { // Submit dummy contents ImGui::TableSetColumnIndex(column); ImGui::Text("Cell %d,%d", column, row); ImGui::SameLine(); // [2.2] Right-click on the ".." to open a custom popup ImGui::PushID(row * COLUMNS_COUNT + column); ImGui::SmallButton(".."); if (ImGui::BeginPopupContextItem()) { ImGui::Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } ImGui::PopID(); } } // [2.3] Right-click anywhere in columns to open another custom popup // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup // to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping) int hovered_column = -1; for (int column = 0; column < COLUMNS_COUNT + 1; column++) { ImGui::PushID(column); if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered) hovered_column = column; if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1)) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup")) { if (column == COLUMNS_COUNT) ImGui::Text("This is a custom popup for unused space after the last column."); else ImGui::Text("This is a custom popup for Column %d", column); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } ImGui::PopID(); } ImGui::EndTable(); ImGui::Text("Hovered column: %d", hovered_column); } ImGui::TreePop(); } // Demonstrate creating multiple tables with the same ID if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Synced instances")) { IMGUI_DEMO_MARKER("Tables/Synced instances"); HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc."); static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings; ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit); ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); for (int n = 0; n < 3; n++) { char buf[32]; sprintf(buf, "Synced Table %d", n); bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen); if (open && ImGui::BeginTable("Table", 3, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5))) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); ImGui::TableHeadersRow(); const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions. for (int cell = 0; cell < cell_count; cell++) { ImGui::TableNextColumn(); ImGui::Text("this cell %d", cell); } ImGui::EndTable(); } } ImGui::TreePop(); } // Demonstrate using Sorting facilities // This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting. // Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified) static const char* template_items_names[] = { "Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango", "Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot" }; if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Sorting")) { IMGUI_DEMO_MARKER("Tables/Sorting"); // Create item list static ImVector items; if (items.Size == 0) { items.resize(50, MyItem()); for (int n = 0; n < items.Size; n++) { const int template_n = n % IM_COUNTOF(template_items_names); MyItem& item = items[n]; item.ID = n; item.Name = template_items_names[template_n]; item.Quantity = (n * n - n) % 20; // Assign default quantities } } // Options static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti); ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1)."); ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate); ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0)."); PopStyleCompact(); if (ImGui::BeginTable("table_sorting", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f)) { // Declare columns // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! // Demonstrate using a mixture of flags among available sort-related flags: // - ImGuiTableColumnFlags_DefaultSort // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_ID); ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Quantity); ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible ImGui::TableHeadersRow(); // Sort our data if sort specs have been changed! if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) if (sort_specs->SpecsDirty) { MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size); sort_specs->SpecsDirty = false; } // Demonstrate using clipper for large vertical lists ImGuiListClipper clipper; clipper.Begin(items.Size); while (clipper.Step()) for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) { // Display a data item MyItem* item = &items[row_n]; ImGui::PushID(item->ID); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%04d", item->ID); ImGui::TableNextColumn(); ImGui::TextUnformatted(item->Name); ImGui::TableNextColumn(); ImGui::SmallButton("None"); ImGui::TableNextColumn(); ImGui::Text("%d", item->Quantity); ImGui::PopID(); } ImGui::EndTable(); } ImGui::TreePop(); } // In this example we'll expose most table flags and settings. // For specific flags and settings refer to the corresponding section for more detailed explanation. // This section is mostly useful to experiment with combining certain flags or settings with each others. //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG] if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Advanced")) { IMGUI_DEMO_MARKER("Tables/Advanced"); static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit; static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None; enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; static int contents_type = CT_SelectableSpanRow; const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" }; static int freeze_cols = 1; static int freeze_rows = 1; static int items_count = IM_COUNTOF(template_items_names) * 2; static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12); static float row_min_height = 0.0f; // Auto static float inner_width_with_scroll = 0.0f; // Auto-extend static bool outer_size_enabled = true; static bool show_headers = true; static bool show_wrapped_text = false; //static ImGuiTextFilter filter; //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing if (ImGui::TreeNode("Options")) { // Make the UI compact because there are so many fields PushStyleCompact(); ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f); if (ImGui::TreeNodeEx("Features:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable); ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); ImGui::CheckboxFlags("ImGuiTableFlags_Sortable", &flags, ImGuiTableFlags_Sortable); ImGui::CheckboxFlags("ImGuiTableFlags_NoSavedSettings", &flags, ImGuiTableFlags_NoSavedSettings); ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags, ImGuiTableFlags_ContextMenuInBody); ImGui::TreePop(); } if (ImGui::TreeNodeEx("Decorations:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)"); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); ImGui::TreePop(); } if (ImGui::TreeNodeEx("Sizing:", ImGuiTreeNodeFlags_DefaultOpen)) { EditTableSizingFlags(&flags); ImGui::SameLine(); HelpMarker("In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical."); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used."); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible."); ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled."); ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth."); ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); ImGui::SameLine(); HelpMarker("Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options."); ImGui::TreePop(); } if (ImGui::TreeNodeEx("Padding:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags, ImGuiTableFlags_PadOuterX); ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags, ImGuiTableFlags_NoPadOuterX); ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags, ImGuiTableFlags_NoPadInnerX); ImGui::TreePop(); } if (ImGui::TreeNodeEx("Scrolling:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); ImGui::TreePop(); } if (ImGui::TreeNodeEx("Sorting:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti); ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1)."); ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate); ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0)."); ImGui::TreePop(); } if (ImGui::TreeNodeEx("Headers:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Checkbox("show_headers", &show_headers); ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); ImGui::CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader); ImGui::SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo)."); ImGui::TreePop(); } if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Checkbox("show_wrapped_text", &show_wrapped_text); ImGui::DragFloat2("##OuterSize", &outer_size_value.x); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::Checkbox("outer_size", &outer_size_enabled); ImGui::SameLine(); HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set):\n" "- The table is output directly in the parent window.\n" "- OuterSize.x < 0.0f will right-align the table.\n" "- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\n" "- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set)."); // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling. // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled. ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX); ImGui::DragFloat("row_min_height", &row_min_height, 1.0f, 0.0f, FLT_MAX); ImGui::SameLine(); HelpMarker("Specify height of the Selectable item."); ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999); ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_COUNTOF(contents_type_names)); //filter.Draw("filter"); ImGui::TreePop(); } ImGui::PopItemWidth(); PopStyleCompact(); ImGui::Spacing(); ImGui::TreePop(); } // Update item list if we changed the number of items static ImVector items; static ImVector selection; static bool items_need_sort = false; if (items.Size != items_count) { items.resize(items_count, MyItem()); for (int n = 0; n < items_count; n++) { const int template_n = n % IM_COUNTOF(template_items_names); MyItem& item = items[n]; item.ID = n; item.Name = template_items_names[template_n]; item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities } } const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList(); const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size; ImVec2 table_scroll_cur, table_scroll_max; // For debug display const ImDrawList* table_draw_list = NULL; // " // Submit table const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f; if (ImGui::BeginTable("table_advanced", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use)) { // Declare columns // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! ImGui::TableSetupColumn("ID", columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID); ImGui::TableSetupColumn("Name", columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); ImGui::TableSetupColumn("Action", columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); ImGui::TableSetupColumn("Quantity", columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity); ImGui::TableSetupColumn("Description", columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description); ImGui::TableSetupColumn("Hidden", columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); // Sort our data if sort specs have been changed! ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs(); if (sort_specs && sort_specs->SpecsDirty) items_need_sort = true; if (sort_specs && items_need_sort && items.Size > 1) { MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size); sort_specs->SpecsDirty = false; } items_need_sort = false; // Take note of whether we are currently sorting based on the Quantity field, // we will use this to trigger sorting when we know the data of this column has been modified. const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0; // Show headers if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0) ImGui::TableAngledHeadersRow(); if (show_headers) ImGui::TableHeadersRow(); // Show data // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here? #if 1 // Demonstrate using clipper for large vertical lists ImGuiListClipper clipper; clipper.Begin(items.Size); while (clipper.Step()) { for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) #else // Without clipper { for (int row_n = 0; row_n < items.Size; row_n++) #endif { MyItem* item = &items[row_n]; //if (!filter.PassFilter(item->Name)) // continue; const bool item_is_selected = selection.contains(item->ID); ImGui::PushID(item->ID); ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height); // For the demo purpose we can select among different type of items submitted in the first column ImGui::TableSetColumnIndex(0); char label[32]; sprintf(label, "%04d", item->ID); if (contents_type == CT_Text) ImGui::TextUnformatted(label); else if (contents_type == CT_Button) ImGui::Button(label); else if (contents_type == CT_SmallButton) ImGui::SmallButton(label); else if (contents_type == CT_FillButton) ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow) { ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None; if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height))) { if (ImGui::GetIO().KeyCtrl) { if (item_is_selected) selection.find_erase_unsorted(item->ID); else selection.push_back(item->ID); } else { selection.clear(); selection.push_back(item->ID); } } } if (ImGui::TableSetColumnIndex(1)) ImGui::TextUnformatted(item->Name); // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity, // and we are currently sorting on the column showing the Quantity. // To avoid triggering a sort while holding the button, we only trigger it when the button has been released. // You will probably need some extra logic if you want to automatically sort when a specific entry changes. if (ImGui::TableSetColumnIndex(2)) { if (ImGui::SmallButton("Chop")) { item->Quantity += 1; } if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; } ImGui::SameLine(); if (ImGui::SmallButton("Eat")) { item->Quantity -= 1; } if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; } } if (ImGui::TableSetColumnIndex(3)) ImGui::Text("%d", item->Quantity); ImGui::TableSetColumnIndex(4); if (show_wrapped_text) ImGui::TextWrapped("Lorem ipsum dolor sit amet"); else ImGui::Text("Lorem ipsum dolor sit amet"); if (ImGui::TableSetColumnIndex(5)) ImGui::Text("1234"); ImGui::PopID(); } } // Store some info to display debug details below table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY()); table_draw_list = ImGui::GetWindowDrawList(); ImGui::EndTable(); } static bool show_debug_details = false; ImGui::Checkbox("Debug details", &show_debug_details); if (show_debug_details && table_draw_list) { ImGui::SameLine(0.0f, 0.0f); const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size; if (table_draw_list == parent_draw_list) ImGui::Text(": DrawCmd: +%d (in same window)", table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count); else ImGui::Text(": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)", table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y); } ImGui::TreePop(); } ImGui::PopID(); DemoWindowColumns(); if (disable_indent) ImGui::PopStyleVar(); } // Demonstrate old/legacy Columns API! // [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!] static void DemoWindowColumns() { bool open = ImGui::TreeNode("Legacy Columns API"); ImGui::SameLine(); HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!"); if (!open) return; // Basic columns if (ImGui::TreeNode("Basic")) { IMGUI_DEMO_MARKER("Columns (legacy API)/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(-FLT_MIN,0.0f))) {} 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(); } if (ImGui::TreeNode("Borders")) { IMGUI_DEMO_MARKER("Columns (legacy API)/Borders"); // NB: Future columns API should allow automatic horizontal borders. static bool h_borders = true; static bool v_borders = true; static int columns_count = 4; const int lines_count = 3; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("##columns_count", &columns_count, 0.1f, 2, 10, "%d columns"); if (columns_count < 2) columns_count = 2; ImGui::SameLine(); ImGui::Checkbox("horizontal", &h_borders); ImGui::SameLine(); ImGui::Checkbox("vertical", &v_borders); ImGui::Columns(columns_count, NULL, v_borders); for (int i = 0; i < columns_count * lines_count; i++) { if (h_borders && ImGui::GetColumnIndex() == 0) ImGui::Separator(); ImGui::PushID(i); ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i); ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); ImGui::Text("Offset %.2f", ImGui::GetColumnOffset()); ImGui::Text("Long text that is likely to clip"); ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f)); ImGui::PopID(); ImGui::NextColumn(); } ImGui::Columns(1); if (h_borders) ImGui::Separator(); ImGui::TreePop(); } // Create multiple items in a same cell before switching to next column if (ImGui::TreeNode("Mixed items")) { IMGUI_DEMO_MARKER("Columns (legacy API)/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(); } // Word wrapping if (ImGui::TreeNode("Word-wrapping")) { IMGUI_DEMO_MARKER("Columns (legacy API)/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("Horizontal Scrolling")) { IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling"); ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f); ImGui::BeginChild("##ScrollingRegion", child_size, ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar); ImGui::Columns(10); // Also demonstrate using clipper for large vertical lists int ITEMS_COUNT = 2000; ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); 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(); } if (ImGui::TreeNode("Tree")) { IMGUI_DEMO_MARKER("Columns (legacy API)/Tree"); ImGui::Columns(2, "tree", true); for (int x = 0; x < 3; x++) { bool open1 = ImGui::TreeNode((void*)(intptr_t)x, "Node%d", x); ImGui::NextColumn(); ImGui::Text("Node contents"); ImGui::NextColumn(); if (open1) { for (int y = 0; y < 3; y++) { bool open2 = ImGui::TreeNode((void*)(intptr_t)y, "Node%d.%d", x, y); ImGui::NextColumn(); ImGui::Text("Node contents"); if (open2) { ImGui::Text("Even more contents"); if (ImGui::TreeNode("Tree in column")) { ImGui::Text("The quick brown fox jumps over the lazy dog"); ImGui::TreePop(); } } ImGui::NextColumn(); if (open2) ImGui::TreePop(); } ImGui::TreePop(); } } ImGui::Columns(1); ImGui::TreePop(); } ImGui::TreePop(); } //----------------------------------------------------------------------------- // [SECTION] DemoWindowInputs() //----------------------------------------------------------------------------- static void DemoWindowInputs() { if (ImGui::CollapsingHeader("Inputs & Focus")) { ImGuiIO& io = ImGui::GetIO(); // Display inputs submitted to ImGuiIO ImGui::SetNextItemOpen(true, ImGuiCond_Once); bool inputs_opened = ImGui::TreeNode("Inputs"); ImGui::SameLine(); HelpMarker( "This is a simplified view. See more detailed input state:\n" "- in 'Tools->Metrics/Debugger->Inputs'.\n" "- in 'Tools->Debug Log->IO'."); if (inputs_opened) { IMGUI_DEMO_MARKER("Inputs & Focus/Inputs"); if (ImGui::IsMousePosValid()) ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); else ImGui::Text("Mouse pos: "); ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); ImGui::Text("Mouse down:"); for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); ImGui::Text("Mouse clicked count:"); for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); } // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows // displaying the data for old/new backends. // User code should never have to go through such hoops! // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END. struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN; ImGui::Text("Keys down:"); for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. ImGui::TreePop(); } // Display ImGuiIO output flags ImGui::SetNextItemOpen(true, ImGuiCond_Once); bool outputs_opened = ImGui::TreeNode("Outputs"); ImGui::SameLine(); HelpMarker( "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui " "to instruct your application of how to route inputs. Typically, when a value is true, it means " "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n" "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, " "and underlying application should ignore mouse inputs (in practice there are many and more subtle " "rules leading to how those flags are set)."); if (outputs_opened) { IMGUI_DEMO_MARKER("Inputs & Focus/Outputs"); ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse); ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("io.WantTextInput: %d", io.WantTextInput); ImGui::Text("io.WantSetMousePos: %d", io.WantSetMousePos); ImGui::Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible); IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override"); if (ImGui::TreeNode("WantCapture override")) { HelpMarker( "Hovering the colored canvas will override io.WantCaptureXXX fields.\n" "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering " "and true when clicking."); static int capture_override_mouse = -1; static int capture_override_keyboard = -1; const char* capture_override_desc[] = { "None", "Set to false", "Set to true" }; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); ImGui::SliderInt("SetNextFrameWantCaptureMouse() on hover", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); ImGui::SliderInt("SetNextFrameWantCaptureKeyboard() on hover", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp); ImGui::ColorButton("##panel", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(128.0f, 96.0f)); // Dummy item if (ImGui::IsItemHovered() && capture_override_mouse != -1) ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1); if (ImGui::IsItemHovered() && capture_override_keyboard != -1) ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1); ImGui::TreePop(); } ImGui::TreePop(); } // Demonstrate using Shortcut() and Routing Policies. // The general flow is: // - Code interested in a chord (e.g. "Ctrl+A") declares their intent. // - Multiple locations may be interested in same chord! Routing helps find a winner. // - Every frame, we resolve all claims and assign one owner if the modifiers are matching. // - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route. // - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags. // - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused. // TL;DR: Most uses will simply be: // - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy. if (ImGui::TreeNode("Shortcuts")) { IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts"); static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat; static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused; ImGui::CheckboxFlags("ImGuiInputFlags_Repeat", &route_options, ImGuiInputFlags_Repeat); ImGui::RadioButton("ImGuiInputFlags_RouteActive", &route_type, ImGuiInputFlags_RouteActive); ImGui::RadioButton("ImGuiInputFlags_RouteFocused (default)", &route_type, ImGuiInputFlags_RouteFocused); ImGui::Indent(); ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteFocused); ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive##0", &route_options, ImGuiInputFlags_RouteOverActive); ImGui::EndDisabled(); ImGui::Unindent(); ImGui::RadioButton("ImGuiInputFlags_RouteGlobal", &route_type, ImGuiInputFlags_RouteGlobal); ImGui::Indent(); ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteGlobal); ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverFocused", &route_options, ImGuiInputFlags_RouteOverFocused); ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive", &route_options, ImGuiInputFlags_RouteOverActive); ImGui::CheckboxFlags("ImGuiInputFlags_RouteUnlessBgFocused", &route_options, ImGuiInputFlags_RouteUnlessBgFocused); ImGui::EndDisabled(); ImGui::Unindent(); ImGui::RadioButton("ImGuiInputFlags_RouteAlways", &route_type, ImGuiInputFlags_RouteAlways); ImGuiInputFlags flags = route_type | route_options; // Merged flags if (route_type != ImGuiInputFlags_RouteGlobal) flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused); ImGui::SeparatorText("Using SetNextItemShortcut()"); ImGui::Text("Ctrl+S"); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, flags | ImGuiInputFlags_Tooltip); ImGui::Button("Save"); ImGui::Text("Alt+F"); ImGui::SetNextItemShortcut(ImGuiMod_Alt | ImGuiKey_F, flags | ImGuiInputFlags_Tooltip); static float f = 0.5f; ImGui::SliderFloat("Factor", &f, 0.0f, 1.0f); ImGui::SeparatorText("Using Shortcut()"); const float line_height = ImGui::GetTextLineHeightWithSpacing(); const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A; ImGui::Text("Ctrl+A"); ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f)); ImGui::BeginChild("WindowA", ImVec2(-FLT_MIN, line_height * 14), true); ImGui::Text("Press Ctrl+A and see who receives it!"); ImGui::Separator(); // 1: Window polling for Ctrl+A ImGui::Text("(in WindowA)"); ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); // 2: InputText also polling for Ctrl+A: it always uses _RouteFocused internally (gets priority when active) // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h) //char str[16] = "Press Ctrl+A"; //ImGui::Spacing(); //ImGui::InputText("InputTextB", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly); //ImGuiID item_id = ImGui::GetItemID(); //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused"); //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "..."); // 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA ImGui::BeginChild("ChildD", ImVec2(-FLT_MIN, line_height * 4), true); ImGui::Text("(in ChildD: not using same Shortcut)"); ImGui::Text("IsWindowFocused: %d", ImGui::IsWindowFocused()); ImGui::EndChild(); // 4: Child window polling for Ctrl+A. It is deeper than WindowA and gets priority when focused. ImGui::BeginChild("ChildE", ImVec2(-FLT_MIN, line_height * 4), true); ImGui::Text("(in ChildE: using same Shortcut)"); ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); ImGui::EndChild(); // 5: In a popup if (ImGui::Button("Open Popup")) ImGui::OpenPopup("PopupF"); if (ImGui::BeginPopup("PopupF")) { ImGui::Text("(in PopupF)"); ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h) //ImGui::InputText("InputTextG", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly); //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "..."); ImGui::EndPopup(); } ImGui::EndChild(); ImGui::PopStyleColor(); ImGui::TreePop(); } // Display mouse cursors if (ImGui::TreeNode("Mouse Cursors")) { IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors"); const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" }; IM_ASSERT(IM_COUNTOF(mouse_cursors_names) == ImGuiMouseCursor_COUNT); ImGuiMouseCursor current = ImGui::GetMouseCursor(); const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A"; ImGui::Text("Current mouse cursor = %d: %s", current, cursor_name); ImGui::BeginDisabled(true); ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); ImGui::EndDisabled(); ImGui::Text("Hover to see mouse cursors:"); 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]; sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]); ImGui::Bullet(); ImGui::Selectable(label, false); if (ImGui::IsItemHovered()) ImGui::SetMouseCursor(i); } ImGui::TreePop(); } if (ImGui::TreeNode("Tabbing")) { IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing"); ImGui::Text("Use Tab/Shift+Tab to cycle through keyboard editable fields."); static char buf[32] = "hello"; ImGui::InputText("1", buf, IM_COUNTOF(buf)); ImGui::InputText("2", buf, IM_COUNTOF(buf)); ImGui::InputText("3", buf, IM_COUNTOF(buf)); ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); ImGui::InputText("4 (tab skip)", buf, IM_COUNTOF(buf)); ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopItemFlag(); ImGui::InputText("5", buf, IM_COUNTOF(buf)); ImGui::TreePop(); } if (ImGui::TreeNode("Focus from code")) { IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code"); bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine(); bool focus_3 = ImGui::Button("Focus on 3"); int has_focus = 0; static char buf[128] = "click on a button to set focus"; if (focus_1) ImGui::SetKeyboardFocusHere(); ImGui::InputText("1", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 1; if (focus_2) ImGui::SetKeyboardFocusHere(); ImGui::InputText("2", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 2; ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); if (focus_3) ImGui::SetKeyboardFocusHere(); ImGui::InputText("3 (tab skip)", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 3; ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopItemFlag(); if (has_focus) ImGui::Text("Item with focus: %d", has_focus); else ImGui::Text("Item with focus: "); // 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 (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code."); ImGui::TreePop(); } if (ImGui::TreeNode("Dragging")) { IMGUI_DEMO_MARKER("Inputs & Focus/Dragging"); ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); for (int button = 0; button < 3; button++) { ImGui::Text("IsMouseDragging(%d):", button); ImGui::Text(" w/ default threshold: %d,", ImGui::IsMouseDragging(button)); ImGui::Text(" w/ zero threshold: %d,", ImGui::IsMouseDragging(button, 0.0f)); ImGui::Text(" w/ large threshold: %d,", ImGui::IsMouseDragging(button, 20.0f)); } ImGui::Button("Drag Me"); if (ImGui::IsItemActive()) ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor // Drag operations gets "unlocked" when the mouse has moved past a certain threshold // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta(). ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f); ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0); ImVec2 mouse_delta = io.MouseDelta; ImGui::Text("GetMouseDragDelta(0):"); ImGui::Text(" w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y); ImGui::Text(" w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y); ImGui::Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y); ImGui::TreePop(); } } } //----------------------------------------------------------------------------- // [SECTION] About Window / ShowAboutWindow() // Access from Dear ImGui Demo -> Tools -> About //----------------------------------------------------------------------------- void ImGui::ShowAboutWindow(bool* p_open) { if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::End(); return; } IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); ImGui::TextLinkOpenURL("Homepage", "https://github.com/ocornut/imgui"); ImGui::SameLine(); ImGui::TextLinkOpenURL("FAQ", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md"); ImGui::SameLine(); ImGui::TextLinkOpenURL("Wiki", "https://github.com/ocornut/imgui/wiki"); ImGui::SameLine(); ImGui::TextLinkOpenURL("Extensions", "https://github.com/ocornut/imgui/wiki/Useful-Extensions"); ImGui::SameLine(); ImGui::TextLinkOpenURL("Releases", "https://github.com/ocornut/imgui/releases"); ImGui::SameLine(); ImGui::TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding"); ImGui::Separator(); ImGui::Text("(c) 2014-2026 Omar Cornut"); ImGui::Text("Developed by Omar Cornut and all Dear ImGui contributors."); ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); ImGui::Text("If your company uses this, please consider funding the project."); 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"); ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18); ImGui::BeginChild(ImGui::GetID("cfg_infos"), child_size, ImGuiChildFlags_FrameStyle); if (copy_to_clipboard) { ImGui::LogToClipboard(); ImGui::LogText("```cpp\n"); // Back quotes will make text appears without formatting when pasting on GitHub } 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_ENABLE_TEST_ENGINE ImGui::Text("define: IMGUI_ENABLE_TEST_ENGINE"); #endif #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_DEFAULT_SHELL_FUNCTIONS ImGui::Text("define: IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS"); #endif #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS"); #endif #ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS"); #endif #ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS"); #endif #ifdef IMGUI_DISABLE_FILE_FUNCTIONS ImGui::Text("define: IMGUI_DISABLE_FILE_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 _MSVC_LANG ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG); #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 #ifdef __EMSCRIPTEN__ ImGui::Text("define: __EMSCRIPTEN__"); #ifdef __EMSCRIPTEN_MAJOR__ ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_MAJOR__, __EMSCRIPTEN_MINOR__, __EMSCRIPTEN_TINY__); #else ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); #endif #endif #ifdef IMGUI_HAS_VIEWPORT ImGui::Text("define: IMGUI_HAS_VIEWPORT"); #endif #ifdef IMGUI_HAS_DOCK ImGui::Text("define: IMGUI_HAS_DOCK"); #endif #ifdef NDEBUG ImGui::Text("define: NDEBUG"); #endif // Heuristic to detect no-op IM_ASSERT() macros // - This is designed so people opening bug reports would convey and notice that they have disabled asserts for Dear ImGui code. // - 16 is > strlen("((void)(_EXPR))") which we suggested in our imconfig.h template as a possible way to disable. int assert_runs_expression = 0; IM_ASSERT(++assert_runs_expression); int assert_expand_len = (int)strlen(IM_STRINGIFY((IM_ASSERT(true)))); bool assert_maybe_disabled = (!assert_runs_expression || assert_expand_len <= 16); ImGui::Text("IM_ASSERT: runs expression: %s. expand size: %s%s", assert_runs_expression ? "OK" : "KO", (assert_expand_len > 16) ? "OK" : "KO", assert_maybe_disabled ? " (MAYBE DISABLED?!)" : ""); if (assert_maybe_disabled) { ImGui::SameLine(); HelpMarker("IM_ASSERT() calls assert() by default. Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes!"); } 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_NoMouse) ImGui::Text(" NoMouse"); if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) ImGui::Text(" NoKeyboard"); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) ImGui::Text(" DockingEnable"); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(" ViewportsEnable"); if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor"); if (io.ConfigDpiScaleFonts) ImGui::Text("io.ConfigDpiScaleFonts"); if (io.ConfigDpiScaleViewports) ImGui::Text("io.ConfigDpiScaleViewports"); if (io.ConfigViewportsNoAutoMerge) ImGui::Text("io.ConfigViewportsNoAutoMerge"); if (io.ConfigViewportsNoTaskBarIcon) ImGui::Text("io.ConfigViewportsNoTaskBarIcon"); if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration"); if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent"); if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit"); if (io.ConfigDockingNoDockingOver) ImGui::Text("io.ConfigDockingNoDockingOver"); if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift"); if (io.ConfigDockingAlwaysTabBar) ImGui::Text("io.ConfigDockingAlwaysTabBar"); if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); if (io.ConfigNavMoveSetMousePos) ImGui::Text("io.ConfigNavMoveSetMousePos"); if (io.ConfigNavCaptureKeyboard) ImGui::Text("io.ConfigNavCaptureKeyboard"); if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges"); if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly"); if (io.ConfigMemoryCompactTimer >= 0.0f) ImGui::Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer); 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"); if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) ImGui::Text(" PlatformHasViewports"); if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)ImGui::Text(" HasMouseHoveredViewport"); if (io.BackendFlags & ImGuiBackendFlags_HasParentViewport) ImGui::Text(" HasParentViewport"); if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset"); if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) ImGui::Text(" RendererHasTextures"); if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports) ImGui::Text(" RendererHasViewports"); ImGui::Separator(); ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height); ImGui::Text("io.Fonts->FontLoaderName: %s", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : "NULL"); 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::LogText("\n```\n"); ImGui::LogFinish(); } ImGui::EndChild(); } ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Style Editor / ShowStyleEditor() //----------------------------------------------------------------------------- // - ShowStyleSelector() // - ShowStyleEditor() //----------------------------------------------------------------------------- // Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. bool ImGui::ShowStyleSelector(const char* label) { // FIXME: This is a bit tricky to get right as style are functions, they don't register a name nor the fact that one is active. // So we keep track of last active one among our limited selection. static int style_idx = -1; const char* style_names[] = { "Dark", "Light", "Classic" }; bool ret = false; if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_COUNTOF(style_names)) ? style_names[style_idx] : "")) { for (int n = 0; n < IM_COUNTOF(style_names); n++) { if (ImGui::Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags_SelectOnNav)) { style_idx = n; ret = true; switch (style_idx) { case 0: ImGui::StyleColorsDark(); break; case 1: ImGui::StyleColorsLight(); break; case 2: ImGui::StyleColorsClassic(); break; } } else if (style_idx == n) ImGui::SetItemDefaultFocus(); } ImGui::EndCombo(); } return ret; } static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags) { if (flags == ImGuiTreeNodeFlags_DrawLinesNone) return "DrawLinesNone"; if (flags == ImGuiTreeNodeFlags_DrawLinesFull) return "DrawLinesFull"; if (flags == ImGuiTreeNodeFlags_DrawLinesToNodes) return "DrawLinesToNodes"; return ""; } // We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code. void ImGui::ShowStyleEditor(ImGuiStyle* ref) { IMGUI_DEMO_MARKER("Tools/Style Editor"); // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to // (without a reference style pointer, we will use one compared locally as a reference) ImGuiStyle& style = GetStyle(); static ImGuiStyle ref_saved_style; // Default to using internal storage as reference static bool init = true; if (init && ref == NULL) ref_saved_style = style; init = false; if (ref == NULL) ref = &ref_saved_style; // The logic behind dynamically changing 'max_border_size' is to not encourage people to increase border size too much: it'll likely reveal lots of subtle rendering artifacts and this isn't a priority right now. // Note that _MainScale is currently internal PLEASE DO NOT USE IN YOUR CODE. const float default_border_size = (float)(int)style._MainScale; const float max_border_size = IM_MAX(default_border_size, 2.0f); PushItemWidth(GetWindowWidth() * 0.50f); { // General SeparatorText("General"); if ((GetIO().BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) { BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!"); BulletText("For instructions, see:"); SameLine(); TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md"); } if (ShowStyleSelector("Colors##Selector")) ref_saved_style = style; ShowFontSelector("Fonts##Selector"); if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f")) style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work. SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize()); DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f); BeginDisabled(GetIO().ConfigDpiScaleFonts); DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f); SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten."); EndDisabled(); // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f) if (SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f")) style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding { bool border = (style.WindowBorderSize > 0.0f); if (Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? default_border_size : 0.0f; } } SameLine(); { bool border = (style.FrameBorderSize > 0.0f); if (Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? default_border_size : 0.0f; } } SameLine(); { bool border = (style.PopupBorderSize > 0.0f); if (Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? default_border_size : 0.0f; } } } // Save/Revert button if (Button("Save Ref")) *ref = ref_saved_style = style; SameLine(); if (Button("Revert Ref")) style = *ref; SameLine(); HelpMarker( "Save/Revert in local non-persistent storage. Default Colors definition are not affected. " "Use \"Export\" below to save them somewhere."); SeparatorText("Details"); if (BeginTabBar("##tabs", ImGuiTabBarFlags_None)) { if (BeginTabItem("Sizes")) { SeparatorText("Main"); SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); SeparatorText("Borders"); SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, max_border_size, "%.0f"); SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, max_border_size, "%.0f"); SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, max_border_size, "%.0f"); SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, max_border_size, "%.0f"); SeparatorText("Rounding"); SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); SeparatorText("Scrollbar"); SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("ScrollbarPadding", &style.ScrollbarPadding, 0.0f, 10.0f, "%.0f"); SeparatorText("Tabs"); SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, max_border_size, "%.0f"); SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, max_border_size, "%.0f"); SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, IM_MAX(3.0f, max_border_size), "%.0f"); SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set."); DragFloat("TabMinWidthBase", &style.TabMinWidthBase, 0.5f, 1.0f, 500.0f, "%.0f"); DragFloat("TabMinWidthShrink", &style.TabMinWidthShrink, 0.5f, 1.0f, 500.0f, "%0.f"); DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f"); DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f"); SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); SeparatorText("Tables"); SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f); SliderFloat2("TableAngledHeadersTextAlign", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f"); SeparatorText("Trees"); bool combo_open = BeginCombo("TreeLinesFlags", GetTreeLinesFlagsName(style.TreeLinesFlags)); SameLine(); HelpMarker("[Experimental] Tree lines may not work in all situations (e.g. using a clipper) and may incurs slight traversal overhead.\n\nImGuiTreeNodeFlags_DrawLinesFull is faster than ImGuiTreeNodeFlags_DrawLinesToNode."); if (combo_open) { const ImGuiTreeNodeFlags options[] = { ImGuiTreeNodeFlags_DrawLinesNone, ImGuiTreeNodeFlags_DrawLinesFull, ImGuiTreeNodeFlags_DrawLinesToNodes }; for (ImGuiTreeNodeFlags option : options) if (Selectable(GetTreeLinesFlagsName(option), style.TreeLinesFlags == option)) style.TreeLinesFlags = option; EndCombo(); } SliderFloat("TreeLinesSize", &style.TreeLinesSize, 0.0f, max_border_size, "%.0f"); SliderFloat("TreeLinesRounding", &style.TreeLinesRounding, 0.0f, 12.0f, "%.0f"); SeparatorText("Windows"); SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); SliderFloat("WindowBorderHoverPadding", &style.WindowBorderHoverPadding, 1.0f, 20.0f, "%.0f"); int window_menu_button_position = style.WindowMenuButtonPosition + 1; if (Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0")) style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1); SeparatorText("Widgets"); SliderFloat("ColorMarkerSize", &style.ColorMarkerSize, 0.0f, 8.0f, "%.0f"); Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0"); SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); SliderFloat("SeparatorSize", &style.SeparatorSize, 0.0f, 10.0f, "%.0f"); SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f"); SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f"); SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f"); SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); SliderFloat("ImageRounding", &style.ImageRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, max_border_size, "%.0f"); SeparatorText("Docking"); //SetCursorPosX(GetCursorPosX() + CalcItemWidth() - GetFrameHeight()); Checkbox("DockingNodeHasCloseButton", &style.DockingNodeHasCloseButton); SliderFloat("DockingSeparatorSize", &style.DockingSeparatorSize, 0.0f, 12.0f, "%.0f"); SeparatorText("Tooltips"); for (int n = 0; n < 2; n++) if (TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav")) { ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav; CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone); CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort); CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal); CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary); CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay); TreePop(); } SeparatorText("Misc"); SliderFloat2("DisplayWindowPadding", (float*)&style.DisplayWindowPadding, 0.0f, 30.0f, "%.0f"); SameLine(); HelpMarker("Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen."); SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); SameLine(); HelpMarker("Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); EndTabItem(); } if (BeginTabItem("Colors")) { static int output_dest = 0; static bool output_only_modified = true; if (Button("Export")) { if (output_dest == 0) LogToClipboard(); else LogToTTY(); LogText("ImVec4* colors = GetStyle().Colors;" IM_NEWLINE); for (int i = 0; i < ImGuiCol_COUNT; i++) { const ImVec4& col = style.Colors[i]; const char* name = GetStyleColorName(i); if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) 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); } LogFinish(); } SameLine(); SetNextItemWidth(GetFontSize() * 10); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); SameLine(); Checkbox("Only Modified Colors", &output_only_modified); static ImGuiTextFilter filter; filter.Draw("Filter colors", GetFontSize() * 16); static ImGuiColorEditFlags alpha_flags = 0; if (RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } SameLine(); if (RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } SameLine(); if (RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } SameLine(); HelpMarker( "In the color list:\n" "Left-click on color square to open color picker,\n" "Right-click to open edit options menu."); SetNextWindowSizeConstraints(ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX)); BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); PushItemWidth(GetFontSize() * -12); for (int i = 0; i < ImGuiCol_COUNT; i++) { const char* name = GetStyleColorName(i); if (!filter.PassFilter(name)) continue; PushID(i); #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (Button("?")) DebugFlashStyleColor((ImGuiCol)i); SetItemTooltip("Flash given color to identify places where it is used."); SameLine(); #endif 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 docs/FONTS.md about using icon fonts. It's really easy and super convenient! SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Save")) { ref->Colors[i] = style.Colors[i]; } SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Revert")) { style.Colors[i] = ref->Colors[i]; } } SameLine(0.0f, style.ItemInnerSpacing.x); TextUnformatted(name); PopID(); } PopItemWidth(); EndChild(); EndTabItem(); } if (BeginTabItem("Fonts")) { ImGuiIO& io = GetIO(); ImFontAtlas* atlas = io.Fonts; ShowFontAtlas(atlas); // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below. // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows Ctrl+Click text to get out of bounds). /* SeparatorText("Legacy Scaling"); const float MIN_SCALE = 0.3f; const float MAX_SCALE = 2.0f; HelpMarker( "Those are old settings provided for convenience.\n" "However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, " "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n" "Using those settings here will give you poor quality results."); PushItemWidth(GetFontSize() * 8); DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything //static float window_scale = 1.0f; //if (DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window // SetWindowFontScale(window_scale); PopItemWidth(); */ EndTabItem(); } if (BeginTabItem("Rendering")) { Checkbox("Anti-aliased lines", &style.AntiAliasedLines); SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex); SameLine(); HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering)."); Checkbox("Anti-aliased fill", &style.AntiAliasedFill); PushItemWidth(GetFontSize() * 8); DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles. DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp); const bool show_samples = IsItemActive(); if (show_samples) SetNextWindowPos(GetCursorScreenPos()); if (show_samples && BeginTooltip()) { TextUnformatted("(R = radius, N = approx number of segments)"); Spacing(); ImDrawList* draw_list = GetWindowDrawList(); const float min_widget_width = CalcTextSize("R: MMM\nN: MMM").x; for (int n = 0; n < 8; n++) { const float RAD_MIN = 5.0f; const float RAD_MAX = 70.0f; const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f); BeginGroup(); // N is not always exact here due to how PathArcTo() function work internally Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad)); const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f); const float offset_x = floorf(canvas_width * 0.5f); const float offset_y = floorf(RAD_MAX); const ImVec2 p1 = GetCursorScreenPos(); draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, GetColorU32(ImGuiCol_Text)); Dummy(ImVec2(canvas_width, RAD_MAX * 2)); /* const ImVec2 p2 = GetCursorScreenPos(); draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, GetColorU32(ImGuiCol_Text)); Dummy(ImVec2(canvas_width, RAD_MAX * 2)); */ EndGroup(); SameLine(); } EndTooltip(); } SameLine(); HelpMarker("When drawing circle primitives with \"num_segments == 0\" tessellation will be calculated automatically."); 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. DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha)."); PopItemWidth(); EndTabItem(); } if (BeginTabItem("Shadows")) { Text("Window shadows:"); ColorEdit4("Color", (float*)&style.Colors[ImGuiCol_WindowShadow], ImGuiColorEditFlags_AlphaBar); SameLine(); HelpMarker("Same as 'WindowShadow' in Colors tab."); SliderFloat("Size", &style.WindowShadowSize, 0.0f, 128.0f, "%.1f"); SameLine(); HelpMarker("Set shadow size to zero to disable shadows."); SliderFloat("Offset distance", &style.WindowShadowOffsetDist, 0.0f, 64.0f, "%.0f"); SliderAngle("Offset angle", &style.WindowShadowOffsetAngle); EndTabItem(); } EndTabBar(); } PopItemWidth(); } //----------------------------------------------------------------------------- // [SECTION] User Guide / ShowUserGuide() //----------------------------------------------------------------------------- // We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code. void ImGui::ShowUserGuide() { ImGuiIO& io = GetIO(); BulletText("Double-click on title bar to collapse window."); BulletText( "Click and drag on lower corner or border to resize window.\n" "(double-click to auto fit window to its contents)"); BulletText("Ctrl+Click on a slider or drag box to input value as text."); BulletText("Tab/Shift+Tab to cycle through keyboard editable fields."); BulletText("Ctrl+Tab/Ctrl+Shift+Tab to focus windows."); if (io.FontAllowUserScaling) BulletText("Ctrl+Mouse Wheel to zoom window contents."); BulletText("While inputting text:\n"); Indent(); BulletText("Ctrl+Left/Right to word jump."); BulletText("Ctrl+A or double-click to select all."); BulletText("Ctrl+X/C/V to use clipboard cut/copy/paste."); BulletText("Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo."); BulletText("Escape to revert."); Unindent(); BulletText("With Keyboard controls enabled:"); Indent(); BulletText("Arrow keys or Home/End/PageUp/PageDown to navigate."); BulletText("Space to activate a widget."); BulletText("Return to input text into a widget."); BulletText("Escape to deactivate a widget, close popup,\nexit a child window or the menu layer, clear focus."); BulletText("Alt to jump to the menu layer of a window."); BulletText("Menu or Shift+F10 to open a context menu."); Unindent(); BulletText("With Gamepad controls enabled:"); Indent(); BulletText("D-Pad: Navigate / Tweak / Resize (in Windowing mode)."); BulletText("%s Face button: Activate / Open / Toggle. Hold: activate with text input.", io.ConfigNavSwapGamepadButtons ? "East" : "South"); BulletText("%s Face button: Cancel / Close / Exit.", io.ConfigNavSwapGamepadButtons ? "South" : "East"); BulletText("West Face button: Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows)."); BulletText("North Face button: Open Context Menu."); BulletText("L1/R1: Tweak Slower/Faster, Focus Previous/Next (in Windowing Mode)."); Unindent(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() //----------------------------------------------------------------------------- // - ShowExampleAppMainMenuBar() // - ShowExampleMenuFile() //----------------------------------------------------------------------------- // Demonstrate creating a "main" fullscreen menu bar and populating it. // Note the difference between BeginMainMenuBar() and BeginMenuBar(): // - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!) // - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it. static void ShowExampleAppMainMenuBar() { if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { IMGUI_DEMO_MARKER("Menu/File"); ShowExampleMenuFile(); ImGui::EndMenu(); } if (ImGui::BeginMenu("Edit")) { IMGUI_DEMO_MARKER("Menu/Edit"); if (ImGui::MenuItem("Undo", "Ctrl+Z")) {} if (ImGui::MenuItem("Redo", "Ctrl+Y", false, false)) {} // Disabled item ImGui::Separator(); if (ImGui::MenuItem("Cut", "Ctrl+X")) {} if (ImGui::MenuItem("Copy", "Ctrl+C")) {} if (ImGui::MenuItem("Paste", "Ctrl+V")) {} ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } } // Note that shortcuts are currently provided for display only // (future version will add explicit flags to BeginMenu() to request processing shortcuts) static void ShowExampleMenuFile() { IMGUI_DEMO_MARKER("Examples/Menu"); ImGui::MenuItem("(demo menu)", NULL, false, false); if (ImGui::MenuItem("New")) {} if (ImGui::MenuItem("Open", "Ctrl+O")) {} if (ImGui::BeginMenu("Open Recent")) { ImGui::MenuItem("fish_hat.c"); ImGui::MenuItem("fish_hat.inl"); ImGui::MenuItem("fish_hat.h"); if (ImGui::BeginMenu("More..")) { ImGui::MenuItem("Hello"); ImGui::MenuItem("Sailor"); if (ImGui::BeginMenu("Recurse..")) { ShowExampleMenuFile(); ImGui::EndMenu(); } ImGui::EndMenu(); } ImGui::EndMenu(); } if (ImGui::MenuItem("Save", "Ctrl+S")) {} if (ImGui::MenuItem("Save As..")) {} ImGui::Separator(); if (ImGui::BeginMenu("Options")) { IMGUI_DEMO_MARKER("Examples/Menu/Options"); static bool enabled = true; ImGui::MenuItem("Enabled", "", &enabled); ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Borders); for (int i = 0; i < 10; i++) ImGui::Text("Scrolling Text %d", i); ImGui::EndChild(); static float f = 0.5f; static int n = 0; ImGui::SliderFloat("Value", &f, 0.0f, 1.0f); ImGui::InputFloat("Input", &f, 0.1f); ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0"); ImGui::EndMenu(); } if (ImGui::BeginMenu("Colors")) { IMGUI_DEMO_MARKER("Examples/Menu/Colors"); float sz = ImGui::GetTextLineHeight(); for (int i = 0; i < ImGuiCol_COUNT; i++) { const char* name = ImGui::GetStyleColorName((ImGuiCol)i); ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i)); ImGui::Dummy(ImVec2(sz, sz)); ImGui::SameLine(); ImGui::MenuItem(name); } ImGui::EndMenu(); } // Here we demonstrate appending again to the "Options" menu (which we already created above) // Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice. // In a real code-base using it would make senses to use this feature from very different code locations. if (ImGui::BeginMenu("Options")) // <-- Append! { IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu"); static bool b = true; ImGui::Checkbox("SomeOption", &b); ImGui::EndMenu(); } if (ImGui::BeginMenu("Disabled", false)) // Disabled { IM_ASSERT(0); } if (ImGui::MenuItem("Checked", NULL, true)) {} ImGui::Separator(); if (ImGui::MenuItem("Quit", "Alt+F4")) {} } //----------------------------------------------------------------------------- // [SECTION] Example App: Debug Console / ShowExampleAppConsole() //----------------------------------------------------------------------------- // Demonstrate creating a simple console window, with scrolling, filtering, completion and history. // For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions. struct ExampleAppConsole { char InputBuf[256]; ImVector Items; ImVector Commands; ImVector History; int HistoryPos; // -1: new line, 0..History.Size-1 browsing history. ImGuiTextFilter Filter; bool AutoScroll; bool ScrollToBottom; ExampleAppConsole() { ClearLog(); memset(InputBuf, 0, sizeof(InputBuf)); HistoryPos = -1; // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches. Commands.push_back("HELP"); Commands.push_back("HISTORY"); Commands.push_back("CLEAR"); Commands.push_back("CLASSIFY"); AutoScroll = true; ScrollToBottom = false; AddLog("Welcome to Dear ImGui!"); } ~ExampleAppConsole() { ClearLog(); for (int i = 0; i < History.Size; i++) ImGui::MemFree(History[i]); } // Portable helpers static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; } static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; } static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); } static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; } void ClearLog() { for (int i = 0; i < Items.Size; i++) ImGui::MemFree(Items[i]); Items.clear(); } void AddLog(const char* fmt, ...) IM_FMTARGS(2) { // FIXME-OPT char buf[1024]; va_list args; va_start(args, fmt); vsnprintf(buf, IM_COUNTOF(buf), fmt, args); buf[IM_COUNTOF(buf)-1] = 0; va_end(args); Items.push_back(Strdup(buf)); } void Draw(const char* title, bool* p_open) { ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); if (!ImGui::Begin(title, p_open)) { ImGui::End(); return; } IMGUI_DEMO_MARKER("Examples/Console"); // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar. // So e.g. IsItemHovered() will return true when hovering the title bar. // Here we create a context menu only available from the title bar. if (ImGui::BeginPopupContextItem()) { if (ImGui::MenuItem("Close Console")) *p_open = false; ImGui::EndPopup(); } ImGui::TextWrapped( "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate " "implementation may want to store entries along with extra data such as timestamp, emitter, etc."); ImGui::TextWrapped("Enter 'HELP' for help."); // TODO: display items starting from the bottom if (ImGui::SmallButton("Add Debug Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine(); if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine(); bool copy_to_clipboard = ImGui::SmallButton("Copy"); //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } ImGui::Separator(); // Options menu if (ImGui::BeginPopup("Options")) { ImGui::Checkbox("Auto-scroll", &AutoScroll); ImGui::EndPopup(); } // Options, Filter ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_O, ImGuiInputFlags_Tooltip); if (ImGui::Button("Options")) ImGui::OpenPopup("Options"); ImGui::SameLine(); Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); ImGui::Separator(); // Reserve enough left-over height for 1 separator + 1 input text ImGuiStyle& style = ImGui::GetStyle(); const float footer_height_to_reserve = style.SeparatorSize + style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_HorizontalScrollbar)) { if (ImGui::BeginPopupContextWindow()) { if (ImGui::Selectable("Clear")) ClearLog(); ImGui::EndPopup(); } // Display every line as a separate entry so we can change their color or add custom widgets. // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end()); // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping // to only process visible items. The clipper will automatically measure the height of your first item and then // "seek" to display only items in the visible area. // To use the clipper we can replace your standard loop: // for (int i = 0; i < Items.Size; i++) // With: // ImGuiListClipper clipper; // clipper.Begin(Items.Size); // while (clipper.Step()) // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) // - That your items are evenly spaced (same height) // - That you have cheap random access to your elements (you can access them given their index, // without processing all the ones before) // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property. // We would need random-access on the post-filtered list. // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices // or offsets of items that passed the filtering test, recomputing this array when user changes the filter, // and appending newly elements as they are inserted. This is left as a task to the user until we can manage // to improve this example code! // If your items are of variable height: // - Split them into same height items would be simpler and facilitate random-seeking into your list. // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing if (copy_to_clipboard) ImGui::LogToClipboard(); for (const char* item : Items) { if (!Filter.PassFilter(item)) continue; // Normally you would store more information in your item than just a string. // (e.g. make Items[] an array of structure, store color/type etc.) ImVec4 color; bool has_color = false; if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; } else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; } if (has_color) ImGui::PushStyleColor(ImGuiCol_Text, color); ImGui::TextUnformatted(item); if (has_color) ImGui::PopStyleColor(); } if (copy_to_clipboard) ImGui::LogFinish(); // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame. // Using a scrollbar or mouse-wheel will take away from the bottom edge. if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) ImGui::SetScrollHereY(1.0f); ScrollToBottom = false; ImGui::PopStyleVar(); } ImGui::EndChild(); ImGui::Separator(); // Command-line bool reclaim_focus = false; ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; if (ImGui::InputText("Input", InputBuf, IM_COUNTOF(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) { char* s = InputBuf; Strtrim(s); if (s[0]) ExecCommand(s); strcpy(s, ""); reclaim_focus = true; } // Auto-focus on window apparition ImGui::SetItemDefaultFocus(); if (reclaim_focus) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget ImGui::End(); } void ExecCommand(const char* command_line) { AddLog("# %s\n", command_line); // Insert into history. First find match and delete it so it can be pushed to the back. // This isn't trying to be smart or optimal. HistoryPos = -1; for (int i = History.Size - 1; i >= 0; i--) if (Stricmp(History[i], command_line) == 0) { ImGui::MemFree(History[i]); History.erase(History.begin() + i); break; } History.push_back(Strdup(command_line)); // Process command if (Stricmp(command_line, "CLEAR") == 0) { ClearLog(); } else if (Stricmp(command_line, "HELP") == 0) { AddLog("Commands:"); for (int i = 0; i < Commands.Size; i++) AddLog("- %s", Commands[i]); } else if (Stricmp(command_line, "HISTORY") == 0) { int first = History.Size - 10; for (int i = first > 0 ? first : 0; i < History.Size; i++) AddLog("%3d: %s\n", i, History[i]); } else { AddLog("Unknown command: '%s'\n", command_line); } // On command input, we scroll to bottom even if AutoScroll==false ScrollToBottom = true; } // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) { ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; return console->TextEditCallback(data); } int TextEditCallback(ImGuiInputTextCallbackData* data) { //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); switch (data->EventFlag) { case ImGuiInputTextFlags_CallbackCompletion: { // Example of TEXT COMPLETION // Locate beginning of current word const char* word_end = data->Buf + data->CursorPos; const char* word_start = word_end; while (word_start > data->Buf) { const char c = word_start[-1]; if (c == ' ' || c == '\t' || c == ',' || c == ';') break; word_start--; } // Build a list of candidates ImVector candidates; for (int i = 0; i < Commands.Size; i++) if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0) candidates.push_back(Commands[i]); if (candidates.Size == 0) { // No match AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start); } else if (candidates.Size == 1) { // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing. data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start)); data->InsertChars(data->CursorPos, candidates[0]); data->InsertChars(data->CursorPos, " "); } else { // Multiple matches. Complete as much as we can.. // So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches. int match_len = (int)(word_end - word_start); for (;;) { int c = 0; bool all_candidates_matches = true; for (int i = 0; i < candidates.Size && all_candidates_matches; i++) if (i == 0) c = toupper(candidates[i][match_len]); else if (c == 0 || c != toupper(candidates[i][match_len])) all_candidates_matches = false; if (!all_candidates_matches) break; match_len++; } if (match_len > 0) { data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start)); data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len); } // List matches AddLog("Possible matches:\n"); for (int i = 0; i < candidates.Size; i++) AddLog("- %s\n", candidates[i]); } break; } case ImGuiInputTextFlags_CallbackHistory: { // Example of HISTORY const int prev_history_pos = HistoryPos; if (data->EventKey == ImGuiKey_UpArrow) { if (HistoryPos == -1) HistoryPos = History.Size - 1; else if (HistoryPos > 0) HistoryPos--; } else if (data->EventKey == ImGuiKey_DownArrow) { if (HistoryPos != -1) if (++HistoryPos >= History.Size) HistoryPos = -1; } // A better implementation would preserve the data on the current input line along with cursor position. if (prev_history_pos != HistoryPos) { const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : ""; data->DeleteChars(0, data->BufTextLen); data->InsertChars(0, history_str); } } } return 0; } }; static void ShowExampleAppConsole(bool* p_open) { static ExampleAppConsole console; console.Draw("Example: Console", p_open); } //----------------------------------------------------------------------------- // [SECTION] Example App: Debug Log / ShowExampleAppLog() //----------------------------------------------------------------------------- // Usage: // static ExampleAppLog my_log; // my_log.AddLog("Hello %d world\n", 123); // my_log.Draw("title"); struct ExampleAppLog { ImGuiTextBuffer Buf; ImGuiTextFilter Filter; ImVector LineOffsets; // Index to lines offset. We maintain this with AddLog() calls. bool AutoScroll; // Keep scrolling if already at the bottom. ExampleAppLog() { AutoScroll = true; Clear(); } void Clear() { Buf.clear(); LineOffsets.clear(); LineOffsets.push_back(0); } void AddLog(const char* fmt, ...) IM_FMTARGS(2) { int old_size = Buf.size(); va_list args; va_start(args, fmt); Buf.appendfv(fmt, args); 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 + 1); } void Draw(const char* title, bool* p_open = NULL) { if (!ImGui::Begin(title, p_open)) { ImGui::End(); return; } // Options menu if (ImGui::BeginPopup("Options")) { ImGui::Checkbox("Auto-scroll", &AutoScroll); 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(); if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar)) { 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()) { // In this example we don't use the clipper when Filter is enabled. // This is because we don't have random access to the result of 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_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 { // 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 // - B) items all being the same height, // both of which we can handle since we have 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(); // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame. // Using a scrollbar or mouse-wheel will take away from the bottom edge. if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) ImGui::SetScrollHereY(1.0f); } ImGui::EndChild(); ImGui::End(); } }; // Demonstrate creating a simple log window with basic filtering. static void ShowExampleAppLog(bool* p_open) { static ExampleAppLog log; // For the demo: add a debug button _BEFORE_ the normal log window contents // We take advantage of a rarely used feature: 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); IMGUI_DEMO_MARKER("Examples/Log"); if (ImGui::SmallButton("[Debug] Add 5 entries")) { static int counter = 0; const char* categories[3] = { "info", "warn", "error" }; const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" }; for (int n = 0; n < 5; n++) { const char* category = categories[counter % IM_COUNTOF(categories)]; const char* word = words[counter % IM_COUNTOF(words)]; log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n", ImGui::GetFrameCount(), category, ImGui::GetTime(), word); counter++; } } ImGui::End(); // Actually call in the regular Log helper (which will Begin() into the same window as we just did) log.Draw("Example: Log", p_open); } //----------------------------------------------------------------------------- // [SECTION] Example App: Simple Layout / ShowExampleAppLayout() //----------------------------------------------------------------------------- // Demonstrate create a window with multiple child windows. static void ShowExampleAppLayout(bool* p_open) { ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar)) { IMGUI_DEMO_MARKER("Examples/Simple layout"); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Close", "Ctrl+W")) { *p_open = false; } ImGui::EndMenu(); } ImGui::EndMenuBar(); } // Left static int selected = 0; { ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX); for (int i = 0; i < 100; i++) { char label[128]; sprintf(label, "MyObject %d", i); if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SelectOnNav)) selected = i; } ImGui::EndChild(); } ImGui::SameLine(); // Right { ImGui::BeginGroup(); ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us ImGui::Text("MyObject: %d", selected); ImGui::Separator(); 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(); if (ImGui::Button("Save")) {} ImGui::EndGroup(); } } ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() //----------------------------------------------------------------------------- // Some of the interactions are a bit lack-luster: // - We would want pressing validating or leaving the filter to somehow restore focus. // - We may want more advanced filtering (child nodes) and clipper support: both will need extra work. // - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties. //----------------------------------------------------------------------------- struct ExampleAppPropertyEditor { ImGuiTextFilter Filter; ExampleTreeNode* SelectedNode = NULL; bool UseClipper = false; void Draw(ExampleTreeNode* root_node) { IMGUI_DEMO_MARKER("Examples/Property editor"); // Left side: draw tree // - Currently using a table to benefit from RowBg feature // - Our tree node are all of equal height, facilitating the use of a clipper. if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened)) { ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); ImGui::Checkbox("Use Clipper", &UseClipper); ImGui::SameLine(); ImGui::Text("(%d root nodes)", root_node->Childs.Size); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) Filter.Build(); ImGui::PopItemFlag(); if (ImGui::BeginTable("##list", 1, ImGuiTableFlags_RowBg)) { if (UseClipper) DrawClippedTree(root_node); else DrawTree(root_node); ImGui::EndTable(); } } ImGui::EndChild(); // Right side: draw properties ImGui::SameLine(); ImGui::BeginGroup(); // Lock X position if (ExampleTreeNode* node = SelectedNode) { ImGui::Text("%s", node->Name); ImGui::TextDisabled("UID: 0x%08X", node->UID); ImGui::Separator(); if (ImGui::BeginTable("##properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) { // Push object ID after we entered the table, so table is shared for all objects ImGui::PushID((int)node->UID); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger if (node->HasData) { // In a typical application, the structure description would be derived from a data-driven system. // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. // - Limits and some details are hard-coded to simplify the demo. for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) { ImGui::TableNextRow(); ImGui::PushID(field_desc.Name); ImGui::TableNextColumn(); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(field_desc.Name); ImGui::TableNextColumn(); void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); switch (field_desc.DataType) { case ImGuiDataType_Bool: { IM_ASSERT(field_desc.DataCount == 1); ImGui::Checkbox("##Editor", (bool*)field_ptr); break; } case ImGuiDataType_S32: { int v_min = INT_MIN, v_max = INT_MAX; ImGui::SetNextItemWidth(-FLT_MIN); ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); break; } case ImGuiDataType_Float: { float v_min = 0.0f, v_max = 1.0f; ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); break; } case ImGuiDataType_String: { ImGui::InputText("##Editor", reinterpret_cast(field_ptr), 28); break; } } ImGui::PopID(); } } ImGui::PopID(); ImGui::EndTable(); } } ImGui::EndGroup(); } // Custom search filter // - Here we apply on root node only. // - This does a case insensitive stristr which is pretty heavy. In a real large-scale app you would likely store a filtered list which in turns would be trivial to linearize. inline bool IsNodePassingFilter(ExampleTreeNode* node) { return node->Parent->Parent != NULL || Filter.PassFilter(node->Name); } // Basic version, recursive. This is how you would generally draw a tree. // - Simple but going to be noticeably costly if you have a large amount of nodes as DrawTreeNode() is called for all of them. // - On my desktop PC (2020), for 10K nodes in an optimized build this takes ~1.2 ms // - Unlike arrays or grids which are very easy to clip, trees are currently more difficult to clip. void DrawTree(ExampleTreeNode* node) { for (ExampleTreeNode* child : node->Childs) if (IsNodePassingFilter(child) && DrawTreeNode(child)) { DrawTree(child); ImGui::TreePop(); } } // More advanced version. Use a alternative clipping technique: fast-forwarding through non-visible chunks. // - On my desktop PC (2020), for 10K nodes in an optimized build this takes ~0.1 ms // (in ExampleTree_CreateDemoTree(), change 'int ROOT_ITEMS_COUNT = 10000' to try with this amount of root nodes). // - 1. Use clipper with indeterminate count (items_count = INT_MAX): we need to call SeekCursorForItem() at the end once we know the count. // - 2. Use SetNextItemStorageID() to specify ID used for open/close storage, making it easy to call TreeNodeGetOpen() on any arbitrary node. // - 3. Linearize tree during traversal: our tree data structure makes it easy to access sibling and parents. // - Unlike clipping for a regular array or grid which may be done using random access limited to visible areas, // this technique requires traversing most accessible nodes. This could be made more optimal with extra work, // but this is a decent simplicity<>speed trade-off. // See https://github.com/ocornut/imgui/issues/3823 for discussions about this. void DrawClippedTree(ExampleTreeNode* root_node) { ExampleTreeNode* node = root_node->Childs[0]; // First node ImGuiListClipper clipper; clipper.Begin(INT_MAX); while (clipper.Step()) while (clipper.UserIndex < clipper.DisplayEnd && node != NULL) node = DrawClippedTreeNodeAndAdvanceToNext(&clipper, node); // Keep going to count nodes and submit final count so we have a reliable scrollbar. // - One could consider caching this value and only refreshing it occasionally e.g. window is focused and an action occurs. // - Incorrect but cheap approximation would be to use 'clipper_current_idx = IM_MAX(clipper_current_idx, root_node->Childs.Size)' instead. // - If either of those is implemented, the general cost will approach zero when scrolling is at the top of the tree. while (node != NULL) node = DrawClippedTreeNodeAndAdvanceToNext(&clipper, node); //clipper.UserIndex = IM_MAX(clipper.UserIndex, root_node->Childs.Size); // <-- Cheap approximation instead of while() loop above. clipper.SeekCursorForItem(clipper.UserIndex); } ExampleTreeNode* DrawClippedTreeNodeAndAdvanceToNext(ImGuiListClipper* clipper, ExampleTreeNode* node) { if (IsNodePassingFilter(node)) { // Draw node if within visible range bool is_open = false; if (clipper->UserIndex >= clipper->DisplayStart && clipper->UserIndex < clipper->DisplayEnd) { is_open = DrawTreeNode(node); } else { is_open = (node->Childs.Size > 0 && ImGui::TreeNodeGetOpen((ImGuiID)node->UID)); if (is_open) ImGui::TreePush(node->Name); } clipper->UserIndex++; // Next node: recurse into childs if (is_open) return node->Childs[0]; } // Next node: next sibling, otherwise move back to parent while (node != NULL) { if (node->IndexInParent + 1 < node->Parent->Childs.Size) return node->Parent->Childs[node->IndexInParent + 1]; node = node->Parent; if (node->Parent == NULL) break; ImGui::TreePop(); } return NULL; } // To support node with same name we incorporate node->UID into the item ID. // (this would more naturally be done using PushID(node->UID) + TreeNodeEx(node->Name, tree_flags), // but it would require in DrawClippedTreeNodeAndAdvanceToNext() to add PushID() before TreePush(), and PopID() after TreePop(), // so instead we use TreeNodeEx(node->UID, tree_flags, "%s", node->Name) here) bool DrawTreeNode(ExampleTreeNode* node) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Left arrow support tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines if (node == SelectedNode) tree_flags |= ImGuiTreeNodeFlags_Selected; // Draw selection highlight if (node->Childs.Size == 0) tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen; // Use _NoTreePushOnOpen + set is_open=false to avoid unnecessarily push/pop on leaves. if (node->DataMyBool == false) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); ImGui::SetNextItemStorageID((ImGuiID)node->UID); // Use node->UID as storage id bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node->UID, tree_flags, "%s", node->Name); if (node->Childs.Size == 0) is_open = false; if (node->DataMyBool == false) ImGui::PopStyleColor(); if (ImGui::IsItemFocused()) SelectedNode = node; return is_open; } }; // Demonstrate creating a simple property editor. static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data) { ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Example: Property editor", p_open)) { ImGui::End(); return; } IMGUI_DEMO_MARKER("Examples/Property Editor"); static ExampleAppPropertyEditor property_editor; if (demo_data->DemoTree == NULL) demo_data->DemoTree = ExampleTree_CreateDemoTree(); property_editor.Draw(demo_data->DemoTree); ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Long Text / ShowExampleAppLongText() //----------------------------------------------------------------------------- // Demonstrate/test rendering huge amount of text, and the incidence of clipping. static void ShowExampleAppLongText(bool* p_open) { ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Example: Long text display", p_open)) { ImGui::End(); return; } IMGUI_DEMO_MARKER("Examples/Long text display"); static int test_type = 0; static ImGuiTextBuffer log; static int lines = 0; ImGui::Text("Printing unusually long amount of text."); ImGui::Combo("Test type", &test_type, "Single call to TextUnformatted()\0" "Multiple calls to Text(), clipped\0" "Multiple calls to Text(), not clipped (slow)\0"); ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size()); if (ImGui::Button("Clear")) { log.clear(); lines = 0; } ImGui::SameLine(); if (ImGui::Button("Add 1000 lines")) { for (int i = 0; i < 1000; i++) log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines + i); lines += 1000; } ImGui::BeginChild("Log"); switch (test_type) { case 0: // Single call to TextUnformatted() with a big buffer ImGui::TextUnformatted(log.begin(), log.end()); break; case 1: { // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); ImGuiListClipper clipper; clipper.Begin(lines); while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); ImGui::PopStyleVar(); break; } case 2: // Multiple calls to Text(), not clipped (slow) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); for (int i = 0; i < lines; i++) ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); ImGui::PopStyleVar(); break; } ImGui::EndChild(); ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() //----------------------------------------------------------------------------- // Demonstrate creating a window which gets auto-resized according to its content. static void ShowExampleAppAutoResize(bool* p_open) { if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::End(); return; } IMGUI_DEMO_MARKER("Examples/Auto-resizing window"); static int lines = 10; ImGui::TextUnformatted( "Window will resize every-frame to the size of its content.\n" "Note that you probably don't want to query the window size to\n" "output your content because that would create a feedback loop."); ImGui::SliderInt("Number of lines", &lines, 1, 20); for (int i = 0; i < lines; i++) ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() //----------------------------------------------------------------------------- // Demonstrate creating a window with custom resize constraints. // Note that size constraints currently don't work on a docked window (when in 'docking' branch) static void ShowExampleAppConstrainedResize(bool* p_open) { struct CustomConstraints { // Helper functions to demonstrate programmatic constraints // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier. // FIXME: None of the three demos works consistently when resizing from borders. static void AspectRatio(ImGuiSizeCallbackData* data) { float aspect_ratio = *(float*)data->UserData; data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio); } static void Square(ImGuiSizeCallbackData* data) { data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y); } static void Step(ImGuiSizeCallbackData* data) { float step = *(float*)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); } }; const char* test_desc[] = { "Between 100x100 and 500x500", "At least 100x100", "Resize vertical + lock current width", "Resize horizontal + lock current height", "Width Between 400 and 500", "Height at least 400", "Custom: Aspect Ratio 16:9", "Custom: Always Square", "Custom: Fixed Steps (100)", }; // Options static bool auto_resize = false; static bool window_padding = true; static int type = 6; // Aspect Ratio static int display_lines = 10; // Submit constraint float aspect_ratio = 16.0f / 9.0f; float fixed_step = 100.0f; if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500)); // Between 100x100 and 500x500 if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Resize vertical + lock current width if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Resize horizontal + lock current height if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width Between and 400 and 500 if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, FLT_MAX)); // Height at least 400 if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio); // Aspect ratio if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step); // Fixed Step // Submit window if (!window_padding) ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; const bool window_open = ImGui::Begin("Example: Constrained Resize", p_open, window_flags); if (!window_padding) ImGui::PopStyleVar(); IMGUI_DEMO_MARKER("Examples/Constrained Resizing window"); if (window_open) { if (ImGui::GetIO().KeyShift) { // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture) ImVec2 avail_size = ImGui::GetContentRegionAvail(); ImVec2 pos = ImGui::GetCursorScreenPos(); ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size); ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10)); ImGui::Text("%.2f x %.2f", avail_size.x, avail_size.y); } else { ImGui::Text("(Hold Shift to display a dummy viewport)"); if (ImGui::IsWindowDocked()) ImGui::Text("Warning: Sizing Constraints won't work if the window is docked!"); if (ImGui::Button("Set 200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); ImGui::Combo("Constraint", &type, test_desc, IM_COUNTOF(test_desc)); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); ImGui::Checkbox("Auto-resize", &auto_resize); ImGui::Checkbox("Window padding", &window_padding); for (int i = 0; i < display_lines; i++) ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, ""); } } ImGui::End(); } //----------------------------------------------------------------------------- // [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. static void ShowExampleAppSimpleOverlay(bool* p_open) { static int location = 0; ImGuiIO& io = ImGui::GetIO(); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; if (location >= 0) { const float PAD = 10.0f; const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! ImVec2 work_size = viewport->WorkSize; ImVec2 window_pos, window_pos_pivot; window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD); window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD); window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f; window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f; ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowViewport(viewport->ID); window_flags |= ImGuiWindowFlags_NoMove; } else if (location == -2) { // Center window ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); window_flags |= ImGuiWindowFlags_NoMove; } ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background if (ImGui::Begin("Example: Simple overlay", p_open, window_flags)) { IMGUI_DEMO_MARKER("Examples/Simple overlay"); // Scroll up to the beginning of this function to see overlay flags ImGui::Text("Simple overlay\n" "(right-click to change position)"); ImGui::Separator(); if (ImGui::IsMousePosValid()) ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y); else ImGui::Text("Mouse Position: "); if (ImGui::BeginPopupContextWindow()) { if (ImGui::MenuItem("Custom", NULL, location == -1)) location = -1; if (ImGui::MenuItem("Center", NULL, location == -2)) location = -2; if (ImGui::MenuItem("Top-left", NULL, location == 0)) location = 0; if (ImGui::MenuItem("Top-right", NULL, location == 1)) location = 1; if (ImGui::MenuItem("Bottom-left", NULL, location == 2)) location = 2; if (ImGui::MenuItem("Bottom-right", NULL, location == 3)) location = 3; if (p_open && ImGui::MenuItem("Close")) *p_open = false; ImGui::EndPopup(); } } ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen() //----------------------------------------------------------------------------- // Demonstrate creating a window covering the entire screen/viewport static void ShowExampleAppFullscreen(bool* p_open) { static bool use_work_area = true; static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.) // Based on your use case you may want one or the other. const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos); ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size); if (ImGui::Begin("Example: Fullscreen window", p_open, flags)) { IMGUI_DEMO_MARKER("Examples/Fullscreen window"); ImGui::Checkbox("Use work area instead of main area", &use_work_area); ImGui::SameLine(); HelpMarker("Main Area = entire viewport,\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\n\nEnable the main-menu bar in Examples menu to see the difference."); ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground); ImGui::CheckboxFlags("ImGuiWindowFlags_NoDecoration", &flags, ImGuiWindowFlags_NoDecoration); ImGui::Indent(); ImGui::CheckboxFlags("ImGuiWindowFlags_NoTitleBar", &flags, ImGuiWindowFlags_NoTitleBar); ImGui::CheckboxFlags("ImGuiWindowFlags_NoCollapse", &flags, ImGuiWindowFlags_NoCollapse); ImGui::CheckboxFlags("ImGuiWindowFlags_NoScrollbar", &flags, ImGuiWindowFlags_NoScrollbar); ImGui::Unindent(); if (p_open && ImGui::Button("Close this window")) *p_open = false; } ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() //----------------------------------------------------------------------------- // Demonstrate the use of "##" and "###" in identifiers to manipulate ID generation. // This applies to all regular items as well. // Read FAQ section "How can I have multiple widgets with the same label?" for details. static void ShowExampleAppWindowTitles(bool*) { const ImGuiViewport* viewport = ImGui::GetMainViewport(); const ImVec2 base_pos = viewport->Pos; // By default, Windows are uniquely identified by their title. // You can use the "##" and "###" markers to manipulate the display/ID. // Using "##" to display same title but have unique identifier. ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##1"); IMGUI_DEMO_MARKER("Examples/Manipulating window titles##1"); ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); ImGui::End(); ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##2"); IMGUI_DEMO_MARKER("Examples/Manipulating window titles##2"); ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); ImGui::End(); // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" char buf[128]; sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount()); ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver); ImGui::Begin(buf); IMGUI_DEMO_MARKER("Examples/Manipulating window titles##3"); ImGui::Text("This window has a changing title."); ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() //----------------------------------------------------------------------------- // Add a |_| looking shape static void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz) { const ImVec2 pos_norms[] = { { 0.0f, 0.0f }, { 0.3f, 0.0f }, { 0.3f, 0.7f }, { 0.7f, 0.7f }, { 0.7f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } }; for (const ImVec2& p : pos_norms) draw_list->PathLineTo(ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y))); } // Demonstrate using the low-level ImDrawList to draw custom shapes. static void ShowExampleAppCustomRendering(bool* p_open) { if (!ImGui::Begin("Example: Custom rendering", p_open)) { ImGui::End(); return; } IMGUI_DEMO_MARKER("Examples/Custom rendering"); // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not // exposed outside (to avoid messing with your types) In this example we are not using the maths operators! if (ImGui::BeginTabBar("##TabBar")) { if (ImGui::BeginTabItem("Primitives")) { IMGUI_DEMO_MARKER("Examples/Custom rendering/Primitives"); ImGui::PushItemWidth(-ImGui::GetFontSize() * 15); ImDrawList* draw_list = ImGui::GetWindowDrawList(); // Draw gradients // (note that those are currently exacerbating our sRGB/Linear issues) // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well.. ImGui::Text("Gradients"); ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight()); { ImVec2 p0 = ImGui::GetCursorScreenPos(); ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y); ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255)); ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255)); draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a); ImGui::InvisibleButton("##gradient1", gradient_size); } { ImVec2 p0 = ImGui::GetCursorScreenPos(); ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y); ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255)); ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255)); draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a); ImGui::InvisibleButton("##gradient2", gradient_size); } // Draw a bunch of primitives ImGui::Text("All primitives"); static float sz = 36.0f; static float thickness = 3.0f; static int ngon_sides = 6; static bool circle_segments_override = false; static int circle_segments_override_v = 12; static bool curve_segments_override = false; static int curve_segments_override_v = 8; static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 100.0f, "%.0f"); ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); ImGui::SliderInt("N-gon sides", &ngon_sides, 3, 12); ImGui::Checkbox("##circlesegmentoverride", &circle_segments_override); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); circle_segments_override |= ImGui::SliderInt("Circle segments override", &circle_segments_override_v, 3, 40); ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); curve_segments_override |= ImGui::SliderInt("Curves segments override", &curve_segments_override_v, 3, 40); ImGui::ColorEdit4("Color", &colf.x); const ImVec2 p = ImGui::GetCursorScreenPos(); const ImU32 col = ImColor(colf); const float spacing = 10.0f; const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight; const float rounding = sz / 5.0f; const int circle_segments = circle_segments_override ? circle_segments_override_v : 0; const int curve_segments = curve_segments_override ? curve_segments_override_v : 0; const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves const ImVec2 cp4[4] = { ImVec2(0.0f, 0.0f), ImVec2(sz * 1.3f, sz * 0.3f), ImVec2(sz - sz * 1.3f, sz - sz * 0.3f), ImVec2(sz, sz) }; float x = p.x + 4.0f; float y = p.y + 4.0f; for (int n = 0; n < 2; n++) { // First line uses a thickness of 1.0f, second line uses the configurable thickness float th = (n == 0) ? 1.0f : thickness; draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), ImVec2(sz*0.5f, sz*0.3f), col, -0.3f, circle_segments, th); x += sz + spacing; // Ellipse draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th); x += sz + spacing; // Square draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th); x += sz + spacing; // Square with all rounded corners draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape //draw_list->AddPolyline(concave_shape, IM_COUNTOF(concave_shape), col, ImDrawFlags_Closed, th); draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line // Path draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f); draw_list->PathStroke(col, ImDrawFlags_None, th); x += sz + spacing; // Quadratic Bezier Curve (3 control points) draw_list->AddBezierQuadratic(ImVec2(x + cp3[0].x, y + cp3[0].y), ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), col, th, curve_segments); x += sz + spacing; // Cubic Bezier Curve (4 control points) draw_list->AddBezierCubic(ImVec2(x + cp4[0].x, y + cp4[0].y), ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), col, th, curve_segments); x = p.x + 4; y += sz + spacing; } // Filled shapes draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides); x += sz + spacing; // N-gon draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments); x += sz + spacing; // Circle draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), ImVec2(sz * 0.5f, sz * 0.3f), col, -0.3f, circle_segments); x += sz + spacing;// Ellipse draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle //draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col); x += sz + spacing; // Concave shape draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine) // Path draw_list->PathArcTo(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, 3.141592f * -0.5f, 3.141592f); draw_list->PathFillConvex(col); x += sz + spacing; // Quadratic Bezier Curve (3 control points) draw_list->PathLineTo(ImVec2(x + cp3[0].x, y + cp3[0].y)); draw_list->PathBezierQuadraticCurveTo(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), curve_segments); draw_list->PathFillConvex(col); x += sz + spacing; 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)); x += sz + spacing; ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f)); ImGui::PopItemWidth(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Canvas")) { IMGUI_DEMO_MARKER("Examples/Custom rendering/Canvas"); static ImVector points; static ImVec2 scrolling(0.0f, 0.0f); static bool opt_enable_grid = true; static bool opt_enable_context_menu = true; static bool adding_line = false; ImGui::Checkbox("Enable grid", &opt_enable_grid); ImGui::Checkbox("Enable context menu", &opt_enable_context_menu); ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu."); // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling. // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls. // To use a child window instead we could use, e.g: // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove); // ImGui::PopStyleColor(); // ImGui::PopStyleVar(); // [...] // ImGui::EndChild(); // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive() ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f; if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f; ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y); // Draw border and background color ImGuiIO& io = ImGui::GetIO(); ImDrawList* draw_list = ImGui::GetWindowDrawList(); draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255)); draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255)); // This will catch our interactions ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); const bool is_hovered = ImGui::IsItemHovered(); // Hovered const bool is_active = ImGui::IsItemActive(); // Held const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y); // Add first and second point if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { points.push_back(mouse_pos_in_canvas); points.push_back(mouse_pos_in_canvas); adding_line = true; } if (adding_line) { points.back() = mouse_pos_in_canvas; if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) adding_line = false; } // Pan (we use a zero mouse threshold when there's no context menu) // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc. const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f; if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) { scrolling.x += io.MouseDelta.x; scrolling.y += io.MouseDelta.y; } // Context menu (under default mouse threshold) ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right); if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f) ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); if (ImGui::BeginPopup("context")) { if (adding_line) points.resize(points.size() - 2); adding_line = false; if (ImGui::MenuItem("Remove one", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); } if (ImGui::MenuItem("Remove all", NULL, false, points.Size > 0)) { points.clear(); } ImGui::EndPopup(); } // Draw grid + all lines in the canvas draw_list->PushClipRect(canvas_p0, canvas_p1, true); if (opt_enable_grid) { const float GRID_STEP = 64.0f; for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP) draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40)); for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP) draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40)); } for (int n = 0; n < points.Size; n += 2) draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); draw_list->PopClipRect(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Shadows")) { static float shadow_thickness = 40.0f; static ImVec4 shadow_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); static bool shadow_filled = false; static ImVec4 shape_color = ImVec4(0.9f, 0.6f, 0.3f, 1.0f); static float shape_rounding = 0.0f; static ImVec2 shadow_offset(0.0f, 0.0f); static ImVec4 background_color = ImVec4(0.5f, 0.5f, 0.7f, 1.0f); static bool wireframe = false; static bool aa = true; static int poly_shape_index = 0; ImGui::Checkbox("Shadow filled", &shadow_filled); ImGui::SameLine(); HelpMarker("This will fill the section behind the shape to shadow. It's often unnecessary and wasteful but provided for consistency."); ImGui::Checkbox("Wireframe shapes", &wireframe); ImGui::SameLine(); HelpMarker("This draws the shapes in wireframe so you can see the shadow underneath."); ImGui::Checkbox("Anti-aliasing", &aa); ImGui::DragFloat("Shadow Thickness", &shadow_thickness, 1.0f, 0.0f, 100.0f, "%.02f"); ImGui::SliderFloat2("Offset", (float*)&shadow_offset, -32.0f, 32.0f); ImGui::SameLine(); HelpMarker("Note that currently circles/convex shapes do not support non-zero offsets for unfilled shadows."); ImGui::ColorEdit4("Background Color", &background_color.x); ImGui::ColorEdit4("Shadow Color", &shadow_color.x); ImGui::ColorEdit4("Shape Color", &shape_color.x); ImGui::DragFloat("Shape Rounding", &shape_rounding, 1.0f, 0.0f, 20.0f, "%.02f"); ImGui::Combo("Convex shape", &poly_shape_index, "Shape 1\0Shape 2\0Shape 3\0Shape 4\0Shape 4 (winding reversed)"); ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImDrawListFlags old_flags = draw_list->Flags; if (aa) draw_list->Flags |= ~ImDrawListFlags_AntiAliasedFill; else draw_list->Flags &= ~ImDrawListFlags_AntiAliasedFill; // Fill a strip of background { ImVec2 p = ImGui::GetCursorScreenPos(); draw_list->AddRectFilled(p, ImVec2(p.x + ImGui::GetContentRegionAvail().x, p.y + 200.0f), ImGui::GetColorU32(background_color)); } // Rectangle { ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::Dummy(ImVec2(200.0f, 200.0f)); ImVec2 r1(p.x + 50.0f, p.y + 50.0f); ImVec2 r2(p.x + 150.0f, p.y + 150.0f); ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground; draw_list->AddShadowRect(r1, r2, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_offset, draw_flags, shape_rounding); if (wireframe) draw_list->AddRect(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding); else draw_list->AddRectFilled(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding); } ImGui::SameLine(); // Circle { ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::Dummy(ImVec2(200.0f, 200.0f)); // FIXME-SHADOWS: Offset forced to zero when shadow is not filled because it isn't supported float off = 10.0f; ImVec2 r1(p.x + 50.0f + off, p.y + 50.0f + off); ImVec2 r2(p.x + 150.0f - off, p.y + 150.0f - off); ImVec2 center(p.x + 100.0f, p.y + 100.0f); ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground; draw_list->AddShadowCircle(center, 50.0f, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_filled ? shadow_offset : ImVec2(0.0f, 0.0f), draw_flags, 0); if (wireframe) draw_list->AddCircle(center, 50.0f, ImGui::GetColorU32(shape_color), 0); else draw_list->AddCircleFilled(center, 50.0f, ImGui::GetColorU32(shape_color), 0); } ImGui::SameLine(); // Convex shape { ImVec2 pos = ImGui::GetCursorScreenPos(); ImGui::Dummy(ImVec2(200.0f, 200.0f)); const ImVec2 poly_centre(pos.x + 50.0f, pos.y + 100.0f); ImVec2 poly_points[8]; int poly_points_count = 0; switch (poly_shape_index) { default: case 0: { poly_points[0] = ImVec2(poly_centre.x - 32.0f, poly_centre.y); poly_points[1] = ImVec2(poly_centre.x - 24.0f, poly_centre.y + 24.0f); poly_points[2] = ImVec2(poly_centre.x, poly_centre.y + 32.0f); poly_points[3] = ImVec2(poly_centre.x + 24.0f, poly_centre.y + 24.0f); poly_points[4] = ImVec2(poly_centre.x + 32.0f, poly_centre.y); poly_points[5] = ImVec2(poly_centre.x + 24.0f, poly_centre.y - 24.0f); poly_points[6] = ImVec2(poly_centre.x, poly_centre.y - 32.0f); poly_points[7] = ImVec2(poly_centre.x - 32.0f, poly_centre.y - 32.0f); poly_points_count = 8; break; } case 1: { poly_points[0] = ImVec2(poly_centre.x + 40.0f, poly_centre.y - 20.0f); poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f); poly_points[2] = ImVec2(poly_centre.x - 24.0f, poly_centre.y - 32.0f); poly_points_count = 3; break; } case 2: { poly_points[0] = ImVec2(poly_centre.x - 32.0f, poly_centre.y); poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f); poly_points[2] = ImVec2(poly_centre.x + 32.0f, poly_centre.y); poly_points[3] = ImVec2(poly_centre.x, poly_centre.y - 32.0f); poly_points_count = 4; break; } case 3: { poly_points[0] = ImVec2(poly_centre.x - 4.0f, poly_centre.y - 20.0f); poly_points[1] = ImVec2(poly_centre.x + 12.0f, poly_centre.y + 2.0f); poly_points[2] = ImVec2(poly_centre.x + 8.0f, poly_centre.y + 16.0f); poly_points[3] = ImVec2(poly_centre.x, poly_centre.y + 32.0f); poly_points[4] = ImVec2(poly_centre.x - 16.0f, poly_centre.y - 32.0f); poly_points_count = 5; break; } case 4: // Same as test case 3 but with reversed winding { poly_points[0] = ImVec2(poly_centre.x - 16.0f, poly_centre.y - 32.0f); poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f); poly_points[2] = ImVec2(poly_centre.x + 8.0f, poly_centre.y + 16.0f); poly_points[3] = ImVec2(poly_centre.x + 12.0f, poly_centre.y + 2.0f); poly_points[4] = ImVec2(poly_centre.x - 4.0f, poly_centre.y - 20.0f); poly_points_count = 5; break; } } // FIXME-SHADOWS: Offset forced to zero when shadow is not filled because it isn't supported ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground; draw_list->AddShadowConvexPoly(poly_points, poly_points_count, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_filled ? shadow_offset : ImVec2(0.0f, 0.0f), draw_flags); if (wireframe) draw_list->AddPolyline(poly_points, poly_points_count, ImGui::GetColorU32(shape_color), true, 1.0f); else draw_list->AddConvexPolyFilled(poly_points, poly_points_count, ImGui::GetColorU32(shape_color)); } draw_list->Flags = old_flags; ImGui::EndTabItem(); } if (ImGui::BeginTabItem("BG/FG draw lists")) { IMGUI_DEMO_MARKER("Examples/Custom rendering/BG & FG draw lists"); static bool draw_bg = true; static bool draw_fg = true; ImGui::Checkbox("Draw in Background draw list", &draw_bg); ImGui::SameLine(); HelpMarker("The Background draw list will be rendered below every Dear ImGui windows."); ImGui::Checkbox("Draw in Foreground draw list", &draw_fg); ImGui::SameLine(); HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows."); 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), 0, 10 + 4); if (draw_fg) ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10); ImGui::EndTabItem(); } // Demonstrate out-of-order rendering via channels splitting // We use functions in ImDrawList as each draw list contains a convenience splitter, // but you can also instantiate your own ImDrawListSplitter if you need to nest them. if (ImGui::BeginTabItem("Draw Channels")) { IMGUI_DEMO_MARKER("Examples/Custom rendering/Draw Channels"); ImDrawList* draw_list = ImGui::GetWindowDrawList(); { ImGui::Text("Blue shape is drawn first: appears in back"); ImGui::Text("Red shape is drawn after: appears in front"); ImVec2 p0 = ImGui::GetCursorScreenPos(); draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red ImGui::Dummy(ImVec2(75, 75)); } ImGui::Separator(); { ImGui::Text("Blue shape is drawn first, into channel 1: appears in front"); ImGui::Text("Red shape is drawn after, into channel 0: appears in back"); ImVec2 p1 = ImGui::GetCursorScreenPos(); // Create 2 channels and draw a Blue shape THEN a Red shape. // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls. draw_list->ChannelsSplit(2); draw_list->ChannelsSetCurrent(1); draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue draw_list->ChannelsSetCurrent(0); draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1. // This works by copying draw indices only (vertices are not copied). draw_list->ChannelsMerge(); ImGui::Dummy(ImVec2(75, 75)); ImGui::Text("After reordering, contents of channel 0 appears below channel 1."); } ImGui::EndTabItem(); } ImGui::EndTabBar(); } ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace() //----------------------------------------------------------------------------- struct ImGuiDemoDockspaceArgs { bool IsFullscreen = true; bool KeepWindowPadding = false; // Keep WindowPadding to help understand that DockSpace() is a widget inside the window. ImGuiDockNodeFlags DockSpaceFlags = ImGuiDockNodeFlags_None; }; // THIS IS A DEMO FOR ADVANCED USAGE OF DockSpace(). // MOST REGULAR APPLICATIONS WANTING TO ALLOW DOCKING WINDOWS ON THE EDGE OF YOUR SCREEN CAN SIMPLY USE: // ImGui::NewFrame(); + ImGui::DockSpaceOverViewport(); // Create a dockspace in main viewport // OR: // ImGui::NewFrame(); + ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); // Create a dockspace in main viewport, where central node is transparent. // Demonstrate using DockSpace() to create an explicit docking node within an existing window, with various options. // Read https://github.com/ocornut/imgui/wiki/Docking for details. // The reasons we do not use DockSpaceOverViewport() in this demo is because: // - (1) we allow the host window to be floating/moveable instead of filling the viewport (when args->IsFullscreen == false) // which is mostly to showcase the idea that DockSpace() may be submitted anywhere. // Also see 'Demo->Examples->Documents' for a less abstract version of this. // - (2) we allow the host window to have padding (when args->UsePadding == true) // - (3) we expose variety of other flags. static void ShowExampleAppDockSpaceAdvanced(ImGuiDemoDockspaceArgs* args, bool* p_open) { ImGuiDockNodeFlags dockspace_flags = args->DockSpaceFlags; // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into, // because it would be confusing to have two docking targets within each others. ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking; if (args->IsFullscreen) { // Fullscreen dockspace: practically the same as calling DockSpaceOverViewport(); const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->WorkPos); ImGui::SetNextWindowSize(viewport->WorkSize); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; window_flags |= ImGuiWindowFlags_NoBackground; } else { // Floating dockspace dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode; } // Important: note that we proceed even if Begin() returns false (aka window is collapsed). // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive, // all active windows docked into it will lose their parent and become undocked. // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible. if (!args->KeepWindowPadding) ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("Window with a DockSpace", p_open, window_flags); if (!args->KeepWindowPadding) ImGui::PopStyleVar(); if (args->IsFullscreen) ImGui::PopStyleVar(2); // Submit the DockSpace widget inside our window // - Note that the id here is different from the one used by DockSpaceOverViewport(), so docking state won't get transfered between "Basic" and "Advanced" demos. // - If we made the ShowExampleAppDockSpaceBasic() calculate its own ID and pass it to DockSpaceOverViewport() the ID could easily match. ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); ImGui::End(); } static void ShowExampleAppDockSpaceBasic(ImGuiDockNodeFlags flags) { // Basic version which you can use in many apps: // e.g: // ImGui::DockSpaceOverViewport(); // or: // ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); // Central node will be transparent // or: // ImGuiViewport* viewport = ImGui::GetMainViewport(); // ImGui::DockSpaceOverViewport(0, viewport, ImGuiDockNodeFlags_None); ImGui::DockSpaceOverViewport(0, nullptr, flags); } void ShowExampleAppDockSpace(bool* p_open) { static int opt_demo_mode = 0; static bool opt_demo_mode_changed = false; static ImGuiDemoDockspaceArgs args; if (opt_demo_mode == 0) ShowExampleAppDockSpaceBasic(args.DockSpaceFlags); else ShowExampleAppDockSpaceAdvanced(&args, p_open); // Refocus our window to minimize perceived loss of focus when changing mode (caused by the fact that each use a different window, which would not happen in a real app) if (opt_demo_mode_changed) ImGui::SetNextWindowFocus(); ImGui::Begin("Examples: Dockspace", p_open, ImGuiWindowFlags_MenuBar); opt_demo_mode_changed = false; opt_demo_mode_changed |= ImGui::RadioButton("Basic demo mode", &opt_demo_mode, 0); opt_demo_mode_changed |= ImGui::RadioButton("Advanced demo mode", &opt_demo_mode, 1); ImGui::SeparatorText("Options"); if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable) == 0) { ShowDockingDisabledMessage(); } else if (opt_demo_mode == 0) { args.DockSpaceFlags &= ImGuiDockNodeFlags_PassthruCentralNode; // Allowed flags ImGui::CheckboxFlags("Flag: PassthruCentralNode", &args.DockSpaceFlags, ImGuiDockNodeFlags_PassthruCentralNode); } else if (opt_demo_mode == 1) { ImGui::Checkbox("Fullscreen", &args.IsFullscreen); ImGui::Checkbox("Keep Window Padding", &args.KeepWindowPadding); ImGui::SameLine(); HelpMarker("This is mostly exposed to facilitate understanding that a DockSpace() is _inside_ a window."); ImGui::BeginDisabled(args.IsFullscreen == false); ImGui::CheckboxFlags("Flag: PassthruCentralNode", &args.DockSpaceFlags, ImGuiDockNodeFlags_PassthruCentralNode); ImGui::EndDisabled(); ImGui::CheckboxFlags("Flag: NoDockingOverCentralNode", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoDockingOverCentralNode); ImGui::CheckboxFlags("Flag: NoDockingSplit", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoDockingSplit); ImGui::CheckboxFlags("Flag: NoUndocking", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoUndocking); ImGui::CheckboxFlags("Flag: NoResize", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoResize); ImGui::CheckboxFlags("Flag: AutoHideTabBar", &args.DockSpaceFlags, ImGuiDockNodeFlags_AutoHideTabBar); } // Show demo options and help if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Help")) { ImGui::TextUnformatted( "This demonstrates the use of ImGui::DockSpace() which allows you to manually\ncreate a docking node _within_ another window." "\n" "The \"Basic\" version uses the ImGui::DockSpaceOverViewport() helper. Most applications can probably use this."); ImGui::Separator(); ImGui::TextUnformatted("When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n" "- Drag from window title bar or their tab to dock/undock." "\n" "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n" "- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n" "- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)"); ImGui::Separator(); ImGui::TextUnformatted("More details:"); ImGui::Bullet(); ImGui::SameLine(); ImGui::TextLinkOpenURL("Docking Wiki page", "https://github.com/ocornut/imgui/wiki/Docking"); ImGui::BulletText("Read comments in ShowExampleAppDockSpace()"); ImGui::EndMenu(); } ImGui::EndMenuBar(); } ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() //----------------------------------------------------------------------------- // Simplified structure to mimic a Document model struct MyDocument { char Name[32]; // Document title int UID; // Unique ID (necessary as we can change title) bool Open; // Set when open (we keep an array of all available documents to simplify demo code!) bool OpenPrev; // Copy of Open from last update. bool Dirty; // Set when the document has been modified ImVec4 Color; // An arbitrary variable associated to the document MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f)) { UID = uid; snprintf(Name, sizeof(Name), "%s", name); Open = OpenPrev = open; Dirty = false; Color = color; } void DoOpen() { Open = true; } void DoForceClose() { Open = false; Dirty = false; } void DoSave() { Dirty = false; } }; struct ExampleAppDocuments { ImVector Documents; ImVector CloseQueue; MyDocument* RenamingDoc = NULL; bool RenamingStarted = false; ExampleAppDocuments() { Documents.push_back(MyDocument(0, "Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); Documents.push_back(MyDocument(1, "Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); Documents.push_back(MyDocument(2, "Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); Documents.push_back(MyDocument(3, "Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); Documents.push_back(MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f))); Documents.push_back(MyDocument(5, "Some Document", false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f))); } // As we allow to change document name, we append a never-changing document ID so tabs are stable void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size) { snprintf(out_buf, out_buf_size, "%s###doc%d", doc->Name, doc->UID); } // Display placeholder contents for the Document void DisplayDocContents(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(); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip); if (ImGui::Button("Rename..")) { RenamingDoc = doc; RenamingStarted = true; } ImGui::SameLine(); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip); if (ImGui::Button("Modify")) doc->Dirty = true; ImGui::SameLine(); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip); if (ImGui::Button("Save")) doc->DoSave(); ImGui::SameLine(); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip); if (ImGui::Button("Close")) CloseQueue.push_back(doc); 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 void DisplayDocContextMenu(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("Rename...", "Ctrl+R", false, doc->Open)) RenamingDoc = doc; if (ImGui::MenuItem("Close", "Ctrl+W", false, doc->Open)) CloseQueue.push_back(doc); ImGui::EndPopup(); } // [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. void NotifyOfDocumentsClosedElsewhere() { for (MyDocument& doc : Documents) { if (!doc.Open && doc.OpenPrev) ImGui::SetTabItemClosed(doc.Name); doc.OpenPrev = doc.Open; } } }; void ShowExampleAppDocuments(bool* p_open) { static ExampleAppDocuments app; // Options enum Target { Target_None, Target_Tab, // Create documents as local tab into a local tab bar Target_DockSpaceAndWindow // Create documents as regular windows, and create an embedded dockspace }; static Target opt_target = Target_Tab; static bool opt_reorderable = true; static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; // When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant") // that we emit gets docked into the same spot as the parent window ("Example: Documents"). // This would create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab // not visible, which in turn would stop submitting the "Eggplant" window. // We avoid this problem by submitting our documents window even if our parent window is not currently visible. // Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags_NoDocking. bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar); if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow) { ImGui::End(); return; } IMGUI_DEMO_MARKER("Examples/Documents"); // Menu if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) { int open_count = 0; for (MyDocument& doc : app.Documents) open_count += doc.Open ? 1 : 0; if (ImGui::BeginMenu("Open", open_count < app.Documents.Size)) { for (MyDocument& doc : app.Documents) if (!doc.Open && ImGui::MenuItem(doc.Name)) doc.DoOpen(); ImGui::EndMenu(); } if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) for (MyDocument& doc : app.Documents) app.CloseQueue.push_back(&doc); if (ImGui::MenuItem("Exit") && p_open) *p_open = false; ImGui::EndMenu(); } 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::PushItemWidth(ImGui::GetFontSize() * 12); ImGui::Combo("Output", (int*)&opt_target, "None\0TabBar+Tabs\0DockSpace+Window\0"); ImGui::PopItemWidth(); bool redock_all = false; if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox("Reorderable Tabs", &opt_reorderable); } if (opt_target == Target_DockSpaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); } ImGui::Separator(); // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags. // They have multiple effects: // - Display a dot next to the title. // - Tab is selected when clicking the X close button. // - Closure is not assumed (will wait for user to stop submitting the tab). // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window. // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole. // Tabs if (opt_target == Target_Tab) { ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline; if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) { if (opt_reorderable) app.NotifyOfDocumentsClosedElsewhere(); // [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 (MyDocument& doc : app.Documents) { if (!doc.Open) continue; // As we allow to change document name, we append a never-changing document id so tabs are stable char doc_name_buf[64]; app.GetTabName(&doc, doc_name_buf, sizeof(doc_name_buf)); ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); bool visible = ImGui::BeginTabItem(doc_name_buf, &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; app.CloseQueue.push_back(&doc); } app.DisplayDocContextMenu(&doc); if (visible) { app.DisplayDocContents(&doc); ImGui::EndTabItem(); } } ImGui::EndTabBar(); } } else if (opt_target == Target_DockSpaceAndWindow) { if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable) { app.NotifyOfDocumentsClosedElsewhere(); // Create a DockSpace node where any window can be docked ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(dockspace_id); // Create Windows for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) { MyDocument* doc = &app.Documents[doc_n]; if (!doc->Open) continue; ImGui::SetNextWindowDockID(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0); bool visible = ImGui::Begin(doc->Name, &doc->Open, window_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; app.CloseQueue.push_back(doc); } app.DisplayDocContextMenu(doc); if (visible) app.DisplayDocContents(doc); ImGui::End(); } } else { ShowDockingDisabledMessage(); } } // Early out other contents if (!window_contents_visible) { ImGui::End(); return; } // Display renaming UI if (app.RenamingDoc != NULL) { if (app.RenamingStarted) ImGui::OpenPopup("Rename"); if (ImGui::BeginPopup("Rename")) { ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30); if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_COUNTOF(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue)) { ImGui::CloseCurrentPopup(); app.RenamingDoc = NULL; } if (app.RenamingStarted) ImGui::SetKeyboardFocusHere(-1); ImGui::EndPopup(); } else { app.RenamingDoc = NULL; } app.RenamingStarted = false; } // Display closing confirmation UI if (!app.CloseQueue.empty()) { int close_queue_unsaved_documents = 0; for (int n = 0; n < app.CloseQueue.Size; n++) if (app.CloseQueue[n]->Dirty) close_queue_unsaved_documents++; if (close_queue_unsaved_documents == 0) { // Close documents when all are unsaved for (int n = 0; n < app.CloseQueue.Size; n++) app.CloseQueue[n]->DoForceClose(); app.CloseQueue.clear(); } else { if (!ImGui::IsPopupOpen("Save?")) ImGui::OpenPopup("Save?"); if (ImGui::BeginPopupModal("Save?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("Save change to the following items?"); float item_height = ImGui::GetTextLineHeightWithSpacing(); if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle)) for (MyDocument* doc : app.CloseQueue) if (doc->Dirty) ImGui::Text("%s", doc->Name); ImGui::EndChild(); ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f); if (ImGui::Button("Yes", button_size)) { for (MyDocument* doc : app.CloseQueue) { if (doc->Dirty) doc->DoSave(); doc->DoForceClose(); } app.CloseQueue.clear(); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("No", button_size)) { for (MyDocument* doc : app.CloseQueue) doc->DoForceClose(); app.CloseQueue.clear(); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel", button_size)) { app.CloseQueue.clear(); ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } } ImGui::End(); } //----------------------------------------------------------------------------- // [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser() //----------------------------------------------------------------------------- //#include "imgui_internal.h" // NavMoveRequestTryWrapping() struct ExampleAsset { ImGuiID ID; int Type; ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; } static const ImGuiTableSortSpecs* s_current_sort_specs; static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count) { s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function. if (items_count > 1) qsort(items, (size_t)items_count, sizeof(items[0]), ExampleAsset::CompareWithSortSpecs); s_current_sort_specs = NULL; } // Compare function to be used by qsort() static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs) { const ExampleAsset* a = (const ExampleAsset*)lhs; const ExampleAsset* b = (const ExampleAsset*)rhs; for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) { const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n]; int delta = 0; if (sort_spec->ColumnIndex == 0) delta = ((int)a->ID - (int)b->ID); else if (sort_spec->ColumnIndex == 1) delta = (a->Type - b->Type); if (delta > 0) return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1; if (delta < 0) return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; } return (int)a->ID - (int)b->ID; } }; const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL; struct ExampleAssetsBrowser { // Options bool ShowTypeOverlay = true; bool AllowSorting = true; bool AllowDragUnselected = false; bool AllowBoxSelect = true; float IconSize = 32.0f; int IconSpacing = 10; int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. bool StretchSpacing = true; // State ImVector Items; // Our items ExampleSelectionWithDeletion Selection; // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion) ImGuiID NextItemId = 0; // Unique identifier when creating new items bool RequestDelete = false; // Deferred deletion request bool RequestSort = false; // Deferred sort request float ZoomWheelAccum = 0.0f; // Mouse wheel accumulator to handle smooth wheels better // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way. ImVec2 LayoutItemSize; ImVec2 LayoutItemStep; // == LayoutItemSize + LayoutItemSpacing float LayoutItemSpacing = 0.0f; float LayoutSelectableSpacing = 0.0f; float LayoutOuterPadding = 0.0f; int LayoutColumnCount = 0; int LayoutLineCount = 0; // Functions ExampleAssetsBrowser() { AddItems(10000); } void AddItems(int count) { if (Items.Size == 0) NextItemId = 0; Items.reserve(Items.Size + count); for (int n = 0; n < count; n++, NextItemId++) Items.push_back(ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2)); RequestSort = true; } void ClearItems() { Items.clear(); Selection.Clear(); } // Logic would be written in the main code BeginChild() and outputting to local variables. // We extracted it into a function so we can call it easily from multiple places. void UpdateLayoutSizes(float avail_width) { // Layout: when not stretching: allow extending into right-most spacing. LayoutItemSpacing = (float)IconSpacing; if (StretchSpacing == false) avail_width += floorf(LayoutItemSpacing * 0.5f); // Layout: calculate number of icon per line and number of lines LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize)); LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1); LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount; // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer. if (StretchSpacing && LayoutColumnCount > 1) LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount; LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing); LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f); LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f); } void Draw(const char* title, bool* p_open) { ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver); if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar)) { ImGui::End(); return; } IMGUI_DEMO_MARKER("Examples/Assets Browser"); // Menu bar if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Add 10000 items")) AddItems(10000); if (ImGui::MenuItem("Clear items")) ClearItems(); ImGui::Separator(); if (ImGui::MenuItem("Close", NULL, false, p_open != NULL)) *p_open = false; ImGui::EndMenu(); } if (ImGui::BeginMenu("Edit")) { if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0)) RequestDelete = true; ImGui::EndMenu(); } if (ImGui::BeginMenu("Options")) { ImGui::PushItemWidth(ImGui::GetFontSize() * 10); ImGui::SeparatorText("Contents"); ImGui::Checkbox("Show Type Overlay", &ShowTypeOverlay); ImGui::Checkbox("Allow Sorting", &AllowSorting); ImGui::SeparatorText("Selection Behavior"); ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected); ImGui::Checkbox("Allow box-selection", &AllowBoxSelect); ImGui::SeparatorText("Layout"); ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Use Ctrl+Wheel to zoom"); ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32); ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32); ImGui::Checkbox("Stretch Spacing", &StretchSpacing); ImGui::PopItemWidth(); ImGui::EndMenu(); } ImGui::EndMenuBar(); } // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI if (AllowSorting) { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders; if (ImGui::BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight()))) { ImGui::TableSetupColumn("Index"); ImGui::TableSetupColumn("Type"); ImGui::TableHeadersRow(); if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) if (sort_specs->SpecsDirty || RequestSort) { ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size); sort_specs->SpecsDirty = RequestSort = false; } ImGui::EndTable(); } ImGui::PopStyleVar(); } ImGuiIO& io = ImGui::GetIO(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing))); if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove)) { ImDrawList* draw_list = ImGui::GetWindowDrawList(); const float avail_width = ImGui::GetContentRegionAvail().x; UpdateLayoutSizes(avail_width); // Calculate and store start position. ImVec2 start_pos = ImGui::GetCursorScreenPos(); start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding); ImGui::SetCursorScreenPos(start_pos); // Multi-select ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid; // - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items) if (AllowBoxSelect) ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; // - This feature allows dragging an unselected item without selecting it (rarely used) if (AllowDragUnselected) ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // - Enable keyboard wrapping on X axis // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing: // ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); // When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system) ms_flags |= ImGuiMultiSelectFlags_NavWrapX; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size); // Use custom selection adapter: store ID in selection (recommended) Selection.UserData = this; Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; }; Selection.ApplyRequests(ms_io); const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete; const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1; RequestDelete = false; // Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items) // Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... // But it is necessary for two reasons: // - Selectables uses it by default to visually fill the space between two items. // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do). ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing)); // Rendering parameters const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) }; const ImU32 icon_bg_color = ImGui::GetColorU32(IM_COL32(35, 35, 35, 220)); const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f); const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x); const int column_count = LayoutColumnCount; ImGuiListClipper clipper; clipper.Begin(LayoutLineCount, LayoutItemStep.y); if (item_curr_idx_to_focus != -1) clipper.IncludeItemByIndex(item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped. if (ms_io->RangeSrcItem != -1) clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped. while (clipper.Step()) { for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++) { const int item_min_idx_for_current_line = line_idx * column_count; const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size); for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx) { ExampleAsset* item_data = &Items[item_idx]; ImGui::PushID((int)item_data->ID); // Position item ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y); ImGui::SetCursorScreenPos(pos); ImGui::SetNextItemSelectionUserData(item_idx); bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID); bool item_is_visible = ImGui::IsRectVisible(LayoutItemSize); ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, LayoutItemSize); // Update our selection state immediately (without waiting for EndMultiSelect() requests) // because we use this to alter the color of our text/icon. if (ImGui::IsItemToggledSelection()) item_is_selected = !item_is_selected; // Focus (for after deletion) if (item_curr_idx_to_focus == item_idx) ImGui::SetKeyboardFocusHere(-1); // Drag and drop if (ImGui::BeginDragDropSource()) { // Create payload with full selection OR single unselected item. // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease) if (ImGui::GetDragDropPayload() == NULL) { ImVector payload_items; void* it = NULL; ImGuiID id = 0; if (!item_is_selected) payload_items.push_back(item_data->ID); else while (Selection.GetNextSelectedItem(&it, &id)) payload_items.push_back(id); ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); } // Display payload content in tooltip, by extracting it from the payload data // (we could read from selection, but it is more correct and reusable to read from payload) const ImGuiPayload* payload = ImGui::GetDragDropPayload(); const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID); ImGui::Text("%d assets", payload_count); ImGui::EndDragDropSource(); } // Render icon (a real app would likely display an image/thumbnail here) // Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well. if (item_is_visible) { ImVec2 box_min(pos.x - 1, pos.y - 1); ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color if (ShowTypeOverlay && item_data->Type != 0) { ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_COUNTOF(icon_type_overlay_colors)]; draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col); } if (display_label) { ImU32 label_col = ImGui::GetColorU32(item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled); char label[32]; sprintf(label, "%d", item_data->ID); draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label); } } ImGui::PopID(); } } } clipper.End(); ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing // Context menu if (ImGui::BeginPopupContextWindow()) { ImGui::Text("Selection: %d items", Selection.Size); ImGui::Separator(); if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0)) RequestDelete = true; ImGui::EndPopup(); } ms_io = ImGui::EndMultiSelect(); Selection.ApplyRequests(ms_io); if (want_delete) Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus); // Zooming with Ctrl+Wheel if (ImGui::IsWindowAppearing()) ZoomWheelAccum = 0.0f; if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false) { ZoomWheelAccum += io.MouseWheel; if (fabsf(ZoomWheelAccum) >= 1.0f) { // Calculate hovered item index from mouse location // FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it. const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x; const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y; const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx; //ImGui::SetTooltip("%f,%f -> item %d", hovered_item_nx, hovered_item_ny, hovered_item_idx); // Move those 4 lines in block above for easy debugging // Zoom IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum); IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f); ZoomWheelAccum -= (int)ZoomWheelAccum; UpdateLayoutSizes(avail_width); // Manipulate scroll to that we will land at the same Y location of currently hovered item. // - Calculate next frame position of item under mouse // - Set new scroll position to be used in next ImGui::BeginChild() call. float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(hovered_item_ny, 1.0f)) * LayoutItemStep.y; hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y; float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y; ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y); } } } ImGui::EndChild(); ImGui::Text("Selected: %d/%d items", Selection.Size, Items.Size); ImGui::End(); } }; void ShowExampleAppAssetsBrowser(bool* p_open) { IMGUI_DEMO_MARKER("Examples/Assets Browser"); static ExampleAssetsBrowser assets_browser; assets_browser.Draw("Example: Assets Browser", p_open); } // End of Demo code #else void ImGui::ShowAboutWindow(bool*) {} void ImGui::ShowDemoWindow(bool*) {} void ImGui::ShowUserGuide() {} void ImGui::ShowStyleEditor(ImGuiStyle*) {} bool ImGui::ShowStyleSelector(const char*) { return false; } #endif // #ifndef IMGUI_DISABLE_DEMO_WINDOWS #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui/source/imgui_draw.cpp ================================================ // dear imgui, v1.92.7 WIP // (drawing and font code) /* Index of this file: // [SECTION] STB libraries implementation // [SECTION] Style functions // [SECTION] ImDrawList // [SECTION] ImTriangulator, ImDrawList concave polygon fill // [SECTION] ImDrawList Shadow Primitives // [SECTION] ImDrawListSplitter // [SECTION] ImDrawData // [SECTION] Helpers ShadeVertsXXX functions // [SECTION] ImFontAtlasShadowTexConfig // [SECTION] ImFontConfig // [SECTION] ImFontAtlas, ImFontAtlasBuilder // [SECTION] ImFontAtlas: backend for stb_truetype // [SECTION] ImFontAtlas: glyph ranges helpers // [SECTION] ImFontGlyphRangesBuilder // [SECTION] ImFontBaked, ImFont // [SECTION] ImGui Internal Render Helpers // [SECTION] Decompression code // [SECTION] Default font data (ProggyClean.ttf) // [SECTION] Default font data (ProggyForever.ttf) */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_internal.h" #ifdef IMGUI_ENABLE_FREETYPE #include "misc/freetype/imgui_freetype.h" #endif #include // vsnprintf, sscanf, printf #include // intptr_t // Visual Studio warnings #ifdef _MSC_VER #pragma warning (disable: 4127) // condition expression is constant #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #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 is. #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 #pragma clang diagnostic ignored "-Wcomma" // warning: possible misuse of comma operator here #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier #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. #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type #pragma clang diagnostic ignored "-Wcast-qual" // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier #pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value #pragma GCC diagnostic ignored "-Wstack-protector" // warning: stack protector not protecting local variables: variable length buffer #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers #pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif //------------------------------------------------------------------------- // [SECTION] STB libraries implementation (for stb_truetype and stb_rect_pack) //------------------------------------------------------------------------- // Compile time options: //#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 //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION #ifdef IMGUI_STB_NAMESPACE namespace IMGUI_STB_NAMESPACE { #endif #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration #pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'. #pragma warning (disable: 5262) // (stb_truetype) implicit fall-through occurs here; are you missing a break statement? #pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read. #pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did. #endif #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma clang diagnostic ignored "-Wmissing-prototypes" #pragma clang diagnostic ignored "-Wimplicit-fallthrough" #endif #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" // warning: comparison is always true due to limited range of data type [-Wtype-limits] #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" // warning: this statement may fall through #endif #ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) #ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in another compilation unit #define STBRP_STATIC #define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0) #define STBRP_SORT ImQsort #define STB_RECT_PACK_IMPLEMENTATION #endif #ifdef IMGUI_STB_RECT_PACK_FILENAME #include IMGUI_STB_RECT_PACK_FILENAME #else #include "imstb_rectpack.h" #endif #endif #ifdef IMGUI_ENABLE_STB_TRUETYPE #ifndef STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) #ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in another compilation unit #define STBTT_malloc(x,u) ((void)(u), IM_ALLOC(x)) #define STBTT_free(x,u) ((void)(u), IM_FREE(x)) #define STBTT_assert(x) do { IM_ASSERT(x); } while(0) #define STBTT_fmod(x,y) ImFmod(x,y) #define STBTT_sqrt(x) ImSqrt(x) #define STBTT_pow(x,y) ImPow(x,y) #define STBTT_fabs(x) ImFabs(x) #define STBTT_ifloor(x) ((int)ImFloor(x)) #define STBTT_iceil(x) ((int)ImCeil(x)) #define STBTT_strlen(x) ImStrlen(x) #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #else #define STBTT_DEF extern #endif #ifdef IMGUI_STB_TRUETYPE_FILENAME #include IMGUI_STB_TRUETYPE_FILENAME #else #include "imstb_truetype.h" #endif #endif #endif // IMGUI_ENABLE_STB_TRUETYPE #if defined(__GNUC__) #pragma GCC diagnostic pop #endif #if defined(__clang__) #pragma clang diagnostic pop #endif #if defined(_MSC_VER) #pragma warning (pop) #endif #ifdef IMGUI_STB_NAMESPACE } // namespace ImStb using namespace IMGUI_STB_NAMESPACE; #endif //----------------------------------------------------------------------------- // [SECTION] Style functions //----------------------------------------------------------------------------- void ImGui::StyleColorsDark(ImGuiStyle* dst) { ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); ImVec4* colors = style->Colors; 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(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); colors[ImGuiCol_FrameBg] = ImVec4(0.16f, 0.29f, 0.48f, 0.54f); colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f); colors[ImGuiCol_TitleBgActive] = ImVec4(0.16f, 0.29f, 0.48f, 1.00f); colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f); colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_Separator] = colors[ImGuiCol_Border]; colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f); colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f); 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_InputTextCursor] = colors[ImGuiCol_Text]; colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 0.00f); colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); 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); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f); colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_UnsavedMarker] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_NavCursor] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); colors[ImGuiCol_WindowShadow] = ImVec4(0.08f, 0.08f, 0.08f, 0.35f); } void ImGui::StyleColorsClassic(ImGuiStyle* dst) { ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); ImVec4* colors = style->Colors; colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.85f); colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_PopupBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f); colors[ImGuiCol_Border] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f); colors[ImGuiCol_FrameBgHovered] = ImVec4(0.47f, 0.47f, 0.69f, 0.40f); colors[ImGuiCol_FrameBgActive] = ImVec4(0.42f, 0.41f, 0.64f, 0.69f); colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f); colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f); colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f); colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f); colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f); colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); colors[ImGuiCol_Button] = ImVec4(0.35f, 0.40f, 0.61f, 0.62f); colors[ImGuiCol_ButtonHovered] = ImVec4(0.40f, 0.48f, 0.71f, 0.79f); colors[ImGuiCol_ButtonActive] = ImVec4(0.46f, 0.54f, 0.80f, 1.00f); colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f); colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f); colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f); colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.50f, 0.50f, 0.60f); colors[ImGuiCol_SeparatorHovered] = ImVec4(0.60f, 0.60f, 0.70f, 1.00f); colors[ImGuiCol_SeparatorActive] = ImVec4(0.70f, 0.70f, 0.90f, 1.00f); colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f); 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_InputTextCursor] = colors[ImGuiCol_Text]; colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.53f, 0.53f, 0.87f, 0.00f); colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); 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); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TableHeaderBg] = ImVec4(0.27f, 0.27f, 0.38f, 1.00f); colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.45f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_UnsavedMarker] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); colors[ImGuiCol_WindowShadow] = ImVec4(0.08f, 0.08f, 0.08f, 0.35f); } // Those light colors are better suited with a thicker font than the default one + FrameBorder void ImGui::StyleColorsLight(ImGuiStyle* dst) { ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); ImVec4* colors = style->Colors; colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f); colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f); colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.30f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f); colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f); colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f); colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f); colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f); colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 0.80f); colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.46f, 0.54f, 0.80f, 0.60f); colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_Separator] = ImVec4(0.39f, 0.39f, 0.39f, 0.62f); colors[ImGuiCol_SeparatorHovered] = ImVec4(0.14f, 0.44f, 0.80f, 0.78f); colors[ImGuiCol_SeparatorActive] = ImVec4(0.14f, 0.44f, 0.80f, 1.00f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.35f, 0.35f, 0.35f, 0.17f); 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_InputTextCursor] = colors[ImGuiCol_Text]; colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f); colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 0.00f); colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); 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); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f); colors[ImGuiCol_TableHeaderBg] = ImVec4(0.78f, 0.87f, 0.98f, 1.00f); colors[ImGuiCol_TableBorderStrong] = ImVec4(0.57f, 0.57f, 0.64f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f); colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_UnsavedMarker] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f); colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); colors[ImGuiCol_WindowShadow] = ImVec4(0.08f, 0.08f, 0.08f, 0.35f); } //----------------------------------------------------------------------------- // [SECTION] ImFontAtlasShadowTexConfig //----------------------------------------------------------------------------- void ImFontAtlasShadowTexConfig::SetupDefaults() { TexCornerSize = 16; TexEdgeSize = 1; TexFalloffPower = 4.8f; TexDistanceFieldOffset = 3.8f; TexBlur = true; } int ImFontAtlasShadowTexConfig::CalcConvexTexWidth() const { // We have to pad the texture enough that we don't go off the edges when we expand the corner triangles return (int)((TexCornerSize / ImCos(IM_PI * 0.25f)) + (GetConvexTexPadding() * 2)); } int ImFontAtlasShadowTexConfig::CalcConvexTexHeight() const { return CalcConvexTexWidth(); // Same value } //----------------------------------------------------------------------------- // [SECTION] ImDrawList //----------------------------------------------------------------------------- ImDrawListSharedData::ImDrawListSharedData() { memset((void*)this, 0, sizeof(*this)); InitialFringeScale = 1.0f; for (int i = 0; i < IM_COUNTOF(ArcFastVtx); i++) { const float a = ((float)i * 2 * IM_PI) / (float)IM_COUNTOF(ArcFastVtx); ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); } ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); } ImDrawListSharedData::~ImDrawListSharedData() { IM_ASSERT(DrawLists.Size == 0); } void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) { if (CircleSegmentMaxError == max_error) return; IM_ASSERT(max_error > 0.0f); CircleSegmentMaxError = max_error; for (int i = 0; i < IM_COUNTOF(CircleSegmentCounts); i++) { const float radius = (float)i; CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX); } ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); } ImDrawList::ImDrawList(ImDrawListSharedData* shared_data) { memset((void*)this, 0, sizeof(*this)); _SetDrawListSharedData(shared_data); } ImDrawList::~ImDrawList() { _ClearFreeMemory(); _SetDrawListSharedData(NULL); } void ImDrawList::_SetDrawListSharedData(ImDrawListSharedData* data) { if (_Data != NULL) _Data->DrawLists.find_erase_unsorted(this); _Data = data; if (_Data != NULL) _Data->DrawLists.push_back(this); } // Initialize before use in a new frame. We always have a command ready in the buffer. // In the majority of cases, you would want to call PushClipRect() and PushTexture() after this. void ImDrawList::_ResetForNewFrame() { // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory to match ImDrawCmdHeader. IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0); IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == sizeof(ImVec4)); IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureRef)); IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == offsetof(ImDrawCmdHeader, ClipRect)); IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == offsetof(ImDrawCmdHeader, TexRef)); IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == offsetof(ImDrawCmdHeader, VtxOffset)); if (_Splitter._Count > 1) _Splitter.Merge(this); CmdBuffer.resize(0); IdxBuffer.resize(0); VtxBuffer.resize(0); Flags = _Data->InitialFlags; memset(&_CmdHeader, 0, sizeof(_CmdHeader)); _VtxCurrentIdx = 0; _VtxWritePtr = NULL; _IdxWritePtr = NULL; _ClipRectStack.resize(0); _TextureStack.resize(0); _CallbacksDataBuf.resize(0); _Path.resize(0); _Splitter.Clear(); CmdBuffer.push_back(ImDrawCmd()); _FringeScale = _Data->InitialFringeScale; } void ImDrawList::_ClearFreeMemory() { CmdBuffer.clear(); IdxBuffer.clear(); VtxBuffer.clear(); Flags = ImDrawListFlags_None; _VtxCurrentIdx = 0; _VtxWritePtr = NULL; _IdxWritePtr = NULL; _ClipRectStack.clear(); _TextureStack.clear(); _CallbacksDataBuf.clear(); _Path.clear(); _Splitter.ClearFreeMemory(); } // Note: For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club ImDrawList* ImDrawList::CloneOutput() const { ImDrawList* dst = IM_NEW(ImDrawList(NULL)); dst->CmdBuffer = CmdBuffer; dst->IdxBuffer = IdxBuffer; dst->VtxBuffer = VtxBuffer; dst->Flags = Flags; return dst; } void ImDrawList::AddDrawCmd() { ImDrawCmd draw_cmd; draw_cmd.ClipRect = _CmdHeader.ClipRect; // Same as calling ImDrawCmd_HeaderCopy() draw_cmd.TexRef = _CmdHeader.TexRef; draw_cmd.VtxOffset = _CmdHeader.VtxOffset; draw_cmd.IdxOffset = IdxBuffer.Size; IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); CmdBuffer.push_back(draw_cmd); } // Pop trailing draw command (used before merging or presenting to user) // Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL void ImDrawList::_PopUnusedDrawCmd() { while (CmdBuffer.Size > 0) { ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 || curr_cmd->UserCallback != NULL) return;// break; CmdBuffer.pop_back(); } } void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size) { IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; IM_ASSERT(callback != NULL); IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) { AddDrawCmd(); curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; } curr_cmd->UserCallback = callback; if (userdata_size == 0) { // Store user data directly in command (no indirection) curr_cmd->UserCallbackData = userdata; curr_cmd->UserCallbackDataSize = 0; curr_cmd->UserCallbackDataOffset = -1; } else { // Copy and store user data in a buffer IM_ASSERT(userdata != NULL); IM_ASSERT(userdata_size < (1u << 31)); curr_cmd->UserCallbackData = NULL; // Will be resolved during Render() curr_cmd->UserCallbackDataSize = (int)userdata_size; curr_cmd->UserCallbackDataOffset = _CallbacksDataBuf.Size; _CallbacksDataBuf.resize(_CallbacksDataBuf.Size + (int)userdata_size); memcpy(_CallbacksDataBuf.Data + (size_t)curr_cmd->UserCallbackDataOffset, userdata, userdata_size); } AddDrawCmd(); // Force a new command after us (see comment below) } // Compare ClipRect, TexRef and VtxOffset with a single memcmp() #define ImDrawCmd_HeaderSize (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int)) #define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TexRef, VtxOffset #define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TexRef, VtxOffset #define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset) // Try to merge two last draw commands void ImDrawList::_TryMergeDrawCmds() { IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; ImDrawCmd* prev_cmd = curr_cmd - 1; if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) { prev_cmd->ElemCount += curr_cmd->ElemCount; CmdBuffer.pop_back(); } } // Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. // The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. void ImDrawList::_OnChangedClipRect() { // If current command is used with different settings we need to add a new command IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) { AddDrawCmd(); return; } IM_ASSERT(curr_cmd->UserCallback == NULL); // Try to merge with previous command if it matches, else use current command ImDrawCmd* prev_cmd = curr_cmd - 1; if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL) { CmdBuffer.pop_back(); return; } curr_cmd->ClipRect = _CmdHeader.ClipRect; } void ImDrawList::_OnChangedTexture() { // If current command is used with different settings we need to add a new command IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && curr_cmd->TexRef != _CmdHeader.TexRef) { AddDrawCmd(); return; } // Unlike other _OnChangedXXX functions this may be called by ImFontAtlasUpdateDrawListsTextures() in more locations so we need to handle this case. if (curr_cmd->UserCallback != NULL) return; // Try to merge with previous command if it matches, else use current command ImDrawCmd* prev_cmd = curr_cmd - 1; if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL) { CmdBuffer.pop_back(); return; } curr_cmd->TexRef = _CmdHeader.TexRef; } void ImDrawList::_OnChangedVtxOffset() { // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. _VtxCurrentIdx = 0; IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 if (curr_cmd->ElemCount != 0) { AddDrawCmd(); return; } IM_ASSERT(curr_cmd->UserCallback == NULL); curr_cmd->VtxOffset = _CmdHeader.VtxOffset; } int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const { // Automatic segment count const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy if (radius_idx >= 0 && radius_idx < IM_COUNTOF(_Data->CircleSegmentCounts)) return _Data->CircleSegmentCounts[radius_idx]; // Use cached value else return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); } // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) void ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool intersect_with_current_clip_rect) { ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y); if (intersect_with_current_clip_rect) { ImVec4 current = _CmdHeader.ClipRect; if (cr.x < current.x) cr.x = current.x; if (cr.y < current.y) cr.y = current.y; if (cr.z > current.z) cr.z = current.z; if (cr.w > current.w) cr.w = current.w; } cr.z = ImMax(cr.x, cr.z); cr.w = ImMax(cr.y, cr.w); _ClipRectStack.push_back(cr); _CmdHeader.ClipRect = cr; _OnChangedClipRect(); } void ImDrawList::PushClipRectFullScreen() { PushClipRect(ImVec2(_Data->ClipRectFullscreen.x, _Data->ClipRectFullscreen.y), ImVec2(_Data->ClipRectFullscreen.z, _Data->ClipRectFullscreen.w)); } void ImDrawList::PopClipRect() { _ClipRectStack.pop_back(); _CmdHeader.ClipRect = (_ClipRectStack.Size == 0) ? _Data->ClipRectFullscreen : _ClipRectStack.Data[_ClipRectStack.Size - 1]; _OnChangedClipRect(); } void ImDrawList::PushTexture(ImTextureRef tex_ref) { _TextureStack.push_back(tex_ref); _CmdHeader.TexRef = tex_ref; if (tex_ref._TexData != NULL) IM_ASSERT(tex_ref._TexData->WantDestroyNextFrame == false); _OnChangedTexture(); } void ImDrawList::PopTexture() { _TextureStack.pop_back(); _CmdHeader.TexRef = (_TextureStack.Size == 0) ? ImTextureRef() : _TextureStack.Data[_TextureStack.Size - 1]; _OnChangedTexture(); } // This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTexture()/PopTexture(). void ImDrawList::_SetTexture(ImTextureRef tex_ref) { if (_CmdHeader.TexRef == tex_ref) return; _CmdHeader.TexRef = tex_ref; _TextureStack.back() = tex_ref; _OnChangedTexture(); } // Reserve space for a number of vertices and indices. // You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or // submit the intermediate results. PrimUnreserve() can be used to release unused allocations. void ImDrawList::PrimReserve(int idx_count, int vtx_count) { // Large mesh support (when enabled) IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0); if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset)) { // FIXME: In theory we should be testing that vtx_count <64k here. // In practice, RenderText() relies on reserving ahead for a worst case scenario so it is currently useful for us // to not make that check until we rework the text functions to handle clipping and large horizontal lines better. _CmdHeader.VtxOffset = VtxBuffer.Size; _OnChangedVtxOffset(); } ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; draw_cmd->ElemCount += idx_count; int vtx_buffer_old_size = VtxBuffer.Size; VtxBuffer.resize(vtx_buffer_old_size + vtx_count); _VtxWritePtr = VtxBuffer.Data + vtx_buffer_old_size; int idx_buffer_old_size = IdxBuffer.Size; IdxBuffer.resize(idx_buffer_old_size + idx_count); _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size; } // Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve(). void ImDrawList::PrimUnreserve(int idx_count, int vtx_count) { IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0); ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; draw_cmd->ElemCount -= idx_count; VtxBuffer.shrink(VtxBuffer.Size - vtx_count); IdxBuffer.shrink(IdxBuffer.Size - idx_count); } // Fully unrolled with inline call to keep our debug builds decently fast. void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) { ImVec2 b(c.x, a.y), d(a.x, c.y), uv(_Data->TexUvWhitePixel); ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; _VtxWritePtr += 4; _VtxCurrentIdx += 4; _IdxWritePtr += 6; } void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col) { ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y); ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; _VtxWritePtr += 4; _VtxCurrentIdx += 4; _IdxWritePtr += 6; } void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col) { ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; _VtxWritePtr += 4; _VtxCurrentIdx += 4; _IdxWritePtr += 6; } // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. // - Those macros expects l-values and need to be used as their own statement. // - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers. #define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0 #define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366) #define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0 // 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, ImDrawFlags flags, float thickness) { if (points_count < 2 || (col & IM_COL32_A_MASK) == 0) return; const bool closed = (flags & ImDrawFlags_Closed) != 0; const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw const bool thick_line = (thickness > _FringeScale); if (Flags & ImDrawListFlags_AntiAliasedLines) { // Anti-aliased stroke const float AA_SIZE = _FringeScale; const ImU32 col_trans = col & ~IM_COL32_A_MASK; // Thicknesses <1.0 should behave like thickness 1.0 thickness = ImMax(thickness, 1.0f); const int integer_thickness = (int)thickness; const float fractional_thickness = thickness - integer_thickness; // Do we want to draw this line using a texture? // - For now, only draw integer-width lines using textures to avoid issues with the way scaling occurs, could be improved. // - If AA_SIZE is not 1.0f we cannot use the texture path. const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f); // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)); const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12); const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3); PrimReserve(idx_count, vtx_count); // Temporary buffer // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point _Data->TempBuffer.reserve_discard(points_count * ((use_texture || !thick_line) ? 3 : 5)); ImVec2* temp_normals = _Data->TempBuffer.Data; ImVec2* temp_points = temp_normals + points_count; // Calculate normals (tangents) for each line segment for (int i1 = 0; i1 < count; i1++) { const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; 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]; // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point if (use_texture || !thick_line) { // [PATH 1] Texture-based lines (thick or non-thick) // [PATH 2] Non texture-based lines (non-thick) // The width of the geometry we need to draw - this is essentially pixels for the line itself, plus "one pixel" for AA. // - In the texture-based path, we don't use AA_SIZE here because the +1 is tied to the generated texture // (see ImFontAtlasBuildRenderLinesTexData() function), and so alternate values won't work without changes to that code. // - In the non texture-based paths, we would allow AA_SIZE to potentially be != 1.0f with a patch (e.g. fringe_scale patch to // allow scaling geometry while preserving one-screen-pixel AA fringe). const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend if (!closed) { temp_points[0] = points[0] + temp_normals[0] * half_draw_size; temp_points[1] = points[0] - temp_normals[0] * half_draw_size; temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * half_draw_size; temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * half_draw_size; } // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps) // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment { const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment const unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment // Average normals 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_FIXNORMAL2F(dm_x, dm_y); dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area dm_y *= half_draw_size; // Add temporary vertices for the outer edges 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; if (use_texture) { // Add indices for two triangles _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri _IdxWritePtr += 6; } else { // Add indexes for four triangles _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); // Right tri 1 _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Right tri 2 _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); // Left tri 1 _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); // Left tri 2 _IdxWritePtr += 12; } idx1 = idx2; } // Add vertices for each point on the line if (use_texture) { // If we're using textures we only need to emit the left/right edge vertices ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness]; /*if (fractional_thickness != 0.0f) // Currently always zero when use_texture==false! { const ImVec4 tex_uvs_1 = _Data->TexUvLines[integer_thickness + 1]; tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp() tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness; tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness; tex_uvs.w = tex_uvs.w + (tex_uvs_1.w - tex_uvs.w) * fractional_thickness; }*/ ImVec2 tex_uv0(tex_uvs.x, tex_uvs.y); ImVec2 tex_uv1(tex_uvs.z, tex_uvs.w); for (int i = 0; i < points_count; i++) { _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = tex_uv0; _VtxWritePtr[0].col = col; // Left-side outer edge _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = tex_uv1; _VtxWritePtr[1].col = col; // Right-side outer edge _VtxWritePtr += 2; } } else { // If we're not using a texture, we need the center vertex as well for (int i = 0; i < points_count; i++) { _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // Center of line _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge _VtxWritePtr += 3; } } } else { // [PATH 2] Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend if (!closed) { const int points_last = points_count - 1; temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE); temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness); temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness); temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE); temp_points[points_last * 4 + 0] = points[points_last] + temp_normals[points_last] * (half_inner_thickness + AA_SIZE); temp_points[points_last * 4 + 1] = points[points_last] + temp_normals[points_last] * (half_inner_thickness); temp_points[points_last * 4 + 2] = points[points_last] - temp_normals[points_last] * (half_inner_thickness); temp_points[points_last * 4 + 3] = points[points_last] - temp_normals[points_last] * (half_inner_thickness + AA_SIZE); } // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps) // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment { const int i2 = (i1 + 1) == points_count ? 0 : (i1 + 1); // i2 is the second point of the line segment const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : (idx1 + 4); // Vertex index for end of segment // Average normals 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_FIXNORMAL2F(dm_x, dm_y); 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 vertices 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); _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[12] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[13] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[14] = (ImDrawIdx)(idx1 + 3); _IdxWritePtr[15] = (ImDrawIdx)(idx1 + 3); _IdxWritePtr[16] = (ImDrawIdx)(idx2 + 3); _IdxWritePtr[17] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr += 18; idx1 = idx2; } // Add vertices for (int i = 0; i < points_count; i++) { _VtxWritePtr[0].pos = temp_points[i * 4 + 0]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col_trans; _VtxWritePtr[1].pos = temp_points[i * 4 + 1]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col; _VtxWritePtr[2].pos = temp_points[i * 4 + 2]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col; _VtxWritePtr[3].pos = temp_points[i * 4 + 3]; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col_trans; _VtxWritePtr += 4; } } _VtxCurrentIdx += (ImDrawIdx)vtx_count; } else { // [PATH 4] Non texture-based, Non anti-aliased lines const int idx_count = count * 6; const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges PrimReserve(idx_count, vtx_count); for (int i1 = 0; i1 < count; i1++) { const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; const ImVec2& p1 = points[i1]; const ImVec2& p2 = points[i2]; 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 = opaque_uv; _VtxWritePtr[0].col = col; _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col; _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col; _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col; _VtxWritePtr += 4; _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + 2); _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx + 2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx + 3); _IdxWritePtr += 6; _VtxCurrentIdx += 4; } } } // - We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds. // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col) { if (points_count < 3 || (col & IM_COL32_A_MASK) == 0) return; const ImVec2 uv = _Data->TexUvWhitePixel; if (Flags & ImDrawListFlags_AntiAliasedFill) { // Anti-aliased Fill const float AA_SIZE = _FringeScale; const ImU32 col_trans = col & ~IM_COL32_A_MASK; const int idx_count = (points_count - 2)*3 + points_count * 6; const int vtx_count = (points_count * 2); PrimReserve(idx_count, vtx_count); // Add indexes for fill unsigned int vtx_inner_idx = _VtxCurrentIdx; unsigned int vtx_outer_idx = _VtxCurrentIdx + 1; for (int i = 2; i < points_count; i++) { _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + ((i - 1) << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (i << 1)); _IdxWritePtr += 3; } // Compute normals _Data->TempBuffer.reserve_discard(points_count); ImVec2* temp_normals = _Data->TempBuffer.Data; for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++) { const ImVec2& p0 = points[i0]; const ImVec2& p1 = points[i1]; 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++) { // Average normals const ImVec2& n0 = temp_normals[i0]; const ImVec2& n1 = temp_normals[i1]; float dm_x = (n0.x + n1.x) * 0.5f; float dm_y = (n0.y + n1.y) * 0.5f; IM_FIXNORMAL2F(dm_x, dm_y); dm_x *= AA_SIZE * 0.5f; dm_y *= AA_SIZE * 0.5f; // Add vertices _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 _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr += 6; } _VtxCurrentIdx += (ImDrawIdx)vtx_count; } else { // Non Anti-aliased Fill const int idx_count = (points_count - 2)*3; const int vtx_count = points_count; PrimReserve(idx_count, vtx_count); for (int i = 0; i < vtx_count; i++) { _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; _VtxWritePtr++; } for (int i = 2; i < points_count; i++) { _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + i - 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + i); _IdxWritePtr += 3; } _VtxCurrentIdx += (ImDrawIdx)vtx_count; } } void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step) { if (radius < 0.5f) { _Path.push_back(center); return; } // Calculate arc auto segment step size if (a_step <= 0) a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius); // Make sure we never do steps larger than one quarter of the circle a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4); const int sample_range = ImAbs(a_max_sample - a_min_sample); const int a_next_step = a_step; int samples = sample_range + 1; bool extra_max_sample = false; if (a_step > 1) { samples = sample_range / a_step + 1; const int overstep = sample_range % a_step; if (overstep > 0) { extra_max_sample = true; samples++; // When we have overstep to avoid awkwardly looking one long line and one tiny one at the end, // distribute first step range evenly between them by reducing first step size. if (sample_range > 0) a_step -= (a_step - overstep) / 2; } } _Path.resize(_Path.Size + samples); ImVec2* out_ptr = _Path.Data + (_Path.Size - samples); int sample_index = a_min_sample; if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) { sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; if (sample_index < 0) sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; } if (a_max_sample >= a_min_sample) { for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step) { // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX; const ImVec2 s = _Data->ArcFastVtx[sample_index]; out_ptr->x = center.x + s.x * radius; out_ptr->y = center.y + s.y * radius; out_ptr++; } } else { for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step) { // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more if (sample_index < 0) sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; const ImVec2 s = _Data->ArcFastVtx[sample_index]; out_ptr->x = center.x + s.x * radius; out_ptr->y = center.y + s.y * radius; out_ptr++; } } if (extra_max_sample) { int normalized_max_sample = a_max_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; if (normalized_max_sample < 0) normalized_max_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; const ImVec2 s = _Data->ArcFastVtx[normalized_max_sample]; out_ptr->x = center.x + s.x * radius; out_ptr->y = center.y + s.y * radius; out_ptr++; } IM_ASSERT_PARANOID(_Path.Data + _Path.Size == out_ptr); } void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) { if (radius < 0.5f) { _Path.push_back(center); 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++) { const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); _Path.push_back(ImVec2(center.x + ImCos(a) * radius, center.y + ImSin(a) * radius)); } } // 0: East, 3: South, 6: West, 9: North, 12: East void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12) { if (radius < 0.5f) { _Path.push_back(center); return; } _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0); } void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) { if (radius < 0.5f) { _Path.push_back(center); return; } if (num_segments > 0) { _PathArcToN(center, radius, a_min, a_max, num_segments); return; } // Automatic segment count if (radius <= _Data->ArcFastRadiusCutoff) { const bool a_is_reverse = a_max < a_min; // We are going to use precomputed values for mid samples. // Determine first and last sample in lookup table that belong to the arc. const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f); const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f); const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f); const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f); const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0); const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; const bool a_emit_start = ImAbs(a_min_segment_angle - a_min) >= 1e-5f; const bool a_emit_end = ImAbs(a_max - a_max_segment_angle) >= 1e-5f; _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0))); if (a_emit_start) _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius)); if (a_mid_samples > 0) _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0); if (a_emit_end) _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius)); } else { const float arc_length = ImAbs(a_max - a_min); const int circle_segment_count = _CalcCircleAutoSegmentCount(radius); const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length)); _PathArcToN(center, radius, a_min, a_max, arc_segment_count); } } void ImDrawList::PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments) { if (num_segments <= 0) num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here. _Path.reserve(_Path.Size + (num_segments + 1)); const float cos_rot = ImCos(rot); const float sin_rot = ImSin(rot); for (int i = 0; i <= num_segments; i++) { const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y); const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot)); point.x = rel.x + center.x; point.y = rel.y + center.y; _Path.push_back(point); } } ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) { float u = 1.0f - t; float w1 = u * u * u; float w2 = 3 * u * u * t; float w3 = 3 * u * t * t; float w4 = t * t * t; return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x, w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y); } ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t) { float u = 1.0f - t; float w1 = u * u; float w2 = 2 * u * t; float w3 = t * t; return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y); } // Closely mimics ImBezierCubicClosestPointCasteljau() in imgui.cpp static void PathBezierCubicCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) { float dx = x4 - x1; float dy = y4 - y1; float d2 = (x2 - x4) * dy - (y2 - y4) * dx; float d3 = (x3 - x4) * dy - (y3 - y4) * dx; d2 = (d2 >= 0) ? d2 : -d2; d3 = (d3 >= 0) ? d3 : -d3; if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) { path->push_back(ImVec2(x4, y4)); } else if (level < 10) { float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; float x34 = (x3 + x4) * 0.5f, y34 = (y3 + y4) * 0.5f; float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f; float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f; PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); } } static void PathBezierQuadraticCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level) { float dx = x3 - x1, dy = y3 - y1; float det = (x2 - x3) * dy - (y2 - y3) * dx; if (det * det * 4.0f < tess_tol * (dx * dx + dy * dy)) { path->push_back(ImVec2(x3, y3)); } else if (level < 10) { float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1); PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1); } } void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments) { ImVec2 p1 = _Path.back(); if (num_segments == 0) { IM_ASSERT(_Data->CurveTessellationTol > 0.0f); PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated } else { float t_step = 1.0f / (float)num_segments; for (int i_step = 1; i_step <= num_segments; i_step++) _Path.push_back(ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step)); } } void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments) { ImVec2 p1 = _Path.back(); if (num_segments == 0) { IM_ASSERT(_Data->CurveTessellationTol > 0.0f); PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated } else { float t_step = 1.0f / (float)num_segments; for (int i_step = 1; i_step <= num_segments; i_step++) _Path.push_back(ImBezierQuadraticCalc(p1, p2, p3, t_step * i_step)); } } static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags) { /* IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4)); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023) // - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All) if (flags == ~0) { return ImDrawFlags_RoundCornersAll; } // - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code. if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); } // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f' #endif */ // If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values. // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway. // See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section. IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!"); if ((flags & ImDrawFlags_RoundCornersMask_) == 0) flags |= ImDrawFlags_RoundCornersAll; return flags; } void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags) { if (rounding >= 0.5f) { flags = FixRectCornerFlags(flags); rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f); rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f); } if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { PathLineTo(a); PathLineTo(ImVec2(b.x, a.y)); PathLineTo(b); PathLineTo(ImVec2(a.x, b.y)); } else { const float rounding_tl = (flags & ImDrawFlags_RoundCornersTopLeft) ? rounding : 0.0f; const float rounding_tr = (flags & ImDrawFlags_RoundCornersTopRight) ? rounding : 0.0f; const float rounding_br = (flags & ImDrawFlags_RoundCornersBottomRight) ? rounding : 0.0f; const float rounding_bl = (flags & ImDrawFlags_RoundCornersBottomLeft) ? rounding : 0.0f; PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9); PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12); PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3); PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl, 3, 6); } } void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1 + ImVec2(0.5f, 0.5f)); PathLineTo(p2 + ImVec2(0.5f, 0.5f)); PathStroke(col, 0, thickness); } // p_min = upper-left, p_max = lower-right // Note we don't render 1 pixels sized rectangles properly. void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; if (Flags & ImDrawListFlags_AntiAliasedLines) PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags); else PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes. PathStroke(col, ImDrawFlags_Closed, thickness); } void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0) return; if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { PrimReserve(6, 4); PrimRect(p_min, p_max, col); } else { PathRect(p_min, p_max, rounding, flags); PathFillConvex(col); } } // p_min = upper-left, p_max = lower-right void ImDrawList::AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left) { if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0) return; const ImVec2 uv = _Data->TexUvWhitePixel; PrimReserve(6, 4); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 3)); PrimWriteVtx(p_min, uv, col_upr_left); PrimWriteVtx(ImVec2(p_max.x, p_min.y), uv, col_upr_right); PrimWriteVtx(p_max, uv, col_bot_right); PrimWriteVtx(ImVec2(p_min.x, p_max.y), uv, col_bot_left); } void ImDrawList::AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1); PathLineTo(p2); PathLineTo(p3); PathLineTo(p4); PathStroke(col, ImDrawFlags_Closed, thickness); } void ImDrawList::AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1); PathLineTo(p2); PathLineTo(p3); PathLineTo(p4); PathFillConvex(col); } void ImDrawList::AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1); PathLineTo(p2); PathLineTo(p3); PathStroke(col, ImDrawFlags_Closed, thickness); } void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1); PathLineTo(p2); PathLineTo(p3); PathFillConvex(col); } void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness) { if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f) return; if (num_segments <= 0) { // Use arc with automatic segment count _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); _Path.Size--; } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); // 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(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); } PathStroke(col, ImDrawFlags_Closed, thickness); } void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments) { if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f) return; if (num_segments <= 0) { // Use arc with automatic segment count _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); _Path.Size--; } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); // 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(center, radius, 0.0f, a_max, num_segments - 1); } PathFillConvex(col); } // Guaranteed to honor 'num_segments' void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness) { 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(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); PathStroke(col, ImDrawFlags_Closed, thickness); } // Guaranteed to honor 'num_segments' void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments) { 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(center, radius, 0.0f, a_max, num_segments - 1); PathFillConvex(col); } // Ellipse void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; if (num_segments <= 0) num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here. // 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; PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1); PathStroke(col, true, thickness); } void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments) { if ((col & IM_COL32_A_MASK) == 0) return; if (num_segments <= 0) num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here. // 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; PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1); PathFillConvex(col); } // Cubic Bezier takes 4 controls points void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1); PathBezierCubicCurveTo(p2, p3, p4, num_segments); PathStroke(col, 0, thickness); } // Quadratic Bezier takes 3 controls points void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1); PathBezierQuadraticCurveTo(p2, p3, num_segments); PathStroke(col, 0, thickness); } void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) { if ((col & IM_COL32_A_MASK) == 0) return; // Accept null ranges if (text_begin == text_end || text_begin[0] == 0) return; // No need to strlen() here: font->RenderText() will do it and may early out. // Pull default font/size from the shared ImDrawListSharedData instance if (font == NULL) font = _Data->Font; if (font_size == 0.0f) font_size = _Data->FontSize; ImVec4 clip_rect = _CmdHeader.ClipRect; if (cpu_fine_clip_rect) { clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x); clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y); clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); } font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None); } void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) { AddText(_Data->Font, _Data->FontSize, pos, col, text_begin, text_end); } void ImDrawList::AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) return; const bool push_texture_id = tex_ref != _CmdHeader.TexRef; if (push_texture_id) PushTexture(tex_ref); PrimReserve(6, 4); PrimRectUV(p_min, p_max, uv_min, uv_max, col); if (push_texture_id) PopTexture(); } void ImDrawList::AddImageQuad(ImTextureRef tex_ref, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) return; const bool push_texture_id = tex_ref != _CmdHeader.TexRef; if (push_texture_id) PushTexture(tex_ref); PrimReserve(6, 4); PrimQuadUV(p1, p2, p3, p4, uv1, uv2, uv3, uv4, col); if (push_texture_id) PopTexture(); } void ImDrawList::AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0) return; flags = FixRectCornerFlags(flags); if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { AddImage(tex_ref, p_min, p_max, uv_min, uv_max, col); return; } const bool push_texture_id = tex_ref != _CmdHeader.TexRef; if (push_texture_id) PushTexture(tex_ref); int vert_start_idx = VtxBuffer.Size; PathRect(p_min, p_max, rounding, flags); PathFillConvex(col); int vert_end_idx = VtxBuffer.Size; ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true); if (push_texture_id) PopTexture(); } //----------------------------------------------------------------------------- // [SECTION] ImTriangulator, ImDrawList concave polygon fill //----------------------------------------------------------------------------- // Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity. // Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf // Provided as a convenience for user but not used by main library. //----------------------------------------------------------------------------- // - ImTriangulator [Internal] // - AddConcavePolyFilled() //----------------------------------------------------------------------------- enum ImTriangulatorNodeType { ImTriangulatorNodeType_Convex, ImTriangulatorNodeType_Ear, ImTriangulatorNodeType_Reflex }; struct ImTriangulatorNode { ImTriangulatorNodeType Type; int Index; ImVec2 Pos; ImTriangulatorNode* Next; ImTriangulatorNode* Prev; void Unlink() { Next->Prev = Prev; Prev->Next = Next; } }; struct ImTriangulatorNodeSpan { ImTriangulatorNode** Data = NULL; int Size = 0; void push_back(ImTriangulatorNode* node) { Data[Size++] = node; } void find_erase_unsorted(int idx) { for (int i = Size - 1; i >= 0; i--) if (Data[i]->Index == idx) { Data[i] = Data[Size - 1]; Size--; return; } } }; struct ImTriangulator { static int EstimateTriangleCount(int points_count) { return (points_count < 3) ? 0 : points_count - 2; } static int EstimateScratchBufferSize(int points_count) { return sizeof(ImTriangulatorNode) * points_count + sizeof(ImTriangulatorNode*) * points_count * 2; } void Init(const ImVec2* points, int points_count, void* scratch_buffer); void GetNextTriangle(unsigned int out_triangle[3]); // Return relative indexes for next triangle // Internal functions void BuildNodes(const ImVec2* points, int points_count); void BuildReflexes(); void BuildEars(); void FlipNodeList(); bool IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const; void ReclassifyNode(ImTriangulatorNode* node); // Internal members int _TrianglesLeft = 0; ImTriangulatorNode* _Nodes = NULL; ImTriangulatorNodeSpan _Ears; ImTriangulatorNodeSpan _Reflexes; }; // Distribute storage for nodes, ears and reflexes. // FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer // (this would require first building reflexes to bail to convex if empty, without even building nodes) void ImTriangulator::Init(const ImVec2* points, int points_count, void* scratch_buffer) { IM_ASSERT(scratch_buffer != NULL && points_count >= 3); _TrianglesLeft = EstimateTriangleCount(points_count); _Nodes = (ImTriangulatorNode*)scratch_buffer; // points_count x Node _Ears.Data = (ImTriangulatorNode**)(_Nodes + points_count); // points_count x Node* _Reflexes.Data = (ImTriangulatorNode**)(_Nodes + points_count) + points_count; // points_count x Node* BuildNodes(points, points_count); BuildReflexes(); BuildEars(); } void ImTriangulator::BuildNodes(const ImVec2* points, int points_count) { for (int i = 0; i < points_count; i++) { _Nodes[i].Type = ImTriangulatorNodeType_Convex; _Nodes[i].Index = i; _Nodes[i].Pos = points[i]; _Nodes[i].Next = _Nodes + i + 1; _Nodes[i].Prev = _Nodes + i - 1; } _Nodes[0].Prev = _Nodes + points_count - 1; _Nodes[points_count - 1].Next = _Nodes; } void ImTriangulator::BuildReflexes() { ImTriangulatorNode* n1 = _Nodes; for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next) { if (ImTriangleIsClockwise(n1->Prev->Pos, n1->Pos, n1->Next->Pos)) continue; n1->Type = ImTriangulatorNodeType_Reflex; _Reflexes.push_back(n1); } } void ImTriangulator::BuildEars() { ImTriangulatorNode* n1 = _Nodes; for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next) { if (n1->Type != ImTriangulatorNodeType_Convex) continue; if (!IsEar(n1->Prev->Index, n1->Index, n1->Next->Index, n1->Prev->Pos, n1->Pos, n1->Next->Pos)) continue; n1->Type = ImTriangulatorNodeType_Ear; _Ears.push_back(n1); } } void ImTriangulator::GetNextTriangle(unsigned int out_triangle[3]) { if (_Ears.Size == 0) { FlipNodeList(); ImTriangulatorNode* node = _Nodes; for (int i = _TrianglesLeft; i >= 0; i--, node = node->Next) node->Type = ImTriangulatorNodeType_Convex; _Reflexes.Size = 0; BuildReflexes(); BuildEars(); // If we still don't have ears, it means geometry is degenerated. if (_Ears.Size == 0) { // Return first triangle available, mimicking the behavior of convex fill. IM_ASSERT(_TrianglesLeft > 0); // Geometry is degenerated _Ears.Data[0] = _Nodes; _Ears.Size = 1; } } ImTriangulatorNode* ear = _Ears.Data[--_Ears.Size]; out_triangle[0] = ear->Prev->Index; out_triangle[1] = ear->Index; out_triangle[2] = ear->Next->Index; ear->Unlink(); if (ear == _Nodes) _Nodes = ear->Next; ReclassifyNode(ear->Prev); ReclassifyNode(ear->Next); _TrianglesLeft--; } void ImTriangulator::FlipNodeList() { ImTriangulatorNode* prev = _Nodes; ImTriangulatorNode* temp = _Nodes; ImTriangulatorNode* current = _Nodes->Next; prev->Next = prev; prev->Prev = prev; while (current != _Nodes) { temp = current->Next; current->Next = prev; prev->Prev = current; _Nodes->Next = current; current->Prev = _Nodes; prev = current; current = temp; } _Nodes = prev; } // A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm) bool ImTriangulator::IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const { ImTriangulatorNode** p_end = _Reflexes.Data + _Reflexes.Size; for (ImTriangulatorNode** p = _Reflexes.Data; p < p_end; p++) { ImTriangulatorNode* reflex = *p; if (reflex->Index != i0 && reflex->Index != i1 && reflex->Index != i2) if (ImTriangleContainsPoint(v0, v1, v2, reflex->Pos)) return false; } return true; } void ImTriangulator::ReclassifyNode(ImTriangulatorNode* n1) { // Classify node ImTriangulatorNodeType type; const ImTriangulatorNode* n0 = n1->Prev; const ImTriangulatorNode* n2 = n1->Next; if (!ImTriangleIsClockwise(n0->Pos, n1->Pos, n2->Pos)) type = ImTriangulatorNodeType_Reflex; else if (IsEar(n0->Index, n1->Index, n2->Index, n0->Pos, n1->Pos, n2->Pos)) type = ImTriangulatorNodeType_Ear; else type = ImTriangulatorNodeType_Convex; // Update lists when a type changes if (type == n1->Type) return; if (n1->Type == ImTriangulatorNodeType_Reflex) _Reflexes.find_erase_unsorted(n1->Index); else if (n1->Type == ImTriangulatorNodeType_Ear) _Ears.find_erase_unsorted(n1->Index); if (type == ImTriangulatorNodeType_Reflex) _Reflexes.push_back(n1); else if (type == ImTriangulatorNodeType_Ear) _Ears.push_back(n1); n1->Type = type; } // Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes). // (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer! // It is up to caller to ensure not making costly calls that will be outside of visible area. // As concave fill is noticeably more expensive than other primitives, be mindful of this... // Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false') void ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col) { if (points_count < 3 || (col & IM_COL32_A_MASK) == 0) return; const ImVec2 uv = _Data->TexUvWhitePixel; ImTriangulator triangulator; unsigned int triangle[3]; if (Flags & ImDrawListFlags_AntiAliasedFill) { // Anti-aliased Fill const float AA_SIZE = _FringeScale; const ImU32 col_trans = col & ~IM_COL32_A_MASK; const int idx_count = (points_count - 2) * 3 + points_count * 6; const int vtx_count = (points_count * 2); PrimReserve(idx_count, vtx_count); // Add indexes for fill unsigned int vtx_inner_idx = _VtxCurrentIdx; unsigned int vtx_outer_idx = _VtxCurrentIdx + 1; _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2)); triangulator.Init(points, points_count, _Data->TempBuffer.Data); while (triangulator._TrianglesLeft > 0) { triangulator.GetNextTriangle(triangle); _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle[2] << 1)); _IdxWritePtr += 3; } // Compute normals _Data->TempBuffer.reserve_discard(points_count); ImVec2* temp_normals = _Data->TempBuffer.Data; for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++) { const ImVec2& p0 = points[i0]; const ImVec2& p1 = points[i1]; 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++) { // Average normals const ImVec2& n0 = temp_normals[i0]; const ImVec2& n1 = temp_normals[i1]; float dm_x = (n0.x + n1.x) * 0.5f; float dm_y = (n0.y + n1.y) * 0.5f; IM_FIXNORMAL2F(dm_x, dm_y); dm_x *= AA_SIZE * 0.5f; dm_y *= AA_SIZE * 0.5f; // Add vertices _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 _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr += 6; } _VtxCurrentIdx += (ImDrawIdx)vtx_count; } else { // Non Anti-aliased Fill const int idx_count = (points_count - 2) * 3; const int vtx_count = points_count; PrimReserve(idx_count, vtx_count); for (int i = 0; i < vtx_count; i++) { _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; _VtxWritePtr++; } _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2)); triangulator.Init(points, points_count, _Data->TempBuffer.Data); while (triangulator._TrianglesLeft > 0) { triangulator.GetNextTriangle(triangle); _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle[2]); _IdxWritePtr += 3; } _VtxCurrentIdx += (ImDrawIdx)vtx_count; } } //----------------------------------------------------------------------------- // [SECTION] ImDrawList Shadow Primitives //----------------------------------------------------------------------------- // - AddSubtractedRect() [Internal] // - ClipPolygonShape() [Internal] // - AddSubtractedRect() [Internal] // - AddRectShadow() //----------------------------------------------------------------------------- // Adds a rectangle (A) with another rectangle (B) subtracted from it (i.e. the portion of A covered by B is not drawn). Does not handle rounded corners (use the version that takes a convex polygon for that). static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const ImVec2& a_max, const ImVec2& a_min_uv, const ImVec2& a_max_uv, ImVec2 b_min, ImVec2 b_max, ImU32 col) { // Early out without drawing anything if A is zero-size if (a_min.x >= a_max.x || a_min.y >= a_max.y) return; // Early out without drawing anything if B covers A entirely if (a_min.x >= b_min.x && a_max.x <= b_max.x && a_min.y >= b_min.y && a_max.y <= b_max.y) return; // First clip the extents of B to A b_min = ImMax(b_min, a_min); b_max = ImMin(b_max, a_max); if (b_min.x >= b_max.x || b_min.y >= b_max.y) { // B is entirely outside A, so just draw A as-is draw_list->PrimReserve(6, 4); draw_list->PrimRectUV(a_min, a_max, a_min_uv, a_max_uv, col); return; } // Otherwise we need to emit (up to) four quads to cover the visible area... // Our layout looks like this (numbers are vertex indices, letters are quads): // // 0---8------9-----1 // | | B | | // + 4------5 + // | A |xxxxxx| C | // | |xxxxxx| | // + 7------6 + // | | D | | // 3---11-----10----2 const int max_verts = 12; const int max_indices = 6 * 4; // At most four quads draw_list->PrimReserve(max_indices, max_verts); ImDrawIdx* idx_write = draw_list->_IdxWritePtr; ImDrawVert* vtx_write = draw_list->_VtxWritePtr; ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx; // Write vertices vtx_write[0].pos = ImVec2(a_min.x, a_min.y); vtx_write[0].uv = ImVec2(a_min_uv.x, a_min_uv.y); vtx_write[0].col = col; vtx_write[1].pos = ImVec2(a_max.x, a_min.y); vtx_write[1].uv = ImVec2(a_max_uv.x, a_min_uv.y); vtx_write[1].col = col; vtx_write[2].pos = ImVec2(a_max.x, a_max.y); vtx_write[2].uv = ImVec2(a_max_uv.x, a_max_uv.y); vtx_write[2].col = col; vtx_write[3].pos = ImVec2(a_min.x, a_max.y); vtx_write[3].uv = ImVec2(a_min_uv.x, a_max_uv.y); vtx_write[3].col = col; const ImVec2 pos_to_uv_scale = (a_max_uv - a_min_uv) / (a_max - a_min); // Guaranteed never to be a /0 because we check for zero-size A above const ImVec2 pos_to_uv_offset = (a_min_uv / pos_to_uv_scale) - a_min; // Helper that generates an interpolated UV based on position #define LERP_UV(x_pos, y_pos) (ImVec2(((x_pos) + pos_to_uv_offset.x) * pos_to_uv_scale.x, ((y_pos) + pos_to_uv_offset.y) * pos_to_uv_scale.y)) vtx_write[4].pos = ImVec2(b_min.x, b_min.y); vtx_write[4].uv = LERP_UV(b_min.x, b_min.y); vtx_write[4].col = col; vtx_write[5].pos = ImVec2(b_max.x, b_min.y); vtx_write[5].uv = LERP_UV(b_max.x, b_min.y); vtx_write[5].col = col; vtx_write[6].pos = ImVec2(b_max.x, b_max.y); vtx_write[6].uv = LERP_UV(b_max.x, b_max.y); vtx_write[6].col = col; vtx_write[7].pos = ImVec2(b_min.x, b_max.y); vtx_write[7].uv = LERP_UV(b_min.x, b_max.y); vtx_write[7].col = col; vtx_write[8].pos = ImVec2(b_min.x, a_min.y); vtx_write[8].uv = LERP_UV(b_min.x, a_min.y); vtx_write[8].col = col; vtx_write[9].pos = ImVec2(b_max.x, a_min.y); vtx_write[9].uv = LERP_UV(b_max.x, a_min.y); vtx_write[9].col = col; vtx_write[10].pos = ImVec2(b_max.x, a_max.y); vtx_write[10].uv = LERP_UV(b_max.x, a_max.y); vtx_write[10].col = col; vtx_write[11].pos = ImVec2(b_min.x, a_max.y); vtx_write[11].uv = LERP_UV(b_min.x, a_max.y); vtx_write[11].col = col; #undef LERP_UV draw_list->_VtxWritePtr += 12; draw_list->_VtxCurrentIdx += 12; // Write indices for each quad (if it is visible) if (b_min.x > a_min.x) // A { idx_write[0] = (ImDrawIdx)(idx + 0); idx_write[1] = (ImDrawIdx)(idx + 8); idx_write[2] = (ImDrawIdx)(idx + 11); idx_write[3] = (ImDrawIdx)(idx + 0); idx_write[4] = (ImDrawIdx)(idx + 11); idx_write[5] = (ImDrawIdx)(idx + 3); idx_write += 6; } if (b_min.y > a_min.y) // B { idx_write[0] = (ImDrawIdx)(idx + 8); idx_write[1] = (ImDrawIdx)(idx + 9); idx_write[2] = (ImDrawIdx)(idx + 5); idx_write[3] = (ImDrawIdx)(idx + 8); idx_write[4] = (ImDrawIdx)(idx + 5); idx_write[5] = (ImDrawIdx)(idx + 4); idx_write += 6; } if (a_max.x > b_max.x) // C { idx_write[0] = (ImDrawIdx)(idx + 9); idx_write[1] = (ImDrawIdx)(idx + 1); idx_write[2] = (ImDrawIdx)(idx + 2); idx_write[3] = (ImDrawIdx)(idx + 9); idx_write[4] = (ImDrawIdx)(idx + 2); idx_write[5] = (ImDrawIdx)(idx + 10); idx_write += 6; } if (a_max.y > b_max.y) // D { idx_write[0] = (ImDrawIdx)(idx + 7); idx_write[1] = (ImDrawIdx)(idx + 6); idx_write[2] = (ImDrawIdx)(idx + 10); idx_write[3] = (ImDrawIdx)(idx + 7); idx_write[4] = (ImDrawIdx)(idx + 10); idx_write[5] = (ImDrawIdx)(idx + 11); idx_write += 6; } const int used_indices = (int)(idx_write - draw_list->_IdxWritePtr); draw_list->_IdxWritePtr = idx_write; draw_list->PrimUnreserve(max_indices - used_indices, 0); } // Clip a polygonal shape to a rectangle, writing the results into dest_points. The number of points emitted is returned (may be zero if the polygon was entirely outside the rectangle, or the source polygon was not valid). dest_points may still be written to even if zero was returned. // allocated_dest_points should contain the number of allocated points in dest_points - in general this should be the number of source points + 4 to accommodate the worst case. If this is exceeded data will be truncated and -1 returned. Stack space work area is allocated based on this value so it shouldn't be too large. static int ClipPolygonShape(ImVec2* src_points, int num_src_points, ImVec2* dest_points, int allocated_dest_points, ImVec2 clip_rect_min, ImVec2 clip_rect_max) { // Early-out with an empty result if clipping region is zero-sized if (clip_rect_max.x <= clip_rect_min.x || clip_rect_max.y <= clip_rect_min.y) return 0; // Early-out if there is no source geometry if (num_src_points < 3) return 0; // The four clip planes here are indexed as: // 0 = X-, 1 = X+, 2 = Y-, 3 = Y+ ImU8* outflags[2]; // Double-buffered flags for each vertex indicating which of the four clip planes it is outside of outflags[0] = (ImU8*)alloca(2 * allocated_dest_points * sizeof(ImU8)); outflags[1] = outflags[0] + allocated_dest_points; // Calculate initial outflags ImU8 outflags_anded = 0xFF; ImU8 outflags_ored = 0; for (int point_idx = 0; point_idx < num_src_points; point_idx++) { const ImVec2 pos = src_points[point_idx]; const ImU8 point_outflags = (pos.x < clip_rect_min.x ? 1 : 0) | (pos.x > clip_rect_max.x ? 2 : 0) | (pos.y < clip_rect_min.y ? 4 : 0) | (pos.y > clip_rect_max.y ? 8 : 0); outflags[0][point_idx] = point_outflags; // Writing to buffer 0 outflags_anded &= point_outflags; outflags_ored |= point_outflags; } if (outflags_anded != 0) // Entirely clipped by any one plane, so nothing remains return 0; if (outflags_ored == 0) // Entirely within bounds, so trivial accept { if (allocated_dest_points < num_src_points) return -1; // Not sure what the caller was thinking if this happens, but we should handle it gracefully memcpy(dest_points, src_points, num_src_points * sizeof(ImVec2)); return num_src_points; } // Shape needs clipping ImVec2* clip_buf[2]; // Double-buffered work area clip_buf[0] = (ImVec2*)alloca(2 * allocated_dest_points * sizeof(ImVec2)); //-V630 clip_buf[1] = clip_buf[0] + allocated_dest_points; memcpy(clip_buf[0], src_points, num_src_points * sizeof(ImVec2)); int clip_buf_size = num_src_points; // Number of vertices currently in the clip buffer int read_buffer_idx = 0; // The index of the clip buffer/out-flags we are reading (0 or 1) for (int clip_plane = 0; clip_plane < 4; clip_plane++) // 0 = X-, 1 = X+, 2 = Y-, 3 = Y+ { const int clip_plane_bit = 1 << clip_plane; // Bit mask for our current plane in out-flags if ((outflags_ored & clip_plane_bit) == 0) continue; // All vertices are inside this plane, so no need to clip ImVec2* read_vert = &clip_buf[read_buffer_idx][0]; // Clip buffer vertex we are currently reading ImVec2* write_vert = &clip_buf[1 - read_buffer_idx][0]; // Clip buffer vertex we are currently writing ImVec2* write_vert_end = write_vert + allocated_dest_points; // End of the write buffer ImU8* read_outflags = &outflags[read_buffer_idx][0]; // Out-flag we are currently reading ImU8* write_outflags = &outflags[1 - read_buffer_idx][0]; // Out-flag we are currently writing // Keep track of the last vertex visited, initially the last in the list ImVec2* last_vert = &read_vert[clip_buf_size - 1]; ImU8 last_outflags = read_outflags[clip_buf_size - 1]; for (int vert = 0; vert < clip_buf_size; vert++) { ImU8 current_outflags = *(read_outflags++); bool out = (current_outflags & clip_plane_bit) != 0; if (((current_outflags ^ last_outflags) & clip_plane_bit) == 0) // We haven't crossed the clip plane { if (!out) { // Emit vertex as-is if (write_vert >= write_vert_end) return -1; // Ran out of buffer space, so abort *(write_vert++) = *read_vert; *(write_outflags++) = current_outflags; } } else { // Emit a vertex at the intersection point float t = 0.0f; ImVec2 pos0 = *last_vert; ImVec2 pos1 = *read_vert; ImVec2 intersect_pos; switch (clip_plane) { case 0: t = (clip_rect_min.x - pos0.x) / (pos1.x - pos0.x); intersect_pos = ImVec2(clip_rect_min.x, pos0.y + ((pos1.y - pos0.y) * t)); break; // X- case 1: t = (clip_rect_max.x - pos0.x) / (pos1.x - pos0.x); intersect_pos = ImVec2(clip_rect_max.x, pos0.y + ((pos1.y - pos0.y) * t)); break; // X+ case 2: t = (clip_rect_min.y - pos0.y) / (pos1.y - pos0.y); intersect_pos = ImVec2(pos0.x + ((pos1.x - pos0.x) * t), clip_rect_min.y); break; // Y- case 3: t = (clip_rect_max.y - pos0.y) / (pos1.y - pos0.y); intersect_pos = ImVec2(pos0.x + ((pos1.x - pos0.x) * t), clip_rect_max.y); break; // Y+ } if (write_vert >= write_vert_end) return -1; // Ran out of buffer space, so abort // Write new out-flags for the vertex we just emitted *(write_vert++) = intersect_pos; *(write_outflags++) = (intersect_pos.x < clip_rect_min.x ? 1 : 0) | (intersect_pos.x > clip_rect_max.x ? 2 : 0) | (intersect_pos.y < clip_rect_min.y ? 4 : 0) | (intersect_pos.y > clip_rect_max.y ? 8 : 0); if (!out) { // When coming back in, also emit the actual vertex if (write_vert >= write_vert_end) return -1; // Ran out of buffer space, so abort *(write_vert++) = *read_vert; *(write_outflags++) = current_outflags; } last_outflags = current_outflags; } last_vert = read_vert; read_vert++; // Advance to next vertex } clip_buf_size = (int)(write_vert - &clip_buf[1 - read_buffer_idx][0]); // Update buffer size read_buffer_idx = 1 - read_buffer_idx; // Swap buffers } if (clip_buf_size < 3) return 0; // Nothing to return // Copy results to output buffer, removing any redundant vertices int num_out_verts = 0; ImVec2 last_vert = clip_buf[read_buffer_idx][clip_buf_size - 1]; for (int i = 0; i < clip_buf_size; i++) { ImVec2 vert = clip_buf[read_buffer_idx][i]; if (ImLengthSqr(vert - last_vert) <= 0.00001f) continue; dest_points[num_out_verts++] = vert; last_vert = vert; } // Return size (IF this is still a valid shape) return (num_out_verts > 2) ? num_out_verts : 0; } // Adds a rectangle (A) with a convex shape (B) subtracted from it (i.e. the portion of A covered by B is not drawn). static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const ImVec2& a_max, const ImVec2& a_min_uv, const ImVec2& a_max_uv, ImVec2* b_points, int num_b_points, ImU32 col) { // Early out without drawing anything if A is zero-size if (a_min.x >= a_max.x || a_min.y >= a_max.y) return; // First clip B to A const int max_clipped_points = num_b_points + 4; ImVec2* clipped_b_points = (ImVec2*)alloca(max_clipped_points * sizeof(ImVec2)); //-V630 const int num_clipped_points = ClipPolygonShape(b_points, num_b_points, clipped_b_points, max_clipped_points, a_min, a_max); IM_ASSERT(num_clipped_points >= 0); // -1 would indicate max_clipped_points was too small, which shouldn't happen b_points = clipped_b_points; num_b_points = num_clipped_points; if (num_clipped_points == 0) { // B is entirely outside A, so just draw A as-is draw_list->PrimReserve(6, 4); draw_list->PrimRectUV(a_min, a_max, a_min_uv, a_max_uv, col); } else { // We need to generate clipped geometry // To do this we walk the inner polygon and connect each edge to one of the four corners of our rectangle based on the quadrant their normal points at const int max_verts = num_b_points + 4; // Inner points plus the four corners const int max_indices = (num_b_points * 3) + (4 * 3); // Worst case is one triangle per inner edge and then four filler triangles draw_list->PrimReserve(max_indices, max_verts); ImDrawIdx* idx_write = draw_list->_IdxWritePtr; ImDrawVert* vtx_write = draw_list->_VtxWritePtr; ImDrawIdx inner_idx = (ImDrawIdx)draw_list->_VtxCurrentIdx; // Starting index for inner vertices // Write inner vertices const ImVec2 pos_to_uv_scale = (a_max_uv - a_min_uv) / (a_max - a_min); // Guaranteed never to be a /0 because we check for zero-size A above const ImVec2 pos_to_uv_offset = (a_min_uv / pos_to_uv_scale) - a_min; // Helper that generates an interpolated UV based on position #define LERP_UV(x_pos, y_pos) (ImVec2(((x_pos) + pos_to_uv_offset.x) * pos_to_uv_scale.x, ((y_pos) + pos_to_uv_offset.y) * pos_to_uv_scale.y)) for (int i = 0; i < num_b_points; i++) { vtx_write[i].pos = b_points[i]; vtx_write[i].uv = LERP_UV(b_points[i].x, b_points[i].y); vtx_write[i].col = col; } #undef LERP_UV vtx_write += num_b_points; // Write outer vertices ImDrawIdx outer_idx = (ImDrawIdx)(inner_idx + num_b_points); // Starting index for outer vertices ImVec2 outer_verts[4]; outer_verts[0] = ImVec2(a_min.x, a_min.y); // X- Y- (quadrant 0, top left) outer_verts[1] = ImVec2(a_max.x, a_min.y); // X+ Y- (quadrant 1, top right) outer_verts[2] = ImVec2(a_max.x, a_max.y); // X+ Y+ (quadrant 2, bottom right) outer_verts[3] = ImVec2(a_min.x, a_max.y); // X- Y+ (quadrant 3, bottom left) vtx_write[0].pos = outer_verts[0]; vtx_write[0].uv = ImVec2(a_min_uv.x, a_min_uv.y); vtx_write[0].col = col; vtx_write[1].pos = outer_verts[1]; vtx_write[1].uv = ImVec2(a_max_uv.x, a_min_uv.y); vtx_write[1].col = col; vtx_write[2].pos = outer_verts[2]; vtx_write[2].uv = ImVec2(a_max_uv.x, a_max_uv.y); vtx_write[2].col = col; vtx_write[3].pos = outer_verts[3]; vtx_write[3].uv = ImVec2(a_min_uv.x, a_max_uv.y); vtx_write[3].col = col; draw_list->_VtxCurrentIdx += num_b_points + 4; draw_list->_VtxWritePtr += num_b_points + 4; // Now walk the inner vertices in order ImVec2 last_inner_vert = b_points[num_b_points - 1]; int last_inner_vert_idx = num_b_points - 1; int last_outer_vert_idx = -1; int first_outer_vert_idx = -1; // Triangle-area based check for degenerate triangles // Min area (0.1f) is doubled (* 2.0f) because we're calculating (area * 2) here #define IS_DEGENERATE(a, b, c) (ImFabs((((a).x * ((b).y - (c).y)) + ((b).x * ((c).y - (a).y)) + ((c).x * ((a).y - (b).y)))) < (0.1f * 2.0f)) // Check the winding order of the inner vertices using the sign of the triangle area, and set the outer vertex winding to match int outer_vertex_winding = (((b_points[0].x * (b_points[1].y - b_points[2].y)) + (b_points[1].x * (b_points[2].y - b_points[0].y)) + (b_points[2].x * (b_points[0].y - b_points[1].y))) < 0.0f) ? -1 : 1; for (int inner_vert_idx = 0; inner_vert_idx < num_b_points; inner_vert_idx++) { ImVec2 current_inner_vert = b_points[inner_vert_idx]; // Calculate normal (not actually normalized, as for our purposes here it doesn't need to be) ImVec2 normal(current_inner_vert.y - last_inner_vert.y, -(current_inner_vert.x - last_inner_vert.x)); // Calculate the outer vertex index based on the quadrant the normal points at (0=top left, 1=top right, 2=bottom right, 3=bottom left) int outer_vert_idx = (ImFabs(normal.x) > ImFabs(normal.y)) ? ((normal.x >= 0.0f) ? ((normal.y > 0.0f) ? 2 : 1) : ((normal.y > 0.0f) ? 3 : 0)) : ((normal.y >= 0.0f) ? ((normal.x > 0.0f) ? 2 : 3) : ((normal.x > 0.0f) ? 1 : 0)); ImVec2 outer_vert = outer_verts[outer_vert_idx]; // Write the main triangle (connecting the inner edge to the corner) if (!IS_DEGENERATE(last_inner_vert, current_inner_vert, outer_vert)) { idx_write[0] = (ImDrawIdx)(inner_idx + last_inner_vert_idx); idx_write[1] = (ImDrawIdx)(inner_idx + inner_vert_idx); idx_write[2] = (ImDrawIdx)(outer_idx + outer_vert_idx); idx_write += 3; } // We don't initially know which outer vertex we are going to start from, so set that here when processing the first inner vertex if (first_outer_vert_idx == -1) { first_outer_vert_idx = outer_vert_idx; last_outer_vert_idx = outer_vert_idx; } // Now walk the outer edge and write any filler triangles needed (connecting outer edges to the inner vertex) while (outer_vert_idx != last_outer_vert_idx) { int next_outer_vert_idx = (last_outer_vert_idx + outer_vertex_winding) & 3; if (!IS_DEGENERATE(outer_verts[last_outer_vert_idx], outer_verts[next_outer_vert_idx], last_inner_vert)) { idx_write[0] = (ImDrawIdx)(outer_idx + last_outer_vert_idx); idx_write[1] = (ImDrawIdx)(outer_idx + next_outer_vert_idx); idx_write[2] = (ImDrawIdx)(inner_idx + last_inner_vert_idx); idx_write += 3; } last_outer_vert_idx = next_outer_vert_idx; } last_inner_vert = current_inner_vert; last_inner_vert_idx = inner_vert_idx; } // Write remaining filler triangles for any un-traversed outer edges if (first_outer_vert_idx != -1) { while (first_outer_vert_idx != last_outer_vert_idx) { int next_outer_vert_idx = (last_outer_vert_idx + outer_vertex_winding) & 3; if (!IS_DEGENERATE(outer_verts[last_outer_vert_idx], outer_verts[next_outer_vert_idx], last_inner_vert)) { idx_write[0] = (ImDrawIdx)(outer_idx + last_outer_vert_idx); idx_write[1] = (ImDrawIdx)(outer_idx + next_outer_vert_idx); idx_write[2] = (ImDrawIdx)(inner_idx + last_inner_vert_idx); idx_write += 3; } last_outer_vert_idx = next_outer_vert_idx; } } #undef IS_DEGENERATE int used_indices = (int)(idx_write - draw_list->_IdxWritePtr); draw_list->_IdxWritePtr = idx_write; draw_list->PrimUnreserve(max_indices - used_indices, 0); } } void ImDrawList::AddShadowRect(const ImVec2& obj_min, const ImVec2& obj_max, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, float obj_rounding) { if ((shadow_col & IM_COL32_A_MASK) == 0) return; ImVec2* inner_rect_points = NULL; // Points that make up the shape of the inner rectangle (used when it has rounded corners) int inner_rect_points_count = 0; // Generate a path describing the inner rectangle and copy it to our buffer const bool is_filled = (flags & ImDrawFlags_ShadowCutOutShapeBackground) == 0; const bool is_rounded = (obj_rounding > 0.0f) && ((flags & ImDrawFlags_RoundCornersMask_) != ImDrawFlags_RoundCornersNone); // Do we have rounded corners? if (is_rounded && !is_filled) { IM_ASSERT(_Path.Size == 0); PathRect(obj_min, obj_max, obj_rounding, flags); inner_rect_points_count = _Path.Size; inner_rect_points = (ImVec2*)alloca(inner_rect_points_count * sizeof(ImVec2)); //-V630 memcpy(inner_rect_points, _Path.Data, inner_rect_points_count * sizeof(ImVec2)); _Path.Size = 0; } if (is_filled) PrimReserve(6 * 9, 4 * 9); // Reserve space for adding unclipped chunks // Draw the relevant chunks of the texture (the texture is split into a 3x3 grid) // FIXME-OPT: Might make sense to optimize/unroll for the fast paths (filled or not rounded) for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { const int uv_index = x + (y + y + y); // y*3 formatted so as to ensure the compiler avoids an actual multiply const ImVec4 uvs = _Data->ShadowRectUvs[uv_index]; ImVec2 draw_min, draw_max; switch (x) { case 0: draw_min.x = obj_min.x - shadow_thickness; draw_max.x = obj_min.x; break; case 1: draw_min.x = obj_min.x; draw_max.x = obj_max.x; break; case 2: draw_min.x = obj_max.x; draw_max.x = obj_max.x + shadow_thickness; break; } switch (y) { case 0: draw_min.y = obj_min.y - shadow_thickness; draw_max.y = obj_min.y; break; case 1: draw_min.y = obj_min.y; draw_max.y = obj_max.y; break; case 2: draw_min.y = obj_max.y; draw_max.y = obj_max.y + shadow_thickness; break; } ImVec2 uv_min(uvs.x, uvs.y); ImVec2 uv_max(uvs.z, uvs.w); if (is_filled) PrimRectUV(draw_min + shadow_offset, draw_max + shadow_offset, uv_min, uv_max, shadow_col); // No clipping path (draw entire shadow) else if (is_rounded) AddSubtractedRect(this, draw_min + shadow_offset, draw_max + shadow_offset, uv_min, uv_max, inner_rect_points, inner_rect_points_count, shadow_col); // Complex path for rounded rectangles else AddSubtractedRect(this, draw_min + shadow_offset, draw_max + shadow_offset, uv_min, uv_max, obj_min, obj_max, shadow_col); // Simple fast path for non-rounded rectangles } } } // Add a shadow for a convex shape described by points and num_points void ImDrawList::AddShadowConvexPoly(const ImVec2* points, int points_count, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags) { const bool is_filled = (flags & ImDrawFlags_ShadowCutOutShapeBackground) == 0; IM_ASSERT((is_filled || (ImLengthSqr(shadow_offset) < 0.00001f)) && "Drawing circle/convex shape shadows with no center fill and an offset is not currently supported"); IM_ASSERT(points_count >= 3); // Calculate poly vertex order const int vertex_winding = (((points[0].x * (points[1].y - points[2].y)) + (points[1].x * (points[2].y - points[0].y)) + (points[2].x * (points[0].y - points[1].y))) < 0.0f) ? -1 : 1; // If we're using anti-aliasing, then inset the shadow by 0.5 pixels to avoid unpleasant fringing artifacts const bool use_inset_distance = (Flags & ImDrawListFlags_AntiAliasedFill) && (!is_filled); const float inset_distance = 0.5f; const ImVec4 uvs = _Data->ShadowRectUvs[9]; int tex_width = _Data->Font->OwnerAtlas->TexData->Width; int tex_height = _Data->Font->OwnerAtlas->TexData->Height; float inv_tex_width = 1.0f / (float)tex_width; float inv_tex_height = 1.0f / (float)tex_height; ImVec2 solid_uv = ImVec2(uvs.z, uvs.w); // UV at the inside of an edge ImVec2 edge_uv = ImVec2(uvs.x, uvs.w); // UV at the outside of an edge ImVec2 solid_to_edge_delta_texels = edge_uv - solid_uv; // Delta between the solid/edge points in texel-space (we need this in pixels - or, to be more precise, to be at a 1:1 aspect ratio - for the rotation to work) solid_to_edge_delta_texels.x *= (float)tex_width; solid_to_edge_delta_texels.y *= (float)tex_height; // Our basic algorithm here is that we generate a straight section along each edge, and then either one or two curved corner triangles at the corners, // which use an appropriate chunk of the texture to generate a smooth curve. const int num_edges = points_count; // Normalize a vector #define NORMALIZE(vec) ((vec) / ImLength((vec), 0.001f)) const int required_stack_mem = (num_edges * sizeof(ImVec2)) + (num_edges * sizeof(float)); ImU8* base_mem_for_normals_and_edges = (ImU8*)alloca(required_stack_mem); ImU8* mem_for_normals_and_edges = (ImU8*)base_mem_for_normals_and_edges; // Calculate edge normals ImVec2* edge_normals = (ImVec2*)(void*)mem_for_normals_and_edges; mem_for_normals_and_edges += num_edges * sizeof(ImVec2); for (int edge_index = 0; edge_index < num_edges; edge_index++) { ImVec2 edge_start = points[edge_index]; // No need to apply offset here because the normal is unaffected ImVec2 edge_end = points[(edge_index + 1) % num_edges]; ImVec2 edge_normal = NORMALIZE(ImVec2(edge_end.y - edge_start.y, -(edge_end.x - edge_start.x))); edge_normals[edge_index] = edge_normal * (float)vertex_winding; // Flip normals for reverse winding } // Pre-calculate edge scales // We need to do this because we need the edge strips to have widths that match up with the corner sections, otherwise pixel cracking can occur along the boundaries float* edge_size_scales = (float*)(void*)mem_for_normals_and_edges; mem_for_normals_and_edges += num_edges * sizeof(float); IM_ASSERT_PARANOID(mem_for_normals_and_edges == (base_mem_for_normals_and_edges + required_stack_mem)); // Check we used exactly what we allocated { ImVec2 prev_edge_normal = edge_normals[num_edges - 1]; for (int edge_index = 0; edge_index < num_edges; edge_index++) { ImVec2 edge_normal = edge_normals[edge_index]; float cos_angle_coverage = ImDot(edge_normal, prev_edge_normal); if (cos_angle_coverage < 0.999999f) { // If we are covering more than 90 degrees we need an intermediate vertex to stop the required expansion tending towards infinity. // And thus the effective angle will be halved (matches the similar code in loop below) float angle_coverage = ImAcos(cos_angle_coverage); if (cos_angle_coverage <= 0.0f) // -V1051 angle_coverage *= 0.5f; edge_size_scales[edge_index] = 1.0f / ImCos(angle_coverage * 0.5f); // How much we need to expand our size by to avoid clipping the corner of the texture off } else { edge_size_scales[edge_index] = 1.0f; // No corner, thus default scale } prev_edge_normal = edge_normal; } } const int max_vertices = (4 + (3 * 2) + (is_filled ? 1 : 0)) * num_edges; // 4 vertices per edge plus 3*2 for potentially two corner triangles, plus one per vertex for fill const int max_indices = ((6 + (3 * 2)) * num_edges) + (is_filled ? ((num_edges - 2) * 3) : 0); // 2 tris per edge plus up to two corner triangles, plus fill triangles PrimReserve(max_indices, max_vertices); ImDrawIdx* idx_write = _IdxWritePtr; ImDrawVert* vtx_write = _VtxWritePtr; ImDrawIdx current_idx = (ImDrawIdx)_VtxCurrentIdx; //ImVec2 previous_edge_start = points[0] + offset; ImVec2 prev_edge_normal = edge_normals[num_edges - 1]; ImVec2 edge_start = points[0] + shadow_offset; if (use_inset_distance) edge_start -= NORMALIZE(edge_normals[0] + prev_edge_normal) * inset_distance; for (int edge_index = 0; edge_index < num_edges; edge_index++) { ImVec2 edge_end = points[(edge_index + 1) % num_edges] + shadow_offset; ImVec2 edge_normal = edge_normals[edge_index]; const float size_scale_start = edge_size_scales[edge_index]; const float size_scale_end = edge_size_scales[(edge_index + 1) % num_edges]; if (use_inset_distance) edge_end -= NORMALIZE(edge_normals[(edge_index + 1) % num_edges] + edge_normal) * inset_distance; // Add corner section float cos_angle_coverage = ImDot(edge_normal, prev_edge_normal); if (cos_angle_coverage < 0.999999f) // Don't fill if the corner is actually straight { // If we are covering more than 90 degrees we need an intermediate vertex to stop the required expansion tending towards infinity. // And thus the effective angle has been halved (matches the similar code in loop above) int num_steps = (cos_angle_coverage <= 0.0f) ? 2 : 1; for (int step = 0; step < num_steps; step++) { if (num_steps > 1) { if (step == 0) edge_normal = NORMALIZE(edge_normal + prev_edge_normal); // Use half-way normal for first step else edge_normal = edge_normals[edge_index]; // Then use the "real" next edge normal for the second cos_angle_coverage = ImDot(edge_normal, prev_edge_normal); // Recalculate angle } // Calculate UV for the section of the curved texture const float angle_coverage = ImAcos(cos_angle_coverage); const float sin_angle_coverage = ImSin(angle_coverage); ImVec2 edge_delta = solid_to_edge_delta_texels; edge_delta *= size_scale_start; ImVec2 rotated_edge_delta = ImVec2((edge_delta.x * cos_angle_coverage) + (edge_delta.y * sin_angle_coverage), (edge_delta.x * sin_angle_coverage) + (edge_delta.y * cos_angle_coverage)); // Convert from texels back into UV space edge_delta.x *= inv_tex_width; edge_delta.y *= inv_tex_height; rotated_edge_delta.x *= inv_tex_width; rotated_edge_delta.y *= inv_tex_height; ImVec2 expanded_edge_uv = solid_uv + edge_delta; ImVec2 other_edge_uv = solid_uv + rotated_edge_delta; // Rotated UV to encompass the necessary section of the curve float expanded_thickness = shadow_thickness * size_scale_start; // Add a triangle to fill the corner ImVec2 outer_edge_start = edge_start + (prev_edge_normal * expanded_thickness); ImVec2 outer_edge_end = edge_start + (edge_normal * expanded_thickness); vtx_write->pos = edge_start; vtx_write->col = shadow_col; vtx_write->uv = solid_uv; vtx_write++; vtx_write->pos = outer_edge_end; vtx_write->col = shadow_col; vtx_write->uv = expanded_edge_uv; vtx_write++; vtx_write->pos = outer_edge_start; vtx_write->col = shadow_col; vtx_write->uv = other_edge_uv; vtx_write++; *(idx_write++) = current_idx; *(idx_write++) = current_idx + 1; *(idx_write++) = current_idx + 2; current_idx += 3; prev_edge_normal = edge_normal; } } // Add section along edge const float edge_length = ImLength(edge_end - edge_start, 0.0f); if (edge_length > 0.00001f) // Don't try and process degenerate edges { ImVec2 outer_edge_start = edge_start + (edge_normal * shadow_thickness * size_scale_start); ImVec2 outer_edge_end = edge_end + (edge_normal * shadow_thickness * size_scale_end); ImVec2 scaled_edge_uv_start = solid_uv + ((edge_uv - solid_uv) * size_scale_start); ImVec2 scaled_edge_uv_end = solid_uv + ((edge_uv - solid_uv) * size_scale_end); // Write vertices, inner first, then outer vtx_write->pos = edge_start; vtx_write->col = shadow_col; vtx_write->uv = solid_uv; vtx_write++; vtx_write->pos = edge_end; vtx_write->col = shadow_col; vtx_write->uv = solid_uv; vtx_write++; vtx_write->pos = outer_edge_end; vtx_write->col = shadow_col; vtx_write->uv = scaled_edge_uv_end; vtx_write++; vtx_write->pos = outer_edge_start; vtx_write->col = shadow_col; vtx_write->uv = scaled_edge_uv_start; vtx_write++; *(idx_write++) = current_idx; *(idx_write++) = current_idx + 1; *(idx_write++) = current_idx + 2; *(idx_write++) = current_idx; *(idx_write++) = current_idx + 2; *(idx_write++) = current_idx + 3; current_idx += 4; } edge_start = edge_end; } // Fill if requested if (is_filled) { // Add vertices for (int edge_index = 0; edge_index < num_edges; edge_index++) { vtx_write->pos = points[edge_index] + shadow_offset; vtx_write->col = shadow_col; vtx_write->uv = solid_uv; vtx_write++; } // Add triangles for (int edge_index = 2; edge_index < num_edges; edge_index++) { *(idx_write++) = current_idx; *(idx_write++) = (ImDrawIdx)(current_idx + edge_index - 1); *(idx_write++) = (ImDrawIdx)(current_idx + edge_index); } current_idx += (ImDrawIdx)num_edges; } // Release any unused vertices/indices int used_indices = (int)(idx_write - _IdxWritePtr); int used_vertices = (int)(vtx_write - _VtxWritePtr); _IdxWritePtr = idx_write; _VtxWritePtr = vtx_write; _VtxCurrentIdx = current_idx; PrimUnreserve(max_indices - used_indices, max_vertices - used_vertices); #undef NORMALIZE } // Draw a shadow for a circular object // Uses the draw path and so wipes any existing data there void ImDrawList::AddShadowCircle(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, int num_segments) { // Obtain segment count if (num_segments <= 0) { // Automatic segment count const int radius_idx = (int)obj_radius - 1; if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value else num_segments = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(obj_radius, _Data->CircleSegmentMaxError); } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); } // Generate a path describing the inner circle and copy it to our buffer IM_ASSERT(_Path.Size == 0); const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; if (num_segments == 12) PathArcToFast(obj_center, obj_radius, 0, 12 - 1); else PathArcTo(obj_center, obj_radius, 0.0f, a_max, num_segments - 1); // Draw the shadow using the convex shape code AddShadowConvexPoly(_Path.Data, _Path.Size, shadow_col, shadow_thickness, shadow_offset, flags); _Path.Size = 0; } void ImDrawList::AddShadowNGon(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, int num_segments) { IM_ASSERT(num_segments != 0); AddShadowCircle(obj_center, obj_radius, shadow_col, shadow_thickness, shadow_offset, flags, num_segments); } //----------------------------------------------------------------------------- // [SECTION] ImDrawListSplitter //----------------------------------------------------------------------------- // FIXME: This may be a little confusing, trying to be a little too low-level/optimal instead of just doing vector swap.. //----------------------------------------------------------------------------- void ImDrawListSplitter::ClearFreeMemory() { for (int i = 0; i < _Channels.Size; i++) { if (i == _Current) memset(&_Channels[i], 0, sizeof(_Channels[i])); // Current channel is a copy of CmdBuffer/IdxBuffer, don't destruct again _Channels[i]._CmdBuffer.clear(); _Channels[i]._IdxBuffer.clear(); } _Current = 0; _Count = 1; _Channels.clear(); } void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count) { IM_UNUSED(draw_list); IM_ASSERT(_Current == 0 && _Count <= 1 && "Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter."); int old_channels_count = _Channels.Size; if (old_channels_count < channels_count) { _Channels.reserve(channels_count); // Avoid over reserving since this is likely to stay stable _Channels.resize(channels_count); } _Count = channels_count; // Channels[] (24/32 bytes each) hold storage that we'll swap with draw_list->_CmdBuffer/_IdxBuffer // The content of Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to. // When we switch to the next channel, we'll copy draw_list->_CmdBuffer/_IdxBuffer into Channels[0] and then Channels[1] into draw_list->CmdBuffer/_IdxBuffer memset(&_Channels[0], 0, sizeof(ImDrawChannel)); for (int i = 1; i < channels_count; i++) { if (i >= old_channels_count) { IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel(); } else { _Channels[i]._CmdBuffer.resize(0); _Channels[i]._IdxBuffer.resize(0); } } } void ImDrawListSplitter::Merge(ImDrawList* draw_list) { // Note that we never use or rely on _Channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. if (_Count <= 1) return; SetCurrentChannel(draw_list, 0); draw_list->_PopUnusedDrawCmd(); // Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command. int new_cmd_buffer_count = 0; int new_idx_buffer_count = 0; ImDrawCmd* last_cmd = (_Count > 0 && draw_list->CmdBuffer.Size > 0) ? &draw_list->CmdBuffer.back() : NULL; int idx_offset = last_cmd ? last_cmd->IdxOffset + last_cmd->ElemCount : 0; for (int i = 1; i < _Count; i++) { ImDrawChannel& ch = _Channels[i]; if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd() ch._CmdBuffer.pop_back(); if (ch._CmdBuffer.Size > 0 && last_cmd != NULL) { // Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves. // Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter. ImDrawCmd* next_cmd = &ch._CmdBuffer[0]; if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL) { // Merge previous channel last draw command with current channel first draw command if matching. last_cmd->ElemCount += next_cmd->ElemCount; idx_offset += next_cmd->ElemCount; ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges. } } if (ch._CmdBuffer.Size > 0) last_cmd = &ch._CmdBuffer.back(); new_cmd_buffer_count += ch._CmdBuffer.Size; new_idx_buffer_count += ch._IdxBuffer.Size; for (int cmd_n = 0; cmd_n < ch._CmdBuffer.Size; cmd_n++) { ch._CmdBuffer.Data[cmd_n].IdxOffset = idx_offset; idx_offset += ch._CmdBuffer.Data[cmd_n].ElemCount; } } draw_list->CmdBuffer.resize(draw_list->CmdBuffer.Size + new_cmd_buffer_count); draw_list->IdxBuffer.resize(draw_list->IdxBuffer.Size + new_idx_buffer_count); // Write commands and indices in order (they are fairly small structures, we don't copy vertices only indices) ImDrawCmd* cmd_write = draw_list->CmdBuffer.Data + draw_list->CmdBuffer.Size - new_cmd_buffer_count; ImDrawIdx* idx_write = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size - new_idx_buffer_count; for (int i = 1; i < _Count; i++) { ImDrawChannel& ch = _Channels[i]; if (int sz = ch._CmdBuffer.Size) { memcpy(cmd_write, ch._CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } if (int sz = ch._IdxBuffer.Size) { memcpy(idx_write, ch._IdxBuffer.Data, sz * sizeof(ImDrawIdx)); idx_write += sz; } } draw_list->_IdxWritePtr = idx_write; // Ensure there's always a non-callback draw command trailing the command-buffer if (draw_list->CmdBuffer.Size == 0 || draw_list->CmdBuffer.back().UserCallback != NULL) draw_list->AddDrawCmd(); // If current command is used with different settings we need to add a new command ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1]; if (curr_cmd->ElemCount == 0) ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TexRef, VtxOffset else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0) draw_list->AddDrawCmd(); _Count = 1; } void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) { IM_ASSERT(idx >= 0 && idx < _Count); if (_Current == idx) return; // Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap() memcpy(&_Channels.Data[_Current]._CmdBuffer, &draw_list->CmdBuffer, sizeof(draw_list->CmdBuffer)); memcpy(&_Channels.Data[_Current]._IdxBuffer, &draw_list->IdxBuffer, sizeof(draw_list->IdxBuffer)); _Current = idx; memcpy(&draw_list->CmdBuffer, &_Channels.Data[idx]._CmdBuffer, sizeof(draw_list->CmdBuffer)); memcpy(&draw_list->IdxBuffer, &_Channels.Data[idx]._IdxBuffer, sizeof(draw_list->IdxBuffer)); draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size; // If current command is used with different settings we need to add a new command ImDrawCmd* curr_cmd = (draw_list->CmdBuffer.Size == 0) ? NULL : &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1]; if (curr_cmd == NULL) draw_list->AddDrawCmd(); else if (curr_cmd->ElemCount == 0) ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TexRef, VtxOffset else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0) draw_list->AddDrawCmd(); } //----------------------------------------------------------------------------- // [SECTION] ImDrawData //----------------------------------------------------------------------------- void ImDrawData::Clear() { Valid = false; CmdListsCount = TotalIdxCount = TotalVtxCount = 0; CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them. DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f); OwnerViewport = NULL; Textures = NULL; } // Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list // as long at it is expected that the result will be later merged into draw_data->CmdLists[]. void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list) { if (draw_list->CmdBuffer.Size == 0) return; if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) return; // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. // May trigger for you if you are using PrimXXX functions incorrectly. IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) // If this assert triggers because you are drawing lots of stuff manually: // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. // Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. // - If you want large meshes with more than 64K vertices, you can either: // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. // Most example backends already support this from 1.71. Pre-1.71 backends won't. // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); // Your own engine or render API may use different parameters or function calls to specify index sizes. // 2 and 4 bytes indices are generally supported by most graphics API. // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching // the 64K limit to split your draw commands in multiple draw lists. if (sizeof(ImDrawIdx) == 2) IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); // Resolve callback data pointers if (draw_list->_CallbacksDataBuf.Size > 0) for (ImDrawCmd& cmd : draw_list->CmdBuffer) if (cmd.UserCallback != NULL && cmd.UserCallbackDataOffset != -1 && cmd.UserCallbackDataSize > 0) cmd.UserCallbackData = draw_list->_CallbacksDataBuf.Data + cmd.UserCallbackDataOffset; // Add to output list + records state in ImDrawData out_list->push_back(draw_list); draw_data->CmdListsCount++; draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; } void ImDrawData::AddDrawList(ImDrawList* draw_list) { IM_ASSERT(CmdLists.Size == CmdListsCount); draw_list->_PopUnusedDrawCmd(); ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list); } // 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! void ImDrawData::DeIndexAllBuffers() { ImVector new_vtx_buffer; TotalVtxCount = TotalIdxCount = 0; for (ImDrawList* draw_list : CmdLists) { if (draw_list->IdxBuffer.empty()) continue; new_vtx_buffer.resize(draw_list->IdxBuffer.Size); for (int j = 0; j < draw_list->IdxBuffer.Size; j++) new_vtx_buffer[j] = draw_list->VtxBuffer[draw_list->IdxBuffer[j]]; draw_list->VtxBuffer.swap(new_vtx_buffer); draw_list->IdxBuffer.resize(0); TotalVtxCount += draw_list->VtxBuffer.Size; } } // 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 (ImDrawList* draw_list : CmdLists) for (ImDrawCmd& cmd : draw_list->CmdBuffer) 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); } //----------------------------------------------------------------------------- // [SECTION] Helpers ShadeVertsXXX functions //----------------------------------------------------------------------------- // Generic linear color gradient, write to RGB fields, leave A untouched. void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1) { ImVec2 gradient_extent = gradient_p1 - gradient_p0; float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent); ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; const int col0_r = (int)(col0 >> IM_COL32_R_SHIFT) & 0xFF; const int col0_g = (int)(col0 >> IM_COL32_G_SHIFT) & 0xFF; const int col0_b = (int)(col0 >> IM_COL32_B_SHIFT) & 0xFF; const int col_delta_r = ((int)(col1 >> IM_COL32_R_SHIFT) & 0xFF) - col0_r; const int col_delta_g = ((int)(col1 >> IM_COL32_G_SHIFT) & 0xFF) - col0_g; const int col_delta_b = ((int)(col1 >> IM_COL32_B_SHIFT) & 0xFF) - col0_b; for (ImDrawVert* vert = vert_start; vert < vert_end; vert++) { float d = ImDot(vert->pos - gradient_p0, gradient_extent); float t = ImClamp(d * gradient_inv_length2, 0.0f, 1.0f); int r = (int)(col0_r + col_delta_r * t); int g = (int)(col0_g + col_delta_g * t); int b = (int)(col0_b + col_delta_b * t); vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK); } } // Distribute UV over (a, b) rectangle void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp) { const ImVec2 size = b - a; const ImVec2 uv_size = uv_b - uv_a; const ImVec2 scale = ImVec2( size.x != 0.0f ? (uv_size.x / size.x) : 0.0f, size.y != 0.0f ? (uv_size.y / size.y) : 0.0f); ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; if (clamp) { const ImVec2 min = ImMin(uv_a, uv_b); const ImVec2 max = ImMax(uv_a, uv_b); for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) vertex->uv = ImClamp(uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale), min, max); } else { for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) vertex->uv = uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale); } } void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out) { ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out; } //----------------------------------------------------------------------------- // [SECTION] ImFontConfig //----------------------------------------------------------------------------- // FIXME-NEWATLAS: Oversample specification could be more dynamic. For now, favoring automatic selection. ImFontConfig::ImFontConfig() { memset((void*)this, 0, sizeof(*this)); FontDataOwnedByAtlas = true; OversampleH = 0; // Auto == 1 or 2 depending on size OversampleV = 0; // Auto == 1 ExtraSizeScale = 1.0f; GlyphMaxAdvanceX = FLT_MAX; RasterizerMultiply = 1.0f; RasterizerDensity = 1.0f; EllipsisChar = 0; } //----------------------------------------------------------------------------- // [SECTION] ImTextureData //----------------------------------------------------------------------------- // - ImTextureData::Create() // - ImTextureData::DestroyPixels() //----------------------------------------------------------------------------- int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format) { switch (format) { case ImTextureFormat_Alpha8: return 1; case ImTextureFormat_RGBA32: return 4; } IM_ASSERT(0); return 0; } const char* ImTextureDataGetStatusName(ImTextureStatus status) { switch (status) { case ImTextureStatus_OK: return "OK"; case ImTextureStatus_Destroyed: return "Destroyed"; case ImTextureStatus_WantCreate: return "WantCreate"; case ImTextureStatus_WantUpdates: return "WantUpdates"; case ImTextureStatus_WantDestroy: return "WantDestroy"; } return "N/A"; } const char* ImTextureDataGetFormatName(ImTextureFormat format) { switch (format) { case ImTextureFormat_Alpha8: return "Alpha8"; case ImTextureFormat_RGBA32: return "RGBA32"; } return "N/A"; } void ImTextureData::Create(ImTextureFormat format, int w, int h) { IM_ASSERT(Status == ImTextureStatus_Destroyed); DestroyPixels(); Format = format; Status = ImTextureStatus_WantCreate; Width = w; Height = h; BytesPerPixel = ImTextureDataGetFormatBytesPerPixel(format); UseColors = false; Pixels = (unsigned char*)IM_ALLOC(Width * Height * BytesPerPixel); IM_ASSERT(Pixels != NULL); memset(Pixels, 0, Width * Height * BytesPerPixel); UsedRect.x = UsedRect.y = UsedRect.w = UsedRect.h = 0; UpdateRect.x = UpdateRect.y = (unsigned short)~0; UpdateRect.w = UpdateRect.h = 0; } void ImTextureData::DestroyPixels() { if (Pixels) IM_FREE(Pixels); Pixels = NULL; UseColors = false; } //----------------------------------------------------------------------------- // [SECTION] ImFontAtlas, ImFontAtlasBuilder //----------------------------------------------------------------------------- // - Default texture data encoded in ASCII // - ImFontAtlas() // - ImFontAtlas::Clear() // - ImFontAtlas::CompactCache() // - ImFontAtlas::ClearInputData() // - ImFontAtlas::ClearTexData() // - ImFontAtlas::ClearFonts() //----------------------------------------------------------------------------- // - ImFontAtlasUpdateNewFrame() // - ImFontAtlasTextureBlockConvert() // - ImFontAtlasTextureBlockPostProcess() // - ImFontAtlasTextureBlockPostProcessMultiply() // - ImFontAtlasTextureBlockFill() // - ImFontAtlasTextureBlockCopy() // - ImFontAtlasTextureBlockQueueUpload() //----------------------------------------------------------------------------- // - ImFontAtlas::GetTexDataAsAlpha8() [legacy] // - ImFontAtlas::GetTexDataAsRGBA32() [legacy] // - ImFontAtlas::Build() [legacy] //----------------------------------------------------------------------------- // - ImFontAtlas::AddFont() // - ImFontAtlas::AddFontDefault() // - ImFontAtlas::AddFontDefaultBitmap() // - ImFontAtlas::AddFontDefaultVector() // - ImFontAtlas::AddFontFromFileTTF() // - ImFontAtlas::AddFontFromMemoryTTF() // - ImFontAtlas::AddFontFromMemoryCompressedTTF() // - ImFontAtlas::AddFontFromMemoryCompressedBase85TTF() // - ImFontAtlas::RemoveFont() // - ImFontAtlasBuildNotifySetFont() //----------------------------------------------------------------------------- // - ImFontAtlas::AddCustomRect() // - ImFontAtlas::RemoveCustomRect() // - ImFontAtlas::GetCustomRect() // - ImFontAtlas::AddCustomRectFontGlyph() [legacy] // - ImFontAtlas::AddCustomRectFontGlyphForSize() [legacy] // - ImFontAtlasGetMouseCursorTexData() //----------------------------------------------------------------------------- // - ImFontAtlasBuildMain() // - ImFontAtlasBuildSetupFontLoader() // - ImFontAtlasBuildPreloadAllGlyphRanges() // - ImFontAtlasBuildUpdatePointers() // - ImFontAtlasBuildRenderBitmapFromString() // - ImFontAtlasBuildUpdateBasicTexData() // - ImFontAtlasBuildUpdateLinesTexData() // - ImFontAtlasBuildAddFont() // - ImFontAtlasBuildSetupFontBakedEllipsis() // - ImFontAtlasBuildSetupFontBakedBlanks() // - ImFontAtlasBuildSetupFontBakedFallback() // - ImFontAtlasBuildSetupFontSpecialGlyphs() // - ImFontAtlasBuildDiscardBakes() // - ImFontAtlasBuildDiscardFontBakedGlyph() // - ImFontAtlasBuildDiscardFontBaked() // - ImFontAtlasBuildDiscardFontBakes() //----------------------------------------------------------------------------- // - ImFontAtlasAddDrawListSharedData() // - ImFontAtlasRemoveDrawListSharedData() // - ImFontAtlasUpdateDrawListsTextures() // - ImFontAtlasUpdateDrawListsSharedData() //----------------------------------------------------------------------------- // - ImFontAtlasBuildSetTexture() // - ImFontAtlasBuildAddTexture() // - ImFontAtlasBuildMakeSpace() // - ImFontAtlasBuildRepackTexture() // - ImFontAtlasBuildGrowTexture() // - ImFontAtlasBuildRepackOrGrowTexture() // - ImFontAtlasBuildGetTextureSizeEstimate() // - ImFontAtlasBuildCompactTexture() // - ImFontAtlasBuildInit() // - ImFontAtlasBuildDestroy() //----------------------------------------------------------------------------- // - ImFontAtlasPackInit() // - ImFontAtlasPackAllocRectEntry() // - ImFontAtlasPackReuseRectEntry() // - ImFontAtlasPackDiscardRect() // - ImFontAtlasPackAddRect() // - ImFontAtlasPackGetRect() //----------------------------------------------------------------------------- // - ImFontBaked_BuildGrowIndex() // - ImFontBaked_BuildLoadGlyph() // - ImFontBaked_BuildLoadGlyphAdvanceX() // - ImFontAtlasDebugLogTextureRequests() //----------------------------------------------------------------------------- // - ImFontAtlasGetFontLoaderForStbTruetype() //----------------------------------------------------------------------------- // A work of art lies ahead! (. = white layer, X = black layer, others are blank) // The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes. // (This is used when io.MouseDrawCursor = true) const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing. const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = { "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX - XX XX " "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X -X..X X..X" "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X -X...X X...X" "X - X.X - X.....X - X.....X -X...X - X...X- X..X - X...X X...X " "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X - X...X...X " "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX - X.....X " "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX - X...X " "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX - X.X " "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X - X...X " "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X- X.....X " "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X- X...X...X " "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X- X...X X...X " "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X-X...X X...X" "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X-X..X X..X" "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X- XX XX " "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X--------------" "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X - " "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X - " "X.X X..X - -X.......X- X.......X - XX XX - - X..........X - " "XX X..X - - X.....X - X.....X - X.X X.X - - X........X - " " X..X - - X...X - X...X - X..X X..X - - X........X - " " XX - - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX - " "------------- - X - X -X.....................X- ------------------- " " ----------------------------------- X...XXXXXXXXXXXXX...X - " " - X..X X..X - " " - X.X X.X - " " - XX XX - " }; static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] = { // Pos ........ Size ......... Offset ...... { ImVec2( 0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow { ImVec2(13,0), ImVec2( 7,16), ImVec2( 1, 8) }, // ImGuiMouseCursor_TextInput { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_ResizeAll { ImVec2(21,0), ImVec2( 9,23), ImVec2( 4,11) }, // ImGuiMouseCursor_ResizeNS { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 4) }, // ImGuiMouseCursor_ResizeEW { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand { ImVec2(0,3), ImVec2(12,19), ImVec2(0, 0) }, // ImGuiMouseCursor_Wait // Arrow + custom code in ImGui::RenderMouseCursor() { ImVec2(0,3), ImVec2(12,19), ImVec2(0, 0) }, // ImGuiMouseCursor_Progress // Arrow + custom code in ImGui::RenderMouseCursor() { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed }; #define IM_FONTGLYPH_INDEX_UNUSED ((ImU16)-1) // 0xFFFF #define IM_FONTGLYPH_INDEX_NOT_FOUND ((ImU16)-2) // 0xFFFE ImFontAtlas::ImFontAtlas() { memset((void*)this, 0, sizeof(*this)); TexDesiredFormat = ImTextureFormat_RGBA32; TexGlyphPadding = 1; TexMinWidth = 512; TexMinHeight = 128; TexMaxWidth = 8192; TexMaxHeight = 8192; TexRef._TexID = ImTextureID_Invalid; RendererHasTextures = false; // Assumed false by default, as apps can call e.g Atlas::Build() after backend init and before ImGui can update. TexNextUniqueID = 1; FontNextUniqueID = 1; Builder = NULL; // FIXME-SHADOWS: move elsewhere? ShadowRectIds[0] = ShadowRectIds[1] = -1; ShadowTexConfig.SetupDefaults(); } ImFontAtlas::~ImFontAtlas() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't. ClearFonts(); ClearTexData(); TexList.clear_delete(); TexData = NULL; } // If you call this mid-frame, you would need to add new font and bind them! void ImFontAtlas::Clear() { bool backup_renderer_has_textures = RendererHasTextures; RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't. ClearFonts(); ClearTexData(); RendererHasTextures = backup_renderer_has_textures; } void ImFontAtlas::CompactCache() { ImFontAtlasTextureCompact(this); } void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader) { ImFontAtlasBuildSetupFontLoader(this, font_loader); } void ImFontAtlas::ClearInputData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); for (ImFont* font : Fonts) ImFontAtlasFontDestroyOutput(this, font); for (ImFontConfig& font_cfg : Sources) ImFontAtlasFontDestroySourceData(this, &font_cfg); for (ImFont* font : Fonts) { // When clearing this we lose access to the font name and other information used to build the font. font->Sources.clear(); font->Flags |= ImFontFlags_NoLoadGlyphs; } Sources.clear(); // FIXME-SHADOWS: Move elsewhere? ShadowRectIds[0] = ShadowRectIds[1] = -1; } // Clear CPU-side copy of the texture data. void ImFontAtlas::ClearTexData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); IM_ASSERT(RendererHasTextures == false && "Not supported for dynamic atlases, but you may call Clear()."); for (ImTextureData* tex : TexList) tex->DestroyPixels(); //Locked = true; // Hoped to be able to lock this down but some reload patterns may not be happy with it. } void ImFontAtlas::ClearFonts() { // FIXME-NEWATLAS: Illegal to remove currently bound font. IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); for (ImFont* font : Fonts) ImFontAtlasBuildNotifySetFont(this, font, NULL); ImFontAtlasBuildDestroy(this); ClearInputData(); Fonts.clear_delete(); TexIsBuilt = false; for (ImDrawListSharedData* shared_data : DrawListSharedDatas) if (shared_data->FontAtlas == this) { shared_data->Font = NULL; shared_data->FontScale = shared_data->FontSize = 0.0f; } } static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* atlas) { // [LEGACY] Copy back the ImGuiBackendFlags_RendererHasTextures flag from ImGui context. // - This is the 1% exceptional case where that dependency if useful, to bypass an issue where otherwise at the // time of an early call to Build(), it would be impossible for us to tell if the backend supports texture update. // - Without this hack, we would have quite a pitfall as many legacy codebases have an early call to Build(). // Whereas conversely, the portion of people using ImDrawList without ImGui is expected to be pathologically rare. for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas) if (ImGuiContext* imgui_ctx = shared_data->Context) { atlas->RendererHasTextures = (imgui_ctx->IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0; break; } } // Called by NewFrame() for atlases owned by a context. // If you manually manage font atlases, you'll need to call this yourself. // - 'frame_count' needs to be provided because we can gc/prioritize baked fonts based on their age. // - 'frame_count' may not match those of all imgui contexts using this atlas, as contexts may be updated as different frequencies. But generally you can use ImGui::GetFrameCount() on one of your context. void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures) { IM_ASSERT(atlas->Builder == NULL || atlas->Builder->FrameCount < frame_count); // Protection against being called twice. atlas->RendererHasTextures = renderer_has_textures; // Check that font atlas was built or backend support texture reload in which case we can build now if (atlas->RendererHasTextures) { atlas->TexIsBuilt = true; if (atlas->Builder == NULL) // This will only happen if fonts were not already loaded. ImFontAtlasBuildMain(atlas); } // Legacy backend if (!atlas->RendererHasTextures) IM_ASSERT_USER_ERROR(atlas->TexIsBuilt, "Backend does not support ImGuiBackendFlags_RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()."); if (atlas->TexIsBuilt && atlas->Builder->PreloadedAllGlyphsRanges) IM_ASSERT_USER_ERROR(atlas->RendererHasTextures == false, "Called ImFontAtlas::Build() before ImGuiBackendFlags_RendererHasTextures got set! With new backends: you don't need to call Build()."); // Clear BakedCurrent cache, this is important because it ensure the uncached path gets taken once. // We also rely on ImFontBaked* pointers never crossing frames. ImFontAtlasBuilder* builder = atlas->Builder; builder->FrameCount = frame_count; for (ImFont* font : atlas->Fonts) font->LastBaked = NULL; // Garbage collect BakedPool if (builder->BakedDiscardedCount > 0) { int dst_n = 0, src_n = 0; for (; src_n < builder->BakedPool.Size; src_n++) { ImFontBaked* p_src = &builder->BakedPool[src_n]; if (p_src->WantDestroy) continue; ImFontBaked* p_dst = &builder->BakedPool[dst_n++]; if (p_dst == p_src) continue; memcpy(p_dst, p_src, sizeof(ImFontBaked)); builder->BakedMap.SetVoidPtr(p_dst->BakedId, p_dst); } IM_ASSERT(dst_n + builder->BakedDiscardedCount == src_n); builder->BakedPool.Size -= builder->BakedDiscardedCount; builder->BakedDiscardedCount = 0; } // Update texture status for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++) { ImTextureData* tex = atlas->TexList[tex_n]; bool remove_from_list = false; if (tex->Status == ImTextureStatus_OK) { tex->Updates.resize(0); tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0; tex->UpdateRect.w = tex->UpdateRect.h = 0; } if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures) IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK."); // Request destroy // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend. // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM. if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy) { IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates); tex->Status = ImTextureStatus_WantDestroy; } // If a texture has never reached the backend, they don't need to know about it. // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy // when invalidating graphics objects twice, which would previously remove it from the list and crash.) if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL) tex->Status = ImTextureStatus_Destroyed; // Process texture being destroyed if (tex->Status == ImTextureStatus_Destroyed) { IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!"); if (tex->WantDestroyNextFrame) remove_from_list = true; // Destroy was scheduled by us else tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run) } // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering. // We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision. if (tex->Status == ImTextureStatus_WantDestroy) tex->UnusedFrames++; // Destroy and remove if (remove_from_list) { IM_ASSERT(atlas->TexData != tex); tex->DestroyPixels(); IM_DELETE(tex); atlas->TexList.erase(atlas->TexList.begin() + tex_n); tex_n--; } } } void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h) { IM_ASSERT(src_pixels != NULL && dst_pixels != NULL); if (src_fmt == dst_fmt) { int line_sz = w * ImTextureDataGetFormatBytesPerPixel(src_fmt); for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch) memcpy(dst_pixels, src_pixels, line_sz); } else if (src_fmt == ImTextureFormat_Alpha8 && dst_fmt == ImTextureFormat_RGBA32) { for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch) { const ImU8* src_p = (const ImU8*)src_pixels; ImU32* dst_p = (ImU32*)(void*)dst_pixels; for (int nx = w; nx > 0; nx--) *dst_p++ = IM_COL32(255, 255, 255, (unsigned int)(*src_p++)); } } else if (src_fmt == ImTextureFormat_RGBA32 && dst_fmt == ImTextureFormat_Alpha8) { for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch) { const ImU32* src_p = (const ImU32*)(void*)src_pixels; ImU8* dst_p = (ImU8*)dst_pixels; for (int nx = w; nx > 0; nx--) *dst_p++ = ((*src_p++) >> IM_COL32_A_SHIFT) & 0xFF; } } else { IM_ASSERT(0); } } // Source buffer may be written to (used for in-place mods). // Post-process hooks may eventually be added here. void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data) { // Multiply operator (legacy) if (data->FontSrc->RasterizerMultiply != 1.0f) ImFontAtlasTextureBlockPostProcessMultiply(data, data->FontSrc->RasterizerMultiply); } void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor) { unsigned char* pixels = (unsigned char*)data->Pixels; int pitch = data->Pitch; if (data->Format == ImTextureFormat_Alpha8) { for (int ny = data->Height; ny > 0; ny--, pixels += pitch) { ImU8* p = (ImU8*)pixels; for (int nx = data->Width; nx > 0; nx--, p++) { unsigned int v = ImMin((unsigned int)(*p * multiply_factor), (unsigned int)255); *p = (unsigned char)v; } } } else if (data->Format == ImTextureFormat_RGBA32) //-V547 { for (int ny = data->Height; ny > 0; ny--, pixels += pitch) { ImU32* p = (ImU32*)(void*)pixels; for (int nx = data->Width; nx > 0; nx--, p++) { unsigned int a = ImMin((unsigned int)(((*p >> IM_COL32_A_SHIFT) & 0xFF) * multiply_factor), (unsigned int)255); *p = IM_COL32((*p >> IM_COL32_R_SHIFT) & 0xFF, (*p >> IM_COL32_G_SHIFT) & 0xFF, (*p >> IM_COL32_B_SHIFT) & 0xFF, a); } } } else { IM_ASSERT(0); } } // Fill with single color. We don't use this directly but it is convenient for anyone working on uploading custom rects. void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col) { if (dst_tex->Format == ImTextureFormat_Alpha8) { ImU8 col_a = (col >> IM_COL32_A_SHIFT) & 0xFF; for (int y = 0; y < h; y++) memset((ImU8*)dst_tex->GetPixelsAt(dst_x, dst_y + y), col_a, w); } else { for (int y = 0; y < h; y++) { ImU32* p = (ImU32*)(void*)dst_tex->GetPixelsAt(dst_x, dst_y + y); for (int x = w; x > 0; x--, p++) *p = col; } } } // Copy block from one texture to another void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h) { IM_ASSERT(src_tex->Pixels != NULL && dst_tex->Pixels != NULL); IM_ASSERT(src_tex->Format == dst_tex->Format); IM_ASSERT(src_x >= 0 && src_x + w <= src_tex->Width); IM_ASSERT(src_y >= 0 && src_y + h <= src_tex->Height); IM_ASSERT(dst_x >= 0 && dst_x + w <= dst_tex->Width); IM_ASSERT(dst_y >= 0 && dst_y + h <= dst_tex->Height); for (int y = 0; y < h; y++) memcpy(dst_tex->GetPixelsAt(dst_x, dst_y + y), src_tex->GetPixelsAt(src_x, src_y + y), w * dst_tex->BytesPerPixel); } // Queue texture block update for renderer backend void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h) { IM_ASSERT(tex->Status != ImTextureStatus_WantDestroy && tex->Status != ImTextureStatus_Destroyed); IM_ASSERT(x >= 0 && x <= 0xFFFF && y >= 0 && y <= 0xFFFF && w >= 0 && x + w <= 0x10000 && h >= 0 && y + h <= 0x10000); IM_UNUSED(atlas); ImTextureRect req = { (unsigned short)x, (unsigned short)y, (unsigned short)w, (unsigned short)h }; int new_x1 = ImMax(tex->UpdateRect.w == 0 ? 0 : tex->UpdateRect.x + tex->UpdateRect.w, req.x + req.w); int new_y1 = ImMax(tex->UpdateRect.h == 0 ? 0 : tex->UpdateRect.y + tex->UpdateRect.h, req.y + req.h); tex->UpdateRect.x = ImMin(tex->UpdateRect.x, req.x); tex->UpdateRect.y = ImMin(tex->UpdateRect.y, req.y); tex->UpdateRect.w = (unsigned short)(new_x1 - tex->UpdateRect.x); tex->UpdateRect.h = (unsigned short)(new_y1 - tex->UpdateRect.y); tex->UsedRect.x = ImMin(tex->UsedRect.x, req.x); tex->UsedRect.y = ImMin(tex->UsedRect.y, req.y); tex->UsedRect.w = (unsigned short)(ImMax(tex->UsedRect.x + tex->UsedRect.w, req.x + req.w) - tex->UsedRect.x); tex->UsedRect.h = (unsigned short)(ImMax(tex->UsedRect.y + tex->UsedRect.h, req.y + req.h) - tex->UsedRect.y); atlas->TexIsBuilt = false; // No need to queue if status is == ImTextureStatus_WantCreate if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates) { tex->Status = ImTextureStatus_WantUpdates; tex->Updates.push_back(req); } } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS static void GetTexDataAsFormat(ImFontAtlas* atlas, ImTextureFormat format, unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) { ImTextureData* tex = atlas->TexData; if (!atlas->TexIsBuilt || tex == NULL || tex->Pixels == NULL || atlas->TexDesiredFormat != format) { atlas->TexDesiredFormat = format; atlas->Build(); tex = atlas->TexData; } if (out_pixels) { *out_pixels = (unsigned char*)tex->Pixels; }; if (out_width) { *out_width = tex->Width; }; if (out_height) { *out_height = tex->Height; }; if (out_bytes_per_pixel) { *out_bytes_per_pixel = tex->BytesPerPixel; } } void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) { GetTexDataAsFormat(this, ImTextureFormat_Alpha8, out_pixels, out_width, out_height, out_bytes_per_pixel); } void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) { GetTexDataAsFormat(this, ImTextureFormat_RGBA32, out_pixels, out_width, out_height, out_bytes_per_pixel); } bool ImFontAtlas::Build() { ImFontAtlasBuildMain(this); return true; } #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) { // Sanity Checks IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); IM_ASSERT((font_cfg_in->FontData != NULL && font_cfg_in->FontDataSize > 0) || (font_cfg_in->FontLoader != NULL)); //IM_ASSERT(font_cfg_in->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?"); IM_ASSERT(font_cfg_in->RasterizerDensity > 0.0f && "Is ImFontConfig struct correctly initialized?"); if (font_cfg_in->GlyphOffset.x != 0.0f || font_cfg_in->GlyphOffset.y != 0.0f || font_cfg_in->GlyphMinAdvanceX != 0.0f || font_cfg_in->GlyphMaxAdvanceX != FLT_MAX) IM_ASSERT(font_cfg_in->SizePixels != 0.0f && "Specifying glyph offset/advances requires a reference size to base it on."); // Lazily create builder on the first call to AddFont if (Builder == NULL) ImFontAtlasBuildInit(this); // Create new font const bool is_first_font = (Fonts.Size == 0); ImFont* font; if (!font_cfg_in->MergeMode) { font = IM_NEW(ImFont)(); font->FontId = FontNextUniqueID++; font->Flags = font_cfg_in->Flags; font->LegacySize = font_cfg_in->SizePixels; font->CurrentRasterizerDensity = font_cfg_in->RasterizerDensity; Fonts.push_back(font); } else { IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back(); ImFontAtlasFontDiscardBakes(this, font, 0); // Need to discard bakes if the font was already used, because baked->FontLoaderDatas[] will change size. (#9162) } // Add to list Sources.push_back(*font_cfg_in); ImFontConfig* font_cfg = &Sources.back(); if (font_cfg->DstFont == NULL) font_cfg->DstFont = font; font->Sources.push_back(font_cfg); ImFontAtlasBuildUpdatePointers(this); // Pointers to Sources are otherwise dangling after we called Sources.push_back(). // Sanity check // We don't round cfg.SizePixels yet as relative size of merged fonts are used afterwards. if (font_cfg->GlyphExcludeRanges != NULL) { int size = 0; for (const ImWchar* p = font_cfg->GlyphExcludeRanges; p[0] != 0; p++, size++) {} IM_ASSERT((size & 1) == 0 && "GlyphExcludeRanges[] size must be multiple of two!"); IM_ASSERT((size <= 64) && "GlyphExcludeRanges[] size must be small!"); font_cfg->GlyphExcludeRanges = (ImWchar*)ImMemdup(font_cfg->GlyphExcludeRanges, sizeof(font_cfg->GlyphExcludeRanges[0]) * (size + 1)); } if (font_cfg->FontLoader != NULL) { IM_ASSERT(font_cfg->FontLoader->FontBakedLoadGlyph != NULL); IM_ASSERT(font_cfg->FontLoader->LoaderInit == NULL && font_cfg->FontLoader->LoaderShutdown == NULL); // FIXME-NEWATLAS: Unsupported yet. } IM_ASSERT(font_cfg->FontLoaderData == NULL); if (!ImFontAtlasFontSourceInit(this, font_cfg)) { // Rollback (this is a fragile/rarely exercised code-path. TestSuite's "misc_atlas_add_invalid_font" aim to test this) ImFontAtlasFontDestroySourceData(this, font_cfg); Sources.pop_back(); font->Sources.pop_back(); if (!font_cfg->MergeMode) { IM_DELETE(font); Fonts.pop_back(); } return NULL; } ImFontAtlasFontSourceAddToFont(this, font, font_cfg); if (is_first_font) ImFontAtlasBuildNotifySetFont(this, NULL, font); return font; } // Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder) static unsigned int stb_decompress_length(const unsigned char* input); static unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length); static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; } static void Decode85(const unsigned char* src, unsigned char* dst) { while (*src) { unsigned int tmp = Decode85Byte(src[0]) + 85 * (Decode85Byte(src[1]) + 85 * (Decode85Byte(src[2]) + 85 * (Decode85Byte(src[3]) + 85 * Decode85Byte(src[4])))); dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness. src += 5; dst += 4; } } #ifndef IMGUI_DISABLE_DEFAULT_FONT static const char* GetDefaultCompressedFontDataProggyClean(int* out_size); static const char* GetDefaultCompressedFontDataProggyForever(int* out_size); #endif // This duplicates some of the logic in UpdateFontsNewFrame() which is a bit chicken-and-eggy/tricky to extract due to variety of codepaths and possible initialization ordering. static float GetExpectedContextFontSize(ImGuiContext* ctx) { return ((ctx->Style.FontSizeBase > 0.0f) ? ctx->Style.FontSizeBase : 13.0f) * ctx->Style.FontScaleMain * ctx->Style.FontScaleDpi; } // Legacy function with heuristic to select Pixel or Vector font. // The selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold at the time of adding the default font. // Prefer calling AddFontDefaultVector() or AddFontDefaultBitmap() based on your own logic. ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg) { if (OwnerContext == NULL || GetExpectedContextFontSize(OwnerContext) >= 15.0f) return AddFontDefaultVector(font_cfg); else return AddFontDefaultBitmap(font_cfg); } // Load embedded ProggyClean.ttf. Default size 13, disable oversampling. // If you want a similar font which may be better scaled, consider using AddFontDefaultVector(). ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template) { #ifndef IMGUI_DISABLE_DEFAULT_FONT ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); if (!font_cfg_template) font_cfg.PixelSnapH = true; // Prevents sub-integer scaling factors at lower-level layers. if (font_cfg.SizePixels <= 0.0f) font_cfg.SizePixels = 13.0f; // This only serves (1) as a reference for GlyphOffset.y setting and (2) as a default for pre-1.92 backend. if (font_cfg.Name[0] == '\0') ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyClean.ttf"); font_cfg.EllipsisChar = (ImWchar)0x0085; font_cfg.GlyphOffset.y += 1.0f * (font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units int ttf_compressed_size = 0; const char* ttf_compressed = GetDefaultCompressedFontDataProggyClean(&ttf_compressed_size); return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); #else IM_ASSERT(0 && "Function is disabled in this build."); IM_UNUSED(font_cfg_template); return NULL; #endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT } // Load a minimal version of ProggyForever, designed to match our good old ProggyClean, but nicely scalable. // (See build script in https://github.com/ocornut/proggyforever for details) ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) { #ifndef IMGUI_DISABLE_DEFAULT_FONT ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); if (!font_cfg_template) font_cfg.PixelSnapH = true; // Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers. if (font_cfg.SizePixels <= 0.0f) font_cfg.SizePixels = 13.0f; if (font_cfg.Name[0] == '\0') ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyForever.ttf"); font_cfg.ExtraSizeScale *= 1.015f; // Match ProggyClean font_cfg.GlyphOffset.y += 0.5f * (font_cfg.SizePixels / 16.0f); // Closer match ProggyClean + avoid descenders going too high (with current code). int ttf_compressed_size = 0; const char* ttf_compressed = GetDefaultCompressedFontDataProggyForever(&ttf_compressed_size); return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); #else IM_ASSERT(0 && "Function is disabled in this build."); IM_UNUSED(font_cfg_template); return NULL; #endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT } ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); size_t data_size = 0; void* data = ImFileLoadToMemory(filename, "rb", &data_size, 0); if (!data) { if (font_cfg_template == NULL || (font_cfg_template->Flags & ImFontFlags_NoLoadError) == 0) { IMGUI_DEBUG_LOG("While loading '%s'\n", filename); IM_ASSERT_USER_ERROR(0, "Could not load font file!"); } return NULL; } ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); if (font_cfg.Name[0] == '\0') { // Store a short copy of filename into the font name for convenience const char* p; for (p = filename + ImStrlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "%s", p); } return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges); } // NB: Transfer ownership of 'font_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); IM_ASSERT(font_cfg.FontData == NULL); IM_ASSERT(font_data_size > 100 && "Incorrect value for font_data_size!"); // Heuristic to prevent accidentally passing a wrong value to font_data_size. font_cfg.FontData = font_data; font_cfg.FontDataSize = font_data_size; font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels; if (glyph_ranges) font_cfg.GlyphRanges = glyph_ranges; return AddFont(&font_cfg); } ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char*)compressed_ttf_data); unsigned char* buf_decompressed_data = (unsigned char*)IM_ALLOC(buf_decompressed_size); stb_decompress(buf_decompressed_data, (const unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); IM_ASSERT(font_cfg.FontData == NULL); font_cfg.FontDataOwnedByAtlas = true; return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges); } ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges) { int compressed_ttf_size = (((int)ImStrlen(compressed_ttf_data_base85) + 4) / 5) * 4; void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size); Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf); ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges); IM_FREE(compressed_ttf); return font; } // On font removal we need to remove references (otherwise we could queue removal?) // We allow old_font == new_font which forces updating all values (e.g. sizes) void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font) { for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas) { if (shared_data->Font == old_font) shared_data->Font = new_font; if (ImGuiContext* ctx = shared_data->Context) { if (ctx->FrameCount == 0 && old_font == NULL) // While this should work either way, we save ourselves the bother / debugging confusion of running ImGui code so early when it is not needed. continue; if (ctx->IO.FontDefault == old_font) ctx->IO.FontDefault = new_font; if (ctx->Font == old_font) { ImGuiContext* curr_ctx = ImGui::GetCurrentContext(); bool need_bind_ctx = ctx != curr_ctx; if (need_bind_ctx) ImGui::SetCurrentContext(ctx); ImGui::SetCurrentFont(new_font, ctx->FontSizeBase, ctx->FontSize); if (need_bind_ctx) ImGui::SetCurrentContext(curr_ctx); } for (ImFontStackData& font_stack_data : ctx->FontStack) if (font_stack_data.Font == old_font) font_stack_data.Font = new_font; } } } void ImFontAtlas::RemoveFont(ImFont* font) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); ImFontAtlasFontDestroyOutput(this, font); for (ImFontConfig* src : font->Sources) ImFontAtlasFontDestroySourceData(this, src); for (int src_n = 0; src_n < Sources.Size; src_n++) if (Sources[src_n].DstFont == font) Sources.erase(&Sources[src_n--]); bool removed = Fonts.find_erase(font); IM_ASSERT(removed); IM_UNUSED(removed); ImFontAtlasBuildUpdatePointers(this); font->OwnerAtlas = NULL; IM_DELETE(font); // Notify external systems ImFont* new_current_font = Fonts.empty() ? NULL : Fonts[0]; ImFontAtlasBuildNotifySetFont(this, font, new_current_font); } // At it is common to do an AddCustomRect() followed by a GetCustomRect(), we provide an optional 'ImFontAtlasRect* out_r = NULL' argument to retrieve the info straight away. ImFontAtlasRectId ImFontAtlas::AddCustomRect(int width, int height, ImFontAtlasRect* out_r) { IM_ASSERT(width > 0 && width <= 0xFFFF); IM_ASSERT(height > 0 && height <= 0xFFFF); if (Builder == NULL) ImFontAtlasBuildInit(this); ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height); if (r_id == ImFontAtlasRectId_Invalid) return ImFontAtlasRectId_Invalid; if (out_r != NULL) GetCustomRect(r_id, out_r); if (RendererHasTextures) { ImTextureRect* r = ImFontAtlasPackGetRect(this, r_id); ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h); } return r_id; } void ImFontAtlas::RemoveCustomRect(ImFontAtlasRectId id) { if (ImFontAtlasPackGetRectSafe(this, id) == NULL) return; ImFontAtlasPackDiscardRect(this, id); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // This API does not make sense anymore with scalable fonts. // - Prefer adding a font source (ImFontConfig) using a custom/procedural loader. // - You may use ImFontFlags_LockBakedSizes to limit an existing font to known baked sizes: // ImFont* myfont = io.Fonts->AddFontFromFileTTF(....); // myfont->GetFontBaked(16.0f); // myfont->Flags |= ImFontFlags_LockBakedSizes; ImFontAtlasRectId ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int width, int height, float advance_x, const ImVec2& offset) { float font_size = font->LegacySize; return AddCustomRectFontGlyphForSize(font, font_size, codepoint, width, height, advance_x, offset); } // FIXME: we automatically set glyph.Colored=true by default. // If you need to alter this, you can write 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph(). ImFontAtlasRectId ImFontAtlas::AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int width, int height, float advance_x, const ImVec2& offset) { #ifdef IMGUI_USE_WCHAR32 IM_ASSERT(codepoint <= IM_UNICODE_CODEPOINT_MAX); #endif IM_ASSERT(font != NULL); IM_ASSERT(width > 0 && width <= 0xFFFF); IM_ASSERT(height > 0 && height <= 0xFFFF); ImFontBaked* baked = font->GetFontBaked(font_size); ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height); if (r_id == ImFontAtlasRectId_Invalid) return ImFontAtlasRectId_Invalid; ImTextureRect* r = ImFontAtlasPackGetRect(this, r_id); if (RendererHasTextures) ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h); if (baked->IsGlyphLoaded(codepoint)) ImFontAtlasBakedDiscardFontGlyph(this, font, baked, baked->FindGlyph(codepoint)); ImFontGlyph glyph; glyph.Codepoint = codepoint; glyph.AdvanceX = advance_x; glyph.X0 = offset.x; glyph.Y0 = offset.y; glyph.X1 = offset.x + r->w; glyph.Y1 = offset.y + r->h; glyph.Visible = true; glyph.Colored = true; // FIXME: Arbitrary glyph.PackId = r_id; ImFontAtlasBakedAddFontGlyph(this, baked, font->Sources[0], &glyph); return r_id; } #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS bool ImFontAtlas::GetCustomRect(ImFontAtlasRectId id, ImFontAtlasRect* out_r) const { ImTextureRect* r = ImFontAtlasPackGetRectSafe((ImFontAtlas*)this, id); if (r == NULL) return false; IM_ASSERT(TexData->Width > 0 && TexData->Height > 0); // Font atlas needs to be built before we can calculate UV coordinates if (out_r == NULL) return true; out_r->x = r->x; out_r->y = r->y; out_r->w = r->w; out_r->h = r->h; out_r->uv0 = ImVec2((float)(r->x), (float)(r->y)) * TexUvScale; out_r->uv1 = ImVec2((float)(r->x + r->w), (float)(r->y + r->h)) * TexUvScale; return true; } bool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]) { if (cursor_type <= ImGuiMouseCursor_None || cursor_type >= ImGuiMouseCursor_COUNT) return false; if (atlas->Flags & ImFontAtlasFlags_NoMouseCursors) return false; ImTextureRect* r = ImFontAtlasPackGetRect(atlas, atlas->Builder->PackIdMouseCursors); ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r->x, (float)r->y); ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1]; *out_size = size; *out_offset = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][2]; out_uv_border[0] = (pos) * atlas->TexUvScale; out_uv_border[1] = (pos + size) * atlas->TexUvScale; pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W + 1; out_uv_fill[0] = (pos) * atlas->TexUvScale; out_uv_fill[1] = (pos + size) * atlas->TexUvScale; return true; } // When atlas->RendererHasTextures = true, this is only called if no font were loaded. void ImFontAtlasBuildMain(ImFontAtlas* atlas) { IM_ASSERT(!atlas->Locked && "Cannot modify a locked ImFontAtlas!"); if (atlas->TexData && atlas->TexData->Format != atlas->TexDesiredFormat) ImFontAtlasBuildClear(atlas); if (atlas->Builder == NULL) ImFontAtlasBuildInit(atlas); // Default font is none are specified if (atlas->Sources.Size == 0) atlas->AddFontDefault(); // [LEGACY] For backends not supporting RendererHasTextures: preload all glyphs ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas); if (atlas->RendererHasTextures == false) // ~ImGuiBackendFlags_RendererHasTextures ImFontAtlasBuildLegacyPreloadAllGlyphRanges(atlas); atlas->TexIsBuilt = true; } void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v) { // (Only used by stb_truetype builder) // Automatically disable horizontal oversampling over size 36 const float raster_size = baked->Size * baked->RasterizerDensity * src->RasterizerDensity; *out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : (raster_size > 36.0f || src->PixelSnapH) ? 1 : 2; *out_oversample_v = (src->OversampleV != 0) ? src->OversampleV : 1; } // Setup main font loader for the atlas // Every font source (ImFontConfig) will use this unless ImFontConfig::FontLoader specify a custom loader. void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader) { if (atlas->FontLoader == font_loader) return; IM_ASSERT(!atlas->Locked && "Cannot modify a locked ImFontAtlas!"); for (ImFont* font : atlas->Fonts) ImFontAtlasFontDestroyOutput(atlas, font); if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderShutdown) atlas->FontLoader->LoaderShutdown(atlas); atlas->FontLoader = font_loader; atlas->FontLoaderName = font_loader ? font_loader->Name : "NULL"; IM_ASSERT(atlas->FontLoaderData == NULL); if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderInit) atlas->FontLoader->LoaderInit(atlas); for (ImFont* font : atlas->Fonts) ImFontAtlasFontInitOutput(atlas, font); for (ImFont* font : atlas->Fonts) for (ImFontConfig* src : font->Sources) ImFontAtlasFontSourceAddToFont(atlas, font, src); } // Preload all glyph ranges for legacy backends. // This may lead to multiple texture creation which might be a little slower than before. void ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas) { atlas->Builder->PreloadedAllGlyphsRanges = true; for (ImFont* font : atlas->Fonts) { ImFontBaked* baked = font->GetFontBaked(font->LegacySize); if (font->FallbackChar != 0) baked->FindGlyph(font->FallbackChar); if (font->EllipsisChar != 0) baked->FindGlyph(font->EllipsisChar); for (ImFontConfig* src : font->Sources) { const ImWchar* ranges = src->GlyphRanges ? src->GlyphRanges : atlas->GetGlyphRangesDefault(); for (; ranges[0]; ranges += 2) for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 baked->FindGlyph((ImWchar)c); } } } // FIXME: May make ImFont::Sources a ImSpan<> and move ownership to ImFontAtlas void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas) { for (ImFont* font : atlas->Fonts) font->Sources.resize(0); for (ImFontConfig& src : atlas->Sources) src.DstFont->Sources.push_back(&src); } // Render a white-colored bitmap encoded in a string void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char) { ImTextureData* tex = atlas->TexData; IM_ASSERT(x >= 0 && x + w <= tex->Width); IM_ASSERT(y >= 0 && y + h <= tex->Height); switch (tex->Format) { case ImTextureFormat_Alpha8: { ImU8* out_p = (ImU8*)tex->GetPixelsAt(x, y); for (int off_y = 0; off_y < h; off_y++, out_p += tex->Width, in_str += w) for (int off_x = 0; off_x < w; off_x++) out_p[off_x] = (in_str[off_x] == in_marker_char) ? 0xFF : 0x00; break; } case ImTextureFormat_RGBA32: { ImU32* out_p = (ImU32*)tex->GetPixelsAt(x, y); for (int off_y = 0; off_y < h; off_y++, out_p += tex->Width, in_str += w) for (int off_x = 0; off_x < w; off_x++) out_p[off_x] = (in_str[off_x] == in_marker_char) ? IM_COL32_WHITE : IM_COL32_BLACK_TRANS; break; } } } static void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas) { // Pack and store identifier so we can refresh UV coordinates on texture resize. // FIXME-NEWATLAS: User/custom rects where user code wants to store UV coordinates will need to do the same thing. ImFontAtlasBuilder* builder = atlas->Builder; ImVec2i pack_size = (atlas->Flags & ImFontAtlasFlags_NoMouseCursors) ? ImVec2i(2, 2) : ImVec2i(FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H); ImFontAtlasRect r; bool add_and_draw = (atlas->GetCustomRect(builder->PackIdMouseCursors, &r) == false); if (add_and_draw) { builder->PackIdMouseCursors = atlas->AddCustomRect(pack_size.x, pack_size.y, &r); IM_ASSERT(builder->PackIdMouseCursors != ImFontAtlasRectId_Invalid); // Draw to texture if (atlas->Flags & ImFontAtlasFlags_NoMouseCursors) { // 2x2 white pixels ImFontAtlasBuildRenderBitmapFromString(atlas, r.x, r.y, 2, 2, "XX" "XX", 'X'); } else { // 2x2 white pixels + mouse cursors const int x_for_white = r.x; const int x_for_black = r.x + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1; ImFontAtlasBuildRenderBitmapFromString(atlas, x_for_white, r.y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.'); ImFontAtlasBuildRenderBitmapFromString(atlas, x_for_black, r.y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X'); } } // Refresh UV coordinates atlas->TexUvWhitePixel = ImVec2((r.x + 0.5f) * atlas->TexUvScale.x, (r.y + 0.5f) * atlas->TexUvScale.y); } static void ImFontAtlasBuildUpdateLinesTexData(ImFontAtlas* atlas) { if (atlas->Flags & ImFontAtlasFlags_NoBakedLines) return; // Pack and store identifier so we can refresh UV coordinates on texture resize. ImTextureData* tex = atlas->TexData; ImFontAtlasBuilder* builder = atlas->Builder; ImFontAtlasRect r; bool add_and_draw = atlas->GetCustomRect(builder->PackIdLinesTexData, &r) == false; if (add_and_draw) { ImVec2i pack_size = ImVec2i(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1); builder->PackIdLinesTexData = atlas->AddCustomRect(pack_size.x, pack_size.y, &r); IM_ASSERT(builder->PackIdLinesTexData != ImFontAtlasRectId_Invalid); } // Register texture region for thick lines // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them for (int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row { // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle const int y = n; const int line_width = n; const int pad_left = (r.w - line_width) / 2; const int pad_right = r.w - (pad_left + line_width); IM_ASSERT(pad_left + line_width + pad_right == r.w && y < r.h); // Make sure we're inside the texture bounds before we start writing pixels // Write each slice if (add_and_draw && tex->Format == ImTextureFormat_Alpha8) { ImU8* write_ptr = (ImU8*)tex->GetPixelsAt(r.x, r.y + y); for (int i = 0; i < pad_left; i++) *(write_ptr + i) = 0x00; for (int i = 0; i < line_width; i++) *(write_ptr + pad_left + i) = 0xFF; for (int i = 0; i < pad_right; i++) *(write_ptr + pad_left + line_width + i) = 0x00; } else if (add_and_draw && tex->Format == ImTextureFormat_RGBA32) { ImU32* write_ptr = (ImU32*)(void*)tex->GetPixelsAt(r.x, r.y + y); for (int i = 0; i < pad_left; i++) *(write_ptr + i) = IM_COL32(255, 255, 255, 0); for (int i = 0; i < line_width; i++) *(write_ptr + pad_left + i) = IM_COL32_WHITE; for (int i = 0; i < pad_right; i++) *(write_ptr + pad_left + line_width + i) = IM_COL32(255, 255, 255, 0); } // Refresh UV coordinates ImVec2 uv0 = ImVec2((float)(r.x + pad_left - 1), (float)(r.y + y)) * atlas->TexUvScale; ImVec2 uv1 = ImVec2((float)(r.x + pad_left + line_width + 1), (float)(r.y + y + 1)) * atlas->TexUvScale; float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts atlas->TexUvLines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v); } } static void ImFontAtlasBuildUpdateShadowTexData(ImFontAtlas* atlas); // FIXME-SHADOWS: Move impl here. //----------------------------------------------------------------------------------------------------------------------------- // Was tempted to lazily init FontSrc but wouldn't save much + makes it more complicated to detect invalid data at AddFont() bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font) { bool ret = true; for (ImFontConfig* src : font->Sources) if (!ImFontAtlasFontSourceInit(atlas, src)) ret = false; IM_ASSERT(ret); // Unclear how to react to this meaningfully. Assume that result will be same as initial AddFont() call. return ret; } // Keep source/input FontData void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font) { font->ClearOutputData(); for (ImFontConfig* src : font->Sources) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; if (loader && loader->FontSrcDestroy != NULL) loader->FontSrcDestroy(atlas, src); } } void ImFontAtlasFontRebuildOutput(ImFontAtlas* atlas, ImFont* font) { ImFontAtlasFontDestroyOutput(atlas, font); ImFontAtlasFontInitOutput(atlas, font); } //----------------------------------------------------------------------------------------------------------------------------- bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; if (loader->FontSrcInit != NULL && !loader->FontSrcInit(atlas, src)) return false; return true; } void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src) { if (src->MergeMode == false) { font->ClearOutputData(); //font->FontSize = src->SizePixels; font->OwnerAtlas = atlas; IM_ASSERT(font->Sources[0] == src); } atlas->TexIsBuilt = false; // For legacy backends ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, font, src); } void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src) { IM_UNUSED(atlas); // IF YOU GET A CRASH IN THE IM_FREE() CALL HERE AND USED AddFontFromMemoryTTF(): // - DUE TO LEGACY REASON AddFontFromMemoryTTF() TRANSFERS MEMORY OWNERSHIP BY DEFAULT. // - IT WILL THEREFORE CRASH WHEN PASSED DATA WHICH MAY NOT BE FREED BY IMGUI. // - USE `ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; io.Fonts->AddFontFromMemoryTTF(....., &cfg);` to disable passing ownership/ // WE WILL ADDRESS THIS IN A FUTURE REWORK OF THE API. if (src->FontDataOwnedByAtlas) IM_FREE(src->FontData); src->FontData = NULL; if (src->GlyphExcludeRanges) IM_FREE((void*)src->GlyphExcludeRanges); src->GlyphExcludeRanges = NULL; } // Create a compact, baked "..." if it doesn't exist, by using the ".". // This may seem overly complicated right now but the point is to exercise and improve a technique which should be increasingly used. // FIXME-NEWATLAS: This borrows too much from FontLoader's FontLoadGlyph() handlers and suggest that we should add further helpers. static ImFontGlyph* ImFontAtlasBuildSetupFontBakedEllipsis(ImFontAtlas* atlas, ImFontBaked* baked) { ImFont* font = baked->OwnerFont; IM_ASSERT(font->EllipsisChar != 0); const ImFontGlyph* dot_glyph = baked->FindGlyphNoFallback((ImWchar)'.'); if (dot_glyph == NULL) dot_glyph = baked->FindGlyphNoFallback((ImWchar)0xFF0E); if (dot_glyph == NULL) return NULL; ImFontAtlasRectId dot_r_id = dot_glyph->PackId; // Deep copy to avoid invalidation of glyphs and rect pointers ImTextureRect* dot_r = ImFontAtlasPackGetRect(atlas, dot_r_id); const int dot_spacing = 1; const float dot_step = (dot_glyph->X1 - dot_glyph->X0) + dot_spacing; ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, (dot_r->w * 3 + dot_spacing * 2), dot_r->h); ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id); ImFontGlyph glyph_in = {}; ImFontGlyph* glyph = &glyph_in; glyph->Codepoint = font->EllipsisChar; glyph->AdvanceX = ImMax(dot_glyph->AdvanceX, dot_glyph->X0 + dot_step * 3.0f - dot_spacing); // FIXME: Slightly odd for normally mono-space fonts but since this is used for trailing contents. glyph->X0 = dot_glyph->X0; glyph->Y0 = dot_glyph->Y0; glyph->X1 = dot_glyph->X0 + dot_step * 3 - dot_spacing; glyph->Y1 = dot_glyph->Y1; glyph->Visible = true; glyph->PackId = pack_id; glyph = ImFontAtlasBakedAddFontGlyph(atlas, baked, NULL, glyph); dot_glyph = NULL; // Invalidated // Copy to texture, post-process and queue update for backend // FIXME-NEWATLAS-V2: Dot glyph is already post-processed as this point, so this would damage it. dot_r = ImFontAtlasPackGetRect(atlas, dot_r_id); ImTextureData* tex = atlas->TexData; for (int n = 0; n < 3; n++) ImFontAtlasTextureBlockCopy(tex, dot_r->x, dot_r->y, tex, r->x + (dot_r->w + dot_spacing) * n, r->y, dot_r->w, dot_r->h); ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h); return glyph; } // Load fallback in order to obtain its index // (this is called from in hot-path so we avoid extraneous parameters to minimize impact on code size) static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked) { IM_ASSERT(baked->FallbackGlyphIndex == -1); IM_ASSERT(baked->FallbackAdvanceX == 0.0f); ImFont* font = baked->OwnerFont; ImFontGlyph* fallback_glyph = NULL; if (font->FallbackChar != 0) fallback_glyph = baked->FindGlyphNoFallback(font->FallbackChar); if (fallback_glyph == NULL) { ImFontGlyph* space_glyph = baked->FindGlyphNoFallback((ImWchar)' '); ImFontGlyph glyph; glyph.Codepoint = 0; glyph.AdvanceX = space_glyph ? space_glyph->AdvanceX : IM_ROUND(baked->Size * 0.40f); fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->OwnerAtlas, baked, NULL, &glyph); } baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(fallback_glyph); // Storing index avoid need to update pointer on growth and simplify inner loop code baked->FallbackAdvanceX = fallback_glyph->AdvanceX; } static void ImFontAtlasBuildSetupFontBakedBlanks(ImFontAtlas* atlas, ImFontBaked* baked) { // Mark space as always hidden (not strictly correct/necessary. but some e.g. icons fonts don't have a space. it tends to look neater in previews) ImFontGlyph* space_glyph = baked->FindGlyphNoFallback((ImWchar)' '); if (space_glyph != NULL) space_glyph->Visible = false; // Setup Tab character. // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) if (baked->FindGlyphNoFallback('\t') == NULL && space_glyph != NULL) { ImFontGlyph tab_glyph; tab_glyph.Codepoint = '\t'; tab_glyph.AdvanceX = space_glyph->AdvanceX * IM_TABSIZE; ImFontAtlasBakedAddFontGlyph(atlas, baked, NULL, &tab_glyph); } } // Load/identify special glyphs // (note that this is called again for fonts with MergeMode) void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src) { IM_UNUSED(atlas); IM_ASSERT(font->Sources.contains(src)); // Find Fallback character. Actual glyph loaded in GetFontBaked(). const ImWchar fallback_chars[] = { font->FallbackChar, (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' }; if (font->FallbackChar == 0) for (ImWchar candidate_char : fallback_chars) if (candidate_char != 0 && font->IsGlyphInFont(candidate_char)) { font->FallbackChar = (ImWchar)candidate_char; break; } // Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots. const ImWchar ellipsis_chars[] = { src->EllipsisChar, (ImWchar)0x2026, (ImWchar)0x0085 }; if (font->EllipsisChar == 0) for (ImWchar candidate_char : ellipsis_chars) if (candidate_char != 0 && font->IsGlyphInFont(candidate_char)) { font->EllipsisChar = candidate_char; break; } if (font->EllipsisChar == 0) { font->EllipsisChar = 0x0085; font->EllipsisAutoBake = true; } } void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph) { if (glyph->PackId != ImFontAtlasRectId_Invalid) { ImFontAtlasPackDiscardRect(atlas, glyph->PackId); glyph->PackId = ImFontAtlasRectId_Invalid; } ImWchar c = (ImWchar)glyph->Codepoint; IM_ASSERT(font->FallbackChar != c && font->EllipsisChar != c); // Unsupported for simplicity IM_ASSERT(glyph >= baked->Glyphs.Data && glyph < baked->Glyphs.Data + baked->Glyphs.Size); IM_UNUSED(font); baked->IndexLookup[c] = IM_FONTGLYPH_INDEX_UNUSED; baked->IndexAdvanceX[c] = baked->FallbackAdvanceX; } ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id) { IMGUI_DEBUG_LOG_FONT("[font] Created baked %.2fpx\n", font_size); ImFontBaked* baked = atlas->Builder->BakedPool.push_back(ImFontBaked()); baked->Size = font_size; baked->RasterizerDensity = font_rasterizer_density; baked->BakedId = baked_id; baked->OwnerFont = font; baked->LastUsedFrame = atlas->Builder->FrameCount; // Initialize backend data size_t loader_data_size = 0; for (ImFontConfig* src : font->Sources) // Cannot easily be cached as we allow changing backend { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; loader_data_size += loader->FontBakedSrcLoaderDataSize; } baked->FontLoaderDatas = (loader_data_size > 0) ? IM_ALLOC(loader_data_size) : NULL; char* loader_data_p = (char*)baked->FontLoaderDatas; for (ImFontConfig* src : font->Sources) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; if (loader->FontBakedInit) loader->FontBakedInit(atlas, src, baked, loader_data_p); loader_data_p += loader->FontBakedSrcLoaderDataSize; } ImFontAtlasBuildSetupFontBakedBlanks(atlas, baked); return baked; } // FIXME-OPT: This is not a fast query. Adding a BakedCount field in Font might allow to take a shortcut for the most common case. ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density) { ImFontAtlasBuilder* builder = atlas->Builder; for (int step_n = 0; step_n < 2; step_n++) { ImFontBaked* closest_larger_match = NULL; ImFontBaked* closest_smaller_match = NULL; for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++) { ImFontBaked* baked = &builder->BakedPool[baked_n]; if (baked->OwnerFont != font || baked->WantDestroy) continue; if (step_n == 0 && baked->RasterizerDensity != font_rasterizer_density) // First try with same density continue; if (baked->Size > font_size && (closest_larger_match == NULL || baked->Size < closest_larger_match->Size)) closest_larger_match = baked; if (baked->Size < font_size && (closest_smaller_match == NULL || baked->Size > closest_smaller_match->Size)) closest_smaller_match = baked; } if (closest_larger_match) if (closest_smaller_match == NULL || (closest_larger_match->Size >= font_size * 2.0f && closest_smaller_match->Size > font_size * 0.5f)) return closest_larger_match; if (closest_smaller_match) return closest_smaller_match; } return NULL; } void ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked) { ImFontAtlasBuilder* builder = atlas->Builder; IMGUI_DEBUG_LOG_FONT("[font] Discard baked %.2f for \"%s\"\n", baked->Size, font->GetDebugName()); for (ImFontGlyph& glyph : baked->Glyphs) if (glyph.PackId != ImFontAtlasRectId_Invalid) ImFontAtlasPackDiscardRect(atlas, glyph.PackId); char* loader_data_p = (char*)baked->FontLoaderDatas; for (ImFontConfig* src : font->Sources) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; if (loader->FontBakedDestroy) loader->FontBakedDestroy(atlas, src, baked, loader_data_p); loader_data_p += loader->FontBakedSrcLoaderDataSize; } if (baked->FontLoaderDatas) { IM_FREE(baked->FontLoaderDatas); baked->FontLoaderDatas = NULL; } builder->BakedMap.SetVoidPtr(baked->BakedId, NULL); builder->BakedDiscardedCount++; baked->ClearOutputData(); baked->WantDestroy = true; font->LastBaked = NULL; } // use unused_frames==0 to discard everything. void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames) { if (ImFontAtlasBuilder* builder = atlas->Builder) // This can be called from font destructor for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++) { ImFontBaked* baked = &builder->BakedPool[baked_n]; if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount) continue; if (baked->OwnerFont != font || baked->WantDestroy) continue; ImFontAtlasBakedDiscard(atlas, font, baked); } } // use unused_frames==0 to discard everything. void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames) { ImFontAtlasBuilder* builder = atlas->Builder; for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++) { ImFontBaked* baked = &builder->BakedPool[baked_n]; if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount) continue; if (baked->WantDestroy || (baked->OwnerFont->Flags & ImFontFlags_LockBakedSizes)) continue; ImFontAtlasBakedDiscard(atlas, baked->OwnerFont, baked); } } // Those functions are designed to facilitate changing the underlying structures for ImFontAtlas to store an array of ImDrawListSharedData* void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data) { IM_ASSERT(!atlas->DrawListSharedDatas.contains(data)); atlas->DrawListSharedDatas.push_back(data); } void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data) { IM_ASSERT(atlas->DrawListSharedDatas.contains(data)); atlas->DrawListSharedDatas.find_erase(data); } // Update texture identifier in all active draw lists void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex) { for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas) { // If Context 2 uses font owned by Context 1 which already called EndFrame()/Render(), we don't want to mess with draw commands for Context 1 if (shared_data->Context && !shared_data->Context->WithinFrameScope) continue; for (ImDrawList* draw_list : shared_data->DrawLists) { // Replace in command-buffer // (there is not need to replace in ImDrawListSplitter: current channel is in ImDrawList's CmdBuffer[], // other channels will be on SetCurrentChannel() which already needs to compare CmdHeader anyhow) if (draw_list->CmdBuffer.Size > 0 && draw_list->_CmdHeader.TexRef == old_tex) draw_list->_SetTexture(new_tex); // Replace in stack for (ImTextureRef& stacked_tex : draw_list->_TextureStack) if (stacked_tex == old_tex) stacked_tex = new_tex; } } } // Update texture coordinates in all draw list shared context // FIXME-NEWATLAS FIXME-OPT: Doesn't seem necessary to update for all, only one bound to current context? void ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas) { for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas) if (shared_data->FontAtlas == atlas) { shared_data->TexUvWhitePixel = atlas->TexUvWhitePixel; shared_data->TexUvLines = atlas->TexUvLines; } } // Set current texture. This is mostly called from AddTexture() + to handle a failed resize. static void ImFontAtlasBuildSetTexture(ImFontAtlas* atlas, ImTextureData* tex) { ImTextureRef old_tex_ref = atlas->TexRef; atlas->TexData = tex; atlas->TexUvScale = ImVec2(1.0f / tex->Width, 1.0f / tex->Height); atlas->TexRef._TexData = tex; //atlas->TexRef._TexID = tex->TexID; // <-- We intentionally don't do that. It would be misleading and betray promise that both fields aren't set. ImFontAtlasUpdateDrawListsTextures(atlas, old_tex_ref, atlas->TexRef); } // Create a new texture, discard previous one ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h) { ImTextureData* old_tex = atlas->TexData; ImTextureData* new_tex; // FIXME: Cannot reuse texture because old UV may have been used already (unless we remap UV). /*if (old_tex != NULL && old_tex->Status == ImTextureStatus_WantCreate) { // Reuse texture not yet used by backend. IM_ASSERT(old_tex->TexID == ImTextureID_Invalid && old_tex->BackendUserData == NULL); old_tex->DestroyPixels(); old_tex->Updates.clear(); new_tex = old_tex; old_tex = NULL; } else*/ { // Add new new_tex = IM_NEW(ImTextureData)(); new_tex->UniqueID = atlas->TexNextUniqueID++; atlas->TexList.push_back(new_tex); } if (old_tex != NULL) { // Queue old as to destroy next frame old_tex->WantDestroyNextFrame = true; IM_ASSERT(old_tex->Status == ImTextureStatus_OK || old_tex->Status == ImTextureStatus_WantCreate || old_tex->Status == ImTextureStatus_WantUpdates); } new_tex->Create(atlas->TexDesiredFormat, w, h); atlas->TexIsBuilt = false; ImFontAtlasBuildSetTexture(atlas, new_tex); return new_tex; } #if 0 #define STB_IMAGE_WRITE_IMPLEMENTATION #include "../stb/stb_image_write.h" static void ImFontAtlasDebugWriteTexToDisk(ImTextureData* tex, const char* description) { ImGuiContext& g = *GImGui; char buf[128]; ImFormatString(buf, IM_COUNTOF(buf), "[%05d] Texture #%03d - %s.png", g.FrameCount, tex->UniqueID, description); stbi_write_png(buf, tex->Width, tex->Height, tex->BytesPerPixel, tex->Pixels, tex->GetPitch()); // tex->BytesPerPixel is technically not component, but ok for the formats we support. } #endif void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h) { ImFontAtlasBuilder* builder = atlas->Builder; builder->LockDisableResize = true; ImTextureData* old_tex = atlas->TexData; ImTextureData* new_tex = ImFontAtlasTextureAdd(atlas, w, h); new_tex->UseColors = old_tex->UseColors; IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: resize+repack %dx%d => Texture #%03d: %dx%d\n", old_tex->UniqueID, old_tex->Width, old_tex->Height, new_tex->UniqueID, new_tex->Width, new_tex->Height); //for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++) // IMGUI_DEBUG_LOG_FONT("[font] - Baked %.2fpx, %d glyphs, want_destroy=%d\n", builder->BakedPool[baked_n].FontSize, builder->BakedPool[baked_n].Glyphs.Size, builder->BakedPool[baked_n].WantDestroy); //IMGUI_DEBUG_LOG_FONT("[font] - Old packed rects: %d, area %d px\n", builder->RectsPackedCount, builder->RectsPackedSurface); //ImFontAtlasDebugWriteTexToDisk(old_tex, "Before Pack"); // Repack, lose discarded rectangle, copy pixels // FIXME-NEWATLAS: This is unstable because packing order is based on RectsIndex // FIXME-NEWATLAS-V2: Repacking in batch would be beneficial to packing heuristic, and fix stability. // FIXME-NEWATLAS-TESTS: Test calling RepackTexture with size too small to fits existing rects. ImFontAtlasPackInit(atlas); ImVector old_rects; ImVector old_index = builder->RectsIndex; old_rects.swap(builder->Rects); for (ImFontAtlasRectEntry& index_entry : builder->RectsIndex) { if (index_entry.IsUsed == false) continue; ImTextureRect& old_r = old_rects[index_entry.TargetIndex]; if (old_r.w == 0 && old_r.h == 0) continue; ImFontAtlasRectId new_r_id = ImFontAtlasPackAddRect(atlas, old_r.w, old_r.h, &index_entry); if (new_r_id == ImFontAtlasRectId_Invalid) { // Undo, grow texture and try repacking again. // FIXME-NEWATLAS-TESTS: This is a very rarely exercised path! It needs to be automatically tested properly. IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: resize failed. Will grow.\n", new_tex->UniqueID); new_tex->WantDestroyNextFrame = true; builder->Rects.swap(old_rects); builder->RectsIndex = old_index; ImFontAtlasBuildSetTexture(atlas, old_tex); ImFontAtlasTextureGrow(atlas, w, h); // Recurse return; } IM_ASSERT(ImFontAtlasRectId_GetIndex(new_r_id) == builder->RectsIndex.index_from_ptr(&index_entry)); ImTextureRect* new_r = ImFontAtlasPackGetRect(atlas, new_r_id); ImFontAtlasTextureBlockCopy(old_tex, old_r.x, old_r.y, new_tex, new_r->x, new_r->y, new_r->w, new_r->h); } IM_ASSERT(old_rects.Size == builder->Rects.Size + builder->RectsDiscardedCount); builder->RectsDiscardedCount = 0; builder->RectsDiscardedSurface = 0; // Patch glyphs UV for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++) for (ImFontGlyph& glyph : builder->BakedPool[baked_n].Glyphs) if (glyph.PackId != ImFontAtlasRectId_Invalid) { ImTextureRect* r = ImFontAtlasPackGetRect(atlas, glyph.PackId); glyph.U0 = (r->x) * atlas->TexUvScale.x; glyph.V0 = (r->y) * atlas->TexUvScale.y; glyph.U1 = (r->x + r->w) * atlas->TexUvScale.x; glyph.V1 = (r->y + r->h) * atlas->TexUvScale.y; } // Update other cached UV ImFontAtlasBuildUpdateLinesTexData(atlas); ImFontAtlasBuildUpdateBasicTexData(atlas); ImFontAtlasBuildUpdateShadowTexData(atlas); builder->LockDisableResize = false; ImFontAtlasUpdateDrawListsSharedData(atlas); //ImFontAtlasDebugWriteTexToDisk(new_tex, "After Pack"); } void ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_tex_w, int old_tex_h) { //ImFontAtlasDebugWriteTexToDisk(atlas->TexData, "Before Grow"); ImFontAtlasBuilder* builder = atlas->Builder; if (old_tex_w == -1) old_tex_w = atlas->TexData->Width; if (old_tex_h == -1) old_tex_h = atlas->TexData->Height; // FIXME-NEWATLAS-V2: What to do when reaching limits exposed by backend? // FIXME-NEWATLAS-V2: Does ImFontAtlasFlags_NoPowerOfTwoHeight makes sense now? Allow 'lock' and 'compact' operations? IM_ASSERT(ImIsPowerOfTwo(old_tex_w) && ImIsPowerOfTwo(old_tex_h)); IM_ASSERT(ImIsPowerOfTwo(atlas->TexMinWidth) && ImIsPowerOfTwo(atlas->TexMaxWidth) && ImIsPowerOfTwo(atlas->TexMinHeight) && ImIsPowerOfTwo(atlas->TexMaxHeight)); // Grow texture so it follows roughly a square. // - Grow height before width, as width imply more packing nodes. // - Caller should be taking account of RectsDiscardedSurface and may not need to grow. int new_tex_w = (old_tex_h <= old_tex_w) ? old_tex_w : old_tex_w * 2; int new_tex_h = (old_tex_h <= old_tex_w) ? old_tex_h * 2 : old_tex_h; // Handle minimum size first (for pathologically large packed rects) const int pack_padding = atlas->TexGlyphPadding; new_tex_w = ImMax(new_tex_w, ImUpperPowerOfTwo(builder->MaxRectSize.x + pack_padding)); new_tex_h = ImMax(new_tex_h, ImUpperPowerOfTwo(builder->MaxRectSize.y + pack_padding)); new_tex_w = ImClamp(new_tex_w, atlas->TexMinWidth, atlas->TexMaxWidth); new_tex_h = ImClamp(new_tex_h, atlas->TexMinHeight, atlas->TexMaxHeight); if (new_tex_w == old_tex_w && new_tex_h == old_tex_h) return; ImFontAtlasTextureRepack(atlas, new_tex_w, new_tex_h); } void ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas) { // Can some baked contents be ditched? //IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlasBuildMakeSpace()\n"); ImFontAtlasBuilder* builder = atlas->Builder; ImFontAtlasBuildDiscardBakes(atlas, 2); // Currently using a heuristic for repack without growing. if (builder->RectsDiscardedSurface < builder->RectsPackedSurface * 0.20f) ImFontAtlasTextureGrow(atlas); else ImFontAtlasTextureRepack(atlas, atlas->TexData->Width, atlas->TexData->Height); } ImVec2i ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas) { int min_w = ImUpperPowerOfTwo(atlas->TexMinWidth); int min_h = ImUpperPowerOfTwo(atlas->TexMinHeight); if (atlas->Builder == NULL || atlas->TexData == NULL || atlas->TexData->Status == ImTextureStatus_WantDestroy) return ImVec2i(min_w, min_h); ImFontAtlasBuilder* builder = atlas->Builder; min_w = ImMax(ImUpperPowerOfTwo(builder->MaxRectSize.x), min_w); min_h = ImMax(ImUpperPowerOfTwo(builder->MaxRectSize.y), min_h); const int surface_approx = builder->RectsPackedSurface - builder->RectsDiscardedSurface; // Expected surface after repack const int surface_sqrt = (int)sqrtf((float)surface_approx); int new_tex_w; int new_tex_h; if (min_w >= min_h) { new_tex_w = ImMax(min_w, ImUpperPowerOfTwo(surface_sqrt)); new_tex_h = ImMax(min_h, (int)((surface_approx + new_tex_w - 1) / new_tex_w)); if ((atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) == 0) new_tex_h = ImUpperPowerOfTwo(new_tex_h); } else { new_tex_h = ImMax(min_h, ImUpperPowerOfTwo(surface_sqrt)); if ((atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) == 0) new_tex_h = ImUpperPowerOfTwo(new_tex_h); new_tex_w = ImMax(min_w, (int)((surface_approx + new_tex_h - 1) / new_tex_h)); } IM_ASSERT(ImIsPowerOfTwo(new_tex_w) && ImIsPowerOfTwo(new_tex_h)); return ImVec2i(new_tex_w, new_tex_h); } // Clear all output. Invalidates all AddCustomRect() return values! void ImFontAtlasBuildClear(ImFontAtlas* atlas) { ImVec2i new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas); ImFontAtlasBuildDestroy(atlas); ImFontAtlasTextureAdd(atlas, new_tex_size.x, new_tex_size.y); ImFontAtlasBuildInit(atlas); for (ImFontConfig& src : atlas->Sources) ImFontAtlasFontSourceInit(atlas, &src); for (ImFont* font : atlas->Fonts) for (ImFontConfig* src : font->Sources) ImFontAtlasFontSourceAddToFont(atlas, font, src); } // You should not need to call this manually! // If you think you do, let us know and we can advise about policies auto-compact. void ImFontAtlasTextureCompact(ImFontAtlas* atlas) { ImFontAtlasBuilder* builder = atlas->Builder; ImFontAtlasBuildDiscardBakes(atlas, 1); ImTextureData* old_tex = atlas->TexData; ImVec2i old_tex_size = ImVec2i(old_tex->Width, old_tex->Height); ImVec2i new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas); if (builder->RectsDiscardedCount == 0 && new_tex_size.x == old_tex_size.x && new_tex_size.y == old_tex_size.y) return; ImFontAtlasTextureRepack(atlas, new_tex_size.x, new_tex_size.y); } // Calculates the signed distance from sample_pos to the nearest point on the rectangle defined by rect_min->rect_max static float DistanceFromRectangle(const ImVec2& sample_pos, const ImVec2& rect_min, const ImVec2& rect_max) { ImVec2 rect_centre = (rect_min + rect_max) * 0.5f; ImVec2 rect_half_size = (rect_max - rect_min) * 0.5f; ImVec2 local_sample_pos = sample_pos - rect_centre; ImVec2 axis_dist = ImVec2(ImFabs(local_sample_pos.x), ImFabs(local_sample_pos.y)) - rect_half_size; float out_dist = ImLength(ImVec2(ImMax(axis_dist.x, 0.0f), ImMax(axis_dist.y, 0.0f)), 0.00001f); float in_dist = ImMin(ImMax(axis_dist.x, axis_dist.y), 0.0f); return out_dist + in_dist; } // Calculates the signed distance from sample_pos to the point given static float DistanceFromPoint(const ImVec2& sample_pos, const ImVec2& point) { return ImLength(sample_pos - point, 0.0f); } // Perform a single Gaussian blur pass with a fixed kernel size and sigma static void GaussianBlurPass(float* src, float* dest, int size, bool horizontal) { // See http://dev.theomader.com/gaussian-kernel-calculator/ const float coefficients[] = { 0.0f, 0.0f, 0.000003f, 0.000229f, 0.005977f, 0.060598f, 0.24173f, 0.382925f, 0.24173f, 0.060598f, 0.005977f, 0.000229f, 0.000003f, 0.0f, 0.0f }; const int kernel_size = IM_ARRAYSIZE(coefficients); const int sample_step = horizontal ? 1 : size; float* read_ptr = src; float* write_ptr = dest; for (int y = 0; y < size; y++) for (int x = 0; x < size; x++) { float result = 0.0f; int current_offset = (horizontal ? x : y) - ((kernel_size - 1) >> 1); float* sample_ptr = read_ptr - (((kernel_size - 1) >> 1) * sample_step); for (int j = 0; j < kernel_size; j++) { if (current_offset >= 0 && current_offset < size) result += (*sample_ptr) * coefficients[j]; current_offset++; sample_ptr += sample_step; } read_ptr++; *(write_ptr++) = result; } } // Perform an in-place Gaussian blur of a square array of floats with a fixed kernel size and sigma // Uses a stack allocation for the temporary data so potentially dangerous with large size values static void GaussianBlur(float* data, int size) { // Do two passes, one from data into temp and then the second back to data again float* temp = (float*)alloca(size * size * sizeof(float)); GaussianBlurPass(data, temp, size, true); GaussianBlurPass(temp, data, size, false); } // Generate the actual pixel data for rounded corners in the atlas static void ImFontAtlasBuildUpdateShadowTexData(ImFontAtlas* atlas) { // The actual size we want to reserve, including padding const ImFontAtlasShadowTexConfig* shadow_cfg = &atlas->ShadowTexConfig; const unsigned int effective_size = shadow_cfg->CalcRectTexSize() + shadow_cfg->GetRectTexPadding(); const int corner_size = shadow_cfg->TexCornerSize; const int edge_size = shadow_cfg->TexEdgeSize; // Because of the blur, we have to generate the full 3x3 texture here, and then we chop that down to just the 2x2 section we need later. // 'size' correspond to the our 3x3 size, whereas 'shadow_tex_size' correspond to our 2x2 version where duplicate mirrored corners are not stored. // The rectangular shadow texture ImFontAtlasRect r; bool add_and_draw = (atlas->GetCustomRect(atlas->ShadowRectIds[0], &r) == false); if (add_and_draw) { atlas->ShadowRectIds[0] = atlas->AddCustomRect(effective_size, effective_size, &r); IM_ASSERT(atlas->ShadowRectIds[0] != ImFontAtlasRectId_Invalid); const int size = shadow_cfg->TexCornerSize + shadow_cfg->TexEdgeSize + shadow_cfg->TexCornerSize; // The bounds of the rectangle we are generating the shadow from const ImVec2 shadow_rect_min((float)corner_size, (float)corner_size); const ImVec2 shadow_rect_max((float)(corner_size + edge_size), (float)(corner_size + edge_size)); // Remove the padding we added const int padding = shadow_cfg->GetRectTexPadding(); r.x += (unsigned short)padding; r.y += (unsigned short)padding; r.w -= (unsigned short)padding * 2; r.h -= (unsigned short)padding * 2; // Generate distance field // We draw the actual texture content by evaluating the distance field for the inner rectangle float* tex_data = (float*)alloca(size * size * sizeof(float)); for (int y = 0; y < size; y++) for (int x = 0; x < size; x++) { float dist = DistanceFromRectangle(ImVec2((float)x, (float)y), shadow_rect_min, shadow_rect_max); float alpha = 1.0f - ImMin(ImMax(dist + shadow_cfg->TexDistanceFieldOffset, 0.0f) / ImMax(shadow_cfg->TexCornerSize + shadow_cfg->TexDistanceFieldOffset, 0.001f), 1.0f); alpha = ImPow(alpha, shadow_cfg->TexFalloffPower); // Apply power curve to give a nicer falloff tex_data[x + (y * size)] = alpha; } // Blur if (shadow_cfg->TexBlur) GaussianBlur(tex_data, size); // Copy to texture, truncating to the actual required texture size (the bottom/right of the source data is chopped off, as we don't need it - see below). The truncated size is essentially the top 2x2 of our data, plus a little bit of padding for sampling. ImTextureData* tex = atlas->TexData; const int shadow_tex_size = shadow_cfg->CalcRectTexSize(); for (int y = 0; y < shadow_tex_size; y++) for (int x = 0; x < shadow_tex_size; x++) { const float alpha_f = tex_data[x + (y * size)]; const unsigned char alpha_8 = (unsigned char)(0xFF * alpha_f); // FIXME-SHADOWS: May be optimized. switch (atlas->TexData->Format) { case ImTextureFormat_Alpha8: { ImU8* out_p = (ImU8*)(void*)tex->GetPixelsAt(r.x + x, r.y + y); *out_p = alpha_8; break; } case ImTextureFormat_RGBA32: { ImU32* out_p = (ImU32*)(void*)tex->GetPixelsAt(r.x + x, r.y + y); *out_p = IM_COL32(255, 255, 255, alpha_8);; break; } } } } // Refresh UV coordinates // Generate UVs for each of the nine sections, which are arranged in a 3x3 grid starting from 0 in the top-left and going across then down if (atlas->GetCustomRect(atlas->ShadowRectIds[0], &r)) { // Remove the padding we added const int padding = shadow_cfg->GetRectTexPadding(); r.x += (unsigned short)padding; r.y += (unsigned short)padding; r.w -= (unsigned short)padding * 2; r.h -= (unsigned short)padding * 2; for (int i = 0; i < 9; i++) { // The third row/column of the 3x3 grid are generated by flipping the appropriate chunks of the upper 2x2 grid. bool flip_h = false; // Do we need to flip the UVs horizontally? bool flip_v = false; // Do we need to flip the UVs vertically? ImFontAtlasRect sub_rect = r; switch (i % 3) { case 0: sub_rect.w = (unsigned short)corner_size; break; case 1: sub_rect.x += (unsigned short)corner_size; sub_rect.w = (unsigned short)edge_size; break; case 2: sub_rect.w = (unsigned short)corner_size; flip_h = true; break; } switch (i / 3) { case 0: sub_rect.h = (unsigned short)corner_size; break; case 1: sub_rect.y += (unsigned short)corner_size; sub_rect.h = (unsigned short)edge_size; break; case 2: sub_rect.h = (unsigned short)corner_size; flip_v = true; break; } // FIXME-SHADOWS: caching UV !! ImVec2 uv0 = ImVec2((float)(sub_rect.x), (float)(sub_rect.y)) * atlas->TexUvScale; ImVec2 uv1 = ImVec2((float)(sub_rect.x + sub_rect.w), (float)(sub_rect.y + sub_rect.h)) * atlas->TexUvScale; atlas->ShadowRectUvs[i] = ImVec4(flip_h ? uv1.x : uv0.x, flip_v ? uv1.y : uv0.y, flip_h ? uv0.x : uv1.x, flip_v ? uv0.y : uv1.y); } } // The convex shape shadow texture add_and_draw = (atlas->GetCustomRect(atlas->ShadowRectIds[1], &r) == false); if (add_and_draw) { atlas->ShadowRectIds[1] = atlas->AddCustomRect(shadow_cfg->CalcConvexTexWidth() + shadow_cfg->GetConvexTexPadding(), shadow_cfg->CalcConvexTexHeight() + shadow_cfg->GetConvexTexPadding(), &r); IM_ASSERT(atlas->ShadowRectIds[1] != ImFontAtlasRectId_Invalid); const int size = shadow_cfg->TexCornerSize * 2; const int padding = shadow_cfg->GetConvexTexPadding(); // Generate distance field // We draw the actual texture content by evaluating the distance field for the distance from a center point ImVec2 center_point(size * 0.5f, size * 0.5f); float* tex_data = (float*)alloca(size * size * sizeof(float)); for (int y = 0; y < size; y++) for (int x = 0; x < size; x++) { float dist = DistanceFromPoint(ImVec2((float)x, (float)y), center_point); float alpha = 1.0f - ImMin(ImMax((float)dist + shadow_cfg->TexDistanceFieldOffset, 0.0f) / ImMax((float)shadow_cfg->TexCornerSize + shadow_cfg->TexDistanceFieldOffset, 0.001f), 1.0f); alpha = ImPow(alpha, shadow_cfg->TexFalloffPower); // Apply power curve to give a nicer falloff tex_data[x + (y * size)] = alpha; } // Blur if (shadow_cfg->TexBlur) GaussianBlur(tex_data, size); // Copy to texture, truncating to the actual required texture size (the bottom/right of the source data is chopped off, as we don't need it - see below) // We push the data down and right by the amount we padded the top of the texture (see CalcConvexTexWidth/CalcConvexTexHeight) for details const int padded_size = (int)(shadow_cfg->TexCornerSize / ImCos(IM_PI * 0.25f)); const int src_x_offset = padding + (padded_size - shadow_cfg->TexCornerSize); const int src_y_offset = padding + (padded_size - shadow_cfg->TexCornerSize); const int tex_width = shadow_cfg->CalcConvexTexWidth(); const int tex_height = shadow_cfg->CalcConvexTexHeight(); ImTextureData* tex = atlas->TexData; for (int y = 0; y < tex_height; y++) for (int x = 0; x < tex_width; x++) { const int src_x = ImClamp(x - src_x_offset, 0, size - 1); const int src_y = ImClamp(y - src_y_offset, 0, size - 1); const float alpha_f = tex_data[src_x + (src_y * size)]; const unsigned char alpha_8 = (unsigned char)(0xFF * alpha_f); // FIXME-SHADOWS: May be optimized. switch (atlas->TexData->Format) { case ImTextureFormat_Alpha8: { ImU8* out_p = (ImU8*)(void*)tex->GetPixelsAt(r.x + x, r.y + y); *out_p = alpha_8; break; } case ImTextureFormat_RGBA32: { ImU32* out_p = (ImU32*)(void*)tex->GetPixelsAt(r.x + x, r.y + y); *out_p = IM_COL32(255, 255, 255, alpha_8);; break; } } } } // Refresh UV coordinates if (atlas->GetCustomRect(atlas->ShadowRectIds[1], &r)) { // Remove the padding we added const int padding = shadow_cfg->GetConvexTexPadding(); const int tex_width = shadow_cfg->CalcConvexTexWidth(); const int tex_height = shadow_cfg->CalcConvexTexHeight(); r.x += (unsigned short)padding; r.y += (unsigned short)padding; r.w = (unsigned short)(tex_width - (padding * 2)); r.h = (unsigned short)(tex_height - (padding * 2)); ImVec2 uv0 = ImVec2((float)(r.x), (float)(r.y)) * atlas->TexUvScale; ImVec2 uv1 = ImVec2((float)(r.x + r.w), (float)(r.y + r.h)) * atlas->TexUvScale; atlas->ShadowRectUvs[9] = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y); } } // Start packing over current empty texture void ImFontAtlasBuildInit(ImFontAtlas* atlas) { // Select Backend // - Note that we do not reassign to atlas->FontLoader, since it is likely to point to static data which // may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are // using a hot-reloading scheme that messes up static data, store your own instance of FontLoader somewhere // and point to it instead of pointing directly to return value of the GetFontLoaderXXX functions. if (atlas->FontLoader == NULL) { #ifdef IMGUI_ENABLE_FREETYPE atlas->SetFontLoader(ImGuiFreeType::GetFontLoader()); #elif defined(IMGUI_ENABLE_STB_TRUETYPE) atlas->SetFontLoader(ImFontAtlasGetFontLoaderForStbTruetype()); #else IM_ASSERT(0); // Invalid Build function #endif } // Create initial texture size if (atlas->TexData == NULL || atlas->TexData->Pixels == NULL) ImFontAtlasTextureAdd(atlas, ImUpperPowerOfTwo(atlas->TexMinWidth), ImUpperPowerOfTwo(atlas->TexMinHeight)); atlas->Builder = IM_NEW(ImFontAtlasBuilder)(); if (atlas->FontLoader->LoaderInit) atlas->FontLoader->LoaderInit(atlas); ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas); ImFontAtlasPackInit(atlas); // Add required texture data ImFontAtlasBuildUpdateLinesTexData(atlas); ImFontAtlasBuildUpdateBasicTexData(atlas); ImFontAtlasBuildUpdateShadowTexData(atlas); // Register fonts ImFontAtlasBuildUpdatePointers(atlas); // Update UV coordinates etc. stored in bound ImDrawListSharedData instance ImFontAtlasUpdateDrawListsSharedData(atlas); //atlas->TexIsBuilt = true; // Lazily initialize char/text classifier // FIXME: This could be practically anywhere, and should eventually be parameters to CalcTextSize/word-wrapping code, but there's no obvious spot now. ImTextInitClassifiers(); } // Destroy builder and all cached glyphs. Do not destroy actual fonts. void ImFontAtlasBuildDestroy(ImFontAtlas* atlas) { for (ImFont* font : atlas->Fonts) ImFontAtlasFontDestroyOutput(atlas, font); if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderShutdown) { atlas->FontLoader->LoaderShutdown(atlas); IM_ASSERT(atlas->FontLoaderData == NULL); } IM_DELETE(atlas->Builder); atlas->Builder = NULL; } void ImFontAtlasPackInit(ImFontAtlas * atlas) { ImTextureData* tex = atlas->TexData; ImFontAtlasBuilder* builder = atlas->Builder; // In theory we could decide to reduce the number of nodes, e.g. halve them, and waste a little texture space, but it doesn't seem worth it. const int pack_node_count = tex->Width / 2; builder->PackNodes.resize(pack_node_count); IM_STATIC_ASSERT(sizeof(stbrp_context) <= sizeof(stbrp_context_opaque)); stbrp_init_target((stbrp_context*)(void*)&builder->PackContext, tex->Width, tex->Height, builder->PackNodes.Data, builder->PackNodes.Size); builder->RectsPackedSurface = builder->RectsPackedCount = 0; builder->MaxRectSize = ImVec2i(0, 0); builder->MaxRectBounds = ImVec2i(0, 0); } // This is essentially a free-list pattern, it may be nice to wrap it into a dedicated type. static ImFontAtlasRectId ImFontAtlasPackAllocRectEntry(ImFontAtlas* atlas, int rect_idx) { ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder; int index_idx; ImFontAtlasRectEntry* index_entry; if (builder->RectsIndexFreeListStart < 0) { builder->RectsIndex.resize(builder->RectsIndex.Size + 1); index_idx = builder->RectsIndex.Size - 1; index_entry = &builder->RectsIndex[index_idx]; memset(index_entry, 0, sizeof(*index_entry)); } else { index_idx = builder->RectsIndexFreeListStart; index_entry = &builder->RectsIndex[index_idx]; IM_ASSERT(index_entry->IsUsed == false && index_entry->Generation > 0); // Generation is incremented during DiscardRect builder->RectsIndexFreeListStart = index_entry->TargetIndex; } index_entry->TargetIndex = rect_idx; index_entry->IsUsed = 1; return ImFontAtlasRectId_Make(index_idx, index_entry->Generation); } // Overwrite existing entry static ImFontAtlasRectId ImFontAtlasPackReuseRectEntry(ImFontAtlas* atlas, ImFontAtlasRectEntry* index_entry) { IM_ASSERT(index_entry->IsUsed); index_entry->TargetIndex = atlas->Builder->Rects.Size - 1; int index_idx = atlas->Builder->RectsIndex.index_from_ptr(index_entry); return ImFontAtlasRectId_Make(index_idx, index_entry->Generation); } // This is expected to be called in batches and followed by a repack void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id) { IM_ASSERT(id != ImFontAtlasRectId_Invalid); ImTextureRect* rect = ImFontAtlasPackGetRect(atlas, id); if (rect == NULL) return; ImFontAtlasBuilder* builder = atlas->Builder; int index_idx = ImFontAtlasRectId_GetIndex(id); ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx]; IM_ASSERT(index_entry->IsUsed && index_entry->TargetIndex >= 0); index_entry->IsUsed = false; index_entry->TargetIndex = builder->RectsIndexFreeListStart; index_entry->Generation++; if (index_entry->Generation == 0) index_entry->Generation++; // Keep non-zero on overflow const int pack_padding = atlas->TexGlyphPadding; builder->RectsIndexFreeListStart = index_idx; builder->RectsDiscardedCount++; builder->RectsDiscardedSurface += (rect->w + pack_padding) * (rect->h + pack_padding); rect->w = rect->h = 0; // Clear rectangle so it won't be packed again } // Important: Calling this may recreate a new texture and therefore change atlas->TexData // FIXME-NEWFONTS: Expose other glyph padding settings for custom alteration (e.g. drop shadows). See #7962 ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry) { IM_ASSERT(w > 0 && w <= 0xFFFF); IM_ASSERT(h > 0 && h <= 0xFFFF); ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder; const int pack_padding = atlas->TexGlyphPadding; builder->MaxRectSize.x = ImMax(builder->MaxRectSize.x, w); builder->MaxRectSize.y = ImMax(builder->MaxRectSize.y, h); // Pack ImTextureRect r = { 0, 0, (unsigned short)w, (unsigned short)h }; for (int attempts_remaining = 3; attempts_remaining >= 0; attempts_remaining--) { // Try packing stbrp_rect pack_r = {}; pack_r.w = w + pack_padding; pack_r.h = h + pack_padding; stbrp_pack_rects((stbrp_context*)(void*)&builder->PackContext, &pack_r, 1); r.x = (unsigned short)pack_r.x; r.y = (unsigned short)pack_r.y; if (pack_r.was_packed) break; // If we ran out of attempts, return fallback if (attempts_remaining == 0 || builder->LockDisableResize) { IMGUI_DEBUG_LOG_FONT("[font] Failed packing %dx%d rectangle. Returning fallback.\n", w, h); return ImFontAtlasRectId_Invalid; } // Resize or repack atlas! (this should be a rare event) ImFontAtlasTextureMakeSpace(atlas); } builder->MaxRectBounds.x = ImMax(builder->MaxRectBounds.x, r.x + r.w + pack_padding); builder->MaxRectBounds.y = ImMax(builder->MaxRectBounds.y, r.y + r.h + pack_padding); builder->RectsPackedCount++; builder->RectsPackedSurface += (w + pack_padding) * (h + pack_padding); builder->Rects.push_back(r); if (overwrite_entry != NULL) return ImFontAtlasPackReuseRectEntry(atlas, overwrite_entry); // Write into an existing entry instead of adding one (used during repack) else return ImFontAtlasPackAllocRectEntry(atlas, builder->Rects.Size - 1); } // Generally for non-user facing functions: assert on invalid ID. ImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id) { IM_ASSERT(id != ImFontAtlasRectId_Invalid); int index_idx = ImFontAtlasRectId_GetIndex(id); ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder; ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx]; IM_ASSERT(index_entry->Generation == ImFontAtlasRectId_GetGeneration(id)); IM_ASSERT(index_entry->IsUsed); return &builder->Rects[index_entry->TargetIndex]; } // For user-facing functions: return NULL on invalid ID. // Important: return pointer is valid until next call to AddRect(), e.g. FindGlyph(), CalcTextSize() can all potentially invalidate previous pointers. ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id) { if (id == ImFontAtlasRectId_Invalid) return NULL; int index_idx = ImFontAtlasRectId_GetIndex(id); if (atlas->Builder == NULL) ImFontAtlasBuildInit(atlas); ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder; IM_MSVC_WARNING_SUPPRESS(28182); // Static Analysis false positive "warning C28182: Dereferencing NULL pointer 'builder'" if (index_idx >= builder->RectsIndex.Size) return NULL; ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx]; if (index_entry->Generation != ImFontAtlasRectId_GetGeneration(id) || !index_entry->IsUsed) return NULL; return &builder->Rects[index_entry->TargetIndex]; } // Important! This assume by ImFontConfig::GlyphExcludeRanges[] is a SMALL ARRAY (e.g. <10 entries) // Use "Input Glyphs Overlap Detection Tool" to display a list of glyphs provided by multiple sources in order to set this array up. static bool ImFontAtlasBuildAcceptCodepointForSource(ImFontConfig* src, ImWchar codepoint) { if (const ImWchar* exclude_list = src->GlyphExcludeRanges) for (; exclude_list[0] != 0; exclude_list += 2) if (codepoint >= exclude_list[0] && codepoint <= exclude_list[1]) return false; return true; } static void ImFontBaked_BuildGrowIndex(ImFontBaked* baked, int new_size) { IM_ASSERT(baked->IndexAdvanceX.Size == baked->IndexLookup.Size); if (new_size <= baked->IndexLookup.Size) return; baked->IndexAdvanceX.resize(new_size, -1.0f); baked->IndexLookup.resize(new_size, IM_FONTGLYPH_INDEX_UNUSED); } static void ImFontAtlas_FontHookRemapCodepoint(ImFontAtlas* atlas, ImFont* font, ImWchar* c) { IM_UNUSED(atlas); if (font->RemapPairs.Data.Size != 0) *c = (ImWchar)font->RemapPairs.GetInt((ImGuiID)*c, (int)*c); } static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint, float* only_load_advance_x) { ImFont* font = baked->OwnerFont; ImFontAtlas* atlas = font->OwnerAtlas; if (atlas->Locked || (font->Flags & ImFontFlags_NoLoadGlyphs)) { // Lazily load fallback glyph if (baked->FallbackGlyphIndex == -1 && baked->LoadNoFallback == 0) ImFontAtlasBuildSetupFontBakedFallback(baked); return NULL; } // User remapping hooks ImWchar src_codepoint = codepoint; ImFontAtlas_FontHookRemapCodepoint(atlas, font, &codepoint); //char utf8_buf[5]; //IMGUI_DEBUG_LOG("[font] BuildLoadGlyph U+%04X (%s)\n", (unsigned int)codepoint, ImTextCharToUtf8(utf8_buf, (unsigned int)codepoint)); // Special hook // FIXME-NEWATLAS: it would be nicer if this used a more standardized way of hooking if (codepoint == font->EllipsisChar && font->EllipsisAutoBake) if (ImFontGlyph* glyph = ImFontAtlasBuildSetupFontBakedEllipsis(atlas, baked)) return glyph; // Call backend char* loader_user_data_p = (char*)baked->FontLoaderDatas; int src_n = 0; for (ImFontConfig* src : font->Sources) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; if (!src->GlyphExcludeRanges || ImFontAtlasBuildAcceptCodepointForSource(src, codepoint)) { if (only_load_advance_x == NULL) { ImFontGlyph glyph_buf; if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf, NULL)) { // FIXME: Add hooks for e.g. #7962 glyph_buf.Codepoint = src_codepoint; glyph_buf.SourceIdx = src_n; return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf); } } else { // Special mode but only loading glyphs metrics. Will rasterize and pack later. if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, NULL, only_load_advance_x)) { ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, *only_load_advance_x); return NULL; } } } loader_user_data_p += loader->FontBakedSrcLoaderDataSize; src_n++; } // Lazily load fallback glyph if (baked->LoadNoFallback) return NULL; if (baked->FallbackGlyphIndex == -1) ImFontAtlasBuildSetupFontBakedFallback(baked); // Mark index as not found, so we don't attempt the search twice ImFontBaked_BuildGrowIndex(baked, codepoint + 1); baked->IndexAdvanceX[codepoint] = baked->FallbackAdvanceX; baked->IndexLookup[codepoint] = IM_FONTGLYPH_INDEX_NOT_FOUND; return NULL; } static float ImFontBaked_BuildLoadGlyphAdvanceX(ImFontBaked* baked, ImWchar codepoint) { if (baked->Size >= IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE || baked->LoadNoRenderOnLayout) { // First load AdvanceX value used by CalcTextSize() API then load the rest when loaded by drawing API. float only_advance_x = 0.0f; ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, &only_advance_x); return glyph ? glyph->AdvanceX : only_advance_x; } else { ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, NULL); return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX; } } // The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b IM_MSVC_RUNTIME_CHECKS_OFF static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* baked, unsigned int codepoint) { return ImFontBaked_BuildLoadGlyphAdvanceX(baked, (ImWchar)codepoint); } IM_MSVC_RUNTIME_CHECKS_RESTORE #ifndef IMGUI_DISABLE_DEBUG_TOOLS void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas) { // [DEBUG] Log texture update requests ImGuiContext& g = *GImGui; IM_UNUSED(g); for (ImTextureData* tex : atlas->TexList) { if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) IM_ASSERT(tex->Updates.Size == 0); if (tex->Status == ImTextureStatus_WantCreate) IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: create %dx%d\n", tex->UniqueID, tex->Width, tex->Height); else if (tex->Status == ImTextureStatus_WantDestroy) IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, ImGui::DebugTextureIDToU64(tex->TexID), tex->BackendUserData); else if (tex->Status == ImTextureStatus_WantUpdates) { IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, ImGui::DebugTextureIDToU64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData); for (const ImTextureRect& r : tex->Updates) { IM_UNUSED(r); IM_ASSERT(r.x >= 0 && r.y >= 0); IM_ASSERT(r.x + r.w <= tex->Width && r.y + r.h <= tex->Height); // In theory should subtract PackPadding but it's currently part of atlas and mid-frame change would wreck assert. //IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, ImGui::DebugTextureIDToU64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData); } } } } #endif //------------------------------------------------------------------------- // [SECTION] ImFontAtlas: backend for stb_truetype //------------------------------------------------------------------------- // (imstb_truetype.h in included near the top of this file, when IMGUI_ENABLE_STB_TRUETYPE is set) //------------------------------------------------------------------------- #ifdef IMGUI_ENABLE_STB_TRUETYPE // One for each ConfigData struct ImGui_ImplStbTrueType_FontSrcData { stbtt_fontinfo FontInfo; float ScaleFactor; }; static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) { IM_UNUSED(atlas); ImGui_ImplStbTrueType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplStbTrueType_FontSrcData); IM_ASSERT(src->FontLoaderData == NULL); // Initialize helper structure for font loading and verify that the TTF/OTF data is correct const int font_offset = stbtt_GetFontOffsetForIndex((const unsigned char*)src->FontData, src->FontNo); if (font_offset < 0) { IM_DELETE(bd_font_data); IM_ASSERT_USER_ERROR(0, "stbtt_GetFontOffsetForIndex(): FontData is incorrect, or FontNo cannot be found."); return false; } if (!stbtt_InitFont(&bd_font_data->FontInfo, (const unsigned char*)src->FontData, font_offset)) { IM_DELETE(bd_font_data); IM_ASSERT_USER_ERROR(0, "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize."); return false; } src->FontLoaderData = bd_font_data; const float ref_size = src->DstFont->Sources[0]->SizePixels; if (src->MergeMode && src->SizePixels == 0.0f) src->SizePixels = ref_size; bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f); if (src->MergeMode && src->SizePixels != 0.0f && ref_size != 0.0f) bd_font_data->ScaleFactor *= src->SizePixels / ref_size; // FIXME-NEWATLAS: Should tidy up that a bit bd_font_data->ScaleFactor *= src->ExtraSizeScale; return true; } static void ImGui_ImplStbTrueType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) { IM_UNUSED(atlas); ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; IM_DELETE(bd_font_data); src->FontLoaderData = NULL; } static bool ImGui_ImplStbTrueType_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint) { IM_UNUSED(atlas); ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; IM_ASSERT(bd_font_data != NULL); int glyph_index = stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint); return glyph_index != 0; } static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*) { IM_UNUSED(atlas); ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; if (src->MergeMode == false) { // FIXME-NEWFONTS: reevaluate how to use sizing metrics // FIXME-NEWFONTS: make use of line gap value const float scale_for_layout = bd_font_data->ScaleFactor * baked->Size / src->ExtraSizeScale; int unscaled_ascent, unscaled_descent, unscaled_line_gap; stbtt_GetFontVMetrics(&bd_font_data->FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); baked->Ascent = ImCeil(unscaled_ascent * scale_for_layout); baked->Descent = ImFloor(unscaled_descent * scale_for_layout); } return true; } static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x) { // Search for first font which has the glyph ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; IM_ASSERT(bd_font_data); int glyph_index = stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint); if (glyph_index == 0) return false; // Fonts unit to pixels int oversample_h, oversample_v; ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v); const float scale_for_layout = bd_font_data->ScaleFactor * baked->Size; const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; const float scale_for_raster_x = bd_font_data->ScaleFactor * baked->Size * rasterizer_density * oversample_h; const float scale_for_raster_y = bd_font_data->ScaleFactor * baked->Size * rasterizer_density * oversample_v; // Obtain size and advance int x0, y0, x1, y1; int advance, lsb; stbtt_GetGlyphBitmapBoxSubpixel(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, 0, 0, &x0, &y0, &x1, &y1); stbtt_GetGlyphHMetrics(&bd_font_data->FontInfo, glyph_index, &advance, &lsb); // Load metrics only mode if (out_advance_x != NULL) { IM_ASSERT(out_glyph == NULL); *out_advance_x = advance * scale_for_layout; return true; } // Prepare glyph out_glyph->Codepoint = codepoint; out_glyph->AdvanceX = advance * scale_for_layout; // Pack and retrieve position inside texture atlas // (generally based on stbtt_PackFontRangesRenderIntoRects) const bool is_visible = (x0 != x1 && y0 != y1); if (is_visible) { const int w = (x1 - x0 + oversample_h - 1); const int h = (y1 - y0 + oversample_v - 1); ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h); if (pack_id == ImFontAtlasRectId_Invalid) { // Pathological out of memory case (TexMaxWidth/TexMaxHeight set too small?) IM_ASSERT(pack_id != ImFontAtlasRectId_Invalid && "Out of texture memory."); return false; } ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id); // Render stbtt_GetGlyphBitmapBox(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, &x0, &y0, &x1, &y1); ImFontAtlasBuilder* builder = atlas->Builder; builder->TempBuffer.resize(w * h * 1); unsigned char* bitmap_pixels = builder->TempBuffer.Data; memset(bitmap_pixels, 0, w * h * 1); // Render with oversampling // (those functions conveniently assert if pixels are not cleared, which is another safety layer) float sub_x, sub_y; stbtt_MakeGlyphBitmapSubpixelPrefilter(&bd_font_data->FontInfo, bitmap_pixels, w, h, w, scale_for_raster_x, scale_for_raster_y, 0, 0, oversample_h, oversample_v, &sub_x, &sub_y, glyph_index); const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset. float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f); font_off_x += sub_x; font_off_y += sub_y + IM_ROUND(baked->Ascent); float recip_h = 1.0f / (oversample_h * rasterizer_density); float recip_v = 1.0f / (oversample_v * rasterizer_density); // Register glyph // r->x r->y are coordinates inside texture (in pixels) // glyph.X0, glyph.Y0 are drawing coordinates from base text position, and accounting for oversampling. out_glyph->X0 = x0 * recip_h + font_off_x; out_glyph->Y0 = y0 * recip_v + font_off_y; out_glyph->X1 = (x0 + (int)r->w) * recip_h + font_off_x; out_glyph->Y1 = (y0 + (int)r->h) * recip_v + font_off_y; out_glyph->Visible = true; out_glyph->PackId = pack_id; ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, bitmap_pixels, ImTextureFormat_Alpha8, w); } return true; } const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype() { static ImFontLoader loader; loader.Name = "stb_truetype"; loader.FontSrcInit = ImGui_ImplStbTrueType_FontSrcInit; loader.FontSrcDestroy = ImGui_ImplStbTrueType_FontSrcDestroy; loader.FontSrcContainsGlyph = ImGui_ImplStbTrueType_FontSrcContainsGlyph; loader.FontBakedInit = ImGui_ImplStbTrueType_FontBakedInit; loader.FontBakedDestroy = NULL; loader.FontBakedLoadGlyph = ImGui_ImplStbTrueType_FontBakedLoadGlyph; return &loader; } #endif // IMGUI_ENABLE_STB_TRUETYPE //------------------------------------------------------------------------- // [SECTION] ImFontAtlas: glyph ranges helpers //------------------------------------------------------------------------- // - GetGlyphRangesDefault() // Obsolete functions since 1.92: // - GetGlyphRangesGreek() // - GetGlyphRangesKorean() // - GetGlyphRangesChineseFull() // - GetGlyphRangesChineseSimplifiedCommon() // - GetGlyphRangesJapanese() // - GetGlyphRangesCyrillic() // - GetGlyphRangesThai() // - GetGlyphRangesVietnamese() //----------------------------------------------------------------------------- // Retrieve list of range (2 int per range, values are inclusive) const ImWchar* ImFontAtlas::GetGlyphRangesDefault() { static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0, }; return &ranges[0]; } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS const ImWchar* ImFontAtlas::GetGlyphRangesGreek() { static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x0370, 0x03FF, // Greek and Coptic 0, }; return &ranges[0]; } const ImWchar* ImFontAtlas::GetGlyphRangesKorean() { static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3131, 0x3163, // Korean alphabets 0xAC00, 0xD7A3, // Korean characters 0xFFFD, 0xFFFD, // Invalid 0, }; return &ranges[0]; } const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() { static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x2000, 0x206F, // General Punctuation 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters 0xFFFD, 0xFFFD, // Invalid 0x4e00, 0x9FAF, // CJK Ideograms 0, }; return &ranges[0]; } static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* accumulative_offsets, int accumulative_offsets_count, ImWchar* out_ranges) { for (int n = 0; n < accumulative_offsets_count; n++, out_ranges += 2) { out_ranges[0] = out_ranges[1] = (ImWchar)(base_codepoint + accumulative_offsets[n]); base_codepoint += accumulative_offsets[n]; } out_ranges[0] = 0; } 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 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[] = { 0,1,2,4,1,1,1,1,2,1,3,2,1,2,2,1,1,1,1,1,5,2,1,2,3,3,3,2,2,4,1,1,1,2,1,5,2,3,1,2,1,2,1,1,2,1,1,2,2,1,4,1,1,1,1,5,10,1,2,19,2,1,2,1,2,1,2,1,2, 1,5,1,6,3,2,1,2,2,1,1,1,4,8,5,1,1,4,1,1,3,1,2,1,5,1,2,1,1,1,10,1,1,5,2,4,6,1,4,2,2,2,12,2,1,1,6,1,1,1,4,1,1,4,6,5,1,4,2,2,4,10,7,1,1,4,2,4, 2,1,4,3,6,10,12,5,7,2,14,2,9,1,1,6,7,10,4,7,13,1,5,4,8,4,1,1,2,28,5,6,1,1,5,2,5,20,2,2,9,8,11,2,9,17,1,8,6,8,27,4,6,9,20,11,27,6,68,2,2,1,1, 1,2,1,2,2,7,6,11,3,3,1,1,3,1,2,1,1,1,1,1,3,1,1,8,3,4,1,5,7,2,1,4,4,8,4,2,1,2,1,1,4,5,6,3,6,2,12,3,1,3,9,2,4,3,4,1,5,3,3,1,3,7,1,5,1,1,1,1,2, 3,4,5,2,3,2,6,1,1,2,1,7,1,7,3,4,5,15,2,2,1,5,3,22,19,2,1,1,1,1,2,5,1,1,1,6,1,1,12,8,2,9,18,22,4,1,1,5,1,16,1,2,7,10,15,1,1,6,2,4,1,2,4,1,6, 1,1,3,2,4,1,6,4,5,1,2,1,1,2,1,10,3,1,3,2,1,9,3,2,5,7,2,19,4,3,6,1,1,1,1,1,4,3,2,1,1,1,2,5,3,1,1,1,2,2,1,1,2,1,1,2,1,3,1,1,1,3,7,1,4,1,1,2,1, 1,2,1,2,4,4,3,8,1,1,1,2,1,3,5,1,3,1,3,4,6,2,2,14,4,6,6,11,9,1,15,3,1,28,5,2,5,5,3,1,3,4,5,4,6,14,3,2,3,5,21,2,7,20,10,1,2,19,2,4,28,28,2,3, 2,1,14,4,1,26,28,42,12,40,3,52,79,5,14,17,3,2,2,11,3,4,6,3,1,8,2,23,4,5,8,10,4,2,7,3,5,1,1,6,3,1,2,2,2,5,28,1,1,7,7,20,5,3,29,3,17,26,1,8,4, 27,3,6,11,23,5,3,4,6,13,24,16,6,5,10,25,35,7,3,2,3,3,14,3,6,2,6,1,4,2,3,8,2,1,1,3,3,3,4,1,1,13,2,2,4,5,2,1,14,14,1,2,2,1,4,5,2,3,1,14,3,12, 3,17,2,16,5,1,2,1,8,9,3,19,4,2,2,4,17,25,21,20,28,75,1,10,29,103,4,1,2,1,1,4,2,4,1,2,3,24,2,2,2,1,1,2,1,3,8,1,1,1,2,1,1,3,1,1,1,6,1,5,3,1,1, 1,3,4,1,1,5,2,1,5,6,13,9,16,1,1,1,1,3,2,3,2,4,5,2,5,2,2,3,7,13,7,2,2,1,1,1,1,2,3,3,2,1,6,4,9,2,1,14,2,14,2,1,18,3,4,14,4,11,41,15,23,15,23, 176,1,3,4,1,1,1,1,5,3,1,2,3,7,3,1,1,2,1,2,4,4,6,2,4,1,9,7,1,10,5,8,16,29,1,1,2,2,3,1,3,5,2,4,5,4,1,1,2,2,3,3,7,1,6,10,1,17,1,44,4,6,2,1,1,6, 5,4,2,10,1,6,9,2,8,1,24,1,2,13,7,8,8,2,1,4,1,3,1,3,3,5,2,5,10,9,4,9,12,2,1,6,1,10,1,1,7,7,4,10,8,3,1,13,4,3,1,6,1,3,5,2,1,2,17,16,5,2,16,6, 1,4,2,1,3,3,6,8,5,11,11,1,3,3,2,4,6,10,9,5,7,4,7,4,7,1,1,4,2,1,3,6,8,7,1,6,11,5,5,3,24,9,4,2,7,13,5,1,8,82,16,61,1,1,1,4,2,2,16,10,3,8,1,1, 6,4,2,1,3,1,1,1,4,3,8,4,2,2,1,1,1,1,1,6,3,5,1,1,4,6,9,2,1,1,1,2,1,7,2,1,6,1,5,4,4,3,1,8,1,3,3,1,3,2,2,2,2,3,1,6,1,2,1,2,1,3,7,1,8,2,1,2,1,5, 2,5,3,5,10,1,2,1,1,3,2,5,11,3,9,3,5,1,1,5,9,1,2,1,5,7,9,9,8,1,3,3,3,6,8,2,3,2,1,1,32,6,1,2,15,9,3,7,13,1,3,10,13,2,14,1,13,10,2,1,3,10,4,15, 2,15,15,10,1,3,9,6,9,32,25,26,47,7,3,2,3,1,6,3,4,3,2,8,5,4,1,9,4,2,2,19,10,6,2,3,8,1,2,2,4,2,1,9,4,4,4,6,4,8,9,2,3,1,1,1,1,3,5,5,1,3,8,4,6, 2,1,4,12,1,5,3,7,13,2,5,8,1,6,1,2,5,14,6,1,5,2,4,8,15,5,1,23,6,62,2,10,1,1,8,1,2,2,10,4,2,2,9,2,1,1,3,2,3,1,5,3,3,2,1,3,8,1,1,1,11,3,1,1,4, 3,7,1,14,1,2,3,12,5,2,5,1,6,7,5,7,14,11,1,3,1,8,9,12,2,1,11,8,4,4,2,6,10,9,13,1,1,3,1,5,1,3,2,4,4,1,18,2,3,14,11,4,29,4,2,7,1,3,13,9,2,2,5, 3,5,20,7,16,8,5,72,34,6,4,22,12,12,28,45,36,9,7,39,9,191,1,1,1,4,11,8,4,9,2,3,22,1,1,1,1,4,17,1,7,7,1,11,31,10,2,4,8,2,3,2,1,4,2,16,4,32,2, 3,19,13,4,9,1,5,2,14,8,1,1,3,6,19,6,5,1,16,6,2,10,8,5,1,2,3,1,5,5,1,11,6,6,1,3,3,2,6,3,8,1,1,4,10,7,5,7,7,5,8,9,2,1,3,4,1,1,3,1,3,3,2,6,16, 1,4,6,3,1,10,6,1,3,15,2,9,2,10,25,13,9,16,6,2,2,10,11,4,3,9,1,2,6,6,5,4,30,40,1,10,7,12,14,33,6,3,6,7,3,1,3,1,11,14,4,9,5,12,11,49,18,51,31, 140,31,2,2,1,5,1,8,1,10,1,4,4,3,24,1,10,1,3,6,6,16,3,4,5,2,1,4,2,57,10,6,22,2,22,3,7,22,6,10,11,36,18,16,33,36,2,5,5,1,1,1,4,10,1,4,13,2,7, 5,2,9,3,4,1,7,43,3,7,3,9,14,7,9,1,11,1,1,3,7,4,18,13,1,14,1,3,6,10,73,2,2,30,6,1,11,18,19,13,22,3,46,42,37,89,7,3,16,34,2,2,3,9,1,7,1,1,1,2, 2,4,10,7,3,10,3,9,5,28,9,2,6,13,7,3,1,3,10,2,7,2,11,3,6,21,54,85,2,1,4,2,2,1,39,3,21,2,2,5,1,1,1,4,1,1,3,4,15,1,3,2,4,4,2,3,8,2,20,1,8,7,13, 4,1,26,6,2,9,34,4,21,52,10,4,4,1,5,12,2,11,1,7,2,30,12,44,2,30,1,1,3,6,16,9,17,39,82,2,2,24,7,1,7,3,16,9,14,44,2,1,2,1,2,3,5,2,4,1,6,7,5,3, 2,6,1,11,5,11,2,1,18,19,8,1,3,24,29,2,1,3,5,2,2,1,13,6,5,1,46,11,3,5,1,1,5,8,2,10,6,12,6,3,7,11,2,4,16,13,2,5,1,1,2,2,5,2,28,5,2,23,10,8,4, 4,22,39,95,38,8,14,9,5,1,13,5,4,3,13,12,11,1,9,1,27,37,2,5,4,4,63,211,95,2,2,2,1,3,5,2,1,1,2,2,1,1,1,3,2,4,1,2,1,1,5,2,2,1,1,2,3,1,3,1,1,1, 3,1,4,2,1,3,6,1,1,3,7,15,5,3,2,5,3,9,11,4,2,22,1,6,3,8,7,1,4,28,4,16,3,3,25,4,4,27,27,1,4,1,2,2,7,1,3,5,2,28,8,2,14,1,8,6,16,25,3,3,3,14,3, 3,1,1,2,1,4,6,3,8,4,1,1,1,2,3,6,10,6,2,3,18,3,2,5,5,4,3,1,5,2,5,4,23,7,6,12,6,4,17,11,9,5,1,1,10,5,12,1,1,11,26,33,7,3,6,1,17,7,1,5,12,1,11, 2,4,1,8,14,17,23,1,2,1,7,8,16,11,9,6,5,2,6,4,16,2,8,14,1,11,8,9,1,1,1,9,25,4,11,19,7,2,15,2,12,8,52,7,5,19,2,16,4,36,8,1,16,8,24,26,4,6,2,9, 5,4,36,3,28,12,25,15,37,27,17,12,59,38,5,32,127,1,2,9,17,14,4,1,2,1,1,8,11,50,4,14,2,19,16,4,17,5,4,5,26,12,45,2,23,45,104,30,12,8,3,10,2,2, 3,3,1,4,20,7,2,9,6,15,2,20,1,3,16,4,11,15,6,134,2,5,59,1,2,2,2,1,9,17,3,26,137,10,211,59,1,2,4,1,4,1,1,1,2,6,2,3,1,1,2,3,2,3,1,3,4,4,2,3,3, 1,4,3,1,7,2,2,3,1,2,1,3,3,3,2,2,3,2,1,3,14,6,1,3,2,9,6,15,27,9,34,145,1,1,2,1,1,1,1,2,1,1,1,1,2,2,2,3,1,2,1,1,1,2,3,5,8,3,5,2,4,1,3,2,2,2,12, 4,1,1,1,10,4,5,1,20,4,16,1,15,9,5,12,2,9,2,5,4,2,26,19,7,1,26,4,30,12,15,42,1,6,8,172,1,1,4,2,1,1,11,2,2,4,2,1,2,1,10,8,1,2,1,4,5,1,2,5,1,8, 4,1,3,4,2,1,6,2,1,3,4,1,2,1,1,1,1,12,5,7,2,4,3,1,1,1,3,3,6,1,2,2,3,3,3,2,1,2,12,14,11,6,6,4,12,2,8,1,7,10,1,35,7,4,13,15,4,3,23,21,28,52,5, 26,5,6,1,7,10,2,7,53,3,2,1,1,1,2,163,532,1,10,11,1,3,3,4,8,2,8,6,2,2,23,22,4,2,2,4,2,1,3,1,3,3,5,9,8,2,1,2,8,1,10,2,12,21,20,15,105,2,3,1,1, 3,2,3,1,1,2,5,1,4,15,11,19,1,1,1,1,5,4,5,1,1,2,5,3,5,12,1,2,5,1,11,1,1,15,9,1,4,5,3,26,8,2,1,3,1,1,15,19,2,12,1,2,5,2,7,2,19,2,20,6,26,7,5, 2,2,7,34,21,13,70,2,128,1,1,2,1,1,2,1,1,3,2,2,2,15,1,4,1,3,4,42,10,6,1,49,85,8,1,2,1,1,4,4,2,3,6,1,5,7,4,3,211,4,1,2,1,2,5,1,2,4,2,2,6,5,6, 10,3,4,48,100,6,2,16,296,5,27,387,2,2,3,7,16,8,5,38,15,39,21,9,10,3,7,59,13,27,21,47,5,21,6 }; static ImWchar base_ranges[] = // not zero-terminated { 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x2000, 0x206F, // General Punctuation 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_COUNTOF(base_ranges) + IM_COUNTOF(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; if (!full_ranges[0]) { memcpy(full_ranges, base_ranges, sizeof(base_ranges)); UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_COUNTOF(accumulative_offsets_from_0x4E00), full_ranges + IM_COUNTOF(base_ranges)); } return &full_ranges[0]; } const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() { // 2999 ideograms code points for Japanese // - 2136 Joyo (meaning "for regular use" or "for common use") Kanji code points // - 863 Jinmeiyo (meaning "for personal name") Kanji code points // - Sourced from official information provided by the government agencies of Japan: // - List of Joyo Kanji by the Agency for Cultural Affairs // - https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/ // - List of Jinmeiyo Kanji by the Ministry of Justice // - http://www.moj.go.jp/MINJI/minji86.html // - Available under the terms of the Creative Commons Attribution 4.0 International (CC BY 4.0). // - https://creativecommons.org/licenses/by/4.0/legalcode // - You can generate this code by the script at: // - https://github.com/vaiorabbit/everyday_use_kanji // - References: // - List of Joyo Kanji // - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji // - List of Jinmeiyo Kanji // - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji // - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details. // 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[] = { 0,1,2,4,1,1,1,1,2,1,3,3,2,2,1,5,3,5,7,5,6,1,2,1,7,2,6,3,1,8,1,1,4,1,1,18,2,11,2,6,2,1,2,1,5,1,2,1,3,1,2,1,2,3,3,1,1,2,3,1,1,1,12,7,9,1,4,5,1, 1,2,1,10,1,1,9,2,2,4,5,6,9,3,1,1,1,1,9,3,18,5,2,2,2,2,1,6,3,7,1,1,1,1,2,2,4,2,1,23,2,10,4,3,5,2,4,10,2,4,13,1,6,1,9,3,1,1,6,6,7,6,3,1,2,11,3, 2,2,3,2,15,2,2,5,4,3,6,4,1,2,5,2,12,16,6,13,9,13,2,1,1,7,16,4,7,1,19,1,5,1,2,2,7,7,8,2,6,5,4,9,18,7,4,5,9,13,11,8,15,2,1,1,1,2,1,2,2,1,2,2,8, 2,9,3,3,1,1,4,4,1,1,1,4,9,1,4,3,5,5,2,7,5,3,4,8,2,1,13,2,3,3,1,14,1,1,4,5,1,3,6,1,5,2,1,1,3,3,3,3,1,1,2,7,6,6,7,1,4,7,6,1,1,1,1,1,12,3,3,9,5, 2,6,1,5,6,1,2,3,18,2,4,14,4,1,3,6,1,1,6,3,5,5,3,2,2,2,2,12,3,1,4,2,3,2,3,11,1,7,4,1,2,1,3,17,1,9,1,24,1,1,4,2,2,4,1,2,7,1,1,1,3,1,2,2,4,15,1, 1,2,1,1,2,1,5,2,5,20,2,5,9,1,10,8,7,6,1,1,1,1,1,1,6,2,1,2,8,1,1,1,1,5,1,1,3,1,1,1,1,3,1,1,12,4,1,3,1,1,1,1,1,10,3,1,7,5,13,1,2,3,4,6,1,1,30, 2,9,9,1,15,38,11,3,1,8,24,7,1,9,8,10,2,1,9,31,2,13,6,2,9,4,49,5,2,15,2,1,10,2,1,1,1,2,2,6,15,30,35,3,14,18,8,1,16,10,28,12,19,45,38,1,3,2,3, 13,2,1,7,3,6,5,3,4,3,1,5,7,8,1,5,3,18,5,3,6,1,21,4,24,9,24,40,3,14,3,21,3,2,1,2,4,2,3,1,15,15,6,5,1,1,3,1,5,6,1,9,7,3,3,2,1,4,3,8,21,5,16,4, 5,2,10,11,11,3,6,3,2,9,3,6,13,1,2,1,1,1,1,11,12,6,6,1,4,2,6,5,2,1,1,3,3,6,13,3,1,1,5,1,2,3,3,14,2,1,2,2,2,5,1,9,5,1,1,6,12,3,12,3,4,13,2,14, 2,8,1,17,5,1,16,4,2,2,21,8,9,6,23,20,12,25,19,9,38,8,3,21,40,25,33,13,4,3,1,4,1,2,4,1,2,5,26,2,1,1,2,1,3,6,2,1,1,1,1,1,1,2,3,1,1,1,9,2,3,1,1, 1,3,6,3,2,1,1,6,6,1,8,2,2,2,1,4,1,2,3,2,7,3,2,4,1,2,1,2,2,1,1,1,1,1,3,1,2,5,4,10,9,4,9,1,1,1,1,1,1,5,3,2,1,6,4,9,6,1,10,2,31,17,8,3,7,5,40,1, 7,7,1,6,5,2,10,7,8,4,15,39,25,6,28,47,18,10,7,1,3,1,1,2,1,1,1,3,3,3,1,1,1,3,4,2,1,4,1,3,6,10,7,8,6,2,2,1,3,3,2,5,8,7,9,12,2,15,1,1,4,1,2,1,1, 1,3,2,1,3,3,5,6,2,3,2,10,1,4,2,8,1,1,1,11,6,1,21,4,16,3,1,3,1,4,2,3,6,5,1,3,1,1,3,3,4,6,1,1,10,4,2,7,10,4,7,4,2,9,4,3,1,1,1,4,1,8,3,4,1,3,1, 6,1,4,2,1,4,7,2,1,8,1,4,5,1,1,2,2,4,6,2,7,1,10,1,1,3,4,11,10,8,21,4,6,1,3,5,2,1,2,28,5,5,2,3,13,1,2,3,1,4,2,1,5,20,3,8,11,1,3,3,3,1,8,10,9,2, 10,9,2,3,1,1,2,4,1,8,3,6,1,7,8,6,11,1,4,29,8,4,3,1,2,7,13,1,4,1,6,2,6,12,12,2,20,3,2,3,6,4,8,9,2,7,34,5,1,18,6,1,1,4,4,5,7,9,1,2,2,4,3,4,1,7, 2,2,2,6,2,3,25,5,3,6,1,4,6,7,4,2,1,4,2,13,6,4,4,3,1,5,3,4,4,3,2,1,1,4,1,2,1,1,3,1,11,1,6,3,1,7,3,6,2,8,8,6,9,3,4,11,3,2,10,12,2,5,11,1,6,4,5, 3,1,8,5,4,6,6,3,5,1,1,3,2,1,2,2,6,17,12,1,10,1,6,12,1,6,6,19,9,6,16,1,13,4,4,15,7,17,6,11,9,15,12,6,7,2,1,2,2,15,9,3,21,4,6,49,18,7,3,2,3,1, 6,8,2,2,6,2,9,1,3,6,4,4,1,2,16,2,5,2,1,6,2,3,5,3,1,2,5,1,2,1,9,3,1,8,6,4,8,11,3,1,1,1,1,3,1,13,8,4,1,3,2,2,1,4,1,11,1,5,2,1,5,2,5,8,6,1,1,7, 4,3,8,3,2,7,2,1,5,1,5,2,4,7,6,2,8,5,1,11,4,5,3,6,18,1,2,13,3,3,1,21,1,1,4,1,4,1,1,1,8,1,2,2,7,1,2,4,2,2,9,2,1,1,1,4,3,6,3,12,5,1,1,1,5,6,3,2, 4,8,2,2,4,2,7,1,8,9,5,2,3,2,1,3,2,13,7,14,6,5,1,1,2,1,4,2,23,2,1,1,6,3,1,4,1,15,3,1,7,3,9,14,1,3,1,4,1,1,5,8,1,3,8,3,8,15,11,4,14,4,4,2,5,5, 1,7,1,6,14,7,7,8,5,15,4,8,6,5,6,2,1,13,1,20,15,11,9,2,5,6,2,11,2,6,2,5,1,5,8,4,13,19,25,4,1,1,11,1,34,2,5,9,14,6,2,2,6,1,1,14,1,3,14,13,1,6, 12,21,14,14,6,32,17,8,32,9,28,1,2,4,11,8,3,1,14,2,5,15,1,1,1,1,3,6,4,1,3,4,11,3,1,1,11,30,1,5,1,4,1,5,8,1,1,3,2,4,3,17,35,2,6,12,17,3,1,6,2, 1,1,12,2,7,3,3,2,1,16,2,8,3,6,5,4,7,3,3,8,1,9,8,5,1,2,1,3,2,8,1,2,9,12,1,1,2,3,8,3,24,12,4,3,7,5,8,3,3,3,3,3,3,1,23,10,3,1,2,2,6,3,1,16,1,16, 22,3,10,4,11,6,9,7,7,3,6,2,2,2,4,10,2,1,1,2,8,7,1,6,4,1,3,3,3,5,10,12,12,2,3,12,8,15,1,1,16,6,6,1,5,9,11,4,11,4,2,6,12,1,17,5,13,1,4,9,5,1,11, 2,1,8,1,5,7,28,8,3,5,10,2,17,3,38,22,1,2,18,12,10,4,38,18,1,4,44,19,4,1,8,4,1,12,1,4,31,12,1,14,7,75,7,5,10,6,6,13,3,2,11,11,3,2,5,28,15,6,18, 18,5,6,4,3,16,1,7,18,7,36,3,5,3,1,7,1,9,1,10,7,2,4,2,6,2,9,7,4,3,32,12,3,7,10,2,23,16,3,1,12,3,31,4,11,1,3,8,9,5,1,30,15,6,12,3,2,2,11,19,9, 14,2,6,2,3,19,13,17,5,3,3,25,3,14,1,1,1,36,1,3,2,19,3,13,36,9,13,31,6,4,16,34,2,5,4,2,3,3,5,1,1,1,4,3,1,17,3,2,3,5,3,1,3,2,3,5,6,3,12,11,1,3, 1,2,26,7,12,7,2,14,3,3,7,7,11,25,25,28,16,4,36,1,2,1,6,2,1,9,3,27,17,4,3,4,13,4,1,3,2,2,1,10,4,2,4,6,3,8,2,1,18,1,1,24,2,2,4,33,2,3,63,7,1,6, 40,7,3,4,4,2,4,15,18,1,16,1,1,11,2,41,14,1,3,18,13,3,2,4,16,2,17,7,15,24,7,18,13,44,2,2,3,6,1,1,7,5,1,7,1,4,3,3,5,10,8,2,3,1,8,1,1,27,4,2,1, 12,1,2,1,10,6,1,6,7,5,2,3,7,11,5,11,3,6,6,2,3,15,4,9,1,1,2,1,2,11,2,8,12,8,5,4,2,3,1,5,2,2,1,14,1,12,11,4,1,11,17,17,4,3,2,5,5,7,3,1,5,9,9,8, 2,5,6,6,13,13,2,1,2,6,1,2,2,49,4,9,1,2,10,16,7,8,4,3,2,23,4,58,3,29,1,14,19,19,11,11,2,7,5,1,3,4,6,2,18,5,12,12,17,17,3,3,2,4,1,6,2,3,4,3,1, 1,1,1,5,1,1,9,1,3,1,3,6,1,8,1,1,2,6,4,14,3,1,4,11,4,1,3,32,1,2,4,13,4,1,2,4,2,1,3,1,11,1,4,2,1,4,4,6,3,5,1,6,5,7,6,3,23,3,5,3,5,3,3,13,3,9,10, 1,12,10,2,3,18,13,7,160,52,4,2,2,3,2,14,5,4,12,4,6,4,1,20,4,11,6,2,12,27,1,4,1,2,2,7,4,5,2,28,3,7,25,8,3,19,3,6,10,2,2,1,10,2,5,4,1,3,4,1,5, 3,2,6,9,3,6,2,16,3,3,16,4,5,5,3,2,1,2,16,15,8,2,6,21,2,4,1,22,5,8,1,1,21,11,2,1,11,11,19,13,12,4,2,3,2,3,6,1,8,11,1,4,2,9,5,2,1,11,2,9,1,1,2, 14,31,9,3,4,21,14,4,8,1,7,2,2,2,5,1,4,20,3,3,4,10,1,11,9,8,2,1,4,5,14,12,14,2,17,9,6,31,4,14,1,20,13,26,5,2,7,3,6,13,2,4,2,19,6,2,2,18,9,3,5, 12,12,14,4,6,2,3,6,9,5,22,4,5,25,6,4,8,5,2,6,27,2,35,2,16,3,7,8,8,6,6,5,9,17,2,20,6,19,2,13,3,1,1,1,4,17,12,2,14,7,1,4,18,12,38,33,2,10,1,1, 2,13,14,17,11,50,6,33,20,26,74,16,23,45,50,13,38,33,6,6,7,4,4,2,1,3,2,5,8,7,8,9,3,11,21,9,13,1,3,10,6,7,1,2,2,18,5,5,1,9,9,2,68,9,19,13,2,5, 1,4,4,7,4,13,3,9,10,21,17,3,26,2,1,5,2,4,5,4,1,7,4,7,3,4,2,1,6,1,1,20,4,1,9,2,2,1,3,3,2,3,2,1,1,1,20,2,3,1,6,2,3,6,2,4,8,1,3,2,10,3,5,3,4,4, 3,4,16,1,6,1,10,2,4,2,1,1,2,10,11,2,2,3,1,24,31,4,10,10,2,5,12,16,164,15,4,16,7,9,15,19,17,1,2,1,1,5,1,1,1,1,1,3,1,4,3,1,3,1,3,1,2,1,1,3,3,7, 2,8,1,2,2,2,1,3,4,3,7,8,12,92,2,10,3,1,3,14,5,25,16,42,4,7,7,4,2,21,5,27,26,27,21,25,30,31,2,1,5,13,3,22,5,6,6,11,9,12,1,5,9,7,5,5,22,60,3,5, 13,1,1,8,1,1,3,3,2,1,9,3,3,18,4,1,2,3,7,6,3,1,2,3,9,1,3,1,3,2,1,3,1,1,1,2,1,11,3,1,6,9,1,3,2,3,1,2,1,5,1,1,4,3,4,1,2,2,4,4,1,7,2,1,2,2,3,5,13, 18,3,4,14,9,9,4,16,3,7,5,8,2,6,48,28,3,1,1,4,2,14,8,2,9,2,1,15,2,4,3,2,10,16,12,8,7,1,1,3,1,1,1,2,7,4,1,6,4,38,39,16,23,7,15,15,3,2,12,7,21, 37,27,6,5,4,8,2,10,8,8,6,5,1,2,1,3,24,1,16,17,9,23,10,17,6,1,51,55,44,13,294,9,3,6,2,4,2,2,15,1,1,1,13,21,17,68,14,8,9,4,1,4,9,3,11,7,1,1,1, 5,6,3,2,1,1,1,2,3,8,1,2,2,4,1,5,5,2,1,4,3,7,13,4,1,4,1,3,1,1,1,5,5,10,1,6,1,5,2,1,5,2,4,1,4,5,7,3,18,2,9,11,32,4,3,3,2,4,7,11,16,9,11,8,13,38, 32,8,4,2,1,1,2,1,2,4,4,1,1,1,4,1,21,3,11,1,16,1,1,6,1,3,2,4,9,8,57,7,44,1,3,3,13,3,10,1,1,7,5,2,7,21,47,63,3,15,4,7,1,16,1,1,2,8,2,3,42,15,4, 1,29,7,22,10,3,78,16,12,20,18,4,67,11,5,1,3,15,6,21,31,32,27,18,13,71,35,5,142,4,10,1,2,50,19,33,16,35,37,16,19,27,7,1,133,19,1,4,8,7,20,1,4, 4,1,10,3,1,6,1,2,51,5,40,15,24,43,22928,11,1,13,154,70,3,1,1,7,4,10,1,2,1,1,2,1,2,1,2,2,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1, 3,2,1,1,1,1,2,1,1, }; static ImWchar base_ranges[] = // not zero-terminated { 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_COUNTOF(base_ranges) + IM_COUNTOF(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; if (!full_ranges[0]) { memcpy(full_ranges, base_ranges, sizeof(base_ranges)); UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_COUNTOF(accumulative_offsets_from_0x4E00), full_ranges + IM_COUNTOF(base_ranges)); } return &full_ranges[0]; } const ImWchar* ImFontAtlas::GetGlyphRangesCyrillic() { static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x0400, 0x052F, // Cyrillic + Cyrillic Supplement 0x2DE0, 0x2DFF, // Cyrillic Extended-A 0xA640, 0xA69F, // Cyrillic Extended-B 0, }; return &ranges[0]; } const ImWchar* ImFontAtlas::GetGlyphRangesThai() { static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin 0x2010, 0x205E, // Punctuations 0x0E00, 0x0E7F, // Thai 0, }; return &ranges[0]; } 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]; } #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //----------------------------------------------------------------------------- // [SECTION] ImFontGlyphRangesBuilder //----------------------------------------------------------------------------- void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) { if (text_end == NULL) text_end = text + strlen(text); while (text < text_end) { unsigned int c = 0; int c_len = ImTextCharFromUtf8(&c, text, text_end); text += c_len; if (c_len == 0) break; AddChar((ImWchar)c); } } void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 AddChar((ImWchar)c); } void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) { const int max_codepoint = IM_UNICODE_CODEPOINT_MAX; for (int n = 0; n <= max_codepoint; n++) if (GetBit(n)) { out_ranges->push_back((ImWchar)n); while (n < max_codepoint && GetBit(n + 1)) n++; out_ranges->push_back((ImWchar)n); } out_ranges->push_back(0); } //----------------------------------------------------------------------------- // [SECTION] ImFontBaked, ImFont //----------------------------------------------------------------------------- ImFontBaked::ImFontBaked() { memset((void*)this, 0, sizeof(*this)); FallbackGlyphIndex = -1; } void ImFontBaked::ClearOutputData() { FallbackAdvanceX = 0.0f; Glyphs.clear(); IndexAdvanceX.clear(); IndexLookup.clear(); FallbackGlyphIndex = -1; Ascent = Descent = 0.0f; MetricsTotalSurface = 0; } ImFont::ImFont() { memset((void*)this, 0, sizeof(*this)); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS Scale = 1.0f; #endif } ImFont::~ImFont() { ClearOutputData(); } void ImFont::ClearOutputData() { if (ImFontAtlas* atlas = OwnerAtlas) ImFontAtlasFontDiscardBakes(atlas, this, 0); //FallbackChar = EllipsisChar = 0; memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); LastBaked = NULL; } // API is designed this way to avoid exposing the 8K page size // e.g. use with IsGlyphRangeUnused(0, 255) bool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last) { unsigned int page_begin = (c_begin / 8192); unsigned int page_last = (c_last / 8192); for (unsigned int page_n = page_begin; page_n <= page_last; page_n++) if ((page_n >> 3) < sizeof(Used8kPagesMap)) if (Used8kPagesMap[page_n >> 3] & (1 << (page_n & 7))) return false; return true; } // x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero. // Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis). // - 'src' is not necessarily == 'this->Sources' because multiple source fonts+configs can be used to build one target font. ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph) { int glyph_idx = baked->Glyphs.Size; baked->Glyphs.push_back(*in_glyph); ImFontGlyph* glyph = &baked->Glyphs[glyph_idx]; IM_ASSERT(baked->Glyphs.Size < 0xFFFE); // IndexLookup[] hold 16-bit values and -1/-2 are reserved. // Set UV from packed rectangle if (glyph->PackId != ImFontAtlasRectId_Invalid) { ImTextureRect* r = ImFontAtlasPackGetRect(atlas, glyph->PackId); IM_ASSERT(glyph->U0 == 0.0f && glyph->V0 == 0.0f && glyph->U1 == 0.0f && glyph->V1 == 0.0f); glyph->U0 = (r->x) * atlas->TexUvScale.x; glyph->V0 = (r->y) * atlas->TexUvScale.y; glyph->U1 = (r->x + r->w) * atlas->TexUvScale.x; glyph->V1 = (r->y + r->h) * atlas->TexUvScale.y; baked->MetricsTotalSurface += r->w * r->h; } if (src != NULL) { // Clamp & recenter if needed const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; float advance_x = ImClamp(glyph->AdvanceX, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale); if (advance_x != glyph->AdvanceX) { float char_off_x = src->PixelSnapH ? ImTrunc((advance_x - glyph->AdvanceX) * 0.5f) : (advance_x - glyph->AdvanceX) * 0.5f; glyph->X0 += char_off_x; glyph->X1 += char_off_x; } // Snap to pixel if (src->PixelSnapH) advance_x = IM_ROUND(advance_x); // Bake spacing glyph->AdvanceX = advance_x + src->GlyphExtraAdvanceX; } if (glyph->Colored) atlas->TexPixelsUseColors = atlas->TexData->UseColors = true; // Update lookup tables const int codepoint = glyph->Codepoint; ImFontBaked_BuildGrowIndex(baked, codepoint + 1); baked->IndexAdvanceX[codepoint] = glyph->AdvanceX; baked->IndexLookup[codepoint] = (ImU16)glyph_idx; const int page_n = codepoint / 8192; baked->OwnerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7); return glyph; } // FIXME: Code is duplicated with code above. void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x) { IM_UNUSED(atlas); if (src != NULL) { // Clamp & recenter if needed const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; advance_x = ImClamp(advance_x, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale); // Snap to pixel if (src->PixelSnapH) advance_x = IM_ROUND(advance_x); // Bake spacing advance_x += src->GlyphExtraAdvanceX; } ImFontBaked_BuildGrowIndex(baked, codepoint + 1); baked->IndexAdvanceX[codepoint] = advance_x; } // Copy to texture, post-process and queue update for backend void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch) { ImTextureData* tex = atlas->TexData; IM_ASSERT(r->x + r->w <= tex->Width && r->y + r->h <= tex->Height); ImFontAtlasTextureBlockConvert(src_pixels, src_fmt, src_pitch, (unsigned char*)tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h); ImFontAtlasPostProcessData pp_data = { atlas, baked->OwnerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h }; ImFontAtlasTextureBlockPostProcess(&pp_data); ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h); } void ImFont::AddRemapChar(ImWchar from_codepoint, ImWchar to_codepoint) { RemapPairs.SetInt((ImGuiID)from_codepoint, (int)to_codepoint); } // Find glyph, load if necessary, return fallback if missing ImFontGlyph* ImFontBaked::FindGlyph(ImWchar c) { if (c < (size_t)IndexLookup.Size) IM_LIKELY { const int i = (int)IndexLookup.Data[c]; if (i == IM_FONTGLYPH_INDEX_NOT_FOUND) return &Glyphs.Data[FallbackGlyphIndex]; if (i != IM_FONTGLYPH_INDEX_UNUSED) return &Glyphs.Data[i]; } ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL); return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex]; } // Attempt to load but when missing, return NULL instead of FallbackGlyph ImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c) { if (c < (size_t)IndexLookup.Size) IM_LIKELY { const int i = (int)IndexLookup.Data[c]; if (i == IM_FONTGLYPH_INDEX_NOT_FOUND) return NULL; if (i != IM_FONTGLYPH_INDEX_UNUSED) return &Glyphs.Data[i]; } LoadNoFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites. ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL); LoadNoFallback = false; return glyph; } bool ImFontBaked::IsGlyphLoaded(ImWchar c) { if (c < (size_t)IndexLookup.Size) IM_LIKELY { const int i = (int)IndexLookup.Data[c]; if (i == IM_FONTGLYPH_INDEX_NOT_FOUND) return false; if (i != IM_FONTGLYPH_INDEX_UNUSED) return true; } return false; } // This is not fast query bool ImFont::IsGlyphInFont(ImWchar c) { ImFontAtlas* atlas = OwnerAtlas; ImFontAtlas_FontHookRemapCodepoint(atlas, this, &c); for (ImFontConfig* src : Sources) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; if (loader->FontSrcContainsGlyph != NULL && loader->FontSrcContainsGlyph(atlas, src, c)) return true; } return false; } // This is manually inlined in CalcTextSizeA() and CalcWordWrapPosition(), with a non-inline call to BuildLoadGlyphGetAdvanceOrFallback(). IM_MSVC_RUNTIME_CHECKS_OFF float ImFontBaked::GetCharAdvance(ImWchar c) { if ((int)c < IndexAdvanceX.Size) { // Missing glyphs fitting inside index will have stored FallbackAdvanceX already. const float x = IndexAdvanceX.Data[c]; if (x >= 0.0f) return x; } return ImFontBaked_BuildLoadGlyphAdvanceX(this, c); } IM_MSVC_RUNTIME_CHECKS_RESTORE ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density) { struct { ImGuiID FontId; float BakedSize; float RasterizerDensity; } hashed_data; hashed_data.FontId = font_id; hashed_data.BakedSize = baked_size; hashed_data.RasterizerDensity = rasterizer_density; return ImHashData(&hashed_data, sizeof(hashed_data)); } // ImFontBaked pointers are valid for the entire frame but shall never be kept between frames. ImFontBaked* ImFont::GetFontBaked(float size, float density) { ImFontBaked* baked = LastBaked; // Round font size // - ImGui::PushFont() will already round, but other paths calling GetFontBaked() directly also needs it (e.g. ImFontAtlasBuildPreloadAllGlyphRanges) size = ImGui::GetRoundedFontSize(size); if (density < 0.0f) density = CurrentRasterizerDensity; if (baked && baked->Size == size && baked->RasterizerDensity == density) return baked; ImFontAtlas* atlas = OwnerAtlas; ImFontAtlasBuilder* builder = atlas->Builder; baked = ImFontAtlasBakedGetOrAdd(atlas, this, size, density); if (baked == NULL) return NULL; baked->LastUsedFrame = builder->FrameCount; LastBaked = baked; return baked; } ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density) { // FIXME-NEWATLAS: Design for picking a nearest size based on some criteria? // FIXME-NEWATLAS: Altering font density won't work right away. IM_ASSERT(font_size > 0.0f && font_rasterizer_density > 0.0f); ImGuiID baked_id = ImFontAtlasBakedGetId(font->FontId, font_size, font_rasterizer_density); ImFontAtlasBuilder* builder = atlas->Builder; ImFontBaked** p_baked_in_map = (ImFontBaked**)builder->BakedMap.GetVoidPtrRef(baked_id); ImFontBaked* baked = *p_baked_in_map; if (baked != NULL) { IM_ASSERT(baked->Size == font_size && baked->OwnerFont == font && baked->BakedId == baked_id); return baked; } // If atlas is locked, find closest match // FIXME-OPT: This is not an optimal query. if ((font->Flags & ImFontFlags_LockBakedSizes) || atlas->Locked) { baked = ImFontAtlasBakedGetClosestMatch(atlas, font, font_size, font_rasterizer_density); if (baked != NULL) return baked; if (atlas->Locked) { IM_ASSERT(!atlas->Locked && "Cannot use dynamic font size with a locked ImFontAtlas!"); // Locked because rendering backend does not support ImGuiBackendFlags_RendererHasTextures! return NULL; } } // Create new baked = ImFontAtlasBakedAdd(atlas, font, font_size, font_rasterizer_density, baked_id); *p_baked_in_map = baked; // To avoid 'builder->BakedMap.SetVoidPtr(baked_id, baked);' while we can. return baked; } // Trim trailing space and find beginning of next line const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags) { if ((flags & ImDrawTextFlags_WrapKeepBlanks) == 0) while (text < text_end && ImCharIsBlankA(*text)) text++; if (text < text_end && *text == '\n') text++; return text; } void ImTextClassifierClear(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class) { for (unsigned int c = codepoint_min; c < codepoint_end; c++) ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c); } void ImTextClassifierSetCharClass(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, unsigned int c) { IM_ASSERT(c >= codepoint_min && c < codepoint_end); IM_UNUSED(codepoint_end); c -= codepoint_min; const ImU32 shift = (c & 15) << 1; bits[c >> 4] = (bits[c >> 4] & ~(0x03 << shift)) | (char_class << shift); } void ImTextClassifierSetCharClassFromStr(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, const char* s) { const char* s_end = s + strlen(s); while (*s) { unsigned int c; s += ImTextCharFromUtf8(&c, s, s_end); ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c); } } #define ImTextClassifierGet(_BITS, _CHAR_OFFSET) ((_BITS[(_CHAR_OFFSET) >> 4] >> (((_CHAR_OFFSET) & 15) << 1)) & 0x03) // 2-bit per character static ImU32 g_CharClassifierIsSeparator_0000_007f[128 / 16] = {}; static ImU32 g_CharClassifierIsSeparator_3000_300f[ 16 / 16] = {}; void ImTextInitClassifiers() { if (ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, ',') != 0) return; // List of hardcoded separators: .,;!?'" // Making this dynamic given known ranges is trivial BUT requires us to standardize where you pass them as parameters. (#3002, #8503) ImTextClassifierClear(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Other); ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Blank, " \t"); ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Punct, ".,;!?\""); ImTextClassifierClear(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Other); ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Blank, 0x3000); ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Punct, 0x3001); ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Punct, 0x3002); } // Simple word-wrapping for English, not full-featured. Please submit failing cases! // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end. // Refer to imgui_test_suite's "drawlist_text_wordwrap_1" for tests. const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags) { // For references, possible wrap point marked with ^ // "aaa bbb, ccc,ddd. eee fff. ggg!" // ^ ^ ^ ^ ^__ ^ ^ // Skip extra blanks after a line returns (that includes not counting them in width computation) // e.g. "Hello world" --> "Hello" "World" // Cut words that cannot possibly fit within one line. // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" ImFontBaked* baked = font->GetFontBaked(size); const float scale = size / baked->Size; float line_width = 0.0f; float blank_width = 0.0f; wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters const char* s = text; IM_ASSERT(text_end != NULL); int prev_type = ImWcharClass_Other; const bool keep_blanks = (flags & ImDrawTextFlags_WrapKeepBlanks) != 0; // Find next wrapping point //const char* span_begin = s; const char* span_end = s; float span_width = 0.0f; while (s < text_end) { unsigned int c = (unsigned int)*s; const char* next_s; if (c < 0x80) next_s = s + 1; else next_s = s + ImTextCharFromUtf8(&c, s, text_end); if (c < 32) { if (c == '\n') return s; // Direct return, skip "Wrap_width is too small to fit anything" path. if (c == '\r') { s = next_s; // Fast-skip continue; } } // Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);' float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f; if (char_width < 0.0f) char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c); // Classify current character int curr_type; if (c < 128) curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, c); else if (c >= 0x3000 && c < 0x3010) curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_3000_300f, c & 15); //-V578 else curr_type = ImWcharClass_Other; if (curr_type == ImWcharClass_Blank) { // End span: 'A ' or '. ' if (prev_type != ImWcharClass_Blank && !keep_blanks) { span_end = s; line_width += span_width; span_width = 0.0f; } blank_width += char_width; } else { // End span: '.X' unless X is a digit if (prev_type == ImWcharClass_Punct && curr_type != ImWcharClass_Punct && !(c >= '0' && c <= '9')) // FIXME: Digit checks might be removed if allow custom separators (#8503) { span_end = s; line_width += span_width + blank_width; span_width = blank_width = 0.0f; } // End span: 'A ' or '. ' else if (prev_type == ImWcharClass_Blank && keep_blanks) { span_end = s; line_width += span_width + blank_width; span_width = blank_width = 0.0f; } span_width += char_width; } if (span_width + blank_width + line_width > wrap_width) { if (span_width + blank_width > wrap_width) break; // FIXME: Narrow wrapping e.g. "A quick brown" -> "Quic|k br|own", would require knowing if span is going to be longer than wrap_width. //if (span_width > wrap_width && !is_blank && !was_blank) // return s; return span_end; } prev_type = curr_type; s = next_s; } // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. // +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol). if (s == text && text < text_end) return s + ImTextCountUtf8BytesFromChar(s, text_end); return s; } const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width) { return ImFontCalcWordWrapPositionEx(this, size, text, text_end, wrap_width, ImDrawTextFlags_None); } ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags) { if (!text_end) text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this. if (!text_end_display) text_end_display = text_end; ImFontBaked* baked = font->GetFontBaked(size); const float line_height = size; const float scale = line_height / baked->Size; ImVec2 text_size = ImVec2(0, 0); float line_width = 0.0f; const bool word_wrap_enabled = (wrap_width > 0.0f); const char* word_wrap_eol = NULL; const char* s = text_begin; while (s < text_end_display) { // Word-wrapping if (word_wrap_enabled) { // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. if (!word_wrap_eol) word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, s, text_end, wrap_width - line_width, flags); if (s >= word_wrap_eol) { if (text_size.x < line_width) text_size.x = line_width; text_size.y += line_height; line_width = 0.0f; s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks if (flags & ImDrawTextFlags_StopOnNewLine) break; word_wrap_eol = NULL; continue; } } // Decode and advance source const char* prev_s = s; unsigned int c = (unsigned int)*s; if (c < 0x80) s += 1; else s += ImTextCharFromUtf8(&c, s, text_end); if (c == '\n') { text_size.x = ImMax(text_size.x, line_width); text_size.y += line_height; line_width = 0.0f; if (flags & ImDrawTextFlags_StopOnNewLine) break; continue; } if (c == '\r') continue; // Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);' float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f; if (char_width < 0.0f) char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c); char_width *= scale; if (line_width + char_width >= max_width) { s = prev_s; break; } line_width += char_width; } if (text_size.x < line_width) text_size.x = line_width; if (out_offset != NULL) *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n text_size.y += line_height; if (out_remaining != NULL) *out_remaining = s; return text_size; } ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining) { return ImFontCalcTextSizeEx(this, size, max_width, wrap_width, text_begin, text_end, text_end, out_remaining, NULL, ImDrawTextFlags_None); } // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip) { ImFontBaked* baked = GetFontBaked(size); const ImFontGlyph* glyph = baked->FindGlyph(c); if (!glyph || !glyph->Visible) return; if (glyph->Colored) col |= ~IM_COL32_A_MASK; float scale = (size >= 0.0f) ? (size / baked->Size) : 1.0f; float x = IM_TRUNC(pos.x); float y = IM_TRUNC(pos.y); float x1 = x + glyph->X0 * scale; float x2 = x + glyph->X1 * scale; if (cpu_fine_clip && (x1 > cpu_fine_clip->z || x2 < cpu_fine_clip->x)) return; float y1 = y + glyph->Y0 * scale; float y2 = y + glyph->Y1 * scale; float u1 = glyph->U0; float v1 = glyph->V0; float u2 = glyph->U1; float v2 = glyph->V1; // Always CPU fine clip. Code extracted from RenderText(). // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads. if (cpu_fine_clip != NULL) { if (x1 < cpu_fine_clip->x) { u1 = u1 + (1.0f - (x2 - cpu_fine_clip->x) / (x2 - x1)) * (u2 - u1); x1 = cpu_fine_clip->x; } if (y1 < cpu_fine_clip->y) { v1 = v1 + (1.0f - (y2 - cpu_fine_clip->y) / (y2 - y1)) * (v2 - v1); y1 = cpu_fine_clip->y; } if (x2 > cpu_fine_clip->z) { u2 = u1 + ((cpu_fine_clip->z - x1) / (x2 - x1)) * (u2 - u1); x2 = cpu_fine_clip->z; } if (y2 > cpu_fine_clip->w) { v2 = v1 + ((cpu_fine_clip->w - y1) / (y2 - y1)) * (v2 - v1); y2 = cpu_fine_clip->w; } if (y1 >= y2) return; } draw_list->PrimReserve(6, 4); draw_list->PrimRectUV(ImVec2(x1, y1), ImVec2(x2, y2), ImVec2(u1, v1), ImVec2(u2, v2), col); } // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. // DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2026. Use ImDrawList::AddText(). void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags) { // Align to be pixel perfect begin: float x = IM_TRUNC(pos.x); float y = IM_TRUNC(pos.y); if (y > clip_rect.w) return; if (!text_end) text_end = text_begin + ImStrlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. const float line_height = size; ImFontBaked* baked = GetFontBaked(size); const float scale = size / baked->Size; const float origin_x = x; const bool word_wrap_enabled = (wrap_width > 0.0f); // Fast-forward to first visible line const char* s = text_begin; if (y + line_height < clip_rect.y) while (y + line_height < clip_rect.y && s < text_end) { const char* line_end = (const char*)ImMemchr(s, '\n', text_end - s); if (word_wrap_enabled) { // FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPosition(). // If the specs for CalcWordWrapPosition() were reworked to optionally return on \n we could combine both. // However it is still better than nothing performing the fast-forward! s = ImFontCalcWordWrapPositionEx(this, size, s, line_end ? line_end : text_end, wrap_width, flags); s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); } else { s = line_end ? line_end + 1 : text_end; } y += line_height; } // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve() // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm) if (text_end - s > 10000 && !word_wrap_enabled) { const char* s_end = s; float y_end = y; while (y_end < clip_rect.w && s_end < text_end) { s_end = (const char*)ImMemchr(s_end, '\n', text_end - s_end); s_end = s_end ? s_end + 1 : text_end; y_end += line_height; } text_end = s_end; } if (s == text_end) return; // IMHEX PATCH BEGIN bool is_subpixel = false; if (!this->Sources.empty()) { const int flags = this->Sources[0]->FontLoaderFlags; is_subpixel = (flags & ImGuiFreeTypeLoaderFlags_SubPixel) != 0; } void ImGui_ImplOpenGL3_TurnFontShadersOn(const ImDrawList *parent_list, const ImDrawCmd *cmd); void ImGui_ImplOpenGL3_TurnFontShadersOff(const ImDrawList *parent_list, const ImDrawCmd *cmd); if (is_subpixel) { draw_list->AddCallback(ImGui_ImplOpenGL3_TurnFontShadersOn, NULL); draw_list->AddCallback(ImDrawCallback_ResetRenderState, NULL); } // IMHEX PATCH END // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) const int vtx_count_max = (int)(text_end - s) * 4; const int idx_count_max = (int)(text_end - s) * 6; const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max; draw_list->PrimReserve(idx_count_max, vtx_count_max); ImDrawVert* vtx_write = draw_list->_VtxWritePtr; ImDrawIdx* idx_write = draw_list->_IdxWritePtr; unsigned int vtx_index = draw_list->_VtxCurrentIdx; const int cmd_count = draw_list->CmdBuffer.Size; const bool cpu_fine_clip = (flags & ImDrawTextFlags_CpuFineClip) != 0; const ImU32 col_untinted = col | ~IM_COL32_A_MASK; const char* word_wrap_eol = NULL; while (s < text_end) { if (word_wrap_enabled) { // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. if (!word_wrap_eol) word_wrap_eol = ImFontCalcWordWrapPositionEx(this, size, s, text_end, wrap_width - (x - origin_x), flags); if (s >= word_wrap_eol) { x = origin_x; y += line_height; if (y > clip_rect.w) break; // break out of main loop word_wrap_eol = NULL; s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks continue; } } // Decode and advance source unsigned int c = (unsigned int)*s; if (c < 0x80) s += 1; else s += ImTextCharFromUtf8(&c, s, text_end); if (c < 32) { if (c == '\n') { x = origin_x; y += line_height; if (y > clip_rect.w) break; // break out of main loop continue; } if (c == '\r') continue; } const ImFontGlyph* glyph = baked->FindGlyph((ImWchar)c); //if (glyph == NULL) // continue; float char_width = glyph->AdvanceX * scale; if (glyph->Visible) { // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w float x1 = x + glyph->X0 * scale; float x2 = x + glyph->X1 * scale; float y1 = y + glyph->Y0 * scale; float y2 = y + glyph->Y1 * scale; if (x1 <= clip_rect.z && x2 >= clip_rect.x) { // Render a character float u1 = glyph->U0; float v1 = glyph->V0; float u2 = glyph->U1; float v2 = glyph->V1; // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads. if (cpu_fine_clip) { if (x1 < clip_rect.x) { u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1); x1 = clip_rect.x; } if (y1 < clip_rect.y) { v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1); y1 = clip_rect.y; } if (x2 > clip_rect.z) { u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1); x2 = clip_rect.z; } if (y2 > clip_rect.w) { v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1); y2 = clip_rect.w; } if (y1 >= y2) { x += char_width; continue; } } // Support for untinted glyphs ImU32 glyph_col = glyph->Colored ? col_untinted : col; // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here: { vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; idx_write[0] = (ImDrawIdx)(vtx_index); idx_write[1] = (ImDrawIdx)(vtx_index + 1); idx_write[2] = (ImDrawIdx)(vtx_index + 2); idx_write[3] = (ImDrawIdx)(vtx_index); idx_write[4] = (ImDrawIdx)(vtx_index + 2); idx_write[5] = (ImDrawIdx)(vtx_index + 3); vtx_write += 4; vtx_index += 4; idx_write += 6; } } } x += char_width; } // Edge case: calling RenderText() with unloaded glyphs triggering texture change. It doesn't happen via ImGui:: calls because CalcTextSize() is always used. if (cmd_count != draw_list->CmdBuffer.Size) //-V547 { IM_ASSERT(draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount == 0); draw_list->CmdBuffer.pop_back(); draw_list->PrimUnreserve(idx_count_max, vtx_count_max); draw_list->AddDrawCmd(); //IMGUI_DEBUG_LOG("RenderText: cancel and retry to missing glyphs.\n"); // [DEBUG] //draw_list->AddRectFilled(pos, pos + ImVec2(10, 10), IM_COL32(255, 0, 0, 255)); // [DEBUG] goto begin; //RenderText(draw_list, size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip); // FIXME-OPT: Would a 'goto begin' be better for code-gen? //return; } // Give back unused vertices (clipped ones, blanks) ~ this is essentially a PrimUnreserve() action. draw_list->VtxBuffer.Size = (int)(vtx_write - draw_list->VtxBuffer.Data); // Same as calling shrink() draw_list->IdxBuffer.Size = (int)(idx_write - draw_list->IdxBuffer.Data); draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size); draw_list->_VtxWritePtr = vtx_write; draw_list->_IdxWritePtr = idx_write; draw_list->_VtxCurrentIdx = vtx_index; // IMHEX PATCH BEGIN if (is_subpixel) { draw_list->AddCallback(ImGui_ImplOpenGL3_TurnFontShadersOff, NULL); draw_list->AddCallback(ImDrawCallback_ResetRenderState, NULL); } // IMHEX PATCH END } //----------------------------------------------------------------------------- // [SECTION] ImGui Internal Render Helpers //----------------------------------------------------------------------------- // Vaguely redesigned to stop accessing ImGui global state: // - RenderArrow() // - RenderBullet() // - RenderCheckMark() // - RenderArrowDockMenu() // - RenderArrowPointingAt() // - RenderRectFilledInRangeH() // - RenderRectFilledWithHole() //----------------------------------------------------------------------------- // Function in need of a redesign (legacy mess) // - RenderColorRectWithAlphaCheckerboard() //----------------------------------------------------------------------------- // 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(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale) { const float h = draw_list->_Data->FontSize * 1.00f; float r = h * 0.40f * scale; ImVec2 center = pos + 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; } draw_list->AddTriangleFilled(center + a, center + b, center + c, col); } void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col) { // FIXME-OPT: This should be baked in font now that it's easier. float font_size = draw_list->_Data->FontSize; draw_list->AddCircleFilled(pos, font_size * 0.20f, col, (font_size < 22) ? 8 : (font_size < 40) ? 12 : 0); // Hardcode optimal/nice tessellation threshold } void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz) { 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; draw_list->PathLineTo(ImVec2(bx - third, by - third)); draw_list->PathLineTo(ImVec2(bx, by)); draw_list->PathLineTo(ImVec2(bx + third * 2.0f, by - third * 2.0f)); draw_list->PathStroke(col, 0, thickness); } // Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) { switch (direction) { case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return; case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return; case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return; case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return; case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings } } // This is less wide than RenderArrow() and we use in dock nodes instead of the regular RenderArrow() to denote a change of functionality, // and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window. void ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col) { draw_list->AddRectFilled(p_min + ImVec2(sz * 0.20f, sz * 0.15f), p_min + ImVec2(sz * 0.80f, sz * 0.30f), col); RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.50f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col); } static inline float ImAcos01(float x) { if (x <= 0.0f) return IM_PI * 0.5f; if (x >= 1.0f) return 0.0f; return ImAcos(x); //return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do. } // FIXME: Cleanup and move code to ImDrawList. // - Before 2025-12-04: RenderRectFilledRangeH() with 'float x_start_norm, float x_end_norm` <- normalized // - After 2025-12-04: RenderRectFilledInRangeH() with 'float x1, float x2' <- absolute coords!! void ImGui::RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding) { if (fill_x0 > fill_x1) return; ImVec2 p0 = ImVec2(fill_x0, rect.Min.y); ImVec2 p1 = ImVec2(fill_x1, rect.Max.y); if (rounding == 0.0f) { draw_list->AddRectFilled(p0, p1, col, 0.0f); return; } rounding = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5f, (rect.Max.y - rect.Min.y) * 0.5f) - 1.0f, 0.0f, rounding); 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 == 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 } else { draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b); // BL draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e); // TR } if (p1.x > rect.Min.x + rounding) { const float arc1_b = ImAcos01(1.0f - (rect.Max.x - p1.x) * inv_rounding); const float arc1_e = ImAcos01(1.0f - (rect.Max.x - p0.x) * inv_rounding); const float x1 = ImMin(p1.x, rect.Max.x - rounding); if (arc1_b == arc1_e) { draw_list->PathLineTo(ImVec2(x1, p0.y)); draw_list->PathLineTo(ImVec2(x1, p1.y)); } 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 } else { draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b); // TR draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e); // BR } } draw_list->PathFillConvex(col); } void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding) { const bool fill_L = (inner.Min.x > outer.Min.x); const bool fill_R = (inner.Max.x < outer.Max.x); const bool fill_U = (inner.Min.y > outer.Min.y); const bool fill_D = (inner.Max.y < outer.Max.y); if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft); if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight); if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft); if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); } ImDrawFlags ImGui::CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold) { bool round_l = r_in.Min.x <= r_outer.Min.x + threshold; bool round_r = r_in.Max.x >= r_outer.Max.x - threshold; bool round_t = r_in.Min.y <= r_outer.Min.y + threshold; bool round_b = r_in.Max.y >= r_outer.Max.y - threshold; return ImDrawFlags_RoundCornersNone | ((round_t && round_l) ? ImDrawFlags_RoundCornersTopLeft : 0) | ((round_t && round_r) ? ImDrawFlags_RoundCornersTopRight : 0) | ((round_b && round_l) ? ImDrawFlags_RoundCornersBottomLeft : 0) | ((round_b && round_r) ? ImDrawFlags_RoundCornersBottomRight : 0); } // Helper for ColorPicker4() // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. // Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether. // FIXME: uses ImGui::GetColorU32 void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, ImDrawFlags flags) { if ((flags & ImDrawFlags_RoundCornersMask_) == 0) flags = ImDrawFlags_RoundCornersDefault_; if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) { ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags); int yi = 0; for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) { float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); if (y2 <= y1) continue; for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) { float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); if (x2 <= x1) continue; ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; } if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; } // Combine flags cell_flags = (flags == ImDrawFlags_RoundCornersNone || cell_flags == ImDrawFlags_RoundCornersNone) ? ImDrawFlags_RoundCornersNone : (cell_flags & flags); draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding, cell_flags); } } } else { draw_list->AddRectFilled(p_min, p_max, col, rounding, flags); } } //----------------------------------------------------------------------------- // [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. // The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size. // Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h //----------------------------------------------------------------------------- static unsigned int stb_decompress_length(const unsigned char *input) { return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; } static unsigned char *stb__barrier_out_e, *stb__barrier_out_b; static const unsigned char *stb__barrier_in_b; static unsigned char *stb__dout; static void stb__match(const unsigned char *data, unsigned int length) { // INVERSE of memmove... write each byte before copying the next... IM_ASSERT(stb__dout + length <= stb__barrier_out_e); if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; } if (data < stb__barrier_out_b) { stb__dout = stb__barrier_out_e+1; return; } while (length--) *stb__dout++ = *data++; } static void stb__lit(const unsigned char *data, unsigned int length) { IM_ASSERT(stb__dout + length <= stb__barrier_out_e); if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; } if (data < stb__barrier_in_b) { stb__dout = stb__barrier_out_e+1; return; } memcpy(stb__dout, data, length); stb__dout += length; } #define stb__in2(x) ((i[x] << 8) + i[(x)+1]) #define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) #define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) static const unsigned char *stb_decompress_token(const unsigned char *i) { if (*i >= 0x20) { // use fewer if's for cases that expand small if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); } else { // more ifs for cases that expand large, since overhead is amortized if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; } return i; } static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) { const unsigned long ADLER_MOD = 65521; unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; unsigned long blocklen = buflen % 5552; unsigned long i; while (buflen) { for (i=0; i + 7 < blocklen; i += 8) { s1 += buffer[0], s2 += s1; s1 += buffer[1], s2 += s1; s1 += buffer[2], s2 += s1; s1 += buffer[3], s2 += s1; s1 += buffer[4], s2 += s1; s1 += buffer[5], s2 += s1; s1 += buffer[6], s2 += s1; s1 += buffer[7], s2 += s1; buffer += 8; } for (; i < blocklen; ++i) s1 += *buffer++, s2 += s1; s1 %= ADLER_MOD, s2 %= ADLER_MOD; buflen -= blocklen; blocklen = 5552; } return (unsigned int)(s2 << 16) + (unsigned int)s1; } static unsigned int stb_decompress(unsigned char *output, const unsigned char *i, unsigned int /*length*/) { if (stb__in4(0) != 0x57bC0000) return 0; if (stb__in4(4) != 0) return 0; // error! stream is > 4GB const unsigned int olen = stb_decompress_length(i); stb__barrier_in_b = i; stb__barrier_out_e = output + olen; stb__barrier_out_b = output; i += 16; stb__dout = output; for (;;) { const unsigned char *old_i = i; i = stb_decompress_token(i); if (i == old_i) { if (*i == 0x05 && i[1] == 0xfa) { IM_ASSERT(stb__dout == output + olen); if (stb__dout != output + olen) return 0; if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2)) return 0; return olen; } else { IM_ASSERT(0); /* NOTREACHED */ return 0; } } IM_ASSERT(stb__dout <= output + olen); if (stb__dout > output + olen) return 0; } } //----------------------------------------------------------------------------- // [SECTION] Default font data (ProggyClean.ttf) //----------------------------------------------------------------------------- // MIT License / Copyright (c) 2004, 2005 Tristan Grimmer // Download and more information at https://github.com/bluescan/proggyfonts //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_DEFAULT_FONT // File: 'ProggyClean.ttf' (41208 bytes) // Exported using binary_to_compressed_c.exe -u8 "ProggyClean.ttf" proggy_clean_ttf static const unsigned int proggy_clean_ttf_compressed_size = 9583; static const unsigned char proggy_clean_ttf_compressed_data[9583] = { 87,188,0,0,0,0,0,0,0,0,160,248,0,4,0,0,55,0,1,0,0,0,12,0,128,0,3,0,64,79,83,47,50,136,235,116,144,0,0,1,72,130,21,44,78,99,109,97,112,2,18,35,117,0,0,3,160,130,19,36,82,99,118,116, 32,130,23,130,2,33,4,252,130,4,56,2,103,108,121,102,18,175,137,86,0,0,7,4,0,0,146,128,104,101,97,100,215,145,102,211,130,27,32,204,130,3,33,54,104,130,16,39,8,66,1,195,0,0,1,4,130, 15,59,36,104,109,116,120,138,0,126,128,0,0,1,152,0,0,2,6,108,111,99,97,140,115,176,216,0,0,5,130,30,41,2,4,109,97,120,112,1,174,0,218,130,31,32,40,130,16,44,32,110,97,109,101,37,89, 187,150,0,0,153,132,130,19,44,158,112,111,115,116,166,172,131,239,0,0,155,36,130,51,44,210,112,114,101,112,105,2,1,18,0,0,4,244,130,47,32,8,132,203,46,1,0,0,60,85,233,213,95,15,60, 245,0,3,8,0,131,0,34,183,103,119,130,63,43,0,0,189,146,166,215,0,0,254,128,3,128,131,111,130,241,33,2,0,133,0,32,1,130,65,38,192,254,64,0,0,3,128,131,16,130,5,32,1,131,7,138,3,33,2, 0,130,17,36,1,1,0,144,0,130,121,130,23,38,2,0,8,0,64,0,10,130,9,32,118,130,9,130,6,32,0,130,59,33,1,144,131,200,35,2,188,2,138,130,16,32,143,133,7,37,1,197,0,50,2,0,131,0,33,4,9,131, 5,145,3,43,65,108,116,115,0,64,0,0,32,172,8,0,131,0,35,5,0,1,128,131,77,131,3,33,3,128,191,1,33,1,128,130,184,35,0,0,128,0,130,3,131,11,32,1,130,7,33,0,128,131,1,32,1,136,9,32,0,132, 15,135,5,32,1,131,13,135,27,144,35,32,1,149,25,131,21,32,0,130,0,32,128,132,103,130,35,132,39,32,0,136,45,136,97,133,17,130,5,33,0,0,136,19,34,0,128,1,133,13,133,5,32,128,130,15,132, 131,32,3,130,5,32,3,132,27,144,71,32,0,133,27,130,29,130,31,136,29,131,63,131,3,65,63,5,132,5,132,205,130,9,33,0,0,131,9,137,119,32,3,132,19,138,243,130,55,32,1,132,35,135,19,131,201, 136,11,132,143,137,13,130,41,32,0,131,3,144,35,33,128,0,135,1,131,223,131,3,141,17,134,13,136,63,134,15,136,53,143,15,130,96,33,0,3,131,4,130,3,34,28,0,1,130,5,34,0,0,76,130,17,131, 9,36,28,0,4,0,48,130,17,46,8,0,8,0,2,0,0,0,127,0,255,32,172,255,255,130,9,34,0,0,129,132,9,130,102,33,223,213,134,53,132,22,33,1,6,132,6,64,4,215,32,129,165,216,39,177,0,1,141,184, 1,255,133,134,45,33,198,0,193,1,8,190,244,1,28,1,158,2,20,2,136,2,252,3,20,3,88,3,156,3,222,4,20,4,50,4,80,4,98,4,162,5,22,5,102,5,188,6,18,6,116,6,214,7,56,7,126,7,236,8,78,8,108, 8,150,8,208,9,16,9,74,9,136,10,22,10,128,11,4,11,86,11,200,12,46,12,130,12,234,13,94,13,164,13,234,14,80,14,150,15,40,15,176,16,18,16,116,16,224,17,82,17,182,18,4,18,110,18,196,19, 76,19,172,19,246,20,88,20,174,20,234,21,64,21,128,21,166,21,184,22,18,22,126,22,198,23,52,23,142,23,224,24,86,24,186,24,238,25,54,25,150,25,212,26,72,26,156,26,240,27,92,27,200,28, 4,28,76,28,150,28,234,29,42,29,146,29,210,30,64,30,142,30,224,31,36,31,118,31,166,31,166,32,16,130,1,52,46,32,138,32,178,32,200,33,20,33,116,33,152,33,238,34,98,34,134,35,12,130,1, 33,128,35,131,1,60,152,35,176,35,216,36,0,36,74,36,104,36,144,36,174,37,6,37,96,37,130,37,248,37,248,38,88,38,170,130,1,8,190,216,39,64,39,154,40,10,40,104,40,168,41,14,41,32,41,184, 41,248,42,54,42,96,42,96,43,2,43,42,43,94,43,172,43,230,44,32,44,52,44,154,45,40,45,92,45,120,45,170,45,232,46,38,46,166,47,38,47,182,47,244,48,94,48,200,49,62,49,180,50,30,50,158, 51,30,51,130,51,238,52,92,52,206,53,58,53,134,53,212,54,38,54,114,54,230,55,118,55,216,56,58,56,166,57,18,57,116,57,174,58,46,58,154,59,6,59,124,59,232,60,58,60,150,61,34,61,134,61, 236,62,86,62,198,63,42,63,154,64,18,64,106,64,208,65,54,65,162,66,8,66,64,66,122,66,184,66,240,67,98,67,204,68,42,68,138,68,238,69,88,69,182,69,226,70,84,70,180,71,20,71,122,71,218, 72,84,72,198,73,64,0,36,70,21,8,8,77,3,0,7,0,11,0,15,0,19,0,23,0,27,0,31,0,35,0,39,0,43,0,47,0,51,0,55,0,59,0,63,0,67,0,71,0,75,0,79,0,83,0,87,0,91,0,95,0,99,0,103,0,107,0,111,0,115, 0,119,0,123,0,127,0,131,0,135,0,139,0,143,0,0,17,53,51,21,49,150,3,32,5,130,23,32,33,130,3,211,7,151,115,32,128,133,0,37,252,128,128,2,128,128,190,5,133,74,32,4,133,6,206,5,42,0,7, 1,128,0,0,2,0,4,0,0,65,139,13,37,0,1,53,51,21,7,146,3,32,3,130,19,32,1,141,133,32,3,141,14,131,13,38,255,0,128,128,0,6,1,130,84,35,2,128,4,128,140,91,132,89,32,51,65,143,6,139,7,33, 1,0,130,57,32,254,130,3,32,128,132,4,32,4,131,14,138,89,35,0,0,24,0,130,0,33,3,128,144,171,66,55,33,148,115,65,187,19,32,5,130,151,143,155,163,39,32,1,136,182,32,253,134,178,132,7, 132,200,145,17,32,3,65,48,17,165,17,39,0,0,21,0,128,255,128,3,65,175,17,65,3,27,132,253,131,217,139,201,155,233,155,27,131,67,131,31,130,241,33,255,0,131,181,137,232,132,15,132,4,138, 247,34,255,0,128,179,238,32,0,130,0,32,20,65,239,48,33,0,19,67,235,10,32,51,65,203,14,65,215,11,32,7,154,27,135,39,32,33,130,35,33,128,128,130,231,32,253,132,231,32,128,132,232,34, 128,128,254,133,13,136,8,32,253,65,186,5,130,36,130,42,176,234,133,231,34,128,0,0,66,215,44,33,0,1,68,235,6,68,211,19,32,49,68,239,14,139,207,139,47,66,13,7,32,51,130,47,33,1,0,130, 207,35,128,128,1,0,131,222,131,5,130,212,130,6,131,212,32,0,130,10,133,220,130,233,130,226,32,254,133,255,178,233,39,3,1,128,3,0,2,0,4,68,15,7,68,99,12,130,89,130,104,33,128,4,133, 93,130,10,38,0,0,11,1,0,255,0,68,63,16,70,39,9,66,215,8,32,7,68,77,6,68,175,14,32,29,68,195,6,132,7,35,2,0,128,255,131,91,132,4,65,178,5,141,111,67,129,23,165,135,140,107,142,135,33, 21,5,69,71,6,131,7,33,1,0,140,104,132,142,130,4,137,247,140,30,68,255,12,39,11,0,128,0,128,3,0,3,69,171,15,67,251,7,65,15,8,66,249,11,65,229,7,67,211,7,66,13,7,35,1,128,128,254,133, 93,32,254,131,145,132,4,132,18,32,2,151,128,130,23,34,0,0,9,154,131,65,207,8,68,107,15,68,51,7,32,7,70,59,7,135,121,130,82,32,128,151,111,41,0,0,4,0,128,255,0,1,128,1,137,239,33,0, 37,70,145,10,65,77,10,65,212,14,37,0,0,0,5,0,128,66,109,5,70,123,10,33,0,19,72,33,18,133,237,70,209,11,33,0,2,130,113,137,119,136,115,33,1,0,133,43,130,5,34,0,0,10,69,135,6,70,219, 13,66,155,7,65,9,12,66,157,11,66,9,11,32,7,130,141,132,252,66,151,9,137,9,66,15,30,36,0,20,0,128,0,130,218,71,11,42,68,51,8,65,141,7,73,19,15,69,47,23,143,39,66,81,7,32,1,66,55,6,34, 1,128,128,68,25,5,69,32,6,137,6,136,25,32,254,131,42,32,3,66,88,26,148,26,32,0,130,0,32,14,164,231,70,225,12,66,233,7,67,133,19,71,203,15,130,161,32,255,130,155,32,254,139,127,134, 12,164,174,33,0,15,164,159,33,59,0,65,125,20,66,25,7,32,5,68,191,6,66,29,7,144,165,65,105,9,35,128,128,255,0,137,2,133,182,164,169,33,128,128,197,171,130,155,68,235,7,32,21,70,77,19, 66,21,10,68,97,8,66,30,5,66,4,43,34,0,17,0,71,19,41,65,253,20,71,25,23,65,91,15,65,115,7,34,2,128,128,66,9,8,130,169,33,1,0,66,212,13,132,28,72,201,43,35,0,0,0,18,66,27,38,76,231,5, 68,157,20,135,157,32,7,68,185,13,65,129,28,66,20,5,32,253,66,210,11,65,128,49,133,61,32,0,65,135,6,74,111,37,72,149,12,66,203,19,65,147,19,68,93,7,68,85,8,76,4,5,33,255,0,133,129,34, 254,0,128,68,69,8,181,197,34,0,0,12,65,135,32,65,123,20,69,183,27,133,156,66,50,5,72,87,10,67,137,32,33,0,19,160,139,78,251,13,68,55,20,67,119,19,65,91,36,69,177,15,32,254,143,16,65, 98,53,32,128,130,0,32,0,66,43,54,70,141,23,66,23,15,131,39,69,47,11,131,15,70,129,19,74,161,9,36,128,255,0,128,254,130,153,65,148,32,67,41,9,34,0,0,4,79,15,5,73,99,10,71,203,8,32,3, 72,123,6,72,43,8,32,2,133,56,131,99,130,9,34,0,0,6,72,175,5,73,159,14,144,63,135,197,132,189,133,66,33,255,0,73,6,7,70,137,12,35,0,0,0,10,130,3,73,243,25,67,113,12,65,73,7,69,161,7, 138,7,37,21,2,0,128,128,254,134,3,73,116,27,33,128,128,130,111,39,12,0,128,1,0,3,128,2,72,219,21,35,43,0,47,0,67,47,20,130,111,33,21,1,68,167,13,81,147,8,133,230,32,128,77,73,6,32, 128,131,142,134,18,130,6,32,255,75,18,12,131,243,37,128,0,128,3,128,3,74,231,21,135,123,32,29,134,107,135,7,32,21,74,117,7,135,7,134,96,135,246,74,103,23,132,242,33,0,10,67,151,28, 67,133,20,66,141,11,131,11,32,3,77,71,6,32,128,130,113,32,1,81,4,6,134,218,66,130,24,131,31,34,0,26,0,130,0,77,255,44,83,15,11,148,155,68,13,7,32,49,78,231,18,79,7,11,73,243,11,32, 33,65,187,10,130,63,65,87,8,73,239,19,35,0,128,1,0,131,226,32,252,65,100,6,32,128,139,8,33,1,0,130,21,32,253,72,155,44,73,255,20,32,128,71,67,8,81,243,39,67,15,20,74,191,23,68,121, 27,32,1,66,150,6,32,254,79,19,11,131,214,32,128,130,215,37,2,0,128,253,0,128,136,5,65,220,24,147,212,130,210,33,0,24,72,219,42,84,255,13,67,119,16,69,245,19,72,225,19,65,3,15,69,93, 19,131,55,132,178,71,115,14,81,228,6,142,245,33,253,0,132,43,172,252,65,16,11,75,219,8,65,219,31,66,223,24,75,223,10,33,29,1,80,243,10,66,175,8,131,110,134,203,133,172,130,16,70,30, 7,164,183,130,163,32,20,65,171,48,65,163,36,65,143,23,65,151,19,65,147,13,65,134,17,133,17,130,216,67,114,5,164,217,65,137,12,72,147,48,79,71,19,74,169,22,80,251,8,65,173,7,66,157, 15,74,173,15,32,254,65,170,8,71,186,45,72,131,6,77,143,40,187,195,152,179,65,123,38,68,215,57,68,179,15,65,85,7,69,187,14,32,21,66,95,15,67,19,25,32,1,83,223,6,32,2,76,240,7,77,166, 43,65,8,5,130,206,32,0,67,39,54,143,167,66,255,19,82,193,11,151,47,85,171,5,67,27,17,132,160,69,172,11,69,184,56,66,95,6,33,12,1,130,237,32,2,68,179,27,68,175,16,80,135,15,72,55,7, 71,87,12,73,3,12,132,12,66,75,32,76,215,5,169,139,147,135,148,139,81,12,12,81,185,36,75,251,7,65,23,27,76,215,9,87,165,12,65,209,15,72,157,7,65,245,31,32,128,71,128,6,32,1,82,125,5, 34,0,128,254,131,169,32,254,131,187,71,180,9,132,27,32,2,88,129,44,32,0,78,47,40,65,79,23,79,171,14,32,21,71,87,8,72,15,14,65,224,33,130,139,74,27,62,93,23,7,68,31,7,75,27,7,139,15, 74,3,7,74,23,27,65,165,11,65,177,15,67,123,5,32,1,130,221,32,252,71,96,5,74,12,12,133,244,130,25,34,1,0,128,130,2,139,8,93,26,8,65,9,32,65,57,14,140,14,32,0,73,79,67,68,119,11,135, 11,32,51,90,75,14,139,247,65,43,7,131,19,139,11,69,159,11,65,247,6,36,1,128,128,253,0,90,71,9,33,1,0,132,14,32,128,89,93,14,69,133,6,130,44,131,30,131,6,65,20,56,33,0,16,72,179,40, 75,47,12,65,215,19,74,95,19,65,43,11,131,168,67,110,5,75,23,17,69,106,6,75,65,5,71,204,43,32,0,80,75,47,71,203,15,159,181,68,91,11,67,197,7,73,101,13,68,85,6,33,128,128,130,214,130, 25,32,254,74,236,48,130,194,37,0,18,0,128,255,128,77,215,40,65,139,64,32,51,80,159,10,65,147,39,130,219,84,212,43,130,46,75,19,97,74,33,11,65,201,23,65,173,31,33,1,0,79,133,6,66,150, 5,67,75,48,85,187,6,70,207,37,32,71,87,221,13,73,163,14,80,167,15,132,15,83,193,19,82,209,8,78,99,9,72,190,11,77,110,49,89,63,5,80,91,35,99,63,32,70,235,23,81,99,10,69,148,10,65,110, 36,32,0,65,99,47,95,219,11,68,171,51,66,87,7,72,57,7,74,45,17,143,17,65,114,50,33,14,0,65,111,40,159,195,98,135,15,35,7,53,51,21,100,78,9,95,146,16,32,254,82,114,6,32,128,67,208,37, 130,166,99,79,58,32,17,96,99,14,72,31,19,72,87,31,82,155,7,67,47,14,32,21,131,75,134,231,72,51,17,72,78,8,133,8,80,133,6,33,253,128,88,37,9,66,124,36,72,65,12,134,12,71,55,43,66,139, 27,85,135,10,91,33,12,65,35,11,66,131,11,71,32,8,90,127,6,130,244,71,76,11,168,207,33,0,12,66,123,32,32,0,65,183,15,68,135,11,66,111,7,67,235,11,66,111,15,32,254,97,66,12,160,154,67, 227,52,80,33,15,87,249,15,93,45,31,75,111,12,93,45,11,77,99,9,160,184,81,31,12,32,15,98,135,30,104,175,7,77,249,36,69,73,15,78,5,12,32,254,66,151,19,34,128,128,4,87,32,12,149,35,133, 21,96,151,31,32,19,72,35,5,98,173,15,143,15,32,21,143,99,158,129,33,0,0,65,35,52,65,11,15,147,15,98,75,11,33,1,0,143,151,132,15,32,254,99,200,37,132,43,130,4,39,0,10,0,128,1,128,3, 0,104,151,14,97,187,20,69,131,15,67,195,11,87,227,7,33,128,128,132,128,33,254,0,68,131,9,65,46,26,42,0,0,0,7,0,0,255,128,3,128,0,88,223,15,33,0,21,89,61,22,66,209,12,65,2,12,37,0,2, 1,0,3,128,101,83,8,36,0,1,53,51,29,130,3,34,21,1,0,66,53,8,32,0,68,215,6,100,55,25,107,111,9,66,193,11,72,167,8,73,143,31,139,31,33,1,0,131,158,32,254,132,5,33,253,128,65,16,9,133, 17,89,130,25,141,212,33,0,0,93,39,8,90,131,25,93,39,14,66,217,6,106,179,8,159,181,71,125,15,139,47,138,141,87,11,14,76,23,14,65,231,26,140,209,66,122,8,81,179,5,101,195,26,32,47,74, 75,13,69,159,11,83,235,11,67,21,16,136,167,131,106,130,165,130,15,32,128,101,90,24,134,142,32,0,65,103,51,108,23,11,101,231,15,75,173,23,74,237,23,66,15,6,66,46,17,66,58,17,65,105, 49,66,247,55,71,179,12,70,139,15,86,229,7,84,167,15,32,1,95,72,12,89,49,6,33,128,128,65,136,38,66,30,9,32,0,100,239,7,66,247,29,70,105,20,65,141,19,69,81,15,130,144,32,128,83,41,5, 32,255,131,177,68,185,5,133,126,65,97,37,32,0,130,0,33,21,0,130,55,66,195,28,67,155,13,34,79,0,83,66,213,13,73,241,19,66,59,19,65,125,11,135,201,66,249,16,32,128,66,44,11,66,56,17, 68,143,8,68,124,38,67,183,12,96,211,9,65,143,29,112,171,5,32,0,68,131,63,34,33,53,51,71,121,11,32,254,98,251,16,32,253,74,231,10,65,175,37,133,206,37,0,0,8,1,0,0,107,123,11,113,115, 9,33,0,1,130,117,131,3,73,103,7,66,51,18,66,44,5,133,75,70,88,5,32,254,65,39,12,68,80,9,34,12,0,128,107,179,28,68,223,6,155,111,86,147,15,32,2,131,82,141,110,33,254,0,130,15,32,4,103, 184,15,141,35,87,176,5,83,11,5,71,235,23,114,107,11,65,189,16,70,33,15,86,153,31,135,126,86,145,30,65,183,41,32,0,130,0,32,10,65,183,24,34,35,0,39,67,85,9,65,179,15,143,15,33,1,0,65, 28,17,157,136,130,123,32,20,130,3,32,0,97,135,24,115,167,19,80,71,12,32,51,110,163,14,78,35,19,131,19,155,23,77,229,8,78,9,17,151,17,67,231,46,94,135,8,73,31,31,93,215,56,82,171,25, 72,77,8,162,179,169,167,99,131,11,69,85,19,66,215,15,76,129,13,68,115,22,72,79,35,67,113,5,34,0,0,19,70,31,46,65,89,52,73,223,15,85,199,33,95,33,8,132,203,73,29,32,67,48,16,177,215, 101,13,15,65,141,43,69,141,15,75,89,5,70,0,11,70,235,21,178,215,36,10,0,128,0,0,71,207,24,33,0,19,100,67,6,80,215,11,66,67,7,80,43,12,71,106,7,80,192,5,65,63,5,66,217,26,33,0,13,156, 119,68,95,5,72,233,12,134,129,85,81,11,76,165,20,65,43,8,73,136,8,75,10,31,38,128,128,0,0,0,13,1,130,4,32,3,106,235,29,114,179,12,66,131,23,32,7,77,133,6,67,89,12,131,139,116,60,9, 89,15,37,32,0,74,15,7,103,11,22,65,35,5,33,55,0,93,81,28,67,239,23,78,85,5,107,93,14,66,84,17,65,193,26,74,183,10,66,67,34,143,135,79,91,15,32,7,117,111,8,75,56,9,84,212,9,154,134, 32,0,130,0,32,18,130,3,70,171,41,83,7,16,70,131,19,84,191,15,84,175,19,84,167,30,84,158,12,154,193,68,107,15,33,0,0,65,79,42,65,71,7,73,55,7,118,191,16,83,180,9,32,255,76,166,9,154, 141,32,0,130,0,69,195,52,65,225,15,151,15,75,215,31,80,56,10,68,240,17,100,32,9,70,147,39,65,93,12,71,71,41,92,85,15,84,135,23,78,35,15,110,27,10,84,125,8,107,115,29,136,160,38,0,0, 14,0,128,255,0,82,155,24,67,239,8,119,255,11,69,131,11,77,29,6,112,31,8,134,27,105,203,8,32,2,75,51,11,75,195,12,74,13,29,136,161,37,128,0,0,0,11,1,130,163,82,115,8,125,191,17,69,35, 12,74,137,15,143,15,32,1,65,157,12,136,12,161,142,65,43,40,65,199,6,65,19,24,102,185,11,76,123,11,99,6,12,135,12,32,254,130,8,161,155,101,23,9,39,8,0,0,1,128,3,128,2,78,63,17,72,245, 12,67,41,11,90,167,9,32,128,97,49,9,32,128,109,51,14,132,97,81,191,8,130,97,125,99,12,121,35,9,127,75,15,71,79,12,81,151,23,87,97,7,70,223,15,80,245,16,105,97,15,32,254,113,17,6,32, 128,130,8,105,105,8,76,122,18,65,243,21,74,63,7,38,4,1,0,255,0,2,0,119,247,28,133,65,32,255,141,91,35,0,0,0,16,67,63,36,34,59,0,63,77,59,9,119,147,11,143,241,66,173,15,66,31,11,67, 75,8,81,74,16,32,128,131,255,87,181,42,127,43,5,34,255,128,2,120,235,11,37,19,0,23,0,0,37,109,191,14,118,219,7,127,43,14,65,79,14,35,0,0,0,3,73,91,5,130,5,38,3,0,7,0,11,0,0,70,205, 11,88,221,12,32,0,73,135,7,87,15,22,73,135,10,79,153,15,97,71,19,65,49,11,32,1,131,104,121,235,11,80,65,11,142,179,144,14,81,123,46,32,1,88,217,5,112,5,8,65,201,15,83,29,15,122,147, 11,135,179,142,175,143,185,67,247,39,66,199,7,35,5,0,128,3,69,203,15,123,163,12,67,127,7,130,119,71,153,10,141,102,70,175,8,32,128,121,235,30,136,89,100,191,11,116,195,11,111,235,15, 72,39,7,32,2,97,43,5,132,5,94,67,8,131,8,125,253,10,32,3,65,158,16,146,16,130,170,40,0,21,0,128,0,0,3,128,5,88,219,15,24,64,159,32,135,141,65,167,15,68,163,10,97,73,49,32,255,82,58, 7,93,80,8,97,81,16,24,67,87,52,34,0,0,5,130,231,33,128,2,80,51,13,65,129,8,113,61,6,132,175,65,219,5,130,136,77,152,17,32,0,95,131,61,70,215,6,33,21,51,90,53,10,78,97,23,105,77,31, 65,117,7,139,75,24,68,195,9,24,64,22,9,33,0,128,130,11,33,128,128,66,25,5,121,38,5,134,5,134,45,66,40,36,66,59,18,34,128,0,0,66,59,81,135,245,123,103,19,120,159,19,77,175,12,33,255, 0,87,29,10,94,70,21,66,59,54,39,3,1,128,3,0,2,128,4,24,65,7,15,66,47,7,72,98,12,37,0,0,0,3,1,0,24,65,55,21,131,195,32,1,67,178,6,33,4,0,77,141,8,32,6,131,47,74,67,16,24,69,3,20,24, 65,251,7,133,234,130,229,94,108,17,35,0,0,6,0,141,175,86,59,5,162,79,85,166,8,70,112,13,32,13,24,64,67,26,24,71,255,7,123,211,12,80,121,11,69,215,15,66,217,11,69,71,10,131,113,132, 126,119,90,9,66,117,19,132,19,32,0,130,0,24,64,47,59,33,7,0,73,227,5,68,243,15,85,13,12,76,37,22,74,254,15,130,138,33,0,4,65,111,6,137,79,65,107,16,32,1,77,200,6,34,128,128,3,75,154, 12,37,0,16,0,0,2,0,104,115,36,140,157,68,67,19,68,51,15,106,243,15,134,120,70,37,10,68,27,10,140,152,65,121,24,32,128,94,155,7,67,11,8,24,74,11,25,65,3,12,83,89,18,82,21,37,67,200, 5,130,144,24,64,172,12,33,4,0,134,162,74,80,14,145,184,32,0,130,0,69,251,20,32,19,81,243,5,82,143,8,33,5,53,89,203,5,133,112,79,109,15,33,0,21,130,71,80,175,41,36,75,0,79,0,83,121, 117,9,87,89,27,66,103,11,70,13,15,75,191,11,135,67,87,97,20,109,203,5,69,246,8,108,171,5,78,195,38,65,51,13,107,203,11,77,3,17,24,75,239,17,65,229,28,79,129,39,130,175,32,128,123,253, 7,132,142,24,65,51,15,65,239,41,36,128,128,0,0,13,65,171,5,66,163,28,136,183,118,137,11,80,255,15,67,65,7,74,111,8,32,0,130,157,32,253,24,76,35,10,103,212,5,81,175,9,69,141,7,66,150, 29,131,158,24,75,199,28,124,185,7,76,205,15,68,124,14,32,3,123,139,16,130,16,33,128,128,108,199,6,33,0,3,65,191,35,107,11,6,73,197,11,24,70,121,15,83,247,15,24,70,173,23,69,205,14, 32,253,131,140,32,254,136,4,94,198,9,32,3,78,4,13,66,127,13,143,13,32,0,130,0,33,16,0,24,69,59,39,109,147,12,76,253,19,24,69,207,15,69,229,15,130,195,71,90,10,139,10,130,152,73,43, 40,91,139,10,65,131,37,35,75,0,79,0,84,227,12,143,151,68,25,15,80,9,23,95,169,11,34,128,2,128,112,186,5,130,6,83,161,19,76,50,6,130,37,65,145,44,110,83,5,32,16,67,99,6,71,67,15,76, 55,17,140,215,67,97,23,76,69,15,77,237,11,104,211,23,77,238,11,65,154,43,33,0,10,83,15,28,83,13,20,67,145,19,67,141,14,97,149,21,68,9,15,86,251,5,66,207,5,66,27,37,82,1,23,127,71,12, 94,235,10,110,175,24,98,243,15,132,154,132,4,24,66,69,10,32,4,67,156,43,130,198,35,2,1,0,4,75,27,9,69,85,9,95,240,7,32,128,130,35,32,28,66,43,40,24,82,63,23,83,123,12,72,231,15,127, 59,23,116,23,19,117,71,7,24,77,99,15,67,111,15,71,101,8,36,2,128,128,252,128,127,60,11,32,1,132,16,130,18,141,24,67,107,9,32,3,68,194,15,175,15,38,0,11,0,128,1,128,2,80,63,25,32,0, 24,65,73,11,69,185,15,83,243,16,32,0,24,81,165,8,130,86,77,35,6,155,163,88,203,5,24,66,195,30,70,19,19,24,80,133,15,32,1,75,211,8,32,254,108,133,8,79,87,20,65,32,9,41,0,0,7,0,128,0, 0,2,128,2,68,87,15,66,1,16,92,201,16,24,76,24,17,133,17,34,128,0,30,66,127,64,34,115,0,119,73,205,9,66,43,11,109,143,15,24,79,203,11,90,143,15,131,15,155,31,65,185,15,86,87,11,35,128, 128,253,0,69,7,6,130,213,33,1,0,119,178,15,142,17,66,141,74,83,28,6,36,7,0,0,4,128,82,39,18,76,149,12,67,69,21,32,128,79,118,15,32,0,130,0,32,8,131,206,32,2,79,83,9,100,223,14,102, 113,23,115,115,7,24,65,231,12,130,162,32,4,68,182,19,130,102,93,143,8,69,107,29,24,77,255,12,143,197,72,51,7,76,195,15,132,139,85,49,15,130,152,131,18,71,81,23,70,14,11,36,0,10,0,128, 2,69,59,9,89,151,15,66,241,11,76,165,12,71,43,15,75,49,13,65,12,23,132,37,32,0,179,115,130,231,95,181,16,132,77,32,254,67,224,8,65,126,20,79,171,8,32,2,89,81,5,75,143,6,80,41,8,34, 2,0,128,24,81,72,9,32,0,130,0,35,17,0,0,255,77,99,39,95,65,36,67,109,15,24,69,93,11,77,239,5,95,77,23,35,128,1,0,128,24,86,7,8,132,167,32,2,69,198,41,130,202,33,0,26,120,75,44,24,89, 51,15,71,243,12,70,239,11,24,84,3,11,66,7,11,71,255,10,32,21,69,155,35,88,151,12,32,128,74,38,10,65,210,8,74,251,5,65,226,5,75,201,13,32,3,65,9,41,146,41,40,0,0,0,9,1,0,1,0,2,91,99, 19,32,35,106,119,13,70,219,15,83,239,12,137,154,32,2,67,252,19,36,128,0,0,4,1,130,196,32,2,130,8,91,107,8,32,0,135,81,24,73,211,8,132,161,73,164,13,36,0,8,0,128,2,105,123,26,139,67, 76,99,15,34,1,0,128,135,76,83,156,20,92,104,8,67,251,30,24,86,47,27,123,207,12,24,86,7,15,71,227,8,32,4,65,20,20,131,127,32,0,130,123,32,0,71,223,26,32,19,90,195,22,71,223,15,84,200, 6,32,128,133,241,24,84,149,9,67,41,25,36,0,0,0,22,0,88,111,49,32,87,66,21,5,77,3,27,123,75,7,71,143,19,135,183,71,183,19,130,171,74,252,5,131,5,89,87,17,32,1,132,18,130,232,68,11,10, 33,1,128,70,208,16,66,230,18,147,18,130,254,223,255,75,27,23,65,59,15,135,39,155,255,34,128,128,254,104,92,8,33,0,128,65,32,11,65,1,58,33,26,0,130,0,72,71,18,78,55,17,76,11,19,86,101, 12,75,223,11,89,15,11,24,76,87,15,75,235,15,131,15,72,95,7,85,71,11,72,115,11,73,64,6,34,1,128,128,66,215,9,34,128,254,128,134,14,33,128,255,67,102,5,32,0,130,16,70,38,11,66,26,57, 88,11,8,24,76,215,34,78,139,7,95,245,7,32,7,24,73,75,23,32,128,131,167,130,170,101,158,9,82,49,22,118,139,6,32,18,67,155,44,116,187,9,108,55,14,80,155,23,66,131,15,93,77,10,131,168, 32,128,73,211,12,24,75,187,22,32,4,96,71,20,67,108,19,132,19,120,207,8,32,5,76,79,15,66,111,21,66,95,8,32,3,190,211,111,3,8,211,212,32,20,65,167,44,34,75,0,79,97,59,13,32,33,112,63, 10,65,147,19,69,39,19,143,39,24,66,71,9,130,224,65,185,43,94,176,12,65,183,24,71,38,8,24,72,167,7,65,191,38,136,235,24,96,167,12,65,203,62,115,131,13,65,208,42,175,235,67,127,6,32, 4,76,171,29,114,187,5,32,71,65,211,5,65,203,68,72,51,8,164,219,32,0,172,214,71,239,58,78,3,27,66,143,15,77,19,15,147,31,35,33,53,51,21,66,183,10,173,245,66,170,30,150,30,34,0,0,23, 80,123,54,76,1,16,73,125,15,82,245,11,167,253,24,76,85,12,70,184,5,32,254,131,185,37,254,0,128,1,0,128,133,16,117,158,18,92,27,38,65,3,17,130,251,35,17,0,128,254,24,69,83,39,140,243, 121,73,19,109,167,7,81,41,15,24,95,175,12,102,227,15,121,96,11,24,95,189,7,32,3,145,171,154,17,24,77,47,9,33,0,5,70,71,37,68,135,7,32,29,117,171,11,69,87,15,24,79,97,19,24,79,149,23, 131,59,32,1,75,235,5,72,115,11,72,143,7,132,188,71,27,46,131,51,32,0,69,95,6,175,215,32,21,131,167,81,15,19,151,191,151,23,131,215,71,43,5,32,254,24,79,164,24,74,109,8,77,166,13,65, 176,26,88,162,5,98,159,6,171,219,120,247,6,79,29,8,99,169,10,103,59,19,65,209,35,131,35,91,25,19,112,94,15,83,36,8,173,229,33,20,0,88,75,43,71,31,12,65,191,71,33,1,0,130,203,32,254, 131,4,68,66,7,67,130,6,104,61,13,173,215,38,13,1,0,0,0,2,128,67,111,28,74,129,16,104,35,19,79,161,16,87,14,7,138,143,132,10,67,62,36,114,115,5,162,151,67,33,16,108,181,15,143,151,67, 5,5,24,100,242,15,170,153,34,0,0,14,65,51,34,32,55,79,75,9,32,51,74,7,10,65,57,38,132,142,32,254,72,0,14,139,163,32,128,80,254,8,67,158,21,65,63,7,32,4,72,227,27,95,155,12,67,119,19, 124,91,24,149,154,72,177,34,97,223,8,155,151,24,108,227,15,88,147,16,72,117,19,68,35,11,92,253,15,70,199,15,24,87,209,17,32,2,87,233,7,32,1,24,88,195,10,119,24,8,32,3,81,227,24,65, 125,21,35,128,128,0,25,76,59,48,24,90,187,9,97,235,12,66,61,11,91,105,19,24,79,141,11,24,79,117,15,24,79,129,27,90,53,13,130,13,32,253,131,228,24,79,133,40,69,70,8,66,137,31,65,33, 19,96,107,8,68,119,29,66,7,5,68,125,16,65,253,19,65,241,27,24,90,179,13,24,79,143,18,33,128,128,130,246,32,254,130,168,68,154,36,77,51,9,97,47,5,167,195,32,21,131,183,78,239,27,155, 195,78,231,14,201,196,77,11,6,32,5,73,111,37,97,247,12,77,19,31,155,207,78,215,19,162,212,69,17,14,66,91,19,80,143,57,78,203,39,159,215,32,128,93,134,8,24,80,109,24,66,113,15,169,215, 66,115,6,32,4,69,63,33,32,0,101,113,7,86,227,35,143,211,36,49,53,51,21,1,77,185,14,65,159,28,69,251,34,67,56,8,33,9,0,24,107,175,25,90,111,12,110,251,11,119,189,24,119,187,34,87,15, 9,32,4,66,231,37,90,39,7,66,239,8,84,219,15,69,105,23,24,85,27,27,87,31,11,33,1,128,76,94,6,32,1,85,241,7,33,128,128,106,48,10,33,128,128,69,136,11,133,13,24,79,116,49,84,236,8,24, 91,87,9,32,5,165,255,69,115,12,66,27,15,159,15,24,72,247,12,74,178,5,24,80,64,15,33,0,128,143,17,77,89,51,130,214,24,81,43,7,170,215,74,49,8,159,199,143,31,139,215,69,143,5,32,254, 24,81,50,35,181,217,84,123,70,143,195,159,15,65,187,16,66,123,7,65,175,15,65,193,29,68,207,39,79,27,5,70,131,6,32,4,68,211,33,33,67,0,83,143,14,159,207,143,31,140,223,33,0,128,24,80, 82,14,24,93,16,23,32,253,65,195,5,68,227,40,133,214,107,31,7,32,5,67,115,27,87,9,8,107,31,43,66,125,6,32,0,103,177,23,131,127,72,203,36,32,0,110,103,8,155,163,73,135,6,32,19,24,112, 99,10,65,71,11,73,143,19,143,31,126,195,5,24,85,21,9,24,76,47,14,32,254,24,93,77,36,68,207,11,39,25,0,0,255,128,3,128,4,66,51,37,95,247,13,82,255,24,76,39,19,147,221,66,85,27,24,118, 7,8,24,74,249,12,76,74,8,91,234,8,67,80,17,131,222,33,253,0,121,30,44,73,0,16,69,15,6,32,0,65,23,38,69,231,12,65,179,6,98,131,16,86,31,27,24,108,157,14,80,160,8,24,65,46,17,33,4,0, 96,2,18,144,191,65,226,8,68,19,5,171,199,80,9,15,180,199,67,89,5,32,255,24,79,173,28,174,201,24,79,179,50,32,1,24,122,5,10,82,61,10,180,209,83,19,8,32,128,24,80,129,27,111,248,43,131, 71,24,115,103,8,67,127,41,78,213,24,100,247,19,66,115,39,75,107,5,32,254,165,219,78,170,40,24,112,163,49,32,1,97,203,6,65,173,64,32,0,83,54,7,133,217,88,37,12,32,254,131,28,33,128, 3,67,71,44,84,183,6,32,5,69,223,33,96,7,7,123,137,16,192,211,24,112,14,9,32,255,67,88,29,68,14,10,84,197,38,33,0,22,116,47,50,32,87,106,99,9,116,49,15,89,225,15,97,231,23,70,41,19, 82,85,8,93,167,6,32,253,132,236,108,190,7,89,251,5,116,49,58,33,128,128,131,234,32,15,24,74,67,38,70,227,24,24,83,45,23,89,219,12,70,187,12,89,216,19,32,2,69,185,24,141,24,70,143,66, 24,82,119,56,78,24,10,32,253,133,149,132,6,24,106,233,7,69,198,48,178,203,81,243,12,68,211,15,106,255,23,66,91,15,69,193,7,100,39,10,24,83,72,16,176,204,33,19,0,88,207,45,68,21,12, 68,17,10,65,157,53,68,17,6,32,254,92,67,10,65,161,25,69,182,43,24,118,91,47,69,183,18,181,209,111,253,12,89,159,8,66,112,12,69,184,45,35,0,0,0,9,24,80,227,26,73,185,16,118,195,15,131, 15,33,1,0,65,59,15,66,39,27,160,111,66,205,12,148,111,143,110,33,128,128,156,112,24,81,199,8,75,199,23,66,117,20,155,121,32,254,68,126,12,72,213,29,134,239,149,123,89,27,16,148,117, 65,245,8,24,71,159,14,141,134,134,28,73,51,55,109,77,15,105,131,11,68,67,11,76,169,27,107,209,12,102,174,8,32,128,72,100,18,116,163,56,79,203,11,75,183,44,85,119,19,71,119,23,151,227, 32,1,93,27,8,65,122,5,77,102,8,110,120,20,66,23,8,66,175,17,66,63,12,133,12,79,35,8,74,235,33,67,149,16,69,243,15,78,57,15,69,235,16,67,177,7,151,192,130,23,67,84,29,141,192,174,187, 77,67,15,69,11,12,159,187,77,59,10,199,189,24,70,235,50,96,83,19,66,53,23,105,65,19,77,47,12,163,199,66,67,37,78,207,50,67,23,23,174,205,67,228,6,71,107,13,67,22,14,66,85,11,83,187, 38,124,47,49,95,7,19,66,83,23,67,23,19,24,96,78,17,80,101,16,71,98,40,33,0,7,88,131,22,24,89,245,12,84,45,12,102,213,5,123,12,9,32,2,126,21,14,43,255,0,128,128,0,0,20,0,128,255,128, 3,126,19,39,32,75,106,51,7,113,129,15,24,110,135,19,126,47,15,115,117,11,69,47,11,32,2,109,76,9,102,109,9,32,128,75,2,10,130,21,32,254,69,47,6,32,3,94,217,47,32,0,65,247,10,69,15,46, 65,235,31,65,243,15,101,139,10,66,174,14,65,247,16,72,102,28,69,17,14,84,243,9,165,191,88,47,48,66,53,12,32,128,71,108,6,203,193,32,17,75,187,42,73,65,16,65,133,52,114,123,9,167,199, 69,21,37,86,127,44,75,171,11,180,197,78,213,12,148,200,81,97,46,24,95,243,9,32,4,66,75,33,113,103,9,87,243,36,143,225,24,84,27,31,90,145,8,148,216,67,49,5,24,84,34,14,75,155,27,67, 52,13,140,13,36,0,20,0,128,255,24,135,99,46,88,59,43,155,249,80,165,7,136,144,71,161,23,32,253,132,33,32,254,88,87,44,136,84,35,128,0,0,21,81,103,5,94,47,44,76,51,12,143,197,151,15, 65,215,31,24,64,77,13,65,220,20,65,214,14,71,4,40,65,213,13,32,0,130,0,35,21,1,2,0,135,0,34,36,0,72,134,10,36,1,0,26,0,130,134,11,36,2,0,14,0,108,134,11,32,3,138,23,32,4,138,11,34, 5,0,20,134,33,34,0,0,6,132,23,32,1,134,15,32,18,130,25,133,11,37,1,0,13,0,49,0,133,11,36,2,0,7,0,38,134,11,36,3,0,17,0,45,134,11,32,4,138,35,36,5,0,10,0,62,134,23,32,6,132,23,36,3, 0,1,4,9,130,87,131,167,133,11,133,167,133,11,133,167,133,11,37,3,0,34,0,122,0,133,11,133,167,133,11,133,167,133,11,133,167,34,50,0,48,130,1,34,52,0,47,134,5,8,49,49,0,53,98,121,32, 84,114,105,115,116,97,110,32,71,114,105,109,109,101,114,82,101,103,117,108,97,114,84,84,88,32,80,114,111,103,103,121,67,108,101,97,110,84,84,50,48,48,52,47,130,2,53,49,53,0,98,0,121, 0,32,0,84,0,114,0,105,0,115,0,116,0,97,0,110,130,15,32,71,132,15,36,109,0,109,0,101,130,9,32,82,130,5,36,103,0,117,0,108,130,29,32,114,130,43,34,84,0,88,130,35,32,80,130,25,34,111, 0,103,130,1,34,121,0,67,130,27,32,101,132,59,32,84,130,31,33,0,0,65,155,9,34,20,0,0,65,11,6,130,8,135,2,33,1,1,130,9,8,120,1,1,2,1,3,1,4,1,5,1,6,1,7,1,8,1,9,1,10,1,11,1,12,1,13,1,14, 1,15,1,16,1,17,1,18,1,19,1,20,1,21,1,22,1,23,1,24,1,25,1,26,1,27,1,28,1,29,1,30,1,31,1,32,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,0,18,0,19,0,20,0,21,0, 22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,130,187,8,66,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44,0,45,0,46,0,47,0,48,0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0, 57,0,58,0,59,0,60,0,61,0,62,0,63,0,64,0,65,0,66,130,243,9,75,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,0,76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,91,0, 92,0,93,0,94,0,95,0,96,0,97,1,33,1,34,1,35,1,36,1,37,1,38,1,39,1,40,1,41,1,42,1,43,1,44,1,45,1,46,1,47,1,48,1,49,1,50,1,51,1,52,1,53,1,54,1,55,1,56,1,57,1,58,1,59,1,60,1,61,1,62,1, 63,1,64,1,65,0,172,0,163,0,132,0,133,0,189,0,150,0,232,0,134,0,142,0,139,0,157,0,169,0,164,0,239,0,138,0,218,0,131,0,147,0,242,0,243,0,141,0,151,0,136,0,195,0,222,0,241,0,158,0,170, 0,245,0,244,0,246,0,162,0,173,0,201,0,199,0,174,0,98,0,99,0,144,0,100,0,203,0,101,0,200,0,202,0,207,0,204,0,205,0,206,0,233,0,102,0,211,0,208,0,209,0,175,0,103,0,240,0,145,0,214,0, 212,0,213,0,104,0,235,0,237,0,137,0,106,0,105,0,107,0,109,0,108,0,110,0,160,0,111,0,113,0,112,0,114,0,115,0,117,0,116,0,118,0,119,0,234,0,120,0,122,0,121,0,123,0,125,0,124,0,184,0, 161,0,127,0,126,0,128,0,129,0,236,0,238,0,186,14,117,110,105,99,111,100,101,35,48,120,48,48,48,49,141,14,32,50,141,14,32,51,141,14,32,52,141,14,32,53,141,14,32,54,141,14,32,55,141, 14,32,56,141,14,32,57,141,14,32,97,141,14,32,98,141,14,32,99,141,14,32,100,141,14,32,101,141,14,32,102,140,14,33,49,48,141,14,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49, 141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,45,49,102,6,100,101,108,101,116,101,4,69,117,114, 111,140,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236, 32,56,141,236,32,56,141,236,32,56,65,220,13,32,57,65,220,13,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141, 239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,35,57,102,0,0,5,250,72,249,98,247, }; static const char* GetDefaultCompressedFontDataProggyClean(int* out_size) { *out_size = proggy_clean_ttf_compressed_size; return (const char*)proggy_clean_ttf_compressed_data; } //----------------------------------------------------------------------------- // [SECTION] Default font data (ProggyForever-Regular-minimal.ttf) //----------------------------------------------------------------------------- // Based on ProggyForever: https://github.com/ocornut/proggyforever // MIT license / Copyright (c) 2026 Disco Hello, Copyright (c) 2019,2023 Tristan Grimmer //----------------------------------------------------------------------------- // File: 'output/ProggyForever-Regular-minimal.ttf' (18556 bytes) // Exported using binary_to_compressed_c.exe -u8 "output/ProggyForever-Regular-minimal.ttf" proggy_forever_minimal_ttf static const unsigned int proggy_forever_minimal_ttf_compressed_size = 14562; static const unsigned char proggy_forever_minimal_ttf_compressed_data[14562] = { 87,188,0,0,0,0,0,0,0,0,72,124,0,4,0,0,55,0,1,0,0,0,14,0,128,0,3,0,96,70,70,84,77,176,111,174,190,0,0,72,96,130,21,40,28,71,68,69,70,0,136,0,105,130,15,32,64,130,15,44,30,79,83,47,50, 104,97,19,194,0,0,1,104,130,15,44,96,99,109,97,112,177,173,221,139,0,0,3,80,130,19,44,114,99,118,116,32,0,33,2,121,0,0,4,196,130,31,38,4,103,97,115,112,255,255,130,89,34,0,72,56,130, 15,56,8,103,108,121,102,239,245,108,207,0,0,6,76,0,0,62,224,104,101,97,100,44,57,58,3,130,27,32,236,130,3,33,54,104,130,16,35,4,62,0,230,130,75,32,36,130,15,39,36,104,109,116,120,24, 22,19,130,95,33,1,200,130,19,40,136,108,111,99,97,80,9,64,114,130,95,131,15,39,130,109,97,120,112,1,7,0,131,31,32,72,130,47,44,32,110,97,109,101,10,160,159,151,0,0,69,44,130,47,44, 68,112,111,115,116,70,77,175,253,0,0,70,112,130,15,32,197,132,235,32,1,130,9,42,224,136,151,95,15,60,245,0,11,3,232,130,55,36,0,229,175,187,66,132,7,42,178,59,232,255,225,255,68,1, 215,2,176,130,15,34,8,0,2,130,5,131,2,130,51,39,2,131,255,71,0,0,1,184,130,31,34,226,1,215,132,73,131,25,135,3,32,4,132,17,37,192,0,90,0,5,0,131,0,33,2,0,130,44,132,19,34,64,0,46,130, 11,38,0,0,4,1,184,1,144,131,29,35,2,138,2,187,130,17,32,140,133,7,38,1,223,0,49,1,2,0,138,0,37,128,0,0,7,16,0,136,123,33,0,88,130,0,37,0,64,0,32,32,172,133,131,34,2,175,0,131,63,33, 3,0,130,0,35,1,133,2,6,130,6,38,32,0,1,1,184,0,33,130,9,130,161,32,0,132,3,39,0,166,0,116,255,249,0,49,130,113,36,4,0,185,0,135,130,1,38,27,0,22,0,154,0,53,130,25,40,39,0,41,0,79,0, 50,0,45,130,17,32,49,130,11,33,46,0,131,17,36,166,0,143,0,24,132,1,34,48,255,225,130,55,34,38,0,47,130,23,44,52,0,58,0,35,0,42,0,66,0,65,0,19,130,79,32,19,130,47,38,35,0,44,0,13,0, 38,130,21,38,9,0,37,0,13,255,250,130,121,36,5,0,33,0,104,130,47,40,104,0,37,255,242,0,124,0,48,130,95,32,59,130,3,34,37,0,40,130,5,36,62,0,139,0,100,130,57,36,133,0,20,0,62,130,55, 32,50,130,19,40,69,0,69,0,87,0,54,0,29,130,167,32,20,130,107,36,64,0,63,0,188,130,3,36,22,0,21,0,159,130,7,36,40,0,55,0,5,130,17,34,64,0,109,130,33,34,92,0,50,130,5,42,109,0,101,0, 24,0,115,0,109,0,131,130,121,32,48,130,39,36,143,0,114,0,83,130,77,32,19,130,157,34,18,0,73,130,49,32,5,136,3,32,2,130,85,33,52,0,133,1,33,66,0,133,1,32,17,130,125,32,35,130,209,131, 3,36,46,0,1,0,46,132,1,32,47,130,51,32,42,130,25,32,38,130,213,135,3,34,5,0,59,130,99,32,37,132,3,34,51,0,68,132,1,32,42,130,189,33,43,0,135,1,36,24,0,12,0,61,134,1,32,26,130,131,38, 26,0,30,0,0,0,3,134,3,32,28,130,93,130,10,35,0,108,0,3,132,9,36,28,0,4,0,80,130,16,34,16,0,16,132,35,46,126,0,133,0,171,0,210,0,211,0,255,32,172,255,255,130,25,32,32,130,17,34,161, 0,174,130,17,32,212,131,17,45,255,227,255,221,255,194,255,192,255,95,255,191,224,19,132,67,130,36,139,2,34,1,6,0,136,22,35,1,2,0,0,66,53,9,32,0,133,0,32,1,130,142,8,148,4,5,6,7,8,9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69, 70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,0,132,133,135,137,145,149,155,160,159,161,163,162,164,166,168,167,169,170,172,171,173,174,176,178, 177,179,181,180,185,184,186,187,0,112,100,101,105,0,118,158,110,107,0,116,106,0,134,151,0,113,0,0,103,117,132,158,38,108,122,0,165,183,127,99,132,11,38,109,123,0,0,128,131,148,132, 11,130,4,37,182,0,190,60,0,191,130,8,34,0,0,119,130,5,47,130,138,129,139,136,141,142,143,140,50,147,0,146,153,154,152,130,18,32,111,130,3,32,120,130,3,130,2,34,33,2,121,130,5,33,42, 0,133,1,9,155,68,0,86,0,134,0,210,1,14,1,104,1,116,1,148,1,180,1,212,1,232,2,6,2,20,2,38,2,54,2,100,2,122,2,172,2,232,3,2,3,60,3,116,3,134,3,210,4,10,4,40,4,82,4,102,4,122,4,142,4, 204,5,36,5,60,5,118,5,158,5,184,5,208,5,228,6,24,6,44,6,68,6,94,6,118,6,134,6,160,6,182,6,224,7,14,7,64,7,106,7,182,7,200,7,238,8,0,8,28,8,52,8,72,8,96,8,114,8,130,8,148,8,166,8,180, 8,194,9,12,9,58,9,92,9,140,9,182,9,214,10,14,10,44,10,74,10,110,10,134,10,150,10,198,10,230,11,4,11,50,11,96,11,130,11,188,11,216,11,250,12,14,12,42,12,66,12,114,12,142,12,198,12,210, 13,10,13,62,13,126,13,152,13,198,13,238,14,34,14,72,14,90,14,198,14,232,15,32,15,106,15,134,15,200,15,214,15,244,16,16,16,56,16,114,16,128,16,182,16,212,16,230,17,2,17,22,17,60,17, 86,17,132,17,194,18,20,18,76,18,108,18,140,18,176,18,228,19,24,19,74,19,110,19,168,19,198,19,228,20,6,20,56,20,86,20,116,20,150,20,200,20,252,21,44,21,92,21,144,21,212,22,24,22,50, 22,112,22,148,22,186,22,226,23,28,23,56,23,90,23,160,23,246,24,76,24,164,25,18,25,124,25,226,26,94,26,150,26,198,26,248,27,44,27,114,27,144,27,174,27,208,28,2,28,74,28,136,28,174,28, 212,28,254,29,60,29,118,29,172,29,230,30,16,30,58,30,104,30,166,30,206,30,244,31,48,31,112,0,0,0,2,0,33,0,0,1,42,2,154,0,3,0,7,0,46,177,1,0,47,60,178,7,4,0,237,50,177,6,5,220,60,178, 3,2,130,10,34,0,177,3,131,22,32,5,131,22,39,178,7,6,1,252,60,178,1,131,23,52,51,17,33,17,39,51,17,35,33,1,9,232,199,199,2,154,253,102,33,2,88,131,83,38,166,255,254,1,18,2,35,130,81, 61,13,0,0,54,50,22,20,6,34,38,52,55,35,3,55,51,21,198,44,32,32,44,32,85,59,14,1,83,105,31,131,11,36,122,1,4,91,91,130,135,38,116,1,70,1,68,2,7,132,135,37,0,1,35,53,51,7,130,3,8,40, 1,67,64,64,142,65,65,1,70,192,192,192,0,2,255,248,0,3,1,191,2,3,0,3,0,31,0,0,63,1,35,7,37,35,7,51,21,35,7,35,55,132,3,34,53,51,55,130,51,35,55,51,7,51,131,3,53,254,30,92,30,1,29,104, 31,92,106,38,59,38,92,38,58,38,96,110,31,98,112,134,11,34,90,201,115,130,0,33,54,143,130,0,35,54,115,54,144,130,0,32,0,130,85,8,37,49,255,241,1,135,2,22,0,6,0,11,0,52,0,0,55,62,1,53, 52,38,47,1,53,6,21,20,23,30,3,21,20,7,21,35,53,46,1,130,16,8,109,30,2,23,53,46,3,53,52,54,55,53,51,21,30,1,31,1,21,38,39,21,50,249,32,41,36,37,60,72,137,22,40,46,28,141,60,36,67,15, 15,6,21,69,37,31,44,43,22,75,65,60,25,55,15,15,43,67,3,81,7,37,31,30,32,10,73,135,12,61,44,30,4,15,28,50,33,114,15,44,43,1,14,6,6,63,4,11,22,3,162,6,15,26,42,29,53,67,9,45,42,2,11, 5,5,62,29,4,150,0,5,130,1,36,11,1,178,1,250,130,161,52,11,0,19,0,27,0,35,0,0,1,51,1,35,36,50,54,52,38,34,6,20,65,97,7,34,2,20,22,132,17,65,111,5,53,54,50,1,74,71,254,222,71,1,16,41, 30,30,41,29,7,84,60,60,84,59,174,130,9,34,29,41,122,130,9,51,59,84,1,249,254,21,47,27,39,27,27,39,115,56,79,56,56,79,1,26,131,11,33,27,7,131,11,8,59,56,0,0,0,3,0,4,255,246,1,179,2, 21,0,9,0,28,0,62,0,0,55,50,55,46,1,39,6,21,20,22,3,28,2,30,5,23,62,1,53,52,38,35,34,6,1,23,35,38,39,6,35,34,38,53,52,54,55,46,2,130,5,8,101,51,50,22,21,20,14,2,7,22,23,54,53,55,20, 206,52,37,25,110,24,65,80,41,1,2,3,6,8,12,7,37,37,31,23,24,35,1,2,67,72,13,21,60,89,72,104,52,44,22,19,15,63,60,61,51,15,32,25,24,89,58,36,61,44,36,28,131,28,54,59,48,62,1,125,1,7, 3,7,5,10,9,13,16,9,25,40,24,21,25,32,254,143,79,15,24,49,96,66,50,74,33,25,130,17,56,41,69,55,49,20,36,34,20,17,107,69,65,72,1,108,0,1,0,185,1,70,0,255,2,8,130,189,42,0,19,35,53,51, 254,68,68,1,71,193,130,23,8,59,135,255,193,1,49,2,62,0,17,0,0,1,14,1,20,22,23,35,46,4,52,62,2,63,1,1,49,43,55,55,43,60,4,15,38,29,24,23,32,33,11,11,2,61,60,177,161,177,60,6,21,68,70, 103,97,102,75,61,16,16,130,50,32,0,138,63,34,19,30,4,130,220,40,15,1,35,62,1,52,38,39,195,137,57,32,60,131,73,44,2,61,6,22,68,71,103,97,101,75,60,16,16,132,73,32,0,131,63,38,27,0,69, 1,157,1,199,132,127,8,41,7,23,7,39,21,35,53,7,39,55,39,55,23,53,51,21,55,1,157,131,131,33,128,64,129,32,131,131,32,129,64,128,1,77,71,71,50,70,142,142,70,50,134,7,49,0,1,0,22,0,74, 1,162,1,185,0,11,0,0,19,51,21,35,130,62,130,221,37,53,51,251,166,166,62,130,2,38,1,31,59,154,154,59,154,130,39,39,154,255,129,1,30,0,107,0,130,180,32,54,65,133,5,8,37,15,1,39,54,55, 46,1,53,52,209,45,31,21,31,30,11,11,26,55,10,17,25,107,32,22,32,61,40,32,7,7,37,48,41,1,31,21,22,131,163,54,53,0,227,1,131,1,32,0,3,0,0,37,33,53,33,1,131,254,178,1,78,228,60,132,191, 40,166,255,255,1,18,0,105,0,7,67,209,9,67,203,5,37,105,31,44,31,31,44,132,35,38,39,255,222,1,144,2,39,131,63,49,9,1,35,1,1,144,254,221,69,1,34,2,39,253,184,2,72,0,130,21,8,73,41,255, 247,1,143,2,14,0,10,0,21,0,27,0,0,55,50,62,3,53,52,39,7,22,19,34,14,3,21,20,23,55,38,39,50,16,35,34,16,220,17,28,30,21,14,4,190,27,57,17,28,31,20,14,4,189,26,57,178,178,178,51,7,26, 44,78,53,40,34,230,52,1,161,8,131,10,39,39,33,229,52,58,253,234,2,130,186,41,0,1,0,79,0,0,1,104,2,7,130,91,40,0,19,39,55,51,17,51,21,33,130,5,50,81,1,106,68,105,254,233,1,105,1,103, 81,78,254,55,61,61,1,122,130,43,32,50,130,43,37,134,2,17,0,31,0,130,43,49,62,2,51,50,30,2,21,20,6,15,1,33,21,33,53,52,55,54,67,247,5,8,58,35,34,6,7,55,1,8,29,81,37,48,72,36,17,44,59, 147,1,1,254,173,13,1,165,37,40,58,45,26,75,24,1,159,65,5,16,27,30,48,46,21,40,78,58,146,61,46,14,12,1,165,38,80,33,33,47,27,13,0,130,94,41,0,45,255,246,1,139,2,16,0,41,130,12,32,22, 130,93,38,35,34,38,47,1,53,30,130,108,40,54,53,52,38,43,1,53,51,50,131,8,130,101,37,15,1,53,54,51,50,131,35,8,62,1,26,112,107,85,34,78,22,23,7,25,80,41,57,65,65,49,64,64,46,56,55,45, 33,72,20,20,74,68,78,98,51,1,22,30,109,65,84,13,7,6,69,4,12,19,51,49,46,52,58,45,38,37,43,14,7,7,64,23,74,59,50,55,130,119,34,2,0,22,130,111,45,162,2,7,0,2,0,13,0,0,37,17,3,59,1,66, 44,6,54,19,51,1,16,179,237,88,88,67,241,208,100,181,1,18,254,238,58,123,123,73,1,66,130,46,33,0,49,130,171,32,135,130,51,37,40,0,0,19,17,33,130,47,130,143,37,30,3,21,20,14,2,137,181, 36,62,2,53,52,46,130,15,8,68,6,7,72,1,33,221,24,41,25,48,51,38,24,35,61,67,38,43,70,14,14,7,24,76,38,21,39,39,23,23,40,44,25,28,56,14,1,2,1,4,59,127,11,10,25,38,63,40,49,72,38,18,11, 6,5,71,4,11,19,11,25,48,33,33,48,25,12,13,7,130,157,55,41,255,248,1,143,2,8,0,9,0,37,0,0,55,50,53,52,35,34,6,21,20,22,19,65,139,5,130,119,42,53,52,62,2,51,50,22,31,1,21,38,131,26,8, 58,54,224,101,101,41,59,59,51,31,56,49,29,91,82,87,98,36,63,79,48,24,46,11,10,31,59,69,89,36,48,119,121,57,64,63,56,1,39,20,40,68,43,90,89,114,136,73,110,63,31,8,4,3,66,25,95,98,72, 0,130,0,38,1,0,46,0,0,1,138,130,227,32,6,130,227,8,42,53,33,21,3,35,19,46,1,91,197,78,190,1,203,59,29,254,23,1,202,0,0,3,0,39,255,246,1,145,2,17,0,15,0,31,0,52,0,0,54,50,62,2,130,243, 38,34,14,2,20,30,1,18,132,6,32,2,132,18,35,1,23,30,1,65,210,5,35,53,52,55,38,68,239,7,8,50,199,42,32,32,18,18,33,32,40,32,33,18,18,32,72,37,29,29,16,17,29,28,36,28,30,16,16,29,33,48, 52,104,76,77,104,99,84,95,71,70,96,45,7,20,42,63,42,19,7,7,19,130,6,43,20,1,166,6,17,37,53,37,16,6,6,16,130,6,59,17,191,16,72,52,69,78,77,70,107,33,30,91,62,69,70,61,90,0,2,0,41,0, 0,1,143,2,16,65,43,5,43,19,34,21,20,51,50,54,53,52,38,3,34,69,108,11,65,170,5,32,22,131,26,8,44,6,215,100,100,42,59,60,51,30,57,48,29,91,82,86,99,36,64,79,47,24,46,11,11,32,59,68,89, 35,1,216,120,120,56,64,64,56,254,217,20,39,68,44,89,90,65,44,6,35,3,4,65,24,65,44,5,55,2,0,166,0,81,1,18,1,171,0,7,0,15,0,0,18,34,38,52,54,50,22,20,6,71,199,6,38,242,44,32,32,44,32, 76,132,5,39,1,65,31,44,31,31,44,165,132,5,32,0,130,0,36,2,0,143,255,209,134,59,32,24,140,59,35,21,20,14,2,68,137,8,37,243,45,31,31,45,31,130,67,41,22,30,31,11,10,27,55,10,17,25,135, 74,37,32,22,33,60,41,31,68,150,10,8,37,0,1,0,24,0,84,1,160,1,176,0,6,0,0,19,37,21,13,1,21,37,24,1,135,254,200,1,56,254,121,1,30,145,62,111,112,62,145,132,123,38,24,0,152,1,159,1,114, 72,67,5,34,37,33,53,132,1,33,1,159,130,37,32,135,131,3,35,153,59,99,59,130,39,141,79,37,5,21,5,53,45,1,131,79,32,121,130,79,36,200,1,176,145,57,131,81,132,79,42,48,0,0,1,135,2,31,0, 34,0,42,130,121,33,50,22,130,195,44,3,29,1,35,53,52,62,4,53,52,38,35,34,132,209,34,62,4,16,65,25,6,58,226,83,82,29,42,42,29,70,21,33,38,33,22,52,43,25,45,29,22,4,5,52,2,10,34,38,61, 132,239,8,33,2,31,68,52,28,47,33,30,33,17,41,41,22,39,28,29,23,31,16,30,33,16,23,23,8,8,34,4,14,35,26,22,254,75,65,71,9,47,255,225,255,233,1,215,2,32,0,7,0,64,0,0,54,50,71,233,5,39, 5,23,14,4,35,34,46,3,71,98,10,46,21,35,53,6,35,34,38,52,54,51,50,22,31,1,50,132,151,8,122,6,21,20,30,2,51,50,62,3,214,60,43,43,60,43,1,5,38,2,11,38,43,71,37,63,103,67,46,20,141,105, 105,128,15,24,21,10,55,36,43,55,79,80,55,28,49,10,11,45,84,70,81,118,39,61,75,39,35,64,40,30,14,168,56,79,57,57,79,120,46,4,11,27,21,17,33,55,74,79,42,120,163,78,67,24,35,17,7,1,198, 19,31,93,131,94,26,13,13,28,34,57,124,104,53,87,55,30,13,20,20,13,0,0,0,2,0,5,0,0,1,178,2,7,130,9,8,38,10,0,0,55,51,3,55,19,35,39,35,7,35,19,141,158,79,45,169,77,41,193,40,77,169,192, 1,9,61,253,250,136,136,2,6,0,3,0,38,130,47,32,146,130,47,36,8,0,17,0,39,130,49,32,50,69,22,5,36,21,17,21,22,55,131,10,42,35,23,30,4,21,20,14,2,43,1,17,69,159,6,8,61,220,65,52,62,45, 121,71,39,84,49,42,77,12,23,34,25,18,30,50,57,31,194,177,43,62,32,15,49,58,47,52,47,43,189,1,147,156,1,2,4,76,42,33,183,3,7,20,27,48,31,38,57,32,14,2,5,25,43,46,24,42,55,130,110,47, 0,47,255,248,1,137,2,18,0,26,0,0,37,21,6,35,67,62,7,58,23,21,38,35,34,14,2,21,20,30,1,51,50,1,136,49,79,56,86,50,24,112,104,77,51,51,77,130,65,55,15,26,66,50,77,93,73,28,42,74,95,58, 117,151,32,71,45,33,59,74,46,62,91,59,130,233,130,183,33,1,144,130,195,34,6,0,13,131,193,33,53,52,130,172,58,19,50,17,16,35,7,17,199,125,125,84,84,201,200,160,58,202,201,254,109,1, 204,254,253,254,254,1,130,247,34,1,0,52,130,136,32,131,130,51,8,34,11,0,0,19,33,21,33,23,51,21,35,21,33,21,33,53,1,77,254,253,1,227,228,1,4,254,178,2,6,59,153,59,188,59,0,130,0,38, 1,0,58,0,0,1,126,130,47,32,9,132,47,34,35,31,1,130,47,40,35,58,1,67,249,1,181,182,74,130,41,35,158,1,59,241,130,34,33,0,35,130,219,32,148,130,219,32,36,130,219,38,39,35,53,51,21,14, 4,149,224,47,62,1,1,78,1,99,170,2,7,27,32,54,30,56,85,51,130,233,33,78,50,132,233,47,14,25,66,50,27,42,11,67,141,58,223,2,7,18,13,11,143,241,37,11,7,0,1,0,42,130,108,32,141,133,191, 57,51,17,51,21,51,53,51,17,35,53,35,21,43,75,204,75,75,204,2,6,212,212,253,250,247,246,130,39,32,66,130,39,32,117,133,39,36,19,53,33,21,35,130,43,55,5,53,55,17,67,1,50,116,116,254, 206,115,1,203,59,59,254,112,58,1,57,1,1,145,132,231,36,65,255,249,1,118,130,47,32,13,130,231,8,33,51,17,20,6,35,7,53,51,50,54,53,17,35,158,216,66,78,164,147,48,37,140,2,6,254,158,80, 90,1,57,48,58,1,47,130,50,39,0,1,0,19,0,0,1,165,137,139,59,55,51,7,19,35,3,7,21,19,75,233,85,212,221,90,180,57,2,6,230,230,212,254,206,1,4,57,202,130,48,34,1,0,49,130,47,32,135,130, 47,50,5,0,0,55,17,51,17,33,21,49,75,1,10,1,2,5,254,53,58,132,31,131,79,32,164,130,31,56,12,0,0,51,17,51,23,55,51,17,7,17,3,35,3,17,20,100,99,101,100,65,107,57,106,130,130,42,254,253, 251,1,1,197,254,239,1,19,254,130,52,34,1,0,41,130,83,32,142,65,159,5,35,51,3,51,19,130,86,130,48,38,45,3,98,186,72,95,186,130,46,55,90,1,166,253,250,1,166,254,90,0,0,2,0,35,255,250, 1,149,2,18,0,5,0,25,130,229,63,50,16,35,34,16,18,50,62,3,52,46,3,34,14,3,20,30,2,220,184,184,184,167,34,27,30,20,13,13,20,30,27,131,8,33,14,14,130,22,50,17,253,234,2,22,254,37,8,26, 43,78,106,78,44,26,8,8,26,44,130,8,33,43,26,130,179,38,2,0,44,255,255,1,139,132,179,32,30,131,83,71,166,5,45,43,1,21,55,50,22,21,20,6,43,1,28,1,30,130,3,8,47,49,35,17,50,206,32,46, 22,10,10,21,42,30,93,99,85,91,85,84,108,1,1,75,174,1,10,19,31,31,17,17,31,31,18,195,252,80,70,71,89,5,21,55,48,50,29,2,6,130,90,45,0,2,0,13,255,184,1,171,2,15,0,12,0,32,131,91,39,17, 20,7,23,21,35,39,6,143,182,39,198,184,40,84,48,82,40,58,147,187,40,14,254,245,123,67,97,44,91,27,149,193,38,2,0,38,0,0,1,145,130,191,34,9,0,24,130,99,34,35,21,22,73,125,5,35,15,1,35, 17,73,20,5,8,45,7,23,35,39,38,213,99,68,39,34,51,53,139,3,72,179,79,83,57,44,122,79,111,97,1,205,184,1,2,2,46,45,43,47,242,218,2,5,89,69,45,69,16,229,217,1,130,184,42,1,0,42,255,246, 1,141,2,17,0,54,130,91,44,21,46,2,35,34,6,21,20,30,7,23,30,3,73,135,16,32,39,69,75,6,8,95,30,2,23,1,109,6,21,67,35,62,61,6,14,11,25,12,31,9,33,1,23,42,48,29,101,89,43,78,18,18,7,27, 86,45,45,66,61,69,34,48,47,24,103,87,18,40,32,27,8,1,245,71,4,13,21,41,48,11,18,15,11,10,5,7,3,5,1,4,17,33,58,39,78,74,16,9,8,73,5,16,27,46,45,46,37,14,6,17,31,50,34,72,82,5,8,9,84, 93,5,32,9,130,143,32,175,130,235,32,7,130,233,56,53,33,21,35,17,7,17,10,1,164,172,75,1,203,59,59,254,54,1,1,203,0,1,0,37,130,187,32,147,130,35,32,24,66,163,5,32,20,74,49,5,8,50,17, 51,17,20,14,3,34,46,3,37,76,6,19,43,32,67,48,75,6,22,38,68,95,70,38,22,7,166,1,96,254,153,26,35,34,18,54,67,1,95,254,173,33,48,53,33,22,21,32,50,44,130,75,32,13,130,111,32,171,130, 75,8,32,6,0,0,51,3,51,27,1,51,3,174,161,73,134,134,72,161,2,6,254,58,1,198,253,250,0,1,255,249,0,0,1,190,130,35,32,12,135,35,131,38,47,35,11,1,75,81,63,58,63,83,64,57,64,81,73,72,73, 130,47,44,99,1,157,254,98,1,158,253,250,1,184,254,72,130,55,32,255,130,55,32,185,67,123,5,8,33,19,39,51,23,55,51,7,19,35,39,7,35,184,160,80,121,123,81,165,177,80,141,141,80,1,19,243, 195,195,243,254,237,218,218,130,139,32,5,130,47,32,178,130,47,36,8,0,0,55,3,131,47,50,3,21,35,182,176,79,135,134,80,177,75,234,1,28,228,228,254,227,233,130,34,33,0,33,130,4,32,151, 130,39,32,9,65,35,5,60,1,33,21,5,53,1,41,1,102,254,224,1,40,254,138,1,24,1,203,59,53,254,106,58,1,56,1,147,0,130,42,41,0,104,255,189,1,80,2,73,0,7,130,12,55,39,17,51,21,35,17,51,1, 79,162,162,231,231,2,18,1,253,226,56,2,140,0,1,0,76,151,10,41,19,1,35,1,109,1,35,70,254,222,76,150,7,130,31,138,67,52,19,51,17,35,53,51,17,7,104,231,231,163,163,2,73,253,116,56,2,30, 1,130,90,37,0,37,1,63,1,147,65,75,5,33,19,23,131,233,45,55,252,150,82,100,101,82,150,2,6,198,141,141,198,131,139,55,255,241,255,190,1,199,255,247,0,3,0,0,5,33,53,33,1,198,254,44,1, 212,66,56,131,27,39,0,124,1,160,1,60,2,57,131,27,131,63,39,207,108,82,109,2,57,153,153,130,27,8,175,2,0,48,255,246,1,136,1,143,0,17,0,54,0,0,55,50,62,3,53,34,35,38,14,4,21,20,22,39, 52,62,3,23,48,60,1,46,6,35,7,53,50,54,51,21,50,30,2,21,7,35,54,39,6,35,34,38,204,32,48,23,13,2,10,21,24,45,39,32,22,13,51,118,28,50,69,81,45,2,3,7,9,15,18,26,16,122,18,58,34,38,58, 52,29,1,61,2,2,38,99,64,79,44,26,33,49,27,17,1,2,3,10,18,28,20,37,35,72,36,51,28,16,1,1,14,7,17,10,16,10,12,7,5,2,54,2,1,12,33,65,47,241,25,27,61,63,0,0,2,0,50,255,247,1,134,2,49,0, 9,0,30,0,0,54,50,54,53,52,38,34,6,21,20,39,62,4,67,150,5,130,127,8,57,47,1,7,35,17,51,172,112,50,51,110,52,3,1,5,18,23,42,25,71,87,88,59,32,62,16,15,7,61,68,45,82,68,67,83,83,67,68, 212,3,8,20,16,12,102,101,98,106,29,15,15,49,2,47,0,1,0,59,130,91,32,125,130,239,35,20,0,0,1,131,84,36,22,51,50,55,21,72,197,7,8,34,23,21,38,1,13,65,72,72,65,62,49,47,78,85,110,110, 85,78,47,49,1,89,74,76,76,74,43,65,32,106,196,106,32,65,43,139,159,32,7,133,159,133,158,38,19,51,17,35,39,14,4,130,155,8,35,53,52,54,51,50,30,2,31,1,122,200,48,104,48,200,68,62,6,3, 8,26,27,41,20,59,88,87,70,25,42,24,17,3,4,45,150,131,158,51,1,109,253,209,49,3,7,21,15,13,106,98,101,102,12,17,17,6,7,0,130,0,34,2,0,37,130,163,32,147,130,163,37,5,0,26,0,0,19,130, 165,35,51,52,7,20,130,168,32,54,133,169,132,95,8,42,22,29,1,32,227,47,69,224,227,62,67,42,62,46,70,82,89,109,105,79,91,90,254,218,1,89,68,48,116,165,56,79,18,21,64,29,109,95,93,110, 97,73,48,130,83,42,1,0,40,0,3,1,144,2,36,0,19,132,247,37,29,1,51,21,35,17,130,1,33,53,51,130,77,61,59,1,21,1,40,35,31,125,125,68,122,122,65,64,108,1,237,25,37,75,57,254,217,1,39,57, 52,78,62,55,133,147,36,50,255,70,1,134,130,147,32,9,73,49,5,77,51,8,44,51,17,20,6,43,1,53,51,50,62,2,61,1,137,254,8,60,22,23,222,99,99,53,51,51,159,61,90,81,130,136,28,42,23,10,1,5, 18,24,42,25,71,91,91,60,48,52,28,53,142,150,82,68,65,77,1,80,254,110,86,86,58,22,36,37,18,59,2,6,16,12,10,101,100,98,101,20,29,130,162,41,0,62,0,1,1,122,2,49,0,18,74,253,5,8,45,17, 35,53,52,38,35,34,6,29,1,35,17,51,21,54,253,53,72,68,40,37,47,56,68,68,37,1,146,78,67,255,0,250,45,48,61,59,223,2,47,218,60,0,2,0,139,130,59,38,45,2,30,0,11,0,17,130,74,8,40,35,34, 61,1,52,59,1,50,29,1,20,7,53,51,17,35,17,1,35,56,10,10,56,9,161,161,68,1,202,10,64,9,9,64,10,127,59,254,123,1,74,130,232,34,2,0,100,130,231,32,84,130,59,34,12,0,24,130,121,130,47,32, 20,132,221,35,54,53,17,55,138,72,39,165,174,127,112,105,29,37,59,132,72,43,1,76,58,254,98,161,59,52,50,1,100,126,133,79,39,0,1,0,41,0,2,1,142,132,131,38,0,55,23,35,39,7,21,130,183, 53,17,55,51,210,188,81,154,51,70,70,181,80,245,243,201,45,156,2,27,254,201,161,130,118,130,47,32,133,130,179,36,51,2,8,0,5,130,117,131,164,41,35,133,173,69,104,2,7,253,250,1,72,63, 5,46,20,255,254,1,164,1,143,0,30,0,0,1,50,22,21,130,198,36,52,35,34,6,7,138,7,48,51,21,54,51,50,23,62,1,1,68,43,52,61,52,26,26,3,132,4,52,62,62,31,42,52,26,13,48,1,143,69,53,254,234, 1,5,84,34,37,254,238,130,6,42,33,36,254,236,1,133,18,29,49,22,27,130,128,32,1,65,111,5,33,1,147,65,111,20,32,23,65,111,9,33,64,4,65,112,12,35,1,133,48,60,66,31,5,42,42,255,247,1,141, 1,144,0,9,0,17,67,179,11,32,18,130,171,8,40,20,32,53,52,174,92,58,59,90,59,21,166,94,254,158,44,72,80,80,70,70,80,80,1,28,102,102,205,205,102,0,2,0,50,255,78,1,134,1,136,0,67,79,6, 38,54,53,52,34,21,20,19,131,57,8,62,6,35,34,46,2,47,1,21,35,17,51,23,62,4,176,103,48,199,107,66,89,87,71,25,41,24,18,3,3,68,61,7,1,6,20,25,41,38,82,67,148,148,67,1,16,105,99,101,103, 12,18,17,6,6,221,2,48,49,2,8,20,16,13,152,91,35,51,17,35,53,67,171,13,38,160,104,48,200,216,62,68,66,165,5,41,70,87,89,66,23,41,26,19,4,4,134,91,44,6,253,208,221,2,8,21,15,13,103,101, 99,105,132,99,36,0,0,1,0,69,130,4,36,115,1,142,0,19,130,7,35,50,22,31,1,71,239,5,33,29,1,130,173,8,36,7,62,1,1,12,29,52,11,11,4,16,56,31,56,71,68,68,2,17,69,1,142,15,7,8,65,5,12,20, 60,35,245,1,133,53,27,35,130,54,46,0,69,255,254,1,115,1,151,0,38,0,0,55,30,1,130,250,34,39,46,1,80,126,9,39,46,2,35,34,21,20,22,23,79,243,7,8,78,47,1,69,53,109,71,91,83,57,14,32,65, 44,33,62,14,15,6,19,63,33,90,36,57,70,69,88,73,31,70,20,20,88,27,14,32,35,49,15,15,53,44,22,38,36,20,11,6,6,62,4,11,17,57,25,29,8,11,46,55,57,66,12,6,6,0,0,0,1,0,87,0,6,1,97,2,4,0, 17,130,115,41,51,21,35,34,46,3,53,17,51,21,130,9,57,21,20,216,136,125,25,33,41,24,16,68,162,162,65,58,3,15,26,50,34,1,124,118,58,204,70,132,55,40,54,255,246,1,130,1,133,0,21,130,55, 33,53,51,80,10,5,34,55,51,19,65,74,6,8,32,54,68,86,44,62,3,68,1,64,1,3,17,23,45,27,79,73,145,244,241,102,53,39,251,254,124,42,2,7,19,13,12,74,132,67,32,29,130,128,32,155,130,67,53, 6,0,0,1,51,3,35,3,51,19,1,84,70,148,86,147,71,120,1,133,254,123,130,3,32,186,130,38,39,0,1,255,248,255,255,1,192,130,39,33,12,0,132,39,33,39,7,131,42,48,55,51,23,1,124,67,97,63,68, 65,65,97,68,68,53,76,60,130,47,34,123,215,215,130,5,34,208,192,192,132,55,33,0,20,130,55,32,163,130,55,35,11,0,0,37,71,43,5,57,39,51,23,55,51,1,1,162,78,122,120,78,162,149,76,110,109, 76,203,203,155,155,204,185,141,141,77,151,5,38,68,1,137,1,134,0,31,130,12,32,18,77,244,5,44,53,51,50,62,4,53,52,53,35,34,38,53,19,130,230,59,22,51,23,3,1,134,3,12,24,47,32,195,176, 16,23,13,8,3,1,133,71,72,1,68,29,41,137,1,130,122,56,135,48,17,47,52,36,59,12,24,22,34,17,14,4,2,78,49,1,6,229,49,50,1,1,73,132,239,32,64,130,143,32,119,130,95,8,41,14,0,0,55,51,23, 33,53,62,3,39,35,53,33,21,2,137,237,1,254,202,15,50,105,69,1,229,1,45,238,52,52,59,17,58,122,82,1,50,60,254,235,130,54,8,32,0,1,0,63,255,178,1,120,2,84,0,41,0,0,1,21,20,7,22,29,1,20, 22,59,1,21,34,35,34,46,3,61,1,83,197,7,130,9,33,62,3,130,22,8,61,35,34,6,1,11,72,72,38,42,29,31,13,38,54,27,14,3,38,43,51,51,43,38,3,14,27,54,38,44,29,42,38,1,215,100,95,17,17,96,99, 34,29,62,20,26,44,30,23,86,43,32,66,32,42,87,23,30,44,26,19,62,29,130,98,53,0,188,255,200,0,252,2,63,0,3,0,0,23,35,17,51,252,64,64,55,2,117,141,135,32,19,84,60,5,36,50,51,50,30,3,134, 142,130,119,130,9,33,14,3,69,35,5,130,141,55,55,38,172,37,42,29,30,14,37,54,27,15,2,38,44,51,51,44,38,2,15,27,54,37,130,132,41,37,72,72,1,115,100,33,29,62,19,131,129,33,87,42,130,129, 45,43,86,24,29,44,26,20,62,29,33,100,96,17,16,130,248,42,1,0,22,0,177,1,161,1,93,0,33,130,148,34,23,14,4,131,238,39,7,14,1,15,1,39,62,4,131,119,54,55,62,1,55,1,103,58,5,5,17,21,40, 25,25,43,30,28,31,16,20,25,3,3,143,16,62,1,66,13,21,21,45,24,20,26,35,36,21,2,3,45,21,21,13,21,22,44,24,21,26,36,35,22,3,3,45,20,130,229,32,21,130,95,45,163,0,106,0,15,0,31,0,47,0, 0,55,50,22,130,200,36,6,43,1,34,38,130,197,34,54,59,1,157,15,43,51,104,5,7,7,5,71,4,8,8,4,222,131,9,32,70,131,4,32,221,136,9,35,105,7,5,81,131,12,150,4,42,0,0,2,0,159,255,235,1,25, 2,9,90,167,5,33,18,34,82,231,5,59,7,51,23,7,35,53,245,50,35,35,50,35,84,46,29,1,101,1,145,35,49,35,35,49,105,196,155,155,130,51,42,63,255,147,1,121,1,242,0,5,0,28,130,177,48,17,14, 1,21,20,19,17,54,55,21,6,7,21,35,53,46,1,90,19,6,8,62,22,23,21,38,243,50,58,146,57,39,45,51,38,78,102,100,80,38,61,35,40,49,1,38,6,73,67,134,1,25,254,215,2,25,59,21,3,100,101,6,107, 92,95,104,4,97,98,5,20,60,25,0,1,0,40,255,255,1,144,2,16,0,27,130,89,38,51,21,33,53,51,53,35,130,3,81,218,6,32,21,71,122,5,8,39,51,21,35,202,197,254,153,87,73,73,82,79,33,52,9,10,48, 42,50,50,136,136,59,59,59,161,50,82,88,88,10,5,5,64,30,53,63,88,50,0,131,223,38,55,0,63,1,128,1,125,130,223,35,31,0,0,54,82,67,6,8,41,54,20,7,23,7,39,6,34,39,7,39,55,38,52,55,39,55, 23,54,50,23,55,23,7,187,65,46,46,65,46,204,22,60,34,62,31,73,31,62,34,60,22,137,10,49,148,44,61,43,43,61,67,73,30,57,35,60,19,19,60,35,57,30,137,10,75,219,10,36,24,0,0,1,7,130,160, 33,7,21,88,161,9,32,39,130,194,8,37,39,51,23,55,1,178,120,91,115,33,148,148,75,148,148,33,115,91,119,79,135,134,2,6,194,39,53,11,39,182,182,39,11,53,39,194,228,228,130,178,41,0,2,0, 188,255,206,0,252,2,57,83,195,5,36,23,35,17,51,53,67,9,5,40,64,64,50,1,8,92,1,7,0,130,35,59,64,255,188,1,119,2,16,0,15,0,75,0,0,37,62,1,53,52,38,39,38,39,14,1,21,20,22,23,69,167,11, 33,53,22,85,40,5,130,28,35,46,4,53,52,89,5,5,32,54,86,84,10,133,47,8,53,4,21,20,6,1,10,21,28,48,46,25,22,22,27,48,45,25,61,25,21,69,72,25,57,16,16,58,51,36,42,37,47,8,3,28,25,39,17, 14,38,33,25,21,68,74,24,54,14,14,51,51,37,40,36,130,22,33,29,24,131,22,42,142,12,35,15,26,44,25,14,13,12,36,132,7,63,37,18,34,23,44,66,10,5,5,57,26,32,25,20,30,26,4,2,16,14,28,23,34, 19,28,50,16,18,34,24,43,59,131,25,33,27,26,133,25,39,15,15,28,23,33,20,28,50,133,251,42,109,1,212,1,75,2,27,0,11,0,23,73,51,13,33,43,1,73,63,9,39,1,66,58,9,9,58,9,155,131,5,37,8,1, 213,9,52,9,136,2,48,0,0,3,255,248,0,40,1,192,1,218,0,7,0,15,0,36,65,245,9,33,18,50,92,21,5,33,22,52,130,255,35,23,21,38,35,75,119,11,8,53,142,156,111,111,156,111,95,188,133,133,188, 134,92,78,62,57,30,43,34,47,52,52,47,46,31,36,51,61,79,105,147,105,105,147,1,34,127,179,127,127,179,152,126,66,15,38,21,48,49,49,47,18,36,17,131,179,8,41,3,0,92,0,162,1,91,2,18,0,3, 0,18,0,53,0,0,37,35,53,51,39,34,35,34,14,4,21,20,22,51,50,54,7,52,62,2,23,60,1,46,2,88,232,5,8,122,62,6,51,50,22,29,1,35,39,6,35,34,38,1,91,247,247,55,5,8,25,24,37,19,20,8,36,22,49, 41,201,37,58,70,33,8,15,32,22,26,50,11,12,2,18,7,17,12,17,18,9,57,73,45,8,39,54,47,59,163,43,178,1,3,6,11,18,13,24,24,61,16,33,43,14,7,4,8,10,22,14,11,11,6,6,44,1,6,2,6,2,3,1,57,64, 159,38,45,44,0,2,0,50,0,49,1,134,1,112,0,6,0,13,0,0,1,21,7,23,21,39,53,55,133,5,50,1,134,112,112,174,7,111,111,173,173,1,112,67,93,93,66,145,28,79,132,5,35,146,0,0,4,65,59,8,56,9,0, 27,0,35,0,43,0,0,19,22,62,2,53,52,38,43,1,23,30,1,23,35,38,39,130,9,32,21,130,224,36,50,21,20,14,1,67,78,6,65,88,7,54,175,21,36,27,15,29,21,49,86,14,25,46,52,30,17,21,24,27,47,92,103, 34,147,65,89,10,53,1,13,1,2,6,18,15,19,20,102,5,33,72,44,26,33,103,242,74,21,32,172,65,94,11,130,172,41,0,109,1,183,1,75,1,235,0,3,130,12,130,106,38,1,74,220,220,1,183,51,66,31,5,45, 101,1,50,1,82,2,16,0,7,0,15,0,0,18,93,253,14,56,190,59,42,42,59,41,22,98,69,69,98,69,1,94,39,55,39,39,55,138,65,91,65,65,91,133,59,40,24,255,255,1,159,1,151,0,11,131,59,92,65,11,56, 19,33,53,33,251,164,164,62,165,165,62,164,254,121,1,135,1,36,59,115,115,59,114,254,105,87,43,5,40,115,1,133,1,69,2,173,0,24,130,143,130,245,37,7,51,21,35,53,54,85,161,5,8,47,34,6,15, 1,53,54,51,50,22,1,66,17,62,70,152,210,77,32,42,37,28,17,42,12,13,39,51,57,58,2,94,20,33,64,61,39,38,72,29,40,36,18,25,11,6,6,41,20,130,197,32,1,130,223,38,130,1,75,2,176,0,39,131, 79,32,6,88,180,10,91,20,23,8,51,7,22,1,75,68,55,21,49,15,14,50,42,26,48,43,31,31,31,29,36,30,27,21,44,12,12,47,44,49,63,33,30,71,1,214,37,47,7,4,4,42,18,26,25,25,27,38,22,21,18,22, 8,130,13,38,13,42,33,28,29,7,13,130,252,39,1,0,131,1,181,1,53,2,79,123,5,41,51,7,35,235,73,120,56,2,57,131,130,26,51,0,1,0,37,255,107,1,146,1,132,0,36,0,0,37,50,54,63,1,21,133,147, 33,14,4,130,154,36,39,21,35,17,51,66,199,5,8,63,53,55,51,16,21,20,1,120,7,12,4,3,27,29,19,26,4,3,1,4,16,22,42,27,26,47,11,60,64,45,37,43,59,3,65,49,4,2,2,52,15,26,13,13,2,7,18,14,11, 28,26,192,2,24,241,48,53,53,38,251,254,226,24,29,130,108,42,1,0,48,255,185,1,136,2,7,0,16,76,95,7,47,16,21,6,34,47,1,17,34,38,52,54,216,175,52,70,1,130,60,8,34,69,99,99,2,6,253,182, 2,32,253,229,5,2,1,1,1,39,85,121,85,0,0,1,0,159,0,198,1,25,1,63,0,7,0,0,66,114,7,39,195,50,35,35,50,35,1,63,130,4,33,35,50,131,35,36,143,255,119,1,41,130,43,130,95,35,33,22,21,20,65, 92,8,52,39,48,58,1,1,0,40,91,14,31,8,8,24,27,91,73,21,22,41,37,59,130,173,34,46,11,94,132,151,40,114,1,132,1,70,2,167,0,10,131,151,37,21,35,53,51,53,7,130,239,56,250,75,204,76,83,85, 51,1,171,38,38,212,14,40,13,0,3,0,83,0,162,1,101,2,16,130,9,37,13,0,21,0,0,37,130,41,8,47,2,34,6,21,20,22,50,54,53,52,22,32,53,52,54,50,22,21,1,86,249,249,88,69,46,45,71,45,56,254, 239,73,127,73,163,43,1,25,47,54,54,49,49,54,54,197,143,71,130,0,130,114,67,163,15,33,55,21,130,112,35,39,53,51,23,132,7,48,223,173,111,111,166,174,174,112,112,222,28,145,66,93,93,67, 146,132,6,41,0,4,0,19,255,162,1,165,2,64,130,127,36,6,0,17,0,28,130,242,38,23,5,39,23,51,53,55,70,111,8,33,55,3,130,72,32,51,131,14,8,44,53,51,1,155,9,254,120,9,198,102,51,43,43,50, 144,134,187,82,87,48,75,203,75,1,47,37,91,37,203,144,43,188,37,65,65,42,183,1,86,14,39,13,252,39,39,130,81,137,91,34,28,0,39,133,89,32,5,66,236,15,34,39,54,51,130,224,34,20,6,1,143, 100,34,1,73,86,66,254,7,42,41,13,12,1,39,51,57,59,28,254,252,138,113,52,190,77,39,39,71,30,39,36,19,25,12,6,6,42,19,51,27,23,45,1,226,134,123,34,4,0,18,132,215,32,70,134,215,32,56, 133,125,34,23,51,39,138,215,33,39,20,67,43,10,67,42,26,132,243,34,202,102,1,130,244,8,32,49,145,134,45,72,55,21,48,13,13,41,46,36,42,38,34,33,30,31,37,33,25,24,45,10,10,46,44,50,62, 33,30,72,65,11,13,8,68,168,36,47,7,4,4,41,18,29,47,28,37,23,21,20,20,8,4,4,42,12,41,32,29,29,8,14,0,0,2,0,73,255,247,1,111,2,27,0,30,0,38,0,0,55,50,54,63,1,21,14,2,35,34,38,53,52,62, 4,61,1,51,21,20,14,3,21,20,22,73,68,7,8,56,235,29,66,18,19,7,24,69,34,82,78,19,29,34,29,19,71,26,37,38,26,47,71,44,32,32,44,32,48,24,11,12,65,5,12,21,65,54,24,43,30,30,24,31,17,69, 61,30,48,34,31,39,21,31,36,1,129,91,1,6,45,3,0,5,0,0,1,179,2,131,0,3,0,6,0,130,128,36,1,35,39,51,3,90,85,9,37,1,9,56,84,68,52,90,90,9,46,2,38,92,254,61,1,10,61,253,250,135,135,2,6, 0,130,53,143,63,34,7,35,55,139,63,36,59,84,56,72,106,132,63,39,40,193,41,77,169,2,130,92,140,64,36,3,0,5,255,255,132,127,32,6,79,77,5,32,19,130,63,34,51,23,35,138,66,38,220,61,51,78, 69,78,51,90,223,5,133,68,33,101,62,130,69,32,151,138,134,131,135,33,255,254,130,71,61,123,0,20,0,23,0,31,0,0,19,34,6,21,35,52,54,51,50,22,51,50,54,53,55,51,20,6,35,34,38,138,85,49, 169,12,12,47,36,34,26,62,15,11,13,1,46,34,36,28,58,45,138,164,45,81,19,16,33,43,34,17,9,8,25,51,35,254,109,139,103,32,4,136,175,38,2,0,10,0,22,0,34,91,131,12,81,53,11,72,74,11,138, 187,32,147,72,84,9,33,9,190,136,89,32,56,72,93,13,32,0,136,207,38,149,0,2,0,14,0,30,132,101,52,39,20,22,23,51,62,1,53,52,38,34,6,23,19,35,39,35,7,35,19,38,68,11,5,32,20,130,97,52,56, 18,16,44,16,19,33,47,34,112,159,77,40,193,41,77,159,47,60,84,60,130,98,55,110,17,26,6,6,26,17,21,31,31,101,254,25,135,135,1,231,28,51,40,55,55,40,51,130,89,38,2,255,255,1,182,2,7,130, 109,32,19,130,97,40,17,35,3,55,21,51,21,35,53,131,88,8,40,37,7,35,23,55,7,235,37,75,186,128,202,127,37,68,150,1,23,1,122,1,111,1,190,1,13,254,244,55,188,59,134,133,2,5,2,61,153,1,60, 0,78,87,5,38,119,1,137,2,16,0,40,130,12,91,224,8,49,55,21,6,7,22,21,20,6,35,34,39,53,22,51,50,53,52,39,88,3,6,8,66,23,21,38,1,8,38,57,32,14,25,66,50,77,51,37,56,33,45,38,45,25,24,31, 46,26,54,81,48,23,112,104,77,51,51,1,213,32,60,74,46,62,91,59,46,73,21,5,36,32,31,30,9,45,11,31,22,32,3,42,74,93,56,117,151,32,71,45,130,187,38,52,0,3,1,132,2,135,71,103,5,8,41,55, 33,21,33,17,33,21,35,31,1,21,35,19,35,39,51,127,1,4,254,178,1,71,253,1,241,242,142,57,84,68,63,59,2,6,59,153,1,59,1,48,93,130,175,36,2,0,52,0,4,150,59,34,7,35,55,138,59,36,173,84,56, 72,64,135,59,33,140,93,130,60,135,59,32,136,130,119,34,18,0,0,140,119,66,185,5,138,62,38,86,61,52,78,70,78,52,136,65,33,110,62,131,66,33,0,3,130,127,32,0,130,127,32,133,130,67,34,23, 0,35,74,129,25,32,3,134,213,35,23,51,21,35,74,141,11,33,9,58,137,222,39,2,63,8,52,9,9,52,8,133,5,33,253,252,132,173,42,59,0,2,0,66,255,254,1,118,2,131,65,31,5,8,49,1,35,17,23,21,33, 53,51,17,35,53,33,39,35,39,51,1,117,115,115,254,206,116,116,1,50,106,56,84,68,1,202,254,111,1,57,59,1,144,59,33,92,0,0,0,2,0,66,0,3,130,59,65,91,6,130,59,32,51,132,59,32,39,130,59, 130,227,56,1,117,116,116,254,206,115,115,1,50,60,84,56,72,1,207,254,112,59,57,1,145,1,59,125,65,31,5,133,59,65,31,6,143,59,34,51,23,35,137,62,32,148,65,30,5,137,65,32,95,65,31,7,137, 187,65,31,28,34,23,35,17,131,153,32,55,131,213,65,31,12,32,188,135,161,33,2,61,67,88,11,32,115,132,170,34,1,145,59,131,227,45,17,0,4,1,167,2,11,0,18,0,37,0,0,55,92,29,5,8,79,43,1,21, 51,21,35,21,50,19,50,30,3,20,14,3,43,1,53,35,53,51,53,50,200,19,36,42,30,20,20,30,42,36,19,62,99,99,62,1,31,56,61,45,29,29,45,61,56,30,139,46,46,138,62,9,27,42,75,98,75,42,27,8,162, 53,188,1,205,12,35,55,95,125,96,54,35,11,245,53,220,131,103,42,42,0,0,1,142,2,130,0,20,0,30,68,149,22,36,23,51,17,35,3,130,2,33,51,19,68,148,16,41,140,72,95,186,71,4,98,186,2,88,68, 145,10,32,82,92,232,5,37,2,6,254,90,0,3,92,235,6,38,135,0,5,0,9,0,29,92,237,7,35,55,35,39,51,92,241,16,37,238,57,84,68,3,33,92,245,5,34,29,28,34,133,8,42,31,2,17,253,234,2,22,25,93, 253,175,92,247,19,135,95,32,136,130,95,32,12,92,157,5,36,16,35,34,16,55,66,235,5,32,2,93,84,15,32,184,65,210,5,32,77,150,101,37,88,62,93,93,254,11,151,103,42,251,1,149,2,129,0,20,0, 26,0,46,65,41,14,33,55,53,69,191,5,33,23,50,93,202,16,38,169,11,13,47,36,35,25,69,199,6,39,35,29,58,34,184,184,184,168,145,127,32,87,65,64,10,32,70,131,241,44,254,37,8,26,44,78,105, 78,44,27,7,7,27,132,8,36,26,0,0,0,4,65,79,6,131,239,36,17,0,29,0,49,65,81,7,32,37,87,16,10,69,218,11,65,101,16,33,1,30,69,225,10,32,19,145,136,32,17,131,124,32,49,66,150,11,33,253, 244,65,17,17,8,32,1,0,46,0,65,1,137,1,137,0,11,0,0,1,7,23,7,39,7,39,55,39,55,23,55,1,137,129,129,45,128,129,45,130,5,38,129,128,1,94,121,121,42,135,2,47,0,3,0,1,255,234,1,183,2,34, 0,10,0,21,0,39,103,247,10,46,39,20,23,55,38,35,34,14,3,37,7,22,21,16,35,80,146,5,51,53,16,51,50,23,55,220,17,27,30,20,13,7,184,26,49,7,185,26,59,132,12,40,1,70,65,30,184,83,46,54,36, 133,6,32,54,131,157,54,53,56,42,248,58,208,56,42,249,58,8,26,44,78,205,88,65,105,254,245,56,72,25,130,7,48,1,11,56,72,0,0,2,0,46,255,246,1,138,2,131,0,17,130,123,40,0,1,51,17,20,35, 34,53,17,130,6,8,44,22,51,50,62,2,53,3,35,39,51,1,62,75,170,176,75,39,61,30,40,19,7,43,57,84,69,2,6,254,160,176,189,1,83,254,160,67,52,18,33,36,25,1,135,92,161,71,34,7,35,55,139,71, 35,12,84,57,72,144,71,35,227,92,92,0,130,0,139,147,33,24,0,146,147,66,196,5,139,78,32,98,66,191,5,139,81,33,17,34,130,153,33,198,62,130,82,34,3,0,47,136,227,34,29,0,41,146,81,32,19, 66,36,22,44,1,62,76,171,176,75,40,61,30,39,19,7,5,66,30,10,144,103,32,158,72,7,15,32,2,72,111,6,35,130,0,8,0,86,29,5,38,21,35,55,3,51,23,19,130,187,44,1,98,80,177,75,1,177,79,135,90, 84,57,73,130,74,39,227,233,234,1,28,229,1,97,130,170,41,0,2,0,42,255,255,1,142,2,6,130,55,32,21,99,245,10,8,41,55,50,22,21,20,43,1,21,35,17,51,21,50,210,49,61,63,47,93,103,83,95,178, 103,75,75,103,186,43,48,47,42,180,240,75,74,150,127,2,6,92,0,130,222,130,67,32,242,130,67,8,127,28,0,48,0,0,19,20,30,3,21,20,14,1,38,39,53,22,51,50,54,53,52,46,3,53,52,62,2,63,1,38, 54,46,2,35,34,6,21,17,35,17,52,54,51,50,21,34,6,234,34,48,48,33,51,78,88,38,49,51,41,45,33,47,47,33,24,35,34,12,12,1,3,10,14,38,27,46,41,68,73,82,155,47,72,1,50,20,33,27,32,53,35,43, 57,20,5,16,56,21,36,29,24,37,25,27,45,30,28,44,24,15,2,2,1,15,23,22,16,48,50,254,123,1,133,77,74,152,44,130,138,41,0,3,0,38,255,245,1,146,2,69,130,9,34,18,0,61,130,156,49,35,39,51, 19,50,62,2,39,48,42,1,35,14,1,21,20,22,55,130,3,50,31,1,35,38,39,14,1,35,34,38,53,52,54,59,1,60,2,46,5,81,15,9,8,99,1,12,57,118,72,24,36,52,26,10,1,36,47,11,60,52,51,225,10,5,5,68, 9,7,21,76,38,65,80,91,91,92,2,6,8,15,20,30,18,38,71,16,17,3,25,11,24,17,24,26,13,168,1,194,131,253,230,29,47,50,26,2,32,44,38,36,183,116,25,55,15,15,22,37,32,37,64,60,62,70,1,15,9, 18,13,16,11,10,5,18,10,9,63,1,9,3,8,3,4,2,149,171,35,7,35,55,3,131,171,33,42,2,167,170,37,84,118,57,103,80,37,131,170,33,1,35,167,171,34,2,69,131,176,172,65,87,9,39,6,0,21,0,64,0,0, 19,130,171,34,51,23,35,132,174,65,90,41,39,220,66,52,90,56,90,52,97,65,92,45,38,2,26,88,131,131,254,105,65,94,20,33,63,69,65,94,20,135,175,46,51,0,24,0,39,0,82,0,0,1,34,46,2,35,34, 75,234,5,42,30,1,51,50,54,61,1,51,20,14,2,174,191,53,1,13,21,31,16,23,11,26,45,36,37,21,34,30,13,12,13,46,5,13,30,101,173,205,50,1,209,17,20,17,54,43,55,27,26,26,13,14,15,29,33,21, 254,90,148,217,66,56,24,32,4,66,227,6,40,29,0,11,0,23,0,38,0,81,74,25,25,32,19,66,77,45,32,63,68,115,10,32,6,66,85,46,33,1,214,68,147,11,33,254,85,181,211,52,114,0,7,0,15,0,58,0,73, 0,0,18,34,6,20,22,50,54,52,6,34,88,7,5,32,19,67,183,31,67,58,12,44,240,46,33,33,46,33,14,84,60,60,84,60,63,67,191,33,32,194,139,237,46,2,70,31,44,31,31,44,118,56,80,56,56,80,254,249, 67,204,32,33,254,155,136,236,67,215,5,55,5,255,245,1,179,1,143,0,8,0,19,0,89,0,0,1,34,6,23,51,54,39,46,1,130,161,33,1,39,85,233,5,40,37,21,35,28,2,30,5,51,22,79,184,7,34,46,2,47,96, 42,8,32,59,113,200,6,83,206,5,8,164,30,2,31,1,62,4,51,50,22,23,30,1,21,1,56,31,31,3,121,1,2,3,27,217,26,26,14,1,28,53,44,37,1,75,182,2,4,6,10,14,21,12,34,52,9,10,5,15,48,25,25,42,22, 16,2,3,1,4,16,21,36,22,57,60,70,68,50,42,30,19,46,13,14,44,65,19,30,18,12,2,2,1,3,14,19,36,22,58,48,8,2,1,1,90,54,64,23,22,35,38,254,209,18,59,59,32,38,35,31,136,1,1,28,10,28,13,23, 11,14,6,1,17,9,10,60,3,10,16,11,17,16,6,5,2,7,19,15,12,60,56,57,65,28,50,39,14,8,7,58,24,9,13,13,4,5,2,6,15,11,10,59,76,18,42,12,130,248,42,1,0,59,255,119,1,125,1,142,0,37,132,243, 42,21,20,22,51,50,55,21,14,1,15,1,77,223,14,130,222,34,54,51,50,98,88,10,35,14,43,15,15,77,222,8,33,82,106,98,99,5,56,88,74,76,76,74,43,65,11,15,2,2,36,32,31,30,8,46,12,32,22,31,3, 105,96,98,98,114,5,41,0,3,0,37,255,247,1,147,2,69,130,9,34,9,0,30,69,235,5,32,23,98,25,22,42,21,23,32,1,19,57,118,72,55,46,70,98,30,12,40,92,89,1,254,217,1,194,131,236,98,33,17,145, 95,35,7,35,55,7,154,95,36,93,119,56,102,49,147,95,34,2,69,131,147,96,33,0,0,138,195,36,6,0,12,0,33,69,87,8,154,102,39,225,67,52,90,57,90,52,64,148,104,36,26,88,131,131,105,145,202, 33,0,4,65,43,6,68,51,5,34,29,0,50,68,51,25,65,65,27,44,69,57,9,9,57,9,155,58,9,9,58,9,39,147,128,68,5,13,32,125,146,137,8,58,0,0,2,0,51,255,252,1,117,2,59,0,13,0,17,0,0,37,51,21,34, 35,34,38,61,1,35,53,51,21,20,17,35,39,51,1,37,80,24,71,54,65,90,158,57,119,73,51,54,78,63,201,50,251,86,1,132,131,0,130,0,34,2,0,68,130,59,32,116,149,59,48,19,7,35,55,1,37,79,23,72, 53,66,90,158,106,119,57,103,135,60,34,2,7,131,130,61,135,59,36,69,0,6,0,20,65,105,8,32,19,140,126,39,186,66,52,90,56,90,51,40,134,68,70,121,5,32,113,134,131,130,127,32,3,134,127,38, 26,0,11,0,23,0,37,65,69,25,141,86,33,1,44,65,56,5,132,5,32,130,134,92,33,1,212,69,49,12,32,95,135,101,54,2,0,42,255,244,1,142,2,27,0,14,0,47,0,0,55,50,54,53,52,38,47,1,90,34,6,38,19, 20,30,6,21,20,6,72,186,5,8,96,23,46,1,47,1,7,39,55,39,51,23,55,23,7,22,220,45,60,20,10,10,18,50,43,59,60,145,20,6,19,8,13,6,5,89,88,88,89,107,100,5,27,10,11,103,12,88,67,81,47,106, 13,93,36,42,70,71,38,67,15,14,9,69,73,73,69,1,89,1,27,10,29,19,33,29,38,19,86,107,107,94,93,102,8,8,31,12,11,31,34,27,69,49,32,34,28,34,65,115,5,32,62,130,231,44,122,2,32,0,18,0,43, 0,0,19,50,22,21,100,121,12,34,23,54,55,71,88,17,54,35,34,46,2,253,53,72,68,39,38,47,56,68,64,4,37,3,26,45,36,37,20,35,71,51,5,40,14,29,21,21,31,17,22,1,145,100,165,9,51,1,132,47,60, 99,54,44,54,27,26,26,14,13,15,29,33,21,17,20,17,130,124,40,3,0,43,255,244,1,141,2,69,86,227,7,36,1,35,39,51,18,102,253,8,99,73,7,37,1,15,56,119,73,5,99,78,9,37,1,194,131,253,229,71, 99,82,9,35,204,204,102,0,130,65,143,75,35,7,35,55,2,145,75,36,86,118,57,103,96,137,75,34,2,69,131,145,76,137,151,36,6,0,16,0,24,66,77,8,145,78,72,242,6,32,112,138,80,37,26,88,131,131, 254,104,143,158,136,159,32,32,130,79,34,34,0,42,72,179,24,145,99,53,1,15,20,32,16,22,12,25,46,36,38,20,35,29,14,12,13,46,5,14,30,118,137,113,36,1,190,17,20,17,65,92,11,33,254,108,142, 125,32,4,65,103,6,66,215,5,34,33,0,41,66,217,25,146,126,93,1,10,33,9,11,138,118,66,224,13,32,86,142,115,32,3,130,231,36,49,1,159,1,136,130,249,42,19,0,35,0,0,37,33,53,33,39,35,96,108, 13,32,3,142,15,48,1,159,254,121,1,135,161,70,4,7,7,4,70,5,6,6,5,136,9,37,192,59,56,6,5,63,131,18,130,4,33,254,255,130,11,131,35,130,11,32,0,130,0,44,3,0,12,255,222,1,172,1,166,0,7, 0,15,131,107,39,55,50,54,53,52,39,7,22,78,242,6,37,6,37,7,22,21,20,78,241,7,8,65,52,54,51,50,23,55,22,222,47,57,10,169,27,56,9,168,27,46,45,59,1,54,61,33,178,75,44,57,34,62,29,94,83, 71,46,55,34,43,72,80,42,32,189,37,152,42,31,189,33,70,121,68,52,81,204,40,63,26,70,50,81,102,102,39,63,27,67,55,5,51,61,255,246,1,123,2,59,0,3,0,26,0,0,1,35,39,51,23,51,17,105,15,6, 38,61,1,51,21,20,22,51,130,133,8,41,1,13,57,119,73,144,68,63,6,2,5,20,23,38,21,77,61,67,38,43,44,53,1,184,131,182,254,124,57,2,10,23,18,14,72,82,244,240,52,52,59,50,130,84,33,2,0,141, 83,34,7,35,55,148,83,36,83,119,56,102,44,143,83,34,2,59,131,147,84,135,83,36,69,0,6,0,29,66,201,8,32,23,145,169,32,55,66,203,6,32,92,139,171,41,42,45,53,2,2,26,88,131,131,61,143,174, 32,235,130,175,32,3,134,175,95,67,5,32,46,66,85,25,148,197,35,63,58,8,8,95,89,6,33,9,128,140,205,130,117,95,106,13,32,80,143,125,42,0,0,2,0,26,255,107,1,158,2,59,80,107,7,34,2,7,14, 99,148,6,8,49,55,3,51,27,1,7,35,55,1,86,72,140,39,14,32,36,25,18,54,46,30,32,22,160,72,123,105,119,56,102,1,133,254,175,98,35,44,18,5,54,46,54,1,127,254,208,1,230,131,131,130,203,34, 2,0,50,130,79,39,134,2,32,0,9,0,23,0,107,131,10,33,19,50,66,59,5,53,21,35,17,51,21,54,174,89,55,57,86,56,99,80,91,171,67,32,68,68,31,42,66,245,11,37,65,202,2,180,208,61,130,75,32,3, 134,155,32,17,130,155,38,29,0,41,0,0,1,51,142,157,80,34,23,144,177,32,100,71,147,10,144,185,32,118,80,40,14,42,1,0,30,255,249,1,153,2,20,0,43,130,115,40,34,7,51,7,35,6,21,20,21,130, 6,35,30,3,51,50,107,75,6,38,39,35,55,51,38,52,55,130,5,8,76,62,1,51,50,23,21,46,1,1,44,110,20,178,18,163,1,132,17,111,6,27,38,37,23,20,61,27,47,61,88,103,14,65,18,44,2,2,62,18,47,14, 103,83,65,48,28,61,1,220,144,36,30,4,17,15,37,44,60,29,10,19,24,71,28,104,95,37,29,9,28,36,95,105,29,72,24,21,130,246,130,2,36,14,0,174,0,1,130,7,131,2,34,1,0,4,134,11,32,1,130,7,32, 10,134,11,32,2,130,7,32,16,134,11,36,3,0,29,0,78,134,11,131,43,32,112,134,11,36,5,0,9,0,134,134,11,32,6,130,7,44,148,0,3,0,1,4,9,0,0,0,2,0,0,134,11,32,1,130,11,32,6,134,11,32,2,130, 11,32,12,134,11,36,3,0,58,0,18,134,11,32,4,130,23,32,108,134,11,32,5,130,21,32,114,134,11,32,6,130,23,35,144,0,46,0,143,2,39,70,0,111,0,110,0,116,0,131,7,40,114,0,103,0,101,0,32,0, 50,130,36,32,48,130,7,32,58,130,3,32,46,134,7,36,49,0,49,0,45,130,25,131,3,32,48,130,7,51,54,0,0,70,111,110,116,70,111,114,103,101,32,50,46,48,32,58,32,46,130,3,39,49,49,45,50,45,50, 48,50,130,30,133,107,32,86,130,81,36,114,0,115,0,105,132,103,32,32,130,89,40,0,86,101,114,115,105,111,110,32,136,140,33,2,0,139,0,65,75,7,137,19,32,192,130,10,131,251,60,3,0,4,0,5, 0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,130,235,8,50,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,0,32,0,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41, 0,42,0,43,0,44,130,211,36,46,0,47,0,48,130,221,9,48,50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,60,0,61,0,62,0,63,0,64,0,65,0,66,0,67,0,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75, 0,76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,91,0,92,0,93,0,94,1,2,0,96,0,97,1,3,0,163,0,132,0,133,0,189,0,150,0,232,0,134,0,142,0,139,0,157,0,169,0, 138,1,4,0,131,0,147,0,242,0,243,0,141,0,151,0,136,0,195,0,222,0,241,0,158,0,170,0,245,0,244,0,246,0,162,0,173,0,201,0,199,0,174,0,98,0,99,0,144,0,100,0,203,0,101,0,200,0,202,0,207, 0,204,0,205,0,206,0,233,0,102,0,211,0,209,0,175,0,103,0,240,0,145,0,214,0,212,0,213,0,104,0,235,0,237,0,137,0,106,0,105,0,107,0,109,0,108,0,110,0,160,0,111,0,113,0,112,0,114,0,115, 0,117,0,116,0,118,0,119,0,234,0,120,0,122,0,121,0,123,0,125,0,124,0,184,0,161,0,127,0,126,0,128,0,129,0,236,0,238,0,186,1,5,11,118,101,114,116,105,99,97,108,98,97,114,7,117,110,105, 48,48,56,53,9,111,130,20,42,115,99,111,114,101,4,69,117,114,111,0,132,0,38,1,255,255,0,2,0,1,130,11,32,12,130,3,32,22,130,3,131,13,32,98,130,183,34,1,0,4,132,13,130,4,130,2,32,1,130, 3,36,0,229,13,183,147,132,7,42,175,187,66,0,0,0,0,229,178,59,232,5,250,48,120,202,241, }; static const char* GetDefaultCompressedFontDataProggyForever(int* out_size) { *out_size = proggy_forever_minimal_ttf_compressed_size; return (const char*)proggy_forever_minimal_ttf_compressed_data; } #endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui/source/imgui_tables.cpp ================================================ // dear imgui, v1.92.7 WIP // (tables and columns code) /* Index of this file: // [SECTION] Commentary // [SECTION] Header mess // [SECTION] Tables: Main code // [SECTION] Tables: Simple accessors // [SECTION] Tables: Row changes // [SECTION] Tables: Columns changes // [SECTION] Tables: Columns width management // [SECTION] Tables: Drawing // [SECTION] Tables: Sorting // [SECTION] Tables: Headers // [SECTION] Tables: Context Menu // [SECTION] Tables: Settings (.ini data) // [SECTION] Tables: Garbage Collection // [SECTION] Tables: Debugging // [SECTION] Columns, BeginColumns, EndColumns, etc. */ // Navigating this file: // - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. // - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. // - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. //----------------------------------------------------------------------------- // [SECTION] Commentary //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Typical tables call flow: (root level is generally public API): //----------------------------------------------------------------------------- // - BeginTable() user begin into a table // | BeginChild() - (if ScrollX/ScrollY is set) // | TableBeginInitMemory() - first time table is used // | TableResetSettings() - on settings reset // | TableLoadSettings() - on settings load // | TableBeginApplyRequests() - apply queued resizing/reordering/hiding requests // | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame) // | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width // - TableSetupColumn() user submit columns details (optional) // - TableSetupScrollFreeze() user submit scroll freeze information (optional) //----------------------------------------------------------------------------- // - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). // | TableSetupDrawChannels() - setup ImDrawList channels // | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission // | TableBeginContextMenuPopup() // | - TableDrawDefaultContextMenu() - draw right-click context menu contents //----------------------------------------------------------------------------- // - TableHeadersRow() or TableHeader() user submit a headers row (optional) // | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction // | TableOpenContextMenu() - when right-clicked: trigger opening of the default context menu // - TableGetSortSpecs() user queries updated sort specs (optional, generally after submitting headers) // - TableNextRow() user begin into a new row (also automatically called by TableHeadersRow()) // | TableEndRow() - finish existing row // | TableBeginRow() - add a new row // - TableSetColumnIndex() / TableNextColumn() user begin into a cell // | TableEndCell() - close existing column/cell // | TableBeginCell() - enter into current column/cell // - [...] user emit contents //----------------------------------------------------------------------------- // - EndTable() user ends the table // | TableDrawBorders() - draw outer borders, inner vertical borders // | TableMergeDrawChannels() - merge draw channels if clipping isn't required // | EndChild() - (if ScrollX/ScrollY is set) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // TABLE SIZING //----------------------------------------------------------------------------- // (Read carefully because this is subtle but it does make sense!) //----------------------------------------------------------------------------- // About 'outer_size': // Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags. // Default value is ImVec2(0.0f, 0.0f). // X // - outer_size.x <= 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge. // - outer_size.x > 0.0f -> Set Fixed width. // Y with ScrollX/ScrollY disabled: we output table directly in current window // - outer_size.y < 0.0f -> Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful if parent window can vertically scroll. // - outer_size.y = 0.0f -> No minimum height (but will auto extend, unless _NoHostExtendY is set) // - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless _NoHostExtendY is set) // Y with ScrollX/ScrollY enabled: using a child window for scrolling // - outer_size.y < 0.0f -> Bottom-align. Not meaningful if parent window can vertically scroll. // - outer_size.y = 0.0f -> Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window. // - outer_size.y > 0.0f -> Set Exact height. Recommended when using Scrolling on any axis. //----------------------------------------------------------------------------- // Outer size is also affected by the NoHostExtendX/NoHostExtendY flags. // Important to note how the two flags have slightly different behaviors! // - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. // - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible. // In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height. // This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not useful and not easily noticeable). //----------------------------------------------------------------------------- // About 'inner_width': // With ScrollX disabled: // - inner_width -> *ignored* // With ScrollX enabled: // - inner_width < 0.0f -> *illegal* fit in known width (right align from outer_size.x) <-- weird // - inner_width = 0.0f -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns. // - inner_width > 0.0f -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space! //----------------------------------------------------------------------------- // Details: // - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept // of "available space" doesn't make sense. // - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding // of what the value does. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // COLUMNS SIZING POLICIES // (Reference: ImGuiTableFlags_SizingXXX flags and ImGuiTableColumnFlags_WidthXXX flags) //----------------------------------------------------------------------------- // About overriding column sizing policy and width/weight with TableSetupColumn(): // We use a default parameter of -1 for 'init_width'/'init_weight'. // - with ImGuiTableColumnFlags_WidthFixed, init_width <= 0 (default) --> width is automatic // - with ImGuiTableColumnFlags_WidthFixed, init_width > 0 (explicit) --> width is custom // - with ImGuiTableColumnFlags_WidthStretch, init_weight <= 0 (default) --> weight is 1.0f // - with ImGuiTableColumnFlags_WidthStretch, init_weight > 0 (explicit) --> weight is custom // Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f) // and you can fit a 100.0f wide item in it without clipping and with padding honored. //----------------------------------------------------------------------------- // About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag) // - with Table policy ImGuiTableFlags_SizingFixedFit --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width // - with Table policy ImGuiTableFlags_SizingFixedSame --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is max of all contents width // - with Table policy ImGuiTableFlags_SizingStretchSame --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f // - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents // Default Width and default Weight can be overridden when calling TableSetupColumn(). //----------------------------------------------------------------------------- // About mixing Fixed/Auto and Stretch columns together: // - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. // - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place! // that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in. // - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents. // - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weights/widths. //----------------------------------------------------------------------------- // About using column width: // If a column is manually resizable or has a width specified with TableSetupColumn(): // - you may use GetContentRegionAvail().x to query the width available in a given column. // - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width. // If the column is not resizable and has no width specified with TableSetupColumn(): // - its width will be automatic and be set to the max of items submitted. // - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN). // - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN). //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // TABLES CLIPPING/CULLING //----------------------------------------------------------------------------- // About clipping/culling of Rows in Tables: // - For large numbers of rows, it is recommended you use ImGuiListClipper to submit only visible rows. // ImGuiListClipper is reliant on the fact that rows are of equal height. // See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper. // - Note that auto-resizing columns don't play well with using the clipper. // By default a table with _ScrollX but without _Resizable will have column auto-resize. // So, if you want to use the clipper, make sure to either enable _Resizable, either setup columns width explicitly with _WidthFixed. //----------------------------------------------------------------------------- // About clipping/culling of Columns in Tables: // - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing // width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know // it is not going to contribute to row height. // In many situations, you may skip submitting contents for every column but one (e.g. the first one). // - Case A: column is not hidden by user, and at least partially in sight (most common case). // - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output. // - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.). // // [A] [B] [C] // TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() returns false, user can skip submitting items but only if the column doesn't contribute to row height. // SkipItems: false false true -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output. // ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way. // ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway). // // - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row. // However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer. //----------------------------------------------------------------------------- // About clipping/culling of whole Tables: // - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // [SECTION] Header mess //----------------------------------------------------------------------------- #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_internal.h" // System includes #include // intptr_t // 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 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #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" // warning: format specifies type 'int' but the argument has type 'unsigned int' #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 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 #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. #pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type #pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*' #pragma GCC diagnostic ignored "-Wstrict-overflow" #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif //----------------------------------------------------------------------------- // [SECTION] Tables: Main code //----------------------------------------------------------------------------- // - TableFixFlags() [Internal] // - TableFindByID() [Internal] // - BeginTable() // - BeginTableEx() [Internal] // - TableBeginInitMemory() [Internal] // - TableBeginApplyRequests() [Internal] // - TableSetupColumnFlags() [Internal] // - TableUpdateLayout() [Internal] // - TableUpdateBorders() [Internal] // - EndTable() // - TableSetupColumn() // - TableSetupScrollFreeze() //----------------------------------------------------------------------------- // Configuration static const int TABLE_DRAW_CHANNEL_BG0 = 0; static const int TABLE_DRAW_CHANNEL_BG2_FROZEN = 1; static const int TABLE_DRAW_CHANNEL_NOCLIP = 2; // When using ImGuiTableFlags_NoClip (this becomes the last visible channel) static const float TABLE_BORDER_SIZE = 1.0f; // FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering. static const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f; // Extend outside inner borders. static const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f; // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped. // Helper inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_window) { // Adjust flags: set default sizing policy if ((flags & ImGuiTableFlags_SizingMask_) == 0) flags |= ((flags & ImGuiTableFlags_ScrollX) || (outer_window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) ? ImGuiTableFlags_SizingFixedFit : ImGuiTableFlags_SizingStretchSame; // Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame if ((flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) flags |= ImGuiTableFlags_NoKeepColumnsVisible; // Adjust flags: enforce borders when resizable if (flags & ImGuiTableFlags_Resizable) flags |= ImGuiTableFlags_BordersInnerV; // Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on if (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) flags &= ~(ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoHostExtendY); // Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody if (flags & ImGuiTableFlags_NoBordersInBodyUntilResize) flags &= ~ImGuiTableFlags_NoBordersInBody; // Adjust flags: disable saved settings if there's nothing to save if ((flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable)) == 0) flags |= ImGuiTableFlags_NoSavedSettings; // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set) if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings) flags |= ImGuiTableFlags_NoSavedSettings; return flags; } ImGuiTable* ImGui::TableFindByID(ImGuiID id) { ImGuiContext& g = *GImGui; return g.Tables.GetByKey(id); } // Read about "TABLE SIZING" at the top of this file. bool ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width) { ImGuiID id = GetID(str_id); return BeginTableEx(str_id, id, columns_count, flags, outer_size, inner_width); } bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width) { ImGuiContext& g = *GImGui; ImGuiWindow* outer_window = GetCurrentWindow(); if (outer_window->SkipItems) // Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible. return false; // Sanity checks IM_ASSERT(columns_count > 0 && columns_count < IMGUI_TABLE_MAX_COLUMNS); if (flags & ImGuiTableFlags_ScrollX) IM_ASSERT(inner_width >= 0.0f); // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve. // FIXME: coarse clipping because access to table data causes two issues: // - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine. // - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers. // The side-effects of accessing table data on coarse clip would be: // - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations. // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[]. const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; const ImVec2 avail_size = GetContentRegionAvail(); const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, IMGUI_WINDOW_HARD_MIN_SIZE), use_child_window ? ImMax(avail_size.y, IMGUI_WINDOW_HARD_MIN_SIZE) : 0.0f)); const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows! if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) { ItemSize(outer_rect); ItemAdd(outer_rect, id); g.NextWindowData.ClearFlags(); return false; } // [DEBUG] Debug break requested by user if (g.DebugBreakInTable == id) IM_DEBUG_BREAK(); // Acquire storage for the table ImGuiTable* table = g.Tables.GetOrAddByKey(id); // Acquire temporary buffers const int table_idx = g.Tables.GetIndex(table); if (++g.TablesTempDataStacked > g.TablesTempData.Size) g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; temp_data->TableIndex = table_idx; table->DrawSplitter = &table->TempData->DrawSplitter; table->DrawSplitter->Clear(); // Fix flags table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0; flags = TableFixFlags(flags, outer_window); // Initialize const int previous_frame_active = table->LastFrameActive; const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1; const ImGuiTableFlags previous_flags = table->Flags; table->ID = id; table->Flags = flags; table->LastFrameActive = g.FrameCount; table->OuterWindow = table->InnerWindow = outer_window; table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent; temp_data->UserOuterSize = outer_size; // Instance data (for instance 0, TableID == TableInstanceID) ImGuiID instance_id; table->InstanceCurrent = (ImS16)instance_no; if (instance_no > 0) { IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID"); if (table->InstanceDataExtra.Size < instance_no) table->InstanceDataExtra.push_back(ImGuiTableInstanceData()); instance_id = GetIDWithSeed(instance_no, GetIDWithSeed("##Instances", NULL, id)); // Push "##Instances" followed by (int)instance_no in ID stack. } else { instance_id = id; } ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); table_instance->TableInstanceID = instance_id; // When not using a child window, WorkRect.Max will grow as we append contents. if (use_child_window) { // Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent // (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing) ImVec2 override_content_size(FLT_MAX, FLT_MAX); if ((flags & ImGuiTableFlags_ScrollX) && !(flags & ImGuiTableFlags_ScrollY)) override_content_size.y = FLT_MIN; // Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and // never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align // based on the right side of the child window work rect, which would require knowing ahead if we are going to // have decoration taking horizontal spaces (typically a vertical scrollbar). if ((flags & ImGuiTableFlags_ScrollX) && inner_width > 0.0f) override_content_size.x = inner_width; if (override_content_size.x != FLT_MAX || override_content_size.y != FLT_MAX) SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f)); // Reset scroll if we are reactivating it if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0) if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) == 0) SetNextWindowScroll(ImVec2(0.0f, 0.0f)); // Create scrolling region (without border and zero window padding) ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None; ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None; if (flags & ImGuiTableFlags_ScrollX) child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar; BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags); table->InnerWindow = g.CurrentWindow; table->WorkRect = table->InnerWindow->WorkRect; table->OuterRect = table->InnerWindow->Rect(); table->InnerRect = table->InnerWindow->InnerRect; IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f); // Allow submitting when host is measuring if (table->InnerWindow->SkipItems && outer_window_is_measuring_size) table->InnerWindow->SkipItems = false; // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned if (instance_no == 0) { table->HasScrollbarYPrev = table->HasScrollbarYCurr; table->HasScrollbarYCurr = false; } table->HasScrollbarYCurr |= table->InnerWindow->ScrollbarY; } else { // For non-scrolling tables, WorkRect == OuterRect == InnerRect. // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable(). table->WorkRect = table->OuterRect = table->InnerRect = outer_rect; table->HasScrollbarYPrev = table->HasScrollbarYCurr = false; table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking across a table } // Push a standardized ID for both child-using and not-child-using tables PushOverrideID(id); if (instance_no > 0) PushOverrideID(instance_id); // FIXME: Somehow this is not resolved by stack-tool, even tho GetIDWithSeed() submitted the symbol. // Backup a copy of host window members we will modify ImGuiWindow* inner_window = table->InnerWindow; table->HostIndentX = inner_window->DC.Indent.x; table->HostClipRect = inner_window->ClipRect; table->HostSkipItems = inner_window->SkipItems; temp_data->WindowID = inner_window->ID; temp_data->HostBackupWorkRect = inner_window->WorkRect; temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect; temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth; temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752) // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap // problem only affect scrolling tables in this case we can get away with doing it without extra cost). if (inner_window != outer_window) { // FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize, // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with // different x/y values to BeginChild(). if (flags & ImGuiTableFlags_BordersOuterV) { table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x); if (inner_window->DecoOuterSizeX2 == 0.0f) table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x); } if (flags & ImGuiTableFlags_BordersOuterH) { table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y); if (inner_window->DecoOuterSizeY2 == 0.0f) table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y); } } // Padding and Spacing // - None ........Content..... Pad .....Content........ // - PadOuter | Pad ..Content..... Pad .....Content.. Pad | // - PadInner ........Content.. Pad | Pad ..Content........ // - PadOuter+PadInner | Pad ..Content.. Pad | Pad ..Content.. Pad | const bool pad_outer_x = (flags & ImGuiTableFlags_NoPadOuterX) ? false : (flags & ImGuiTableFlags_PadOuterX) ? true : (flags & ImGuiTableFlags_BordersOuterV) != 0; const bool pad_inner_x = (flags & ImGuiTableFlags_NoPadInnerX) ? false : true; const float inner_spacing_for_border = (flags & ImGuiTableFlags_BordersInnerV) ? TABLE_BORDER_SIZE : 0.0f; const float inner_spacing_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) == 0) ? g.Style.CellPadding.x : 0.0f; const float inner_padding_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) != 0) ? g.Style.CellPadding.x : 0.0f; table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border; table->CellSpacingX2 = inner_spacing_explicit; table->CellPaddingX = inner_padding_explicit; const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f; table->OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table->CellPaddingX; table->CurrentColumn = -1; table->CurrentRow = -1; table->RowBgColorCounter = 0; table->LastRowFlags = ImGuiTableRowFlags_None; table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width table->InnerClipRect.ClipWithFull(table->HostClipRect); table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y; table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() table->RowCellPaddingY = 0.0f; table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; table->IsUnfrozenRows = true; table->DeclColumnsCount = table->AngledHeadersCount = 0; if (previous_frame_active + 1 < g.FrameCount) table->IsActiveIdInTable = false; table->AngledHeadersHeight = 0.0f; temp_data->AngledHeadersExtraWidth = 0.0f; // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); // Make table current g.CurrentTable = table; inner_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); outer_window->DC.CurrentTableIdx = table_idx; if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. inner_window->DC.CurrentTableIdx = table_idx; if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0) table->IsResetDisplayOrderRequest = true; // Mark as used to avoid GC if (table_idx >= g.TablesLastTimeActive.Size) g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); g.TablesLastTimeActive[table_idx] = (float)g.Time; temp_data->LastTimeActive = (float)g.Time; table->MemoryCompacted = false; // Setup memory buffer (clear data if columns count changed) ImGuiTableColumn* old_columns_to_preserve = NULL; void* old_columns_raw_data = NULL; const int old_columns_count = table->Columns.size(); if (old_columns_count != 0 && old_columns_count != columns_count) { // Attempt to preserve width and other settings on column count/specs change (#4046) old_columns_to_preserve = table->Columns.Data; old_columns_raw_data = table->RawData; // Free at end of function table->RawData = NULL; } if (table->RawData == NULL) { TableBeginInitMemory(table, columns_count); table->IsInitializing = table->IsSettingsRequestLoad = true; } if (table->IsResetAllRequest) TableResetSettings(table); if (table->IsInitializing) { // Initialize table->SettingsOffset = -1; table->IsSortSpecsDirty = true; table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934) table->InstanceInteracted = -1; table->ContextPopupColumn = -1; table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1; table->AutoFitSingleColumn = -1; table->HoveredColumnBody = table->HoveredColumnBorder = -1; for (int n = 0; n < columns_count; n++) { ImGuiTableColumn* column = &table->Columns[n]; if (old_columns_to_preserve && n < old_columns_count) { *column = old_columns_to_preserve[n]; } else { float width_auto = column->WidthAuto; *column = ImGuiTableColumn(); column->WidthAuto = width_auto; column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; column->DisplayOrder = (ImGuiTableColumnIdx)n; } table->DisplayOrderToIndex[n] = column->DisplayOrder; } } if (old_columns_raw_data) IM_FREE(old_columns_raw_data); // Load settings if (table->IsSettingsRequestLoad) TableLoadSettings(table); // Handle DPI/font resize // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well. // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition. // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory. // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path. const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ? if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit) { const float scale_factor = new_ref_scale_unit / table->RefScale; //IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor); for (int n = 0; n < columns_count; n++) table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor; } table->RefScale = new_ref_scale_unit; // Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call.. // This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user. // Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option. inner_window->SkipItems = true; // Clear names // At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn() if (table->ColumnsNames.Buf.Size > 0) table->ColumnsNames.Buf.resize(0); // Apply queued resizing/reordering/hiding requests TableBeginApplyRequests(table); return true; } // For reference, the average total _allocation count_ for a table is: // + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables[]) // + 1 (for table->RawData allocated below) // + 1 (for table->ColumnsNames, if names are used) // Shared allocations for the maximum number of simultaneously nested tables (generally a very small number) // + 1 (for table->Splitter._Channels) // + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) // Where active_channels_count is variable but often == columns_count or == columns_count + 1, see TableSetupDrawChannels() for details. // Unused channels don't perform their +2 allocations. void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count) { // Allocate single buffer for our arrays const int columns_bit_array_size = (int)ImBitArrayGetStorageSizeInBytes(columns_count); ImSpanAllocator<6> span_allocator; span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn)); span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx)); span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4); for (int n = 3; n < 6; n++) span_allocator.Reserve(n, columns_bit_array_size); table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes()); memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes()); span_allocator.SetArenaBasePtr(table->RawData); span_allocator.GetSpan(0, &table->Columns); span_allocator.GetSpan(1, &table->DisplayOrderToIndex); span_allocator.GetSpan(2, &table->RowCellData); table->EnabledMaskByDisplayOrder = (ImU32*)span_allocator.GetSpanPtrBegin(3); table->EnabledMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(4); table->VisibleMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(5); } // Apply queued resizing/reordering/hiding requests void ImGui::TableBeginApplyRequests(ImGuiTable* table) { // Handle resizing request // (We process this in the TableBegin() of the first instance of each table) // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling? if (table->InstanceCurrent == 0) { if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX) TableSetColumnWidth(table->ResizedColumn, table->ResizedColumnNextWidth); table->LastResizedColumn = table->ResizedColumn; table->ResizedColumnNextWidth = FLT_MAX; table->ResizedColumn = -1; // Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy. // FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings. if (table->AutoFitSingleColumn != -1) { TableSetColumnWidth(table->AutoFitSingleColumn, table->Columns[table->AutoFitSingleColumn].WidthAuto); table->AutoFitSingleColumn = -1; } } // Handle reordering request // Note: we don't clear ReorderColumn after handling the request (FIXME: clarify why or add a test). if (table->InstanceCurrent == 0) { if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1) table->ReorderColumn = -1; table->HeldHeaderColumn = -1; if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0) { // We need to handle reordering across hidden columns. // In the configuration below, moving C to the right of E will lead to: // ... C [D] E ---> ... [D] E C (Column name/index) // ... 2 3 4 ... 2 3 4 (Display order) IM_ASSERT(table->ReorderColumnDir == -1 || table->ReorderColumnDir == +1); IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable); ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn]; ImGuiTableColumn* dst_column = &table->Columns[(table->ReorderColumnDir < 0) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn]; TableSetColumnDisplayOrder(table, table->ReorderColumn, dst_column->DisplayOrder); table->ReorderColumnDir = 0; } } // Handle display order reset request if (table->IsResetDisplayOrderRequest) { for (int n = 0; n < table->ColumnsCount; n++) table->DisplayOrderToIndex[n] = table->Columns[n].DisplayOrder = (ImGuiTableColumnIdx)n; table->IsResetDisplayOrderRequest = false; table->IsSettingsDirty = true; } } // Note that TableSetupScrollFreeze() enforce a display order range for frozen columns. // So reordering a column across the frozen column barrier is illegal and will be undone. void ImGui::TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order) { IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); IM_ASSERT(dst_order >= 0 && dst_order < table->ColumnsCount); ImGuiTableColumn* src_column = &table->Columns[column_n]; const int src_order = src_column->DisplayOrder; if (src_order == dst_order) return; const int reorder_dir = (dst_order < src_order) ? -1 : +1; src_column->DisplayOrder = (ImGuiTableColumnIdx)dst_order; for (int order_n = src_order + reorder_dir; order_n != dst_order + reorder_dir; order_n += reorder_dir) table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir; //IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir); // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[]. Rebuild later from the former. // FIXME-OPT: If this is called multiple times we'd effectively have a O(N^2) thing going on. for (int n = 0; n < table->ColumnsCount; n++) table->DisplayOrderToIndex[table->Columns[n].DisplayOrder] = (ImGuiTableColumnIdx)n; table->IsSettingsDirty = true; } // Adjust flags: default width mode + stretch columns are not allowed when auto extending static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in) { ImGuiTableColumnFlags flags = flags_in; // Sizing Policy if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0) { const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); if (table_sizing_policy == ImGuiTableFlags_SizingFixedFit || table_sizing_policy == ImGuiTableFlags_SizingFixedSame) flags |= ImGuiTableColumnFlags_WidthFixed; else flags |= ImGuiTableColumnFlags_WidthStretch; } else { IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used. } // Resize if ((table->Flags & ImGuiTableFlags_Resizable) == 0) flags |= ImGuiTableColumnFlags_NoResize; // Sorting if ((flags & ImGuiTableColumnFlags_NoSortAscending) && (flags & ImGuiTableColumnFlags_NoSortDescending)) flags |= ImGuiTableColumnFlags_NoSort; // Indentation if ((flags & ImGuiTableColumnFlags_IndentMask_) == 0) flags |= (table->Columns.index_from_ptr(column) == 0) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable; // Alignment //if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0) // flags |= ImGuiTableColumnFlags_AlignCenter; //IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used. // Preserve status flags column->Flags = flags | (column->Flags & ImGuiTableColumnFlags_StatusMask_); // Build an ordered list of available sort directions column->SortDirectionsAvailCount = column->SortDirectionsAvailMask = column->SortDirectionsAvailList = 0; if (table->Flags & ImGuiTableFlags_Sortable) { int count = 0, mask = 0, list = 0; if ((flags & ImGuiTableColumnFlags_PreferSortAscending) != 0 && (flags & ImGuiTableColumnFlags_NoSortAscending) == 0) { mask |= 1 << ImGuiSortDirection_Ascending; list |= ImGuiSortDirection_Ascending << (count << 1); count++; } if ((flags & ImGuiTableColumnFlags_PreferSortDescending) != 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; } if ((flags & ImGuiTableColumnFlags_PreferSortAscending) == 0 && (flags & ImGuiTableColumnFlags_NoSortAscending) == 0) { mask |= 1 << ImGuiSortDirection_Ascending; list |= ImGuiSortDirection_Ascending << (count << 1); count++; } if ((flags & ImGuiTableColumnFlags_PreferSortDescending) == 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; } if ((table->Flags & ImGuiTableFlags_SortTristate) || count == 0) { mask |= 1 << ImGuiSortDirection_None; count++; } column->SortDirectionsAvailList = (ImU8)list; column->SortDirectionsAvailMask = (ImU8)mask; column->SortDirectionsAvailCount = (ImU8)count; ImGui::TableFixColumnSortDirection(table, column); } } // Layout columns for the frame. This is in essence the followup to BeginTable() and this is our largest function. // Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() and other TableSetupXXXXX() functions to be called first. // FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns. // Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns? void ImGui::TableUpdateLayout(ImGuiTable* table) { ImGuiContext& g = *GImGui; IM_ASSERT(table->IsLayoutLocked == false); const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); table->IsDefaultDisplayOrder = true; table->ColumnsEnabledCount = 0; ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount); ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount); table->LeftMostEnabledColumn = -1; table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns. // Process columns in their visible orders as we are building the Prev/Next indices. int count_fixed = 0; // Number of columns that have fixed sizing policies int count_stretch = 0; // Number of columns that have stretch sizing policies int prev_visible_column_idx = -1; bool has_auto_fit_request = false; bool has_resizable = false; float stretch_sum_width_auto = 0.0f; float fixed_max_width_auto = 0.0f; for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { const int column_n = table->DisplayOrderToIndex[order_n]; if (column_n != order_n) table->IsDefaultDisplayOrder = false; ImGuiTableColumn* column = &table->Columns[column_n]; // Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame. // It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway. // We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect. if (table->DeclColumnsCount <= column_n) { TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None); column->NameOffset = -1; column->UserID = 0; column->InitStretchWeightOrWidth = -1.0f; } // Update Enabled state, mark settings and sort specs dirty if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide)) column->IsUserEnabledNextFrame = true; if (column->IsUserEnabled != column->IsUserEnabledNextFrame) { column->IsUserEnabled = column->IsUserEnabledNextFrame; table->IsSettingsDirty = true; } column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0; if (column->SortOrder != -1 && !column->IsEnabled) table->IsSortSpecsDirty = true; if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti)) table->IsSortSpecsDirty = true; // Auto-fit unsized columns const bool start_auto_fit = (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? (column->WidthRequest < 0.0f) : (column->StretchWeight < 0.0f); if (start_auto_fit) column->AutoFitQueue = column->CannotSkipItemsQueue = (1 << 3) - 1; // Fit for three frames if (!column->IsEnabled) { column->IndexWithinEnabledSet = -1; continue; } // Mark as enabled and link to previous/next enabled column column->PrevEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; column->NextEnabledColumn = -1; if (prev_visible_column_idx != -1) table->Columns[prev_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n; else table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n; column->IndexWithinEnabledSet = table->ColumnsEnabledCount++; ImBitArraySetBit(table->EnabledMaskByIndex, column_n); ImBitArraySetBit(table->EnabledMaskByDisplayOrder, column->DisplayOrder); prev_visible_column_idx = column_n; IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder); // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping) // Combine width from regular rows + width from headers unless requested not to. if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0) column->WidthAuto = TableGetColumnWidthAuto(table, column); // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto) const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0; if (column_is_resizable) has_resizable = true; if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f && !column_is_resizable) column->WidthAuto = column->InitStretchWeightOrWidth; if (column->AutoFitQueue != 0x00) has_auto_fit_request = true; if (column->Flags & ImGuiTableColumnFlags_WidthStretch) { stretch_sum_width_auto += column->WidthAuto; count_stretch++; } else { fixed_max_width_auto = ImMax(fixed_max_width_auto, column->WidthAuto); count_fixed++; } } if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) table->IsSortSpecsDirty = true; table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0); // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid // the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). Also see #6510. // FIXME-TABLE: for always auto-resizing columns may not want to do that all the time. if (has_auto_fit_request && table->OuterWindow != table->InnerWindow) table->InnerWindow->SkipItems = false; if (has_auto_fit_request) table->IsSettingsDirty = true; // [Part 3] Fix column flags and record a few extra information. float sum_width_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding. float stretch_sum_weights = 0.0f; // Sum of all weights for stretch columns. table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) continue; ImGuiTableColumn* column = &table->Columns[column_n]; const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0; if (column->Flags & ImGuiTableColumnFlags_WidthFixed) { // Apply same widths policy float width_auto = column->WidthAuto; if (table_sizing_policy == ImGuiTableFlags_SizingFixedSame && (column->AutoFitQueue != 0x00 || !column_is_resizable)) width_auto = fixed_max_width_auto; // Apply automatic width // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!) if (column->AutoFitQueue != 0x00) column->WidthRequest = width_auto; else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && column->IsRequestOutput) column->WidthRequest = width_auto; // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very // large height (= first frame scrollbar display very off + clipper would skip lots of items). // This is merely making the side-effect less extreme, but doesn't properly fixes it. // FIXME: Move this to ->WidthGiven to avoid temporary lossyness? // FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller. if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto) column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale? sum_width_requests += column->WidthRequest; } else { // Initialize stretch weight if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f || !column_is_resizable) { if (column->InitStretchWeightOrWidth > 0.0f) column->StretchWeight = column->InitStretchWeightOrWidth; else if (table_sizing_policy == ImGuiTableFlags_SizingStretchProp) column->StretchWeight = (column->WidthAuto / stretch_sum_width_auto) * count_stretch; else column->StretchWeight = 1.0f; } stretch_sum_weights += column->StretchWeight; if (table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder > column->DisplayOrder) table->LeftMostStretchedColumn = (ImGuiTableColumnIdx)column_n; if (table->RightMostStretchedColumn == -1 || table->Columns[table->RightMostStretchedColumn].DisplayOrder < column->DisplayOrder) table->RightMostStretchedColumn = (ImGuiTableColumnIdx)column_n; } column->IsPreserveWidthAuto = false; sum_width_requests += table->CellPaddingX * 2.0f; } table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed; table->ColumnsStretchSumWeights = stretch_sum_weights; // [Part 4] Apply final widths based on requested widths const ImRect work_rect = table->WorkRect; const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synced tables with mismatching scrollbar state (#5920) const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed); const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests; float width_remaining_for_stretched_columns = width_avail_for_stretched_columns; table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) continue; ImGuiTableColumn* column = &table->Columns[column_n]; // Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest) if (column->Flags & ImGuiTableColumnFlags_WidthStretch) { float weight_ratio = column->StretchWeight / stretch_sum_weights; column->WidthRequest = IM_TRUNC(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f); width_remaining_for_stretched_columns -= column->WidthRequest; } // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column // See additional comments in TableSetColumnWidth(). if (column->NextEnabledColumn == -1 && table->LeftMostStretchedColumn != -1) column->Flags |= ImGuiTableColumnFlags_NoDirectResize_; // Assign final width, record width in case we will need to shrink column->WidthGiven = ImTrunc(ImMax(column->WidthRequest, table->MinColumnWidth)); table->ColumnsGivenWidth += column->WidthGiven; } // [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column). // Using right-to-left distribution (more likely to match resizing cursor). if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths)) for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) { if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) continue; ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]]; if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch)) continue; column->WidthRequest += 1.0f; column->WidthGiven += 1.0f; width_remaining_for_stretched_columns -= 1.0f; } // Determine if table is hovered which will be used to flag columns as hovered. // - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), // but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily // clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem). // - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop. ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); table_instance->HoveredRowLast = table_instance->HoveredRowNext; table_instance->HoveredRowNext = -1; table->HoveredColumnBody = table->HoveredColumnBorder = -1; const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); const ImGuiID backup_active_id = g.ActiveId; g.ActiveId = 0; const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None); g.ActiveId = backup_active_id; // Determine skewed MousePos.x to support angled headers. float mouse_skewed_x = g.IO.MousePos.x; if (table->AngledHeadersHeight > 0.0f) if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight) mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope); // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping. int visible_n = 0; bool has_at_least_one_column_requesting_output = false; bool offset_x_frozen = (table->FreezeColumnsCount > 0); float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1; ImRect host_clip_rect = table->InnerClipRect; //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2; ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount); for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; // Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer); if (offset_x_frozen && table->FreezeColumnsCount == visible_n) { offset_x += work_rect.Min.x - table->OuterRect.Min.x; offset_x_frozen = false; } // Clear status flags column->Flags &= ~ImGuiTableColumnFlags_StatusMask_; if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) { // Hidden column: clear a few fields and we are done with it for the remainder of the function. // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper. column->MinX = column->MaxX = column->WorkMinX = column->ClipRect.Min.x = column->ClipRect.Max.x = offset_x; column->WidthGiven = 0.0f; column->ClipRect.Min.y = work_rect.Min.y; column->ClipRect.Max.y = FLT_MAX; column->ClipRect.ClipWithFull(host_clip_rect); column->IsVisibleX = column->IsVisibleY = column->IsRequestOutput = false; column->IsSkipItems = true; column->ItemWidth = 1.0f; continue; } // Lock start position column->MinX = offset_x; // Lock width based on start position and minimum/maximum width for this position column->WidthMax = TableCalcMaxColumnWidth(table, column_n); column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax); column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth)); column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; // Lock other positions // - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging. // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column. // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow. // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter. const float previous_instance_work_min_x = column->WorkMinX; column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1; column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f); column->ClipRect.Min.x = column->MinX; column->ClipRect.Min.y = work_rect.Min.y; column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX; column->ClipRect.Max.y = FLT_MAX; column->ClipRect.ClipWithFull(host_clip_rect); // Mark column as Clipped (not in sight) // Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables. // FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window->ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY. // Taking advantage of LastOuterHeight would yield good results there... // FIXME-TABLE: Y clipping is disabled because it effectively means not submitting will reduce contents width which is fed to outer_window->DC.CursorMaxPos.x, // and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else). // Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small. column->IsVisibleX = (column->ClipRect.Max.x > column->ClipRect.Min.x); column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y); const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY; if (is_visible) ImBitArraySetBit(table->VisibleMaskByIndex, column_n); // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output. column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0; // Mark column as SkipItems (ignoring all items/layout) // (table->HostSkipItems is a copy of inner_window->SkipItems before we cleared it above in Part 2) column->IsSkipItems = !column->IsEnabled || table->HostSkipItems; if (column->IsSkipItems) IM_ASSERT(!is_visible); if (column->IsRequestOutput && !column->IsSkipItems) has_at_least_one_column_requesting_output = true; // Update status flags column->Flags |= ImGuiTableColumnFlags_IsEnabled; if (is_visible) column->Flags |= ImGuiTableColumnFlags_IsVisible; if (column->SortOrder != -1) column->Flags |= ImGuiTableColumnFlags_IsSorted; // Detect hovered column if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x) { column->Flags |= ImGuiTableColumnFlags_IsHovered; table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; } // Alignment // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in // many cases (to be able to honor this we might be able to store a log of cells width, per row, for // visible rows, but nav/programmatic scroll would have visible artifacts.) //if (column->Flags & ImGuiTableColumnFlags_AlignRight) // column->WorkMinX = ImMax(column->WorkMinX, column->MaxX - column->ContentWidthRowsUnfrozen); //else if (column->Flags & ImGuiTableColumnFlags_AlignCenter) // column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f); // Reset content width variables if (table->InstanceCurrent == 0) { column->ContentMaxXFrozen = column->WorkMinX; column->ContentMaxXUnfrozen = column->WorkMinX; column->ContentMaxXHeadersUsed = column->WorkMinX; column->ContentMaxXHeadersIdeal = column->WorkMinX; } else { // As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation. const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x; column->ContentMaxXFrozen += offset_from_previous_instance; column->ContentMaxXUnfrozen += offset_from_previous_instance; column->ContentMaxXHeadersUsed += offset_from_previous_instance; column->ContentMaxXHeadersIdeal += offset_from_previous_instance; } // Don't decrement auto-fit counters until container window got a chance to submit its items if (table->HostSkipItems == false && table->InstanceCurrent == 0) { column->AutoFitQueue >>= 1; column->CannotSkipItemsQueue >>= 1; } if (visible_n < table->FreezeColumnsCount) host_clip_rect.Min.x = ImClamp(column->MaxX + TABLE_BORDER_SIZE, host_clip_rect.Min.x, host_clip_rect.Max.x); offset_x += column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; visible_n++; } // In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible. // Else if give no chance to a clipper-savvy user to submit rows and therefore total contents height used by scrollbar. if (has_at_least_one_column_requesting_output == false) { table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true; table->Columns[table->LeftMostEnabledColumn].IsSkipItems = false; } // [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it) // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu. const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (is_hovering_table && table->HoveredColumnBody == -1) if (mouse_skewed_x >= unused_x1) table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) table->Flags &= ~ImGuiTableFlags_Resizable; table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0); // [Part 8] Lock actual OuterRect/WorkRect right-most position. // This is done late to handle the case of fixed-columns tables not claiming more widths that they need. // Because of this we are careful with uses of WorkRect and InnerClipRect before this point. if (table->RightMostStretchedColumn != -1) table->Flags &= ~ImGuiTableFlags_NoHostExtendX; if (table->Flags & ImGuiTableFlags_NoHostExtendX) { table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1; table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1); } table->InnerWindow->ParentWorkRect = table->WorkRect; table->BorderX1 = table->InnerClipRect.Min.x; table->BorderX2 = table->InnerClipRect.Max.x; // Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call. float window_content_max_y; if (table->Flags & ImGuiTableFlags_NoHostExtendY) window_content_max_y = table->OuterRect.Max.y; else window_content_max_y = ImMax(table->InnerWindow->ContentRegionRect.Max.y, (table->Flags & ImGuiTableFlags_ScrollY) ? 0.0f : table->OuterRect.Max.y); table->InnerWindow->WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table->InnerWindow->WorkRect.Min.y, table->InnerWindow->WorkRect.Max.y); // [Part 9] Allocate draw channels and setup background cliprect TableSetupDrawChannels(table); // [Part 10] Hit testing on borders if (table->Flags & ImGuiTableFlags_Resizable) TableUpdateBorders(table); table_instance->LastTopHeadersRowHeight = 0.0f; table->IsLayoutLocked = true; table->IsUsingHeaders = false; // Highlight header table->HighlightColumnHeader = -1; if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent) table->HighlightColumnHeader = table->ContextPopupColumn; else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1) if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive)) table->HighlightColumnHeader = table->HoveredColumnBody; // [Part 11] Default context menu // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup(). // - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup(). // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu, // e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options. if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table)) { TableDrawDefaultContextMenu(table, table->Flags); EndPopup(); } // [Part 12] Sanitize and build sort specs before we have a chance to use them for display. // This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change) if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable)) TableSortSpecsBuild(table); // [Part 13] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns) if (table->FreezeColumnsRequest > 0) table->InnerWindow->DecoInnerSizeX1 = table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsRequest - 1]].MaxX - table->OuterRect.Min.x; if (table->FreezeRowsRequest > 0) table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight; table_instance->LastFrozenHeight = 0.0f; // Initial state ImGuiWindow* inner_window = table->InnerWindow; if (table->Flags & ImGuiTableFlags_NoClip) table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); else inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect? } // Process hit-testing on resizing borders. Actual size change will be applied in EndTable() // - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise. void ImGui::TableUpdateBorders(ImGuiTable* table) { ImGuiContext& g = *GImGui; IM_ASSERT(table->Flags & ImGuiTableFlags_Resizable); // At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and // use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height). // Actual columns highlight/render will be performed in EndTable() and not be affected. ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale); const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight; const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight); const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight; for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) continue; const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) continue; // ImGuiTableFlags_NoBordersInBodyUntilResize will be honored in TableDrawBorders() const float border_y2_hit = (table->Flags & ImGuiTableFlags_NoBordersInBody) ? hit_y2_head : hit_y2_body; if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false) continue; if (!column->IsVisibleX && table->LastResizedColumn != column_n) continue; ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent); ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit); ItemAdd(hit_rect, column_id, NULL, ImGuiItemFlags_NoNav); //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100)); bool hovered = false, held = false; bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); if (pressed && IsMouseDoubleClicked(0)) { TableSetColumnWidthAutoSingle(table, column_n); ClearActiveID(); held = false; } if (held) { if (table->LastResizedColumn == -1) table->ResizeLockMinContentsX2 = table->RightMostEnabledColumn != -1 ? table->Columns[table->RightMostEnabledColumn].MaxX : -FLT_MAX; table->ResizedColumn = (ImGuiTableColumnIdx)column_n; table->InstanceInteracted = table->InstanceCurrent; } if ((hovered && g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) || held) { table->HoveredColumnBorder = (ImGuiTableColumnIdx)column_n; SetMouseCursor(ImGuiMouseCursor_ResizeEW); } } } void ImGui::EndTable() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; IM_ASSERT_USER_ERROR_RET(table != NULL, "EndTable() call should only be done while in BeginTable() scope!"); // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border) //IM_ASSERT(table->IsLayoutLocked && "Table unused: never called TableNextRow(), is that the intent?"); // If the user never got to call TableNextRow() or TableNextColumn(), we call layout ourselves to ensure all our // code paths are consistent (instead of just hoping that TableBegin/TableEnd will work), get borders drawn, etc. if (!table->IsLayoutLocked) TableUpdateLayout(table); const ImGuiTableFlags flags = table->Flags; ImGuiWindow* inner_window = table->InnerWindow; ImGuiWindow* outer_window = table->OuterWindow; ImGuiTableTempData* temp_data = table->TempData; IM_ASSERT(inner_window == g.CurrentWindow && inner_window->ID == temp_data->WindowID); IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow); if (table->IsInsideRow) TableEndRow(table); // Context menu in columns body if (flags & ImGuiTableFlags_ContextMenuInBody) if (table->HoveredColumnBody != -1 && !IsAnyItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) TableOpenContextMenu((int)table->HoveredColumnBody); // Finalize table height ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; const float inner_content_max_y = ImCeil(table->RowPosY2); // Rounding final position is important as we currently don't round row height ('Demo->Tables->Outer Size' demo uses non-integer heights) IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); if (inner_window != outer_window) inner_window->DC.CursorMaxPos.y = inner_content_max_y; else if (!(flags & ImGuiTableFlags_NoHostExtendY)) table->OuterRect.Max.y = table->InnerRect.Max.y = ImMax(table->OuterRect.Max.y, inner_content_max_y); // Patch OuterRect/InnerRect height table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y); table_instance->LastOuterHeight = table->OuterRect.GetHeight(); // Setup inner scrolling range // FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y, // but since the later is likely to be impossible to do we'd rather update both axes together. if (table->Flags & ImGuiTableFlags_ScrollX) { const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; float max_pos_x = table->InnerWindow->DC.CursorMaxPos.x; if (table->RightMostEnabledColumn != -1) max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border); if (table->ResizedColumn != -1) max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth; } // Pop clipping rect if (!(flags & ImGuiTableFlags_NoClip)) inner_window->DrawList->PopClipRect(); inner_window->ClipRect = inner_window->DrawList->_ClipRectStack.back(); // Draw borders if ((flags & ImGuiTableFlags_Borders) != 0) TableDrawBorders(table); #if 0 // Strip out dummy channel draw calls // We have no way to prevent user submitting direct ImDrawList calls into a hidden column (but ImGui:: calls will be clipped out) // Pros: remove draw calls which will have no effect. since they'll have zero-size cliprect they may be early out anyway. // Cons: making it harder for users watching metrics/debugger to spot the wasted vertices. if (table->DummyDrawChannel != (ImGuiTableColumnIdx)-1) { ImDrawChannel* dummy_channel = &table->DrawSplitter._Channels[table->DummyDrawChannel]; dummy_channel->_CmdBuffer.resize(0); dummy_channel->_IdxBuffer.resize(0); } #endif // Flatten channels and merge draw calls ImDrawListSplitter* splitter = table->DrawSplitter; splitter->SetCurrentChannel(inner_window->DrawList, 0); if ((table->Flags & ImGuiTableFlags_NoClip) == 0) TableMergeDrawChannels(table); splitter->Merge(inner_window->DrawList); // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable() float auto_fit_width_for_fixed = 0.0f; float auto_fit_width_for_stretched = 0.0f; float auto_fit_width_for_stretched_min = 0.0f; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) { ImGuiTableColumn* column = &table->Columns[column_n]; float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column); if (column->Flags & ImGuiTableColumnFlags_WidthFixed) auto_fit_width_for_fixed += column_width_request; else auto_fit_width_for_stretched += column_width_request; if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && (column->Flags & ImGuiTableColumnFlags_NoResize) != 0) auto_fit_width_for_stretched_min = ImMax(auto_fit_width_for_stretched_min, column_width_request / (column->StretchWeight / table->ColumnsStretchSumWeights)); } const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); table->ColumnsAutoFitWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount + auto_fit_width_for_fixed + ImMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min); // Update scroll if ((table->Flags & ImGuiTableFlags_ScrollX) == 0 && inner_window != outer_window) { inner_window->Scroll.x = 0.0f; } else if (table->LastResizedColumn != -1 && table->ResizedColumn == -1 && inner_window->ScrollbarX && table->InstanceInteracted == table->InstanceCurrent) { // When releasing a column being resized, scroll to keep the resulting column in sight const float neighbor_width_to_keep_visible = table->MinColumnWidth + table->CellPaddingX * 2.0f; ImGuiTableColumn* column = &table->Columns[table->LastResizedColumn]; if (column->MaxX < table->InnerClipRect.Min.x) SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x - neighbor_width_to_keep_visible, 1.0f); else if (column->MaxX > table->InnerClipRect.Max.x) SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x + neighbor_width_to_keep_visible, 1.0f); } // Apply resizing/dragging at the end of the frame if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted) { ImGuiTableColumn* column = &table->Columns[table->ResizedColumn]; const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale)); const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f); table->ResizedColumnNextWidth = new_width; } table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false); // Pop from id stack IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!"); IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); if (table->InstanceCurrent > 0) PopID(); PopID(); // Restore window data that we modified const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos; inner_window->WorkRect = temp_data->HostBackupWorkRect; inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect; inner_window->SkipItems = table->HostSkipItems; outer_window->DC.CursorPos = table->OuterRect.Min; outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth; outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize; outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset; // Layout in outer window // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding // CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414) if (inner_window != outer_window) { short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask; inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently. g.CurrentTable = NULL; // To avoid error recovery recursing EndChild(); g.CurrentTable = table; inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask; } else { table->InnerWindow->DC.TreeDepth--; ItemSize(table->OuterRect.GetSize()); ItemAdd(table->OuterRect, 0); } // Override declared contents width/height to enable auto-resize while not needlessly adding a scrollbar if (table->Flags & ImGuiTableFlags_NoHostExtendX) { // FIXME-TABLE: Could we remove this section? // ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth); } else if (temp_data->UserOuterSize.x <= 0.0f) { // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block // - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags. // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback. const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f); outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size)); } else { outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x); } if (temp_data->UserOuterSize.y <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f; outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size)); } else { // OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags_NoHostExtendY is set) outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, table->OuterRect.Max.y); } // Save settings if (table->IsSettingsDirty) TableSaveSettings(table); table->IsInitializing = false; // Clear or restore current table, if any IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); IM_ASSERT(g.TablesTempDataStacked > 0); temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; g.CurrentTable = temp_data && (temp_data->WindowID == outer_window->ID) ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; if (g.CurrentTable) { g.CurrentTable->TempData = temp_data; g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter; } outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1; NavUpdateCurrentWindowIsScrollPushableX(); } // Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings. // 'init_mask' specify which fields to initialize. static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask) { ImGuiTableColumnFlags flags = column->Flags; if (init_mask & ImGuiTableFlags_Resizable) { float init_width_or_weight = column->InitStretchWeightOrWidth; column->WidthRequest = ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f; column->StretchWeight = (init_width_or_weight > 0.0f && (flags & ImGuiTableColumnFlags_WidthStretch)) ? init_width_or_weight : -1.0f; if (init_width_or_weight > 0.0f) // Disable auto-fit if an explicit width/weight has been specified column->AutoFitQueue = 0x00; } if (init_mask & ImGuiTableFlags_Reorderable) column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column); if (init_mask & ImGuiTableFlags_Hideable) column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1; if (init_mask & ImGuiTableFlags_Sortable) { // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs. column->SortOrder = (flags & ImGuiTableColumnFlags_DefaultSort) ? 0 : -1; column->SortDirection = (flags & ImGuiTableColumnFlags_DefaultSort) ? ((flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending)) : (ImS8)ImGuiSortDirection_None; } } // See "COLUMNS SIZING POLICIES" comments at the top of this file // If (init_width_or_weight <= 0.0f) it is ignored void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, "TableSetupColumn(): called too many times!"); IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount]; table->DeclColumnsCount++; // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa. // Give a grace to users of ImGuiTableFlags_ScrollX. if (table->IsDefaultSizingPolicy && (flags & ImGuiTableColumnFlags_WidthMask_) == 0 && (flags & ImGuiTableFlags_ScrollX) == 0) IM_ASSERT_USER_ERROR_RET(init_width_or_weight <= 0.0f, "TableSetupColumn(): can only specify width/weight if sizing policy is set explicitly in either Table or Column."); // When passing a width automatically enforce WidthFixed policy // (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable) if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f) if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) flags |= ImGuiTableColumnFlags_WidthFixed; if (flags & ImGuiTableColumnFlags_AngledHeader) { flags |= ImGuiTableColumnFlags_NoHeaderLabel; table->AngledHeadersCount++; } TableSetupColumnFlags(table, column, flags); column->UserID = user_id; flags = column->Flags; // Initialize defaults column->InitStretchWeightOrWidth = init_width_or_weight; if (table->IsInitializing) { ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags; if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f) init_flags |= ImGuiTableFlags_Resizable; TableInitColumnDefaults(table, column, init_flags); } // Store name (append with zero-terminator in contiguous buffer) // FIXME: If we recorded the number of \n in names we could compute header row height column->NameOffset = -1; if (label != NULL && label[0] != 0) { column->NameOffset = (ImS16)table->ColumnsNames.size(); table->ColumnsNames.append(label, label + ImStrlen(label) + 1); } } // [Public] void ImGui::TableSetupScrollFreeze(int columns, int rows) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(table->IsLayoutLocked == false && "TableSetupColumn(): need to call before first row!"); IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0; table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0; table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0; table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0; table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered. // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section) for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++) { int order_n = table->DisplayOrderToIndex[column_n]; if (order_n != column_n && order_n >= table->FreezeColumnsRequest) { ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder); ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]); } } } //----------------------------------------------------------------------------- // [SECTION] Tables: Simple accessors //----------------------------------------------------------------------------- // - TableGetColumnCount() // - TableGetColumnName() // - TableGetColumnName() [Internal] // - TableSetColumnEnabled() // - TableGetColumnFlags() // - TableGetCellBgRect() [Internal] // - TableGetColumnResizeID() [Internal] // - TableGetHoveredColumn() [Internal] // - TableGetHoveredRow() [Internal] // - TableSetBgColor() //----------------------------------------------------------------------------- int ImGui::TableGetColumnCount() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; return table ? table->ColumnsCount : 0; } const char* ImGui::TableGetColumnName(int column_n) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table) return NULL; if (column_n < 0) column_n = table->CurrentColumn; return TableGetColumnName(table, column_n); } const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) { if (table->IsLayoutLocked == false && column_n >= table->DeclColumnsCount) return ""; // NameOffset is invalid at this point const ImGuiTableColumn* column = &table->Columns[column_n]; if (column->NameOffset == -1) return ""; return &table->ColumnsNames.Buf[column->NameOffset]; } // Change user accessible enabled/disabled state of a column (often perceived as "showing/hiding" from users point of view) // Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) // - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state. // - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable(). // - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0. // - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu. void ImGui::TableSetColumnEnabled(int column_n, bool enabled) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above if (column_n < 0) column_n = table->CurrentColumn; IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); ImGuiTableColumn* column = &table->Columns[column_n]; column->IsUserEnabledNextFrame = enabled; } // We allow querying for an extra column in order to poll the IsHovered state of the right-most section ImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table) return ImGuiTableColumnFlags_None; if (column_n < 0) column_n = table->CurrentColumn; if (column_n == table->ColumnsCount) return (table->HoveredColumnBody == column_n) ? ImGuiTableColumnFlags_IsHovered : ImGuiTableColumnFlags_None; return table->Columns[column_n].Flags; } // Return the cell rectangle based on currently known height. // - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations. // The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height. // - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right // columns report a small offset so their CellBgRect can extend up to the outer border. // FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling. ImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n) { const ImGuiTableColumn* column = &table->Columns[column_n]; float x1 = column->MinX; float x2 = column->MaxX; //if (column->PrevEnabledColumn == -1) // x1 -= table->OuterPaddingX; //if (column->NextEnabledColumn == -1) // x2 += table->OuterPaddingX; x1 = ImMax(x1, table->WorkRect.Min.x); x2 = ImMin(x2, table->WorkRect.Max.x); return ImRect(x1, table->RowPosY1, x2, table->RowPosY2); } // Return the resizing ID for the right-side of the given column. ImGuiID ImGui::TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no) { IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); ImGuiID instance_id = TableGetInstanceID(table, instance_no); return instance_id + 1 + column_n; // FIXME: #6140: still not ideal } // Return -1 when table is not hovered. return columns_count if hovering the unused space at the right of the right-most visible column. int ImGui::TableGetHoveredColumn() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table) return -1; return (int)table->HoveredColumnBody; } // Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row. // *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value. // This difference with is the reason why this is not public yet. int ImGui::TableGetHoveredRow() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table) return -1; ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); return (int)table_instance->HoveredRowLast; } void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(target != ImGuiTableBgTarget_None); if (color == IM_COL32_DISABLE) color = 0; // We cannot draw neither the cell or row background immediately as we don't know the row height at this point in time. switch (target) { case ImGuiTableBgTarget_CellBg: { if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard return; if (column_n == -1) column_n = table->CurrentColumn; if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n)) return; if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n) table->RowCellDataCurrent++; ImGuiTableCellData* cell_data = &table->RowCellData[table->RowCellDataCurrent]; cell_data->BgColor = color; cell_data->Column = (ImGuiTableColumnIdx)column_n; break; } case ImGuiTableBgTarget_RowBg0: case ImGuiTableBgTarget_RowBg1: { if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard return; IM_ASSERT(column_n == -1); int bg_idx = (target == ImGuiTableBgTarget_RowBg1) ? 1 : 0; table->RowBgColor[bg_idx] = color; break; } default: IM_ASSERT(0); } } //------------------------------------------------------------------------- // [SECTION] Tables: Row changes //------------------------------------------------------------------------- // - TableGetRowIndex() // - TableNextRow() // - TableBeginRow() [Internal] // - TableEndRow() [Internal] //------------------------------------------------------------------------- // [Public] Note: for row coloring we use ->RowBgColorCounter which is the same value without counting header rows int ImGui::TableGetRowIndex() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table) return 0; return table->CurrentRow; } // [Public] Starts into the first cell of a new row void ImGui::TableNextRow(ImGuiTableRowFlags row_flags, float row_min_height) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table->IsLayoutLocked) TableUpdateLayout(table); if (table->IsInsideRow) TableEndRow(table); table->LastRowFlags = table->RowFlags; table->RowFlags = row_flags; table->RowCellPaddingY = g.Style.CellPadding.y; table->RowMinHeight = row_min_height; TableBeginRow(table); // We honor min_row_height requested by user, but cannot guarantee per-row maximum height, // because that would essentially require a unique clipping rectangle per-cell. table->RowPosY2 += table->RowCellPaddingY * 2.0f; table->RowPosY2 = ImMax(table->RowPosY2, table->RowPosY1 + row_min_height); // Disable output until user calls TableNextColumn() table->InnerWindow->SkipItems = true; } // [Internal] Only called by TableNextRow() void ImGui::TableBeginRow(ImGuiTable* table) { ImGuiWindow* window = table->InnerWindow; IM_ASSERT(!table->IsInsideRow); // New row table->CurrentRow++; table->CurrentColumn = -1; table->RowBgColor[0] = table->RowBgColor[1] = IM_COL32_DISABLE; table->RowCellDataCurrent = -1; table->IsInsideRow = true; // Begin frozen rows float next_y1 = table->RowPosY2; if (table->CurrentRow == 0 && table->FreezeRowsCount > 0) next_y1 = window->DC.CursorPos.y = table->OuterRect.Min.y; table->RowPosY1 = table->RowPosY2 = next_y1; table->RowTextBaseline = 0.0f; table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + table->RowCellPaddingY); // This allows users to call SameLine() to share LineSize between columns. window->DC.PrevLineSize = window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // This allows users to call SameLine() to share LineSize between columns, and to call it from first column too. window->DC.IsSameLine = window->DC.IsSetPos = false; window->DC.CursorMaxPos.y = next_y1; // Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging. if (table->RowFlags & ImGuiTableRowFlags_Headers) { TableSetBgColor(ImGuiTableBgTarget_RowBg0, GetColorU32(ImGuiCol_TableHeaderBg)); if (table->CurrentRow == 0) table->IsUsingHeaders = true; } } // [Internal] Called by TableNextRow() void ImGui::TableEndRow(ImGuiTable* table) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(window == table->InnerWindow); IM_ASSERT(table->IsInsideRow); if (table->CurrentColumn != -1) { TableEndCell(table); table->CurrentColumn = -1; } // Logging if (g.LogEnabled) LogRenderedText(NULL, "|"); // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is // likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding. window->DC.CursorPos.y = table->RowPosY2; // Row background fill const float bg_y1 = table->RowPosY1; const float bg_y2 = table->RowPosY2; const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount); const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest); ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); if ((table->RowFlags & ImGuiTableRowFlags_Headers) && (table->CurrentRow == 0 || (table->LastRowFlags & ImGuiTableRowFlags_Headers))) table_instance->LastTopHeadersRowHeight += bg_y2 - bg_y1; const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y); if (is_visible) { // Update data for TableGetHoveredRow() if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0) table_instance->HoveredRowNext = table->CurrentRow; // Decide of background color for the row ImU32 bg_col0 = 0; ImU32 bg_col1 = 0; if (table->RowBgColor[0] != IM_COL32_DISABLE) bg_col0 = table->RowBgColor[0]; else if (table->Flags & ImGuiTableFlags_RowBg) bg_col0 = GetColorU32((table->RowBgColorCounter & 1) ? ImGuiCol_TableRowBgAlt : ImGuiCol_TableRowBg); if (table->RowBgColor[1] != IM_COL32_DISABLE) bg_col1 = table->RowBgColor[1]; // Decide of top border color ImU32 top_border_col = 0; const float border_size = TABLE_BORDER_SIZE; if (table->CurrentRow > 0 && (table->Flags & ImGuiTableFlags_BordersInnerH)) top_border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight; const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0; const bool draw_strong_bottom_border = unfreeze_rows_actual; if ((bg_col0 | bg_col1 | top_border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color) { // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. if ((table->Flags & ImGuiTableFlags_NoClip) == 0) window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4(); table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); } // Draw row background // We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle if (bg_col0 || bg_col1) { ImRect row_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2); row_rect.ClipWith(table->BgClipRect); if (bg_col0 != 0 && row_rect.Min.y < row_rect.Max.y) window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col0); if (bg_col1 != 0 && row_rect.Min.y < row_rect.Max.y) window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col1); } // Draw cell background color if (draw_cell_bg_color) { ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent]; for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++) { // As we render the BG here we need to clip things (for layout we would not) // FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling. const ImGuiTableColumn* column = &table->Columns[cell_data->Column]; ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column); cell_bg_rect.ClipWith(table->BgClipRect); cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX); if (cell_bg_rect.Min.y < cell_bg_rect.Max.y) window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor); } } // Draw top border if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y) window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size); // Draw bottom border at the row unfreezing mark (always strong) if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y) window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size); } // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle) // - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark // end of row and get the new cursor position. if (unfreeze_rows_request) { IM_ASSERT(table->FreezeRowsRequest > 0); for (int column_n = 0; column_n < table->ColumnsCount; column_n++) table->Columns[column_n].NavLayerCurrent = table->NavLayer; const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y); table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; if (unfreeze_rows_actual) { IM_ASSERT(table->IsUnfrozenRows == false); table->IsUnfrozenRows = true; // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y); table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y; table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y); float row_height = table->RowPosY2 - table->RowPosY1; table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y; table->RowPosY1 = table->RowPosY2 - row_height; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; column->DrawChannelCurrent = column->DrawChannelUnfrozen; column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y; } // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); } } if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) table->RowBgColorCounter++; table->IsInsideRow = false; } //------------------------------------------------------------------------- // [SECTION] Tables: Columns changes //------------------------------------------------------------------------- // - TableGetColumnIndex() // - TableSetColumnIndex() // - TableNextColumn() // - TableBeginCell() [Internal] // - TableEndCell() [Internal] //------------------------------------------------------------------------- int ImGui::TableGetColumnIndex() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table) return 0; return table->CurrentColumn; } // [Public] Append into a specific column bool ImGui::TableSetColumnIndex(int column_n) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table) return false; if (table->CurrentColumn != column_n) { if (table->CurrentColumn != -1) TableEndCell(table); IM_ASSERT_USER_ERROR_RETV(column_n >= 0 && column_n < table->ColumnsCount, false, "TableSetColumnIndex() invalid column index!"); TableBeginCell(table, column_n); } // Return whether the column is visible. User may choose to skip submitting items based on this return value, // however they shouldn't skip submitting for columns that may have the tallest contribution to row height. return table->Columns[column_n].IsRequestOutput; } // [Public] Append into the next column, wrap and create a new row when already on last column bool ImGui::TableNextColumn() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!table) return false; if (table->IsInsideRow && table->CurrentColumn + 1 < table->ColumnsCount) { if (table->CurrentColumn != -1) TableEndCell(table); TableBeginCell(table, table->CurrentColumn + 1); } else { TableNextRow(); TableBeginCell(table, 0); } // Return whether the column is visible. User may choose to skip submitting items based on this return value, // however they shouldn't skip submitting for columns that may have the tallest contribution to row height. return table->Columns[table->CurrentColumn].IsRequestOutput; } // [Internal] Called by TableSetColumnIndex()/TableNextColumn() // This is called very frequently, so we need to be mindful of unnecessary overhead. // FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns. void ImGui::TableBeginCell(ImGuiTable* table, int column_n) { ImGuiContext& g = *GImGui; ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiWindow* window = table->InnerWindow; table->CurrentColumn = column_n; // Start position is roughly ~~ CellRect.Min + CellPadding + Indent float start_x = column->WorkMinX; if (column->Flags & ImGuiTableColumnFlags_IndentEnable) start_x += table->RowIndentOffsetX; // ~~ += window.DC.Indent.x - table->HostIndentX, except we locked it for the row. window->DC.CursorPos.x = start_x; window->DC.CursorPos.y = table->RowPosY1 + table->RowCellPaddingY; window->DC.CursorMaxPos.x = window->DC.CursorPos.x; window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x; // PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns. window->DC.CurrLineTextBaseOffset = table->RowTextBaseline; window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent; // Note how WorkRect.Max.y is only set once during layout window->WorkRect.Min.y = window->DC.CursorPos.y; window->WorkRect.Min.x = column->WorkMinX; window->WorkRect.Max.x = column->WorkMaxX; window->DC.ItemWidth = column->ItemWidth; window->SkipItems = column->IsSkipItems; if (column->IsSkipItems) { g.LastItemData.ID = 0; g.LastItemData.StatusFlags = 0; } // Also see TablePushColumnChannel() if (table->Flags & ImGuiTableFlags_NoClip) { // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP); } else { // FIXME-TABLE: Could avoid this if draw channel is dummy channel? SetWindowClipRectBeforeSetChannel(window, column->ClipRect); table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Logging if (g.LogEnabled && !column->IsSkipItems) { LogRenderedText(&window->DC.CursorPos, "|"); g.LogLinePosY = FLT_MAX; } } // [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn() void ImGui::TableEndCell(ImGuiTable* table) { ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; ImGuiWindow* window = table->InnerWindow; if (window->DC.IsSetPos) ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); // Report maximum position so we can infer content size per column. float* p_max_pos_x; if (table->RowFlags & ImGuiTableRowFlags_Headers) p_max_pos_x = &column->ContentMaxXHeadersUsed; // Useful in case user submit contents in header row that is not a TableHeader() call else p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen; *p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x); if (column->IsEnabled) table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->RowCellPaddingY); column->ItemWidth = window->DC.ItemWidth; // Propagate text baseline for the entire row // FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one. table->RowTextBaseline = ImMax(table->RowTextBaseline, window->DC.PrevLineTextBaseOffset); } //------------------------------------------------------------------------- // [SECTION] Tables: Columns width management //------------------------------------------------------------------------- // - TableGetMaxColumnWidth() [Internal] // - TableGetColumnWidthAuto() [Internal] // - TableSetColumnWidth() // - TableSetColumnWidthAutoSingle() [Internal] // - TableSetColumnWidthAutoAll() [Internal] // - TableUpdateColumnsWeightFromWidth() [Internal] //------------------------------------------------------------------------- // Note that actual columns widths are computed in TableUpdateLayout(). //------------------------------------------------------------------------- // Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis. float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n) { const ImGuiTableColumn* column = &table->Columns[column_n]; float max_width = FLT_MAX; const float min_column_distance = table->MinColumnWidth + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2; if (table->Flags & ImGuiTableFlags_ScrollX) { // Frozen columns can't reach beyond visible width else scrolling will naturally break. // (we use DisplayOrder as within a set of multiple frozen column reordering is possible) if (column->DisplayOrder < table->FreezeColumnsRequest) { max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX; max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2; } } else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0) { // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make // sure they are all visible. Because of this we also know that all of the columns will always fit in // table->WorkRect and therefore in table->InnerRect (because ScrollX is off) // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match. // See "table_width_distrib" and "table_width_keep_visible" tests max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - column->MinX; //max_width -= table->CellSpacingX1; max_width -= table->CellSpacingX2; max_width -= table->CellPaddingX * 2.0f; max_width -= table->OuterPaddingX; } return max_width; } // Note this is meant to be stored in column->WidthAuto, please generally use the WidthAuto field float ImGui::TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column) { const float content_width_body = ImMax(column->ContentMaxXFrozen, column->ContentMaxXUnfrozen) - column->WorkMinX; const float content_width_headers = column->ContentMaxXHeadersIdeal - column->WorkMinX; float width_auto = content_width_body; if (!(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth)) width_auto = ImMax(width_auto, content_width_headers); // Non-resizable fixed columns preserve their requested width if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f) if (!(table->Flags & ImGuiTableFlags_Resizable) || (column->Flags & ImGuiTableColumnFlags_NoResize)) width_auto = column->InitStretchWeightOrWidth; return ImMax(width_auto, table->MinColumnWidth); } // 'width' = inner column width, without padding void ImGui::TableSetColumnWidth(int column_n, float width) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; IM_ASSERT(table != NULL && table->IsLayoutLocked == false); IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); ImGuiTableColumn* column_0 = &table->Columns[column_n]; float column_0_width = width; // Apply constraints early // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded) IM_ASSERT(table->MinColumnWidth > 0.0f); const float min_width = table->MinColumnWidth; const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933) column_0_width = ImClamp(column_0_width, min_width, max_width); if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) return; //IMGUI_DEBUG_PRINT("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width); ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL; // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns. // - All fixed: easy. // - All stretch: easy. // - One or more fixed + one stretch: easy. // - One or more fixed + more than one stretch: tricky. // Qt when manual resize is enabled only supports a single _trailing_ stretch column, we support more cases here. // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1. // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user. // Scenarios: // - F1 F2 F3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. Subsequent columns will be offset. // - F1 F2 F3 resize from F3| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered. // - F1 F2 W3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size. // - F1 F2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1) // - W1 W2 W3 resize from W1| or W2| --> ok // - W1 W2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1) // - W1 F2 F3 resize from F3| --> ok: no-op (disabled by Resize Rule 1) // - W1 F2 resize from F2| --> ok: no-op (disabled by Resize Rule 1) // - W1 W2 F3 resize from W1| or W2| --> ok // - W1 F2 W3 resize from W1| or F2| --> ok // - F1 W2 F3 resize from W2| --> ok // - F1 W3 F2 resize from W3| --> ok // - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move. // - W1 F2 F3 resize from F2| --> ok // All resizes from a Wx columns are locking other columns. // Possible improvements: // - W1 W2 W3 resize W1| --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns. // - W3 F1 F2 resize W3| --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix. // [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout(). // If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize. // This is the preferred resize path if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed) if (!column_1 || table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder >= column_0->DisplayOrder) { column_0->WidthRequest = column_0_width; table->IsSettingsDirty = true; return; } // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column) if (column_1 == NULL) column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL; if (column_1 == NULL) return; // Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column. // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b) float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width); column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width; IM_ASSERT(column_0_width > 0.0f && column_1_width > 0.0f); column_0->WidthRequest = column_0_width; column_1->WidthRequest = column_1_width; if ((column_0->Flags | column_1->Flags) & ImGuiTableColumnFlags_WidthStretch) TableUpdateColumnsWeightFromWidth(table); table->IsSettingsDirty = true; } // Disable clipping then auto-fit, will take 2 frames // (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns) void ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n) { // Single auto width uses auto-fit ImGuiTableColumn* column = &table->Columns[column_n]; if (!column->IsEnabled) return; column->CannotSkipItemsQueue = (1 << 0); table->AutoFitSingleColumn = (ImGuiTableColumnIdx)column_n; } void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table) { for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Cannot reset weight of hidden stretch column continue; column->CannotSkipItemsQueue = (1 << 0); column->AutoFitQueue = (1 << 1); } } void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) { IM_ASSERT(table->LeftMostStretchedColumn != -1 && table->RightMostStretchedColumn != -1); // Measure existing quantities float visible_weight = 0.0f; float visible_width = 0.0f; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) continue; IM_ASSERT(column->StretchWeight > 0.0f); visible_weight += column->StretchWeight; visible_width += column->WidthRequest; } IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f); // Apply new weights for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) continue; column->StretchWeight = (column->WidthRequest / visible_width) * visible_weight; IM_ASSERT(column->StretchWeight > 0.0f); } } //------------------------------------------------------------------------- // [SECTION] Tables: Drawing //------------------------------------------------------------------------- // - TablePushBackgroundChannel() [Internal] // - TablePopBackgroundChannel() [Internal] // - TableSetupDrawChannels() [Internal] // - TableMergeDrawChannels() [Internal] // - TableGetColumnBorderCol() [Internal] // - TableDrawBorders() [Internal] //------------------------------------------------------------------------- // FIXME: This could be abstracted and merged with PushColumnsBackground(), by creating a generic struct // with storage for backup cliprect + backup channel + storage for splitter pointer, new clip rect. // This would slightly simplify caller code. // Bg2 is used by Selectable (and possibly other widgets) to render to the background. // Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect. void ImGui::TablePushBackgroundChannel() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiTable* table = g.CurrentTable; // Optimization: avoid SetCurrentChannel() + PushClipRect() table->HostBackupInnerClipRect = window->ClipRect; SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd); table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); } void ImGui::TablePopBackgroundChannel() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiTable* table = g.CurrentTable; // Optimization: avoid PopClipRect() + SetCurrentChannel() SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent); } // Also see TableBeginCell() void ImGui::TablePushColumnChannel(int column_n) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; // Optimization: avoid SetCurrentChannel() + PushClipRect() if (table->Flags & ImGuiTableFlags_NoClip) return; ImGuiWindow* window = g.CurrentWindow; const ImGuiTableColumn* column = &table->Columns[column_n]; SetWindowClipRectBeforeSetChannel(window, column->ClipRect); table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } void ImGui::TablePopColumnChannel() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; // Optimization: avoid PopClipRect() + SetCurrentChannel() if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported. return; ImGuiWindow* window = g.CurrentWindow; const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; SetWindowClipRectBeforeSetChannel(window, column->ClipRect); table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Allocate draw channels. Called by TableUpdateLayout() // - We allocate them following storage order instead of display order so reordering columns won't needlessly // increase overall dormant memory cost. // - We isolate headers draw commands in their own channels instead of just altering clip rects. // This is in order to facilitate merging of draw commands. // - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels. // - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other // channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged. // - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for // horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4). // Draw channel allocation (before merging): // - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call) // - Clip --> 2+D+N channels // - FreezeRows --> 2+D+N*2 (unless scrolling value is zero) // - FreezeRows || FreezeColumns --> 3+D+N*2 (unless scrolling value is zero) // Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0. void ImGui::TableSetupDrawChannels(ImGuiTable* table) { const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1; const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount; const int channels_for_bg = 1 + 1 * freeze_row_multiplier; const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || (memcmp(table->VisibleMaskByIndex, table->EnabledMaskByIndex, ImBitArrayGetStorageSizeInBytes(table->ColumnsCount)) != 0)) ? +1 : 0; const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total); table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN; table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN); int draw_channel_current = 2; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; if (column->IsVisibleX && column->IsVisibleY) { column->DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current); column->DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0)); if (!(table->Flags & ImGuiTableFlags_NoClip)) draw_channel_current++; } else { column->DrawChannelFrozen = column->DrawChannelUnfrozen = table->DummyDrawChannel; } column->DrawChannelCurrent = column->DrawChannelFrozen; } // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default. // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect. // (This technically isn't part of setting up draw channels, but is reasonably related to be done here) table->BgClipRect = table->InnerClipRect; table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect; table->Bg2ClipRectForDrawCmd = table->HostClipRect; IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); } // This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable(). // For simplicity we call it TableMergeDrawChannels() but in fact it only reorder channels + overwrite ClipRect, // actual merging is done by table->DrawSplitter.Merge() which is called right after TableMergeDrawChannels(). // // Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve // this we merge their clip rect and make them contiguous in the channel list, so they can be merged // by the call to DrawSplitter.Merge() following to the call to this function. // We reorder draw commands by arranging them into a maximum of 4 distinct groups: // // 1 group: 2 groups: 2 groups: 4 groups: // [ 0. ] no freeze [ 0. ] row freeze [ 01 ] col freeze [ 01 ] row+col freeze // [ .. ] or no scroll [ 2. ] and v-scroll [ .. ] and h-scroll [ 23 ] and v+h-scroll // // Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled). // When the contents of a column didn't stray off its limit, we move its channels into the corresponding group // based on its position (within frozen rows/columns groups or not). // At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect. // This function assume that each column are pointing to a distinct draw channel, // otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask. // // Column channels will not be merged into one of the 1-4 groups in the following cases: // - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value). // Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds // matches, by e.g. calling SetCursorScreenPos(). // - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here.. // we could do better but it's going to be rare and probably not worth the hassle. // Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd. // // This function is particularly tricky to understand.. take a breath. void ImGui::TableMergeDrawChannels(ImGuiTable* table) { ImGuiContext& g = *GImGui; ImDrawListSplitter* splitter = table->DrawSplitter; const bool has_freeze_v = (table->FreezeRowsCount > 0); const bool has_freeze_h = (table->FreezeColumnsCount > 0); IM_ASSERT(splitter->_Current == 0); // Track which groups we are going to attempt to merge, and which channels goes into each group. struct MergeGroup { ImRect ClipRect; int ChannelsCount = 0; ImBitArrayPtr ChannelsMask = NULL; }; int merge_group_mask = 0x00; MergeGroup merge_groups[4]; // Use a reusable temp buffer for the merge masks as they are dynamically sized. const int max_draw_channels = (4 + table->ColumnsCount * 2); const int size_for_masks_bitarrays_one = (int)ImBitArrayGetStorageSizeInBytes(max_draw_channels); g.TempBuffer.reserve(size_for_masks_bitarrays_one * 5); memset(g.TempBuffer.Data, 0, size_for_masks_bitarrays_one * 5); for (int n = 0; n < IM_COUNTOF(merge_groups); n++) merge_groups[n].ChannelsMask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * n)); ImBitArrayPtr remaining_mask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * 4)); // 1. Scan channels and take note of those which can be merged for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n)) continue; ImGuiTableColumn* column = &table->Columns[column_n]; const int merge_group_sub_count = has_freeze_v ? 2 : 1; for (int merge_group_sub_n = 0; merge_group_sub_n < merge_group_sub_count; merge_group_sub_n++) { const int channel_no = (merge_group_sub_n == 0) ? column->DrawChannelFrozen : column->DrawChannelUnfrozen; // Don't attempt to merge if there are multiple draw calls within the column ImDrawChannel* src_channel = &splitter->_Channels[channel_no]; if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd() src_channel->_CmdBuffer.pop_back(); if (src_channel->_CmdBuffer.Size != 1) continue; // Find out the width of this merge group and check if it will fit in our column // (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it) if (!(column->Flags & ImGuiTableColumnFlags_NoClip)) { float content_max_x; if (!has_freeze_v) content_max_x = ImMax(column->ContentMaxXUnfrozen, column->ContentMaxXHeadersUsed); // No row freeze else if (merge_group_sub_n == 0) content_max_x = ImMax(column->ContentMaxXFrozen, column->ContentMaxXHeadersUsed); // Row freeze: use width before freeze else content_max_x = column->ContentMaxXUnfrozen; // Row freeze: use width after freeze if (content_max_x > column->ClipRect.Max.x) continue; } const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2); IM_ASSERT(channel_no < max_draw_channels); MergeGroup* merge_group = &merge_groups[merge_group_n]; if (merge_group->ChannelsCount == 0) merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); ImBitArraySetBit(merge_group->ChannelsMask, channel_no); merge_group->ChannelsCount++; merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect); merge_group_mask |= (1 << merge_group_n); } // Invalidate current draw channel // (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data) column->DrawChannelCurrent = (ImGuiTableDrawChannelIdx)-1; } // [DEBUG] Display merge groups #if 0 if (g.IO.KeyShift) for (int merge_group_n = 0; merge_group_n < IM_COUNTOF(merge_groups); merge_group_n++) { MergeGroup* merge_group = &merge_groups[merge_group_n]; if (merge_group->ChannelsCount == 0) continue; char buf[32]; ImFormatString(buf, 32, "MG%d:%d", merge_group_n, merge_group->ChannelsCount); ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4); ImVec2 text_size = CalcTextSize(buf, NULL); GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255)); GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL); GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255)); } #endif // 2. Rewrite channel list in our preferred order if (merge_group_mask != 0) { // We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels(). const int LEADING_DRAW_CHANNELS = 2; g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; ImBitArraySetBitRange(remaining_mask, LEADING_DRAW_CHANNELS, splitter->_Count); ImBitArrayClearBit(remaining_mask, table->Bg2DrawChannelUnfrozen); IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS); //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect; ImRect host_rect = table->HostClipRect; for (int merge_group_n = 0; merge_group_n < IM_COUNTOF(merge_groups); merge_group_n++) { if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount) { MergeGroup* merge_group = &merge_groups[merge_group_n]; ImRect merge_clip_rect = merge_group->ClipRect; // Extend outer-most clip limits to match those of host, so draw calls can be merged even if // outer-most columns have some outer padding offsetting them from their parent ClipRect. // The principal cases this is dealing with are: // - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge // - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge // FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit // within host clip rect, solely because of the half-padding difference between window->WorkRect and window->InnerClipRect. if ((merge_group_n & 1) == 0 || !has_freeze_h) merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x); if ((merge_group_n & 2) == 0 || !has_freeze_v) merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y); if ((merge_group_n & 1) != 0) merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x); if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0) merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y); //GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); // [DEBUG] //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200)); //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200)); remaining_count -= merge_group->ChannelsCount; for (int n = 0; n < (size_for_masks_bitarrays_one >> 2); n++) remaining_mask[n] &= ~merge_group->ChannelsMask[n]; for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++) { // Copy + overwrite new clip rect if (!IM_BITARRAY_TESTBIT(merge_group->ChannelsMask, n)) continue; IM_BITARRAY_CLEARBIT(merge_group->ChannelsMask, n); merge_channels_count--; ImDrawChannel* channel = &splitter->_Channels[n]; IM_ASSERT(channel->_CmdBuffer.Size == 1 && merge_clip_rect.Contains(ImRect(channel->_CmdBuffer[0].ClipRect))); channel->_CmdBuffer[0].ClipRect = merge_clip_rect.ToVec4(); memcpy(dst_tmp++, channel, sizeof(ImDrawChannel)); } } // Make sure Bg2DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0/Bg1 and Bg2 frozen are fixed to 0 and 1) if (merge_group_n == 1 && has_freeze_v) memcpy(dst_tmp++, &splitter->_Channels[table->Bg2DrawChannelUnfrozen], sizeof(ImDrawChannel)); } // Append unmergeable channels that we didn't reorder at the end of the list for (int n = 0; n < splitter->_Count && remaining_count != 0; n++) { if (!IM_BITARRAY_TESTBIT(remaining_mask, n)) continue; ImDrawChannel* channel = &splitter->_Channels[n]; memcpy(dst_tmp++, channel, sizeof(ImDrawChannel)); remaining_count--; } IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size); memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel)); } } static ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n) { const bool is_hovered = (table->HoveredColumnBorder == column_n); const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); if (is_resized || is_hovered) return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered); if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize))) return table->BorderColorStrong; return table->BorderColorLight; } // FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow) void ImGui::TableDrawBorders(ImGuiTable* table) { ImGuiWindow* inner_window = table->InnerWindow; if (!table->OuterWindow->ClipRect.Overlaps(table->OuterRect)) return; ImDrawList* inner_drawlist = inner_window->DrawList; table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); // Draw inner border and resizing feedback ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); const float border_size = TABLE_BORDER_SIZE; const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f); const float draw_y2_body = table->InnerRect.Max.y; const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1; if (table->Flags & ImGuiTableFlags_BordersInnerV) { for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) continue; const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; const bool is_hovered = (table->HoveredColumnBorder == column_n); const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); if (column->MaxX > table->InnerClipRect.Max.x && !is_resized) continue; // Decide whether right-most column is visible if (column->NextEnabledColumn == -1 && !is_resizable) if ((table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame || (table->Flags & ImGuiTableFlags_NoHostExtendX)) continue; if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size.. continue; // Draw in outer window so right-most column won't be clipped float draw_y2 = draw_y2_head; if (is_frozen_separator) draw_y2 = draw_y2_body; else if ((table->Flags & ImGuiTableFlags_NoBordersInBodyUntilResize) != 0 && (is_hovered || is_resized)) draw_y2 = draw_y2_body; else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0) draw_y2 = draw_y2_body; if (draw_y2 > draw_y1) inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size); } } // Draw outer border // FIXME: could use AddRect or explicit VLine/HLine helper? if (table->Flags & ImGuiTableFlags_BordersOuter) { // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part // of it in inner window, and the part that's over scrollbars in the outer window..) // Either solution currently won't allow us to use a larger border size: the border would clipped. const ImRect outer_border = table->OuterRect; const ImU32 outer_col = table->BorderColorStrong; if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) { inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterV) { inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size); inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterH) { inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size); inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size); } } if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) { // Draw bottom-most row border between it is above outer border. const float border_y = table->RowPosY2; if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y) inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size); } inner_drawlist->PopClipRect(); } //------------------------------------------------------------------------- // [SECTION] Tables: Sorting //------------------------------------------------------------------------- // - TableGetSortSpecs() // - TableFixColumnSortDirection() [Internal] // - TableGetColumnNextSortDirection() [Internal] // - TableSetColumnSortDirection() [Internal] // - TableSortSpecsSanitize() [Internal] // - TableSortSpecsBuild() [Internal] //------------------------------------------------------------------------- // Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set) // When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have // changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, // else you may wastefully sort your data every frame! // Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()! ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable)) return NULL; // Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths. if (!table->IsLayoutLocked) TableUpdateLayout(table); TableSortSpecsBuild(table); return &table->SortSpecs; } static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n) { IM_ASSERT(n < column->SortDirectionsAvailCount); return (ImGuiSortDirection)((column->SortDirectionsAvailList >> (n << 1)) & 0x03); } // Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending) void ImGui::TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column) { if (column->SortOrder == -1 || (column->SortDirectionsAvailMask & (1 << column->SortDirection)) != 0) return; column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0); table->IsSortSpecsDirty = true; } // Calculate next sort direction that would be set after clicking the column // - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click. // - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op. IM_STATIC_ASSERT(ImGuiSortDirection_None == 0 && ImGuiSortDirection_Ascending == 1 && ImGuiSortDirection_Descending == 2); ImGuiSortDirection ImGui::TableGetColumnNextSortDirection(ImGuiTableColumn* column) { IM_ASSERT(column->SortDirectionsAvailCount > 0); if (column->SortOrder == -1) return TableGetColumnAvailSortDirection(column, 0); for (int n = 0; n < 3; n++) if (column->SortDirection == TableGetColumnAvailSortDirection(column, n)) return TableGetColumnAvailSortDirection(column, (n + 1) % column->SortDirectionsAvailCount); IM_ASSERT(0); return ImGuiSortDirection_None; } // Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert // the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code. void ImGui::TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (!(table->Flags & ImGuiTableFlags_SortMulti)) append_to_sort_specs = false; if (!(table->Flags & ImGuiTableFlags_SortTristate)) IM_ASSERT(sort_direction != ImGuiSortDirection_None); ImGuiTableColumnIdx sort_order_max = 0; if (append_to_sort_specs) for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) sort_order_max = ImMax(sort_order_max, table->Columns[other_column_n].SortOrder); ImGuiTableColumn* column = &table->Columns[column_n]; column->SortDirection = (ImU8)sort_direction; if (column->SortDirection == ImGuiSortDirection_None) column->SortOrder = -1; else if (column->SortOrder == -1 || !append_to_sort_specs) column->SortOrder = append_to_sort_specs ? sort_order_max + 1 : 0; for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) { ImGuiTableColumn* other_column = &table->Columns[other_column_n]; if (other_column != column && !append_to_sort_specs) other_column->SortOrder = -1; TableFixColumnSortDirection(table, other_column); } table->IsSettingsDirty = true; table->IsSortSpecsDirty = true; } void ImGui::TableSortSpecsSanitize(ImGuiTable* table) { IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable); // Clear SortOrder from hidden column and verify that there's no gap or duplicate. int sort_order_count = 0; ImU64 sort_order_mask = 0x00; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; if (column->SortOrder != -1 && !column->IsEnabled) column->SortOrder = -1; if (column->SortOrder == -1) continue; sort_order_count++; sort_order_mask |= ((ImU64)1 << column->SortOrder); IM_ASSERT(sort_order_count < (int)sizeof(sort_order_mask) * 8); } const bool need_fix_linearize = ((ImU64)1 << sort_order_count) != (sort_order_mask + 1); const bool need_fix_single_sort_order = (sort_order_count > 1) && !(table->Flags & ImGuiTableFlags_SortMulti); if (need_fix_linearize || need_fix_single_sort_order) { ImU64 fixed_mask = 0x00; for (int sort_n = 0; sort_n < sort_order_count; sort_n++) { // Fix: Rewrite sort order fields if needed so they have no gap or duplicate. // (e.g. SortOrder 0 disappeared, SortOrder 1..2 exists --> rewrite then as SortOrder 0..1) int column_with_smallest_sort_order = -1; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) if ((fixed_mask & ((ImU64)1 << (ImU64)column_n)) == 0 && table->Columns[column_n].SortOrder != -1) if (column_with_smallest_sort_order == -1 || table->Columns[column_n].SortOrder < table->Columns[column_with_smallest_sort_order].SortOrder) column_with_smallest_sort_order = column_n; IM_ASSERT(column_with_smallest_sort_order != -1); fixed_mask |= ((ImU64)1 << column_with_smallest_sort_order); table->Columns[column_with_smallest_sort_order].SortOrder = (ImGuiTableColumnIdx)sort_n; // Fix: Make sure only one column has a SortOrder if ImGuiTableFlags_MultiSortable is not set. if (need_fix_single_sort_order) { sort_order_count = 1; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) if (column_n != column_with_smallest_sort_order) table->Columns[column_n].SortOrder = -1; break; } } } // Fallback default sort order (if no column with the ImGuiTableColumnFlags_DefaultSort flag) if (sort_order_count == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; if (column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_NoSort)) { sort_order_count = 1; column->SortOrder = 0; column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0); break; } } table->SortSpecsCount = (ImGuiTableColumnIdx)sort_order_count; } void ImGui::TableSortSpecsBuild(ImGuiTable* table) { bool dirty = table->IsSortSpecsDirty; if (dirty) { TableSortSpecsSanitize(table); table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); table->SortSpecs.SpecsDirty = true; // Mark as dirty for user table->IsSortSpecsDirty = false; // Mark as not dirty for us } // Write output // May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable() ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; if (dirty && sort_specs != NULL) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; if (column->SortOrder == -1) continue; IM_ASSERT(column->SortOrder < table->SortSpecsCount); ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; sort_spec->ColumnUserID = column->UserID; sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection; } table->SortSpecs.Specs = sort_specs; table->SortSpecs.SpecsCount = table->SortSpecsCount; } //------------------------------------------------------------------------- // [SECTION] Tables: Headers //------------------------------------------------------------------------- // - TableGetHeaderRowHeight() [Internal] // - TableGetHeaderAngledMaxLabelWidth() [Internal] // - TableHeadersRow() // - TableHeader() // - TableAngledHeadersRow() // - TableAngledHeadersRowEx() [Internal] //------------------------------------------------------------------------- float ImGui::TableGetHeaderRowHeight() { // Caring for a minor edge case: // Calculate row height, for the unlikely case that some labels may be taller than others. // If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height. // In your custom header row you may omit this all together and just call TableNextRow() without a height... ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; float row_height = g.FontSize; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) if ((table->Columns[column_n].Flags & ImGuiTableColumnFlags_NoHeaderLabel) == 0) row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(table, column_n)).y); return row_height + g.Style.CellPadding.y * 2.0f; } float ImGui::TableGetHeaderAngledMaxLabelWidth() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; float width = 0.0f; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader) width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x); return width + g.Style.CellPadding.y * 2.0f; // Swap padding } // [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). // The intent is that advanced users willing to create customized headers would not need to use this helper // and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets. // See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. // This code is intentionally written to not make much use of internal functions, to give you better direction // if you need to write your own. // FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public. void ImGui::TableHeadersRow() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); // Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make // it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this. if (!table->IsLayoutLocked) TableUpdateLayout(table); // Open row const float row_height = TableGetHeaderRowHeight(); TableNextRow(ImGuiTableRowFlags_Headers, row_height); const float row_y1 = GetCursorScreenPos().y; if (table->HostSkipItems) // Merely an optimization, you may skip in your own code. return; const int columns_count = TableGetColumnCount(); for (int column_n = 0; column_n < columns_count; column_n++) { if (!TableSetColumnIndex(column_n)) continue; // Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation) const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); PushID(column_n); TableHeader(name); PopID(); } // Allow opening popup from the right-most section after the last column. ImVec2 mouse_pos = ImGui::GetMousePos(); if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count) if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height) TableOpenContextMenu(columns_count); // Will open a non-column-specific popup. } // Emit a column header (text + optional sort order) // We cpu-clip text here so that all columns headers can be merged into a same draw call. // Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader() void ImGui::TableHeader(const char* label) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return; ImGuiTable* table = g.CurrentTable; IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(table->CurrentColumn != -1); const int column_n = table->CurrentColumn; ImGuiTableColumn* column = &table->Columns[column_n]; // Label if (label == NULL) label = ""; const char* label_end = FindRenderedTextEnd(label); ImVec2 label_size = CalcTextSize(label, label_end, true); ImVec2 label_pos = window->DC.CursorPos; // If we already got a row height, there's use that. // FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect? ImRect cell_r = TableGetCellBgRect(table, column_n); float label_height = ImMax(label_size.y, table->RowMinHeight - table->RowCellPaddingY * 2.0f); // Calculate ideal size for sort order arrow float w_arrow = 0.0f; float w_sort_text = 0.0f; bool sort_arrow = false; char sort_order_suf[4] = ""; const float ARROW_SCALE = 0.65f; if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) { w_arrow = ImTrunc(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x); if (column->SortOrder != -1) sort_arrow = true; if (column->SortOrder > 0) { ImFormatString(sort_order_suf, IM_COUNTOF(sort_order_suf), "%d", column->SortOrder + 1); w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x; } } // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging. float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow; column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, sort_arrow ? cell_r.Max.x : ImMin(max_pos_x, cell_r.Max.x)); column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x); // Keep header highlighted when context menu is open. ImGuiID id = window->GetID(label); ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f)); ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal if (!ItemAdd(bb, id)) return; //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. const bool highlight = (table->HighlightColumnHeader == column_n); bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap); if (held || hovered || highlight) { const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn); } else { // Submit single cell bg color in the case we didn't submit a full header row if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); } RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding); if (held) table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; // Drag and drop to re-order columns. // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone. if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive) { // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x table->ReorderColumn = (ImGuiTableColumnIdx)column_n; table->InstanceInteracted = table->InstanceCurrent; // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder. if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x) if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL) if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder)) if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) table->ReorderColumnDir = -1; if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x) if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL) if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder)) if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) table->ReorderColumnDir = +1; } // Sort order arrow const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x); if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) { if (column->SortOrder != -1) { float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text); float y = label_pos.y; if (column->SortOrder > 0) { PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f)); RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf); PopStyleColor(); x += w_sort_text; } RenderArrow(window->DrawList, ImVec2(x, y), GetColorU32(ImGuiCol_Text), column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE); } // Handle clicking on column header to adjust Sort Order if (pressed && table->ReorderColumn != column_n) { ImGuiSortDirection sort_direction = TableGetColumnNextSortDirection(column); TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift); } } // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will // be merged into a single draw call. //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE); RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size); const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); if (text_clipped && hovered && g.ActiveId == 0) SetItemTooltip("%.*s", (int)(label_end - label), label); // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden if (IsMouseReleased(1) && IsItemHovered()) TableOpenContextMenu(column_n); } // Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets. // FIXME: No hit-testing/button on the angled header. void ImGui::TableAngledHeadersRow() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; ImGuiTableTempData* temp_data = table->TempData; temp_data->AngledHeadersRequests.resize(0); temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount); // Which column needs highlight? const ImGuiID row_id = GetID("##AngledHeaders"); ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); int highlight_column_n = table->HighlightColumnHeader; if (highlight_column_n == -1 && table->HoveredColumnBody != -1) if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive))) highlight_column_n = table->HoveredColumnBody; // Build up request ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg); ImU32 col_text = GetColorU32(ImGuiCol_Text); for (int order_n = 0; order_n < table->ColumnsCount; order_n++) if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) { const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here. continue; ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 }; temp_data->AngledHeadersRequests.push_back(request); } // Render row TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size); } // Important: data must be fed left to right void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; ImGuiWindow* window = g.CurrentWindow; ImDrawList* draw_list = window->DrawList; IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(table->CurrentRow == -1 && "Must be first row"); if (max_label_width == 0.0f) max_label_width = TableGetHeaderAngledMaxLabelWidth(); // Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user. const bool flip_label = (angle < 0.0f); angle -= IM_PI * 0.5f; const float cos_a = ImCos(angle); const float sin_a = ImSin(angle); const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a; const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a; const ImVec2 unit_right = ImVec2(cos_a, sin_a); // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow() // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other. const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f; const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y)); table->AngledHeadersHeight = row_height; table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f; const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right // Declare row, override and draw our own background TableNextRow(ImGuiTableRowFlags_Headers, row_height); TableNextColumn(); const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, table->RowPosY2); table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0); float clip_rect_min_x = table->BgClipRect.Min.x; if (table->FreezeColumnsCount > 0) clip_rect_min_x = ImMax(clip_rect_min_x, table->Columns[table->FreezeColumnsCount - 1].MaxX); TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color. PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns ButtonBehavior(row_r, row_id, NULL, NULL); KeepAliveID(row_id); const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f); const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component const ImVec2 align = g.Style.TableAngledHeadersTextAlign; // Draw background and labels in first pass, then all borders. float max_x = -FLT_MAX; for (int pass = 0; pass < 2; pass++) for (int order_n = 0; order_n < data_count; order_n++) { const ImGuiTableHeaderData* request = &data[order_n]; const int column_n = request->Index; ImGuiTableColumn* column = &table->Columns[column_n]; ImVec2 bg_shape[4]; bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y); bg_shape[1] = ImVec2(column->MinX, row_r.Max.y); bg_shape[2] = bg_shape[1] + header_angled_vector; bg_shape[3] = bg_shape[0] + header_angled_vector; if (pass == 0) { // Draw shape draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0); draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight max_x = ImMax(max_x, bg_shape[3].x); // Draw label // - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset. // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated. const char* label_name = TableGetColumnName(table, column_n); const char* label_name_end = FindRenderedTextEnd(label_name); const float line_off_step_x = (g.FontSize / -sin_a); const int label_lines = ImTextCountLines(label_name, label_name_end); // Left<>Right alignment float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f; float line_off_for_align_x = ImFloor(ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x); line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x; // Register header width column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x); while (label_name < label_name_end) { const char* label_name_eol = strchr(label_name, '\n'); if (label_name_eol == NULL) label_name_eol = label_name_end; // FIXME: Individual line clipping for right-most column is broken for negative angles. ImVec2 label_size = CalcTextSize(label_name, label_name_eol); float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text. float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x); ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height)); int vtx_idx_begin = draw_list->_VtxCurrentIdx; PushStyleColor(ImGuiCol_Text, request->TextColor); RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size); PopStyleColor(); int vtx_idx_end = draw_list->_VtxCurrentIdx; // Up<>Down alignment const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f); const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f); // Rotate and offset label ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y); ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y); line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x; pivot_out += unit_right * padding.y; if (flip_label) pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x)); pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x; ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); } label_name = label_name_eol + 1; } } if (pass == 1) { // Draw border draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n)); } } PopClipRect(); PopClipRect(); table->TempData->AngledHeadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX); } //------------------------------------------------------------------------- // [SECTION] Tables: Context Menu //------------------------------------------------------------------------- // - TableOpenContextMenu() [Internal] // - TableBeginContextMenuPopup() [Internal] // - TableDrawDefaultContextMenu() [Internal] //------------------------------------------------------------------------- // Use -1 to open menu not specific to a given column. void ImGui::TableOpenContextMenu(int column_n) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; if (column_n == -1 && table->CurrentColumn != -1) // When called within a column automatically use this one (for consistency) column_n = table->CurrentColumn; if (column_n == table->ColumnsCount) // To facilitate using with TableGetHoveredColumn() column_n = -1; IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount); if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { table->IsContextPopupOpen = true; table->ContextPopupColumn = (ImGuiTableColumnIdx)column_n; table->InstanceInteracted = table->InstanceCurrent; const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); OpenPopupEx(context_menu_id, ImGuiPopupFlags_None); } } bool ImGui::TableBeginContextMenuPopup(ImGuiTable* table) { if (!table->IsContextPopupOpen || table->InstanceCurrent != table->InstanceInteracted) return false; const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings)) return true; table->IsContextPopupOpen = false; return false; } // Output context menu into current window (generally a popup) // FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data? // Sections to display are pulled from 'flags_for_section_to_display', which is typically == table->Flags. // - ImGuiTableFlags_Resizable -> display Sizing menu items // - ImGuiTableFlags_Reorderable -> display "Reset Order" ////- ImGuiTableFlags_Sortable -> display sorting options (disabled) // - ImGuiTableFlags_Hideable -> display columns visibility menu items // It means if you have a custom context menus you can call this section and omit some sections, and add your own. void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return; bool want_separator = false; const int column_n = (table->ContextPopupColumn >= 0 && table->ContextPopupColumn < table->ColumnsCount) ? table->ContextPopupColumn : -1; ImGuiTableColumn* column = (column_n != -1) ? &table->Columns[column_n] : NULL; // Sizing if (flags_for_section_to_display & ImGuiTableFlags_Resizable) { if (column != NULL) { const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled; if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableSizeOne), NULL, false, can_resize)) // "###SizeOne" TableSetColumnWidthAutoSingle(table, column_n); } const char* size_all_desc; if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame) size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllFit); // "###SizeAll" All fixed else size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllDefault); // "###SizeAll" All stretch or mixed if (MenuItem(size_all_desc, NULL)) TableSetColumnWidthAutoAll(table); want_separator = true; } // Ordering if (flags_for_section_to_display & ImGuiTableFlags_Reorderable) { if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder)) table->IsResetDisplayOrderRequest = true; want_separator = true; } // Reset all (should work but seems unnecessary/noisy to expose?) //if (MenuItem("Reset all")) // table->IsResetAllRequest = true; // Sorting // (modify TableOpenContextMenu() to add _Sortable flag if enabling this) #if 0 if ((flags_for_section_to_display & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0) { if (want_separator) Separator(); want_separator = true; bool append_to_sort_specs = g.IO.KeyShift; if (MenuItem("Sort in Ascending Order", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Ascending, (column->Flags & ImGuiTableColumnFlags_NoSortAscending) == 0)) TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Ascending, append_to_sort_specs); if (MenuItem("Sort in Descending Order", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Descending, (column->Flags & ImGuiTableColumnFlags_NoSortDescending) == 0)) TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Descending, append_to_sort_specs); } #endif // Hiding / Visibility if (flags_for_section_to_display & ImGuiTableFlags_Hideable) { if (want_separator) Separator(); want_separator = true; PushItemFlag(ImGuiItemFlags_AutoClosePopups, false); for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) { ImGuiTableColumn* other_column = &table->Columns[other_column_n]; if (other_column->Flags & ImGuiTableColumnFlags_Disabled) continue; const char* name = TableGetColumnName(table, other_column_n); if (name == NULL || name[0] == 0) name = ""; // Make sure we can't hide the last active column bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true; if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1) menu_item_active = false; if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active)) other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled; } PopItemFlag(); } } //------------------------------------------------------------------------- // [SECTION] Tables: Settings (.ini data) //------------------------------------------------------------------------- // FIXME: The binding/finding/creating flow are too confusing. //------------------------------------------------------------------------- // - TableSettingsInit() [Internal] // - TableSettingsCalcChunkSize() [Internal] // - TableSettingsCreate() [Internal] // - TableSettingsFindByID() [Internal] // - TableGetBoundSettings() [Internal] // - TableResetSettings() // - TableSaveSettings() [Internal] // - TableLoadSettings() [Internal] // - TableSettingsHandler_ClearAll() [Internal] // - TableSettingsHandler_ApplyAll() [Internal] // - TableSettingsHandler_ReadOpen() [Internal] // - TableSettingsHandler_ReadLine() [Internal] // - TableSettingsHandler_WriteAll() [Internal] // - TableSettingsInstallHandler() [Internal] //------------------------------------------------------------------------- // [Init] 1: TableSettingsHandler_ReadXXXX() Load and parse .ini file into TableSettings. // [Main] 2: TableLoadSettings() When table is created, bind Table to TableSettings, serialize TableSettings data into Table. // [Main] 3: TableSaveSettings() When table properties are modified, serialize Table data into bound or new TableSettings, mark .ini as dirty. // [Main] 4: TableSettingsHandler_WriteAll() When .ini file is dirty (which can come from other source), save TableSettings into .ini file. //------------------------------------------------------------------------- // Clear and initialize empty settings instance static void TableSettingsInit(ImGuiTableSettings* settings, ImGuiID id, int columns_count, int columns_count_max) { IM_PLACEMENT_NEW(settings) ImGuiTableSettings(); ImGuiTableColumnSettings* settings_column = settings->GetColumnSettings(); for (int n = 0; n < columns_count_max; n++, settings_column++) IM_PLACEMENT_NEW(settings_column) ImGuiTableColumnSettings(); settings->ID = id; settings->ColumnsCount = (ImGuiTableColumnIdx)columns_count; settings->ColumnsCountMax = (ImGuiTableColumnIdx)columns_count_max; settings->WantApply = true; } static size_t TableSettingsCalcChunkSize(int columns_count) { return sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings); } ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count) { ImGuiContext& g = *GImGui; ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count)); TableSettingsInit(settings, id, columns_count, columns_count); return settings; } // Find existing settings ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id) { // FIXME-OPT: Might want to store a lookup map for this? ImGuiContext& g = *GImGui; for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) if (settings->ID == id) return settings; return NULL; } // Get settings for a given table, NULL if none ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table) { if (table->SettingsOffset != -1) { ImGuiContext& g = *GImGui; ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset); IM_ASSERT(settings->ID == table->ID); if (settings->ColumnsCountMax >= table->ColumnsCount) return settings; // OK settings->ID = 0; // Invalidate storage, we won't fit because of a count change } return NULL; } // Restore initial state of table (with or without saved settings) void ImGui::TableResetSettings(ImGuiTable* table) { table->IsInitializing = table->IsSettingsDirty = true; table->IsResetAllRequest = false; table->IsSettingsRequestLoad = false; // Don't reload from ini table->SettingsLoadedFlags = ImGuiTableFlags_None; // Mark as nothing loaded so our initialized data becomes authoritative } void ImGui::TableSaveSettings(ImGuiTable* table) { table->IsSettingsDirty = false; if (table->Flags & ImGuiTableFlags_NoSavedSettings) return; // Bind or create settings data ImGuiContext& g = *GImGui; ImGuiTableSettings* settings = TableGetBoundSettings(table); if (settings == NULL) { settings = TableSettingsCreate(table->ID, table->ColumnsCount); table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings); } settings->ColumnsCount = (ImGuiTableColumnIdx)table->ColumnsCount; // Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings IM_ASSERT(settings->ID == table->ID); IM_ASSERT(settings->ColumnsCount == table->ColumnsCount && settings->ColumnsCountMax >= settings->ColumnsCount); ImGuiTableColumn* column = table->Columns.Data; ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); bool save_ref_scale = false; settings->SaveFlags = ImGuiTableFlags_None; for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++) { const float width_or_weight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->StretchWeight : column->WidthRequest; column_settings->WidthOrWeight = width_or_weight; column_settings->Index = (ImGuiTableColumnIdx)n; column_settings->DisplayOrder = column->DisplayOrder; column_settings->SortOrder = column->SortOrder; column_settings->SortDirection = column->SortDirection; column_settings->IsEnabled = column->IsUserEnabled; column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0; if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0) save_ref_scale = true; // We skip saving some data in the .ini file when they are unnecessary to restore our state. // Note that fixed width where initial width was derived from auto-fit will always be saved as InitStretchWeightOrWidth will be 0.0f. // FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet so it's always saved when present. if (width_or_weight != column->InitStretchWeightOrWidth) settings->SaveFlags |= ImGuiTableFlags_Resizable; if (column->DisplayOrder != n) settings->SaveFlags |= ImGuiTableFlags_Reorderable; if (column->SortOrder != -1) settings->SaveFlags |= ImGuiTableFlags_Sortable; if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) settings->SaveFlags |= ImGuiTableFlags_Hideable; } settings->SaveFlags &= table->Flags; settings->RefScale = save_ref_scale ? table->RefScale : 0.0f; MarkIniSettingsDirty(); } void ImGui::TableLoadSettings(ImGuiTable* table) { ImGuiContext& g = *GImGui; table->IsSettingsRequestLoad = false; if (table->Flags & ImGuiTableFlags_NoSavedSettings) return; // Bind settings ImGuiTableSettings* settings; if (table->SettingsOffset == -1) { settings = TableSettingsFindByID(table->ID); if (settings == NULL) return; if (settings->ColumnsCount != table->ColumnsCount) // Allow settings if columns count changed. We could otherwise decide to return... table->IsSettingsDirty = true; table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings); } else { settings = TableGetBoundSettings(table); } table->SettingsLoadedFlags = settings->SaveFlags; table->RefScale = settings->RefScale; // Initialize default columns settings for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; TableInitColumnDefaults(table, column, ~0); column->AutoFitQueue = 0x00; } // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++) { int column_n = column_settings->Index; if (column_n < 0 || column_n >= table->ColumnsCount) continue; ImGuiTableColumn* column = &table->Columns[column_n]; if (settings->SaveFlags & ImGuiTableFlags_Resizable) { if (column_settings->IsStretch) column->StretchWeight = column_settings->WidthOrWeight; else column->WidthRequest = column_settings->WidthOrWeight; } if (settings->SaveFlags & ImGuiTableFlags_Reorderable) column->DisplayOrder = column_settings->DisplayOrder; if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1) column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1; column->SortOrder = column_settings->SortOrder; column->SortDirection = column_settings->SortDirection; } // Fix display order and build index if (settings->SaveFlags & ImGuiTableFlags_Reorderable) TableFixDisplayOrder(table); for (int column_n = 0; column_n < table->ColumnsCount; column_n++) table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; } struct ImGuiTableFixDisplayOrderColumnData { ImGuiTableColumnIdx Idx; ImGuiTable* Table; // This is unfortunate but we don't have userdata in qsort api. }; // Sort by DisplayOrder and then Index static int IMGUI_CDECL TableFixDisplayOrderComparer(const void* lhs, const void* rhs) { const ImGuiTable* table = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Table; const ImGuiTableColumnIdx lhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Idx; const ImGuiTableColumnIdx rhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)rhs)->Idx; const int order_delta = (table->Columns[lhs_idx].DisplayOrder - table->Columns[rhs_idx].DisplayOrder); return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1; } // Fix invalid display order data: compact values (0,1,3 -> 0,1,2); preserve relative order (0,3,1 -> 0,2,1); deduplicate (0,4,1,1 -> 0,3,1,2) void ImGui::TableFixDisplayOrder(ImGuiTable* table) { ImGuiContext& g = *GImGui; g.TempBuffer.reserve((int)(sizeof(ImGuiTableFixDisplayOrderColumnData) * table->ColumnsCount)); // FIXME: Maybe wrap those two lines as a helper. ImGuiTableFixDisplayOrderColumnData* fdo_columns = (ImGuiTableFixDisplayOrderColumnData*)(void*)g.TempBuffer.Data; for (int n = 0; n < table->ColumnsCount; n++) { fdo_columns[n].Idx = (ImGuiTableColumnIdx)n; fdo_columns[n].Table = table; } ImQsort(fdo_columns, (size_t)table->ColumnsCount, sizeof(ImGuiTableFixDisplayOrderColumnData), TableFixDisplayOrderComparer); for (int n = 0; n < table->ColumnsCount; n++) table->Columns[fdo_columns[n].Idx].DisplayOrder = (ImGuiTableColumnIdx)n; } static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; for (int i = 0; i != g.Tables.GetMapSize(); i++) if (ImGuiTable* table = g.Tables.TryGetMapData(i)) table->SettingsOffset = -1; g.SettingsTables.clear(); } // Apply to existing windows (if any) static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; for (int i = 0; i != g.Tables.GetMapSize(); i++) if (ImGuiTable* table = g.Tables.TryGetMapData(i)) { table->IsSettingsRequestLoad = true; table->SettingsOffset = -1; } } static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { ImGuiID id = 0; int columns_count = 0; if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2) return NULL; if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(id)) { if (settings->ColumnsCountMax >= columns_count) { TableSettingsInit(settings, id, columns_count, settings->ColumnsCountMax); // Recycle return settings; } settings->ID = 0; // Invalidate storage, we won't fit because of a count change } return ImGui::TableSettingsCreate(id, columns_count); } static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) { // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" ImGuiTableSettings* settings = (ImGuiTableSettings*)entry; float f = 0.0f; int column_n = 0, r = 0, n = 0; if (sscanf(line, "RefScale=%f", &f) == 1) { settings->RefScale = f; return; } if (sscanf(line, "Column %d%n", &column_n, &r) == 1) { if (column_n < 0 || column_n >= settings->ColumnsCount) return; line = ImStrSkipBlank(line + r); char c = 0; ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n; column->Index = (ImGuiTableColumnIdx)column_n; if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; } if (sscanf(line, "Width=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsStretch = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; } if (sscanf(line, "Weight=%f%n", &f, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsStretch = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; } if (sscanf(line, "Visible=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->IsEnabled = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; } if (sscanf(line, "Order=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImGuiTableColumnIdx)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; } if (sscanf(line, "Sort=%d%c%n", &n, &c, &r) == 2) { line = ImStrSkipBlank(line + r); column->SortOrder = (ImGuiTableColumnIdx)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; } } } static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { ImGuiContext& g = *ctx; for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) { if (settings->ID == 0) // Skip ditched settings continue; // TableSaveSettings() may clear some of those flags when we establish that the data can be stripped // (e.g. Order was unchanged) const bool save_size = (settings->SaveFlags & ImGuiTableFlags_Resizable) != 0; const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0; const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0; const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0; // We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings". buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount); if (settings->RefScale != 0.0f) buf->appendf("RefScale=%g\n", settings->RefScale); ImGuiTableColumnSettings* column = settings->GetColumnSettings(); for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++) { // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1); if (!save_column) continue; buf->appendf("Column %-2d", column_n); if (column->UserID != 0) { buf->appendf(" UserID=%08X", column->UserID); } if (save_size && column->IsStretch) { buf->appendf(" Weight=%.4f", column->WidthOrWeight); } if (save_size && !column->IsStretch) { buf->appendf(" Width=%d", (int)column->WidthOrWeight); } if (save_visible) { buf->appendf(" Visible=%d", column->IsEnabled); } if (save_order) { buf->appendf(" Order=%d", column->DisplayOrder); } if (save_sort && column->SortOrder != -1) { buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); } buf->append("\n"); } buf->append("\n"); } } void ImGui::TableSettingsAddSettingsHandler() { ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Table"; ini_handler.TypeHash = ImHashStr("Table"); ini_handler.ClearAllFn = TableSettingsHandler_ClearAll; ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen; ini_handler.ReadLineFn = TableSettingsHandler_ReadLine; ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll; ini_handler.WriteAllFn = TableSettingsHandler_WriteAll; AddSettingsHandler(&ini_handler); } //------------------------------------------------------------------------- // [SECTION] Tables: Garbage Collection //------------------------------------------------------------------------- // - TableRemove() [Internal] // - TableGcCompactTransientBuffers() [Internal] // - TableGcCompactSettings() [Internal] //------------------------------------------------------------------------- // Remove Table data (currently only used by TestEngine) void ImGui::TableRemove(ImGuiTable* table) { //IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID); ImGuiContext& g = *GImGui; int table_idx = g.Tables.GetIndex(table); //memset(table->RawData.Data, 0, table->RawData.size_in_bytes()); //memset(table, 0, sizeof(ImGuiTable)); g.Tables.Remove(table->ID, table); g.TablesLastTimeActive[table_idx] = -1.0f; } // Free up/compact internal Table buffers for when it gets unused void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) { //IMGUI_DEBUG_PRINT("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); ImGuiContext& g = *GImGui; IM_ASSERT(table->MemoryCompacted == false); table->SortSpecs.Specs = NULL; table->SortSpecsMulti.clear(); table->IsSortSpecsDirty = true; // FIXME: In theory shouldn't have to leak into user performing a sort on resume. table->ColumnsNames.clear(); table->MemoryCompacted = true; for (int n = 0; n < table->ColumnsCount; n++) table->Columns[n].NameOffset = -1; g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f; } void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) { temp_data->DrawSplitter.ClearFreeMemory(); temp_data->LastTimeActive = -1.0f; } // Compact and remove unused settings data (currently only used by TestEngine) void ImGui::TableGcCompactSettings() { ImGuiContext& g = *GImGui; int required_memory = 0; for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) if (settings->ID != 0) required_memory += (int)TableSettingsCalcChunkSize(settings->ColumnsCount); if (required_memory == g.SettingsTables.Buf.Size) return; ImChunkStream new_chunk_stream; new_chunk_stream.Buf.reserve(required_memory); for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) if (settings->ID != 0) memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount)); g.SettingsTables.swap(new_chunk_stream); } //------------------------------------------------------------------------- // [SECTION] Tables: Debugging //------------------------------------------------------------------------- // - DebugNodeTable() [Internal] //------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_DEBUG_TOOLS static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy) { sizing_policy &= ImGuiTableFlags_SizingMask_; if (sizing_policy == ImGuiTableFlags_SizingFixedFit) { return "FixedFit"; } if (sizing_policy == ImGuiTableFlags_SizingFixedSame) { return "FixedSame"; } if (sizing_policy == ImGuiTableFlags_SizingStretchProp) { return "StretchProp"; } if (sizing_policy == ImGuiTableFlags_SizingStretchSame) { return "StretchSame"; } return "N/A"; } void ImGui::DebugNodeTable(ImGuiTable* table) { ImGuiContext& g = *GImGui; const bool is_active = (table->LastFrameActive >= g.FrameCount - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*"); if (!is_active) { PopStyleColor(); } if (IsItemHovered()) GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255)); if (IsItemVisible() && table->HoveredColumnBody != -1) GetForegroundDrawList(table->OuterWindow)->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255)); if (!open) return; if (table->InstanceCurrent > 0) Text("** %d instances of same table! Some data below will refer to last instance.", table->InstanceCurrent + 1); if (g.IO.ConfigDebugIsDebuggerPresent) { if (DebugBreakButton("**DebugBreak**", "in BeginTable()")) g.DebugBreakInTable = table->ID; SameLine(); } bool clear_settings = SmallButton("Clear settings"); BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags)); BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : ""); BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX); BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder); BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn); for (int n = 0; n < table->InstanceCurrent + 1; n++) { ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, n); BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, table_instance->HoveredRowLast, table_instance->LastOuterHeight); } //BulletText("BgDrawChannels: %d/%d", 0, table->BgDrawChannelUnfrozen); float sum_weights = 0.0f; for (int n = 0; n < table->ColumnsCount; n++) if (table->Columns[n].Flags & ImGuiTableColumnFlags_WidthStretch) sum_weights += table->Columns[n].StretchWeight; for (int n = 0; n < table->ColumnsCount; n++) { ImGuiTableColumn* column = &table->Columns[n]; const char* name = TableGetColumnName(table, n); char buf[512]; ImFormatString(buf, IM_COUNTOF(buf), "Column %d order %d '%s': offset %+.2f to %+.2f%s\n" "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n" "MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n" "Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..", n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, (n < table->FreezeColumnsRequest) ? " (Frozen)" : "", column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen, column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->StretchWeight > 0.0f ? (column->StretchWeight / sum_weights) * 100.0f : 0.0f, column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x, column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX, column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserID, column->Flags, (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? "WidthStretch " : "", (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? "WidthFixed " : "", (column->Flags & ImGuiTableColumnFlags_NoResize) ? "NoResize " : ""); Bullet(); Selectable(buf); if (IsItemHovered()) { ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y); GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255)); } } if (ImGuiTableSettings* settings = TableGetBoundSettings(table)) DebugNodeTableSettings(settings); if (clear_settings) table->IsResetAllRequest = true; TreePop(); } void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings) { if (!TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount)) return; BulletText("SaveFlags: 0x%08X", settings->SaveFlags); BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax); for (int n = 0; n < settings->ColumnsCount; n++) { ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n]; ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None; BulletText("Column %d Order %d SortOrder %d %s Vis %d %s %7.3f UserID 0x%08X", n, column_settings->DisplayOrder, column_settings->SortOrder, (sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---", column_settings->IsEnabled, column_settings->IsStretch ? "Weight" : "Width ", column_settings->WidthOrWeight, column_settings->UserID); } TreePop(); } #else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS void ImGui::DebugNodeTable(ImGuiTable*) {} void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {} #endif //------------------------------------------------------------------------- // [SECTION] Columns, BeginColumns, EndColumns, etc. // (This is a legacy API, prefer using BeginTable/EndTable!) //------------------------------------------------------------------------- // FIXME: sizing is lossy when columns width is very small (default width may turn negative etc.) //------------------------------------------------------------------------- // - SetWindowClipRectBeforeSetChannel() [Internal] // - GetColumnIndex() // - GetColumnsCount() // - GetColumnOffset() // - GetColumnWidth() // - SetColumnOffset() // - SetColumnWidth() // - PushColumnClipRect() [Internal] // - PushColumnsBackground() [Internal] // - PopColumnsBackground() [Internal] // - FindOrCreateColumns() [Internal] // - GetColumnsID() [Internal] // - BeginColumns() // - NextColumn() // - EndColumns() // - Columns() //------------------------------------------------------------------------- // [Internal] Small optimization to avoid calls to PopClipRect/SetCurrentChannel/PushClipRect in sequences, // they would meddle many times with the underlying ImDrawCmd. // Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let // the subsequent single call to SetCurrentChannel() does it things once. void ImGui::SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect) { ImVec4 clip_rect_vec4 = clip_rect.ToVec4(); window->ClipRect = clip_rect; window->DrawList->_CmdHeader.ClipRect = clip_rect_vec4; window->DrawList->_ClipRectStack.Data[window->DrawList->_ClipRectStack.Size - 1] = clip_rect_vec4; } int ImGui::GetColumnIndex() { ImGuiWindow* window = GetCurrentWindowRead(); return window->DC.CurrentColumns ? window->DC.CurrentColumns->Current : 0; } int ImGui::GetColumnsCount() { ImGuiWindow* window = GetCurrentWindowRead(); return window->DC.CurrentColumns ? window->DC.CurrentColumns->Count : 1; } float ImGui::GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm) { return offset_norm * (columns->OffMaxX - columns->OffMinX); } float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset) { return offset / (columns->OffMaxX - columns->OffMinX); } static const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f; static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index) { // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(column_index > 0); // We are not supposed to drag column 0. IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index)); float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x; x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing); if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths)) x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing); return x; } float ImGui::GetColumnOffset(int column_index) { ImGuiWindow* window = GetCurrentWindowRead(); ImGuiOldColumns* columns = window->DC.CurrentColumns; if (columns == NULL) return 0.0f; if (column_index < 0) column_index = columns->Current; IM_ASSERT(column_index < columns->Columns.Size); const float t = columns->Columns[column_index].OffsetNorm; const float x_offset = ImLerp(columns->OffMinX, columns->OffMaxX, t); return x_offset; } static float GetColumnWidthEx(ImGuiOldColumns* columns, int column_index, bool before_resize = false) { if (column_index < 0) column_index = columns->Current; float offset_norm; if (before_resize) offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize; else offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm; return ImGui::GetColumnOffsetFromNorm(columns, offset_norm); } float ImGui::GetColumnWidth(int column_index) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiOldColumns* columns = window->DC.CurrentColumns; if (columns == NULL) return GetContentRegionAvail().x; if (column_index < 0) column_index = columns->Current; return GetColumnOffsetFromNorm(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm); } void ImGui::SetColumnOffset(int column_index, float offset) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiOldColumns* columns = window->DC.CurrentColumns; IM_ASSERT(columns != NULL); if (column_index < 0) column_index = columns->Current; IM_ASSERT(column_index < columns->Columns.Size); const bool preserve_width = !(columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths) && (column_index < columns->Count - 1); const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f; if (!(columns->Flags & ImGuiOldColumnFlags_NoForceWithinWindow)) offset = ImMin(offset, columns->OffMaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index)); columns->Columns[column_index].OffsetNorm = GetColumnNormFromOffset(columns, offset - columns->OffMinX); if (preserve_width) SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width)); } void ImGui::SetColumnWidth(int column_index, float width) { ImGuiWindow* window = GetCurrentWindowRead(); ImGuiOldColumns* columns = window->DC.CurrentColumns; IM_ASSERT(columns != NULL); if (column_index < 0) column_index = columns->Current; SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width); } void ImGui::PushColumnClipRect(int column_index) { ImGuiWindow* window = GetCurrentWindowRead(); ImGuiOldColumns* columns = window->DC.CurrentColumns; if (column_index < 0) column_index = columns->Current; ImGuiOldColumnData* column = &columns->Columns[column_index]; PushClipRect(column->ClipRect.Min, column->ClipRect.Max, false); } // Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns) void ImGui::PushColumnsBackground() { ImGuiWindow* window = GetCurrentWindowRead(); ImGuiOldColumns* columns = window->DC.CurrentColumns; if (columns->Count == 1) return; // Optimization: avoid SetCurrentChannel() + PushClipRect() columns->HostBackupClipRect = window->ClipRect; SetWindowClipRectBeforeSetChannel(window, columns->HostInitialClipRect); columns->Splitter.SetCurrentChannel(window->DrawList, 0); } void ImGui::PopColumnsBackground() { ImGuiWindow* window = GetCurrentWindowRead(); ImGuiOldColumns* columns = window->DC.CurrentColumns; if (columns->Count == 1) return; // Optimization: avoid PopClipRect() + SetCurrentChannel() SetWindowClipRectBeforeSetChannel(window, columns->HostBackupClipRect); columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1); } ImGuiOldColumns* ImGui::FindOrCreateColumns(ImGuiWindow* window, ImGuiID id) { // We have few columns per window so for now we don't need bother much with turning this into a faster lookup. for (int n = 0; n < window->ColumnsStorage.Size; n++) if (window->ColumnsStorage[n].ID == id) return &window->ColumnsStorage[n]; window->ColumnsStorage.push_back(ImGuiOldColumns()); ImGuiOldColumns* columns = &window->ColumnsStorage.back(); columns->ID = id; return columns; } ImGuiID ImGui::GetColumnsID(const char* str_id, int columns_count) { ImGuiWindow* window = GetCurrentWindow(); // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer. PushID(0x11223347 + (str_id ? 0 : columns_count)); ImGuiID id = window->GetID(str_id ? str_id : "columns"); PopID(); return id; } void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); IM_ASSERT(columns_count >= 1); IM_ASSERT(window->DC.CurrentColumns == NULL); // Nested columns are currently not supported // Acquire storage for the columns set ImGuiID id = GetColumnsID(str_id, columns_count); ImGuiOldColumns* columns = FindOrCreateColumns(window, id); IM_ASSERT(columns->ID == id); columns->Current = 0; columns->Count = columns_count; columns->Flags = flags; window->DC.CurrentColumns = columns; window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); columns->HostCursorPosY = window->DC.CursorPos.y; columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x; columns->HostInitialClipRect = window->ClipRect; columns->HostBackupParentWorkRect = window->ParentWorkRect; window->ParentWorkRect = window->WorkRect; // Set state for first column // We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect const float column_padding = g.Style.ItemSpacing.x; const float half_clip_extend_x = ImTrunc(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize)); const float max_1 = window->WorkRect.Max.x + column_padding - ImMax(column_padding - window->WindowPadding.x, 0.0f); const float max_2 = window->WorkRect.Max.x + half_clip_extend_x; columns->OffMinX = window->DC.Indent.x - column_padding + ImMax(column_padding - window->WindowPadding.x, 0.0f); columns->OffMaxX = ImMax(ImMin(max_1, max_2) - window->Pos.x, columns->OffMinX + 1.0f); columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y; // Clear data if columns count changed if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1) columns->Columns.resize(0); // Initialize default widths columns->IsFirstFrame = (columns->Columns.Size == 0); if (columns->Columns.Size == 0) { columns->Columns.reserve(columns_count + 1); for (int n = 0; n < columns_count + 1; n++) { ImGuiOldColumnData column; column.OffsetNorm = n / (float)columns_count; columns->Columns.push_back(column); } } for (int n = 0; n < columns_count; n++) { // Compute clipping rectangle ImGuiOldColumnData* column = &columns->Columns[n]; float clip_x1 = IM_ROUND(window->Pos.x + GetColumnOffset(n)); float clip_x2 = IM_ROUND(window->Pos.x + GetColumnOffset(n + 1) - 1.0f); column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX); column->ClipRect.ClipWithFull(window->ClipRect); } if (columns->Count > 1) { columns->Splitter.Split(window->DrawList, 1 + columns->Count); columns->Splitter.SetCurrentChannel(window->DrawList, 1); PushColumnClipRect(0); } // We don't generally store Indent.x inside ColumnsOffset because it may be manipulated by the user. float offset_0 = GetColumnOffset(columns->Current); float offset_1 = GetColumnOffset(columns->Current + 1); float width = offset_1 - offset_0; PushItemWidth(width * 0.65f); window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding; window->WorkRect.Max.y = window->ContentRegionRect.Max.y; } void ImGui::NextColumn() { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems || window->DC.CurrentColumns == NULL) return; ImGuiContext& g = *GImGui; ImGuiOldColumns* columns = window->DC.CurrentColumns; if (columns->Count == 1) { window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); IM_ASSERT(columns->Current == 0); return; } // Next column if (++columns->Current == columns->Count) columns->Current = 0; PopItemWidth(); // Optimization: avoid PopClipRect() + SetCurrentChannel() + PushClipRect() // (which would needlessly attempt to update commands in the wrong channel, then pop or overwrite them), ImGuiOldColumnData* column = &columns->Columns[columns->Current]; SetWindowClipRectBeforeSetChannel(window, column->ClipRect); columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1); const float column_padding = g.Style.ItemSpacing.x; columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); if (columns->Current > 0) { // Columns 1+ ignore IndentX (by canceling it out) // FIXME-COLUMNS: Unnecessary, could be locked? window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + column_padding; } else { // New row/line: column 0 honor IndentX. window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); window->DC.IsSameLine = false; columns->LineMinY = columns->LineMaxY; } window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); window->DC.CursorPos.y = columns->LineMinY; window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); window->DC.CurrLineTextBaseOffset = 0.0f; // FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup. float offset_0 = GetColumnOffset(columns->Current); float offset_1 = GetColumnOffset(columns->Current + 1); float width = offset_1 - offset_0; PushItemWidth(width * 0.65f); window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding; } void ImGui::EndColumns() { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); ImGuiOldColumns* columns = window->DC.CurrentColumns; IM_ASSERT(columns != NULL); PopItemWidth(); if (columns->Count > 1) { PopClipRect(); columns->Splitter.Merge(window->DrawList); } const ImGuiOldColumnFlags flags = columns->Flags; columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); window->DC.CursorPos.y = columns->LineMaxY; if (!(flags & ImGuiOldColumnFlags_GrowParentContentsSize)) window->DC.CursorMaxPos.x = columns->HostCursorMaxPosX; // Restore cursor max pos, as columns don't grow parent // Draw columns borders and handle resize // The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy bool is_being_resized = false; if (!(flags & ImGuiOldColumnFlags_NoBorder) && !window->SkipItems) { // We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers. const float y1 = ImMax(columns->HostCursorPosY, window->ClipRect.Min.y); const float y2 = ImMin(window->DC.CursorPos.y, window->ClipRect.Max.y); int dragging_column = -1; for (int n = 1; n < columns->Count; n++) { ImGuiOldColumnData* column = &columns->Columns[n]; float x = window->Pos.x + GetColumnOffset(n); const ImGuiID column_id = columns->ID + ImGuiID(n); const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale); const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav)) continue; bool hovered = false, held = false; if (!(flags & ImGuiOldColumnFlags_NoResize)) { ButtonBehavior(column_hit_rect, column_id, &hovered, &held); if (hovered || held) SetMouseCursor(ImGuiMouseCursor_ResizeEW); if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize)) dragging_column = n; } // Draw column const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); const float xi = IM_TRUNC(x); window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col); } // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame. if (dragging_column != -1) { if (!columns->IsBeingResized) for (int n = 0; n < columns->Count + 1; n++) columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm; columns->IsBeingResized = is_being_resized = true; float x = GetDraggedColumnOffset(columns, dragging_column); SetColumnOffset(dragging_column, x); } } columns->IsBeingResized = is_being_resized; window->WorkRect = window->ParentWorkRect; window->ParentWorkRect = columns->HostBackupParentWorkRect; window->DC.CurrentColumns = NULL; window->DC.ColumnsOffset.x = 0.0f; window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); NavUpdateCurrentWindowIsScrollPushableX(); } void ImGui::Columns(int columns_count, const char* id, bool borders) { ImGuiWindow* window = GetCurrentWindow(); IM_ASSERT(columns_count >= 1); ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder); //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior ImGuiOldColumns* columns = window->DC.CurrentColumns; if (columns != NULL && columns->Count == columns_count && columns->Flags == flags) return; if (columns != NULL) EndColumns(); if (columns_count != 1) BeginColumns(id, columns_count, flags); } //------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui/source/imgui_widgets.cpp ================================================ // dear imgui, v1.92.7 WIP // (widgets code) /* Index of this file: // [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: Typing-Select support // [SECTION] Widgets: Box-Select support // [SECTION] Widgets: Multi-Select support // [SECTION] Widgets: Multi-Select helpers // [SECTION] Widgets: ListBox // [SECTION] Widgets: PlotLines, PlotHistogram // [SECTION] Widgets: Value helpers // [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. // [SECTION] Widgets: BeginTabBar, EndTabBar, etc. // [SECTION] Widgets: BeginTabItem, EndTabItem, etc. // [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc. */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_internal.h" // System includes #include // intptr_t //------------------------------------------------------------------------- // Warnings //------------------------------------------------------------------------- // 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 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #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" // warning: format specifies type 'int' but the argument has type 'unsigned int' #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 #pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used. #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 #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. #pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type #pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*' #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers #pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif //------------------------------------------------------------------------- // Data //------------------------------------------------------------------------- // Widgets static const float DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f; // Time for drag-hold to activate items accepting the ImGuiButtonFlags_PressedOnDragDropHold button behavior. static const float DRAG_MOUSE_THRESHOLD_FACTOR = 0.50f; // Multiplier for the default value of io.MouseDragThreshold to make DragFloat/DragInt react faster to mouse drags. // Those MIN/MAX values are not define because we need to point to them 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); #else static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; static const ImS64 IM_S64_MAX = 9223372036854775807LL; #endif static const ImU64 IM_U64_MIN = 0; #ifdef ULLONG_MAX static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); #else static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); #endif //------------------------------------------------------------------------- // [SECTION] Forward Declarations //------------------------------------------------------------------------- // For InputTextEx() static bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* state, unsigned int* p_char, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false); static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0); //------------------------------------------------------------------------- // [SECTION] Widgets: Text, etc. //------------------------------------------------------------------------- // - TextEx() [Internal] // - TextUnformatted() // - Text() // - TextV() // - TextColored() // - TextColoredV() // - TextDisabled() // - TextDisabledV() // - TextWrapped() // - TextWrappedV() // - LabelText() // - LabelTextV() // - BulletText() // - BulletTextV() //------------------------------------------------------------------------- void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; // Accept null ranges if (text == text_end) text = text_end = ""; // Calculate length const char* text_begin = text; if (text_end == NULL) text_end = text + ImStrlen(text); // FIXME-OPT const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); const float wrap_pos_x = window->DC.TextWrapPos; const bool wrap_enabled = (wrap_pos_x >= 0.0f); if (text_end - text <= 2000 || wrap_enabled) { // Common case 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); ImRect bb(text_pos, text_pos + text_size); ItemSize(text_size, 0.0f); if (!ItemAdd(bb, 0)) return; // Render (we don't hide text after ## in this end-user function) RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); } else { // 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. // - 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(); ImVec2 text_size(0, 0); // Lines to skip (can't skip when logging text) ImVec2 pos = text_pos; if (!g.LogEnabled) { int lines_skippable = (int)((window->ClipRect.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*)ImMemchr(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)) break; const char* line_end = (const char*)ImMemchr(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; } // Count remaining lines int lines_skipped = 0; while (line < text_end) { const char* line_end = (const char*)ImMemchr(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(text_size, 0.0f); ItemAdd(bb, 0); } } 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; va_start(args, fmt); TextV(fmt, args); va_end(args); } void ImGui::TextV(const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; const char* text, *text_end; ImFormatStringToTempBufferV(&text, &text_end, fmt, args); TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); } void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) { va_list args; va_start(args, fmt); TextColoredV(col, fmt, args); va_end(args); } void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) { PushStyleColor(ImGuiCol_Text, col); TextV(fmt, args); PopStyleColor(); } void ImGui::TextDisabled(const char* fmt, ...) { va_list args; va_start(args, fmt); TextDisabledV(fmt, args); va_end(args); } void ImGui::TextDisabledV(const char* fmt, va_list args) { ImGuiContext& g = *GImGui; PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); TextV(fmt, args); PopStyleColor(); } void ImGui::TextWrapped(const char* fmt, ...) { va_list args; va_start(args, fmt); TextWrappedV(fmt, args); va_end(args); } void ImGui::TextWrappedV(const char* fmt, va_list args) { ImGuiContext& g = *GImGui; const bool need_backup = (g.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_backup) PopTextWrapPos(); } void ImGui::TextAligned(float align_x, float size_x, const char* fmt, ...) { va_list args; va_start(args, fmt); TextAlignedV(align_x, size_x, fmt, args); va_end(args); } // align_x: 0.0f = left, 0.5f = center, 1.0f = right. // size_x : 0.0f = shortcut for GetContentRegionAvail().x // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024) void ImGui::TextAlignedV(float align_x, float size_x, const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; const char* text, *text_end; ImFormatStringToTempBufferV(&text, &text_end, fmt, args); const ImVec2 text_size = CalcTextSize(text, text_end); size_x = CalcItemSize(ImVec2(size_x, 0.0f), 0.0f, text_size.y).x; ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); ImVec2 pos_max(pos.x + size_x, window->ClipRect.Max.y); ImVec2 size(ImMin(size_x, text_size.x), text_size.y); window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, pos.x + text_size.x); window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, pos.x + text_size.x); if (align_x > 0.0f && text_size.x < size_x) pos.x += ImTrunc((size_x - text_size.x) * align_x); RenderTextEllipsis(window->DrawList, pos, pos_max, pos_max.x, text, text_end, &text_size); const ImVec2 backup_max_pos = window->DC.CursorMaxPos; ItemSize(size); ItemAdd(ImRect(pos, pos + size), 0); window->DC.CursorMaxPos.x = backup_max_pos.x; // Cancel out extending content size because right-aligned text would otherwise mess it up. if (size_x < text_size.x && IsItemHovered(ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_ForTooltip)) SetTooltip("%.*s", (int)(text_end - text), text); } void ImGui::LabelText(const char* label, const char* fmt, ...) { va_list args; va_start(args, fmt); LabelTextV(label, fmt, args); va_end(args); } // Add a label+text combo aligned to other label+value widgets void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const float w = CalcItemWidth(); const char* value_text_begin, *value_text_end; ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args); const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false); const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 pos = window->DC.CursorPos; const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2)); const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2)); ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, 0)) return; // Render RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f)); if (label_size.x > 0.0f) RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); } void ImGui::BulletText(const char* fmt, ...) { va_list args; va_start(args, fmt); BulletTextV(fmt, args); va_end(args); } // Text with a little bullet aligned to the typical tree node. void ImGui::BulletTextV(const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const char* text_begin, *text_end; ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args); const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y); // Empty text doesn't add padding ImVec2 pos = window->DC.CursorPos; pos.y += window->DC.CurrLineTextBaseOffset; ItemSize(total_size, 0.0f); const ImRect bb(pos, pos + total_size); if (!ItemAdd(bb, 0)) return; // Render ImU32 text_col = GetColorU32(ImGuiCol_Text); RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col); RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false); } //------------------------------------------------------------------------- // [SECTION] Widgets: Main //------------------------------------------------------------------------- // - ButtonBehavior() [Internal] // - Button() // - SmallButton() // - InvisibleButton() // - ArrowButton() // - CloseButton() [Internal] // - CollapseButton() [Internal] // - GetWindowScrollbarID() [Internal] // - GetWindowScrollbarRect() [Internal] // - Scrollbar() [Internal] // - ScrollbarEx() [Internal] // - Image() // - ImageButton() // - Checkbox() // - CheckboxFlagsT() [Internal] // - CheckboxFlags() // - RadioButton() // - ProgressBar() // - Bullet() // - Hyperlink() //------------------------------------------------------------------------- // The ButtonBehavior() function is key to many interactions and used by many/most widgets. // Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags_), // this code is a little complex. // By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags_PressedOnClickRelease button behavior. // See the series of events below and the corresponding state reported by dear imgui: //------------------------------------------------------------------------------------------------------------------------------------------------ // with PressedOnClickRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() // Frame N+0 (mouse is outside bb) - - - - - - // Frame N+1 (mouse moves inside bb) - true - - - - // Frame N+2 (mouse button is down) - true true true - true // Frame N+3 (mouse button is down) - true true - - - // Frame N+4 (mouse moves outside bb) - - true - - - // Frame N+5 (mouse moves inside bb) - true true - - - // Frame N+6 (mouse button is released) true true - - true - // Frame N+7 (mouse button is released) - true - - - - // Frame N+8 (mouse moves outside bb) - - - - - - //------------------------------------------------------------------------------------------------------------------------------------------------ // with PressedOnClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() // Frame N+2 (mouse button is down) true true true true - true // Frame N+3 (mouse button is down) - true true - - - // Frame N+6 (mouse button is released) - true - - true - // Frame N+7 (mouse button is released) - true - - - - //------------------------------------------------------------------------------------------------------------------------------------------------ // with PressedOnRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() // Frame N+2 (mouse button is down) - true - - - true // Frame N+3 (mouse button is down) - true - - - - // Frame N+6 (mouse button is released) true true - - - - // Frame N+7 (mouse button is released) - true - - - - //------------------------------------------------------------------------------------------------------------------------------------------------ // with PressedOnDoubleClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() // Frame N+0 (mouse button is down) - true - - - true // Frame N+1 (mouse button is down) - true - - - - // Frame N+2 (mouse button is released) - true - - - - // Frame N+3 (mouse button is released) - true - - - - // Frame N+4 (mouse button is down) true true true true - true // Frame N+5 (mouse button is down) - true true - - - // Frame N+6 (mouse button is released) - true - - true - // Frame N+7 (mouse button is released) - true - - - - //------------------------------------------------------------------------------------------------------------------------------------------------ // Note that some combinations are supported, // - PressedOnDragDropHold can generally be associated with any flag. // - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported. //------------------------------------------------------------------------------------------------------------------------------------------------ // The behavior of the return-value changes when ImGuiItemFlags_ButtonRepeat is set: // Repeat+ Repeat+ Repeat+ Repeat+ // PressedOnClickRelease PressedOnClick PressedOnRelease PressedOnDoubleClick //------------------------------------------------------------------------------------------------------------------------------------------------- // Frame N+0 (mouse button is down) - true - true // ... - - - - // Frame N + RepeatDelay true true - true // ... - - - - // Frame N + RepeatDelay + RepeatRate*N true true - true //------------------------------------------------------------------------------------------------------------------------------------------------- // - FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc. // And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);' // For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading. // - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature. // One idiom which was previously valid which will now emit a warning is when using multiple overlaid ButtonBehavior() // with same ID and different MouseButton (see #8030). You can fix it by: // (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags. // or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag() bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); // Default behavior inherited from item flags // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that. ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags); if (flags & ImGuiButtonFlags_AllowOverlap) item_flags |= ImGuiItemFlags_AllowOverlap; if (item_flags & ImGuiItemFlags_NoFocus) flags |= ImGuiButtonFlags_NoFocus | ImGuiButtonFlags_NoNavFocus; // Default only reacts to left mouse button if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) flags |= ImGuiButtonFlags_MouseButtonLeft; // Default behavior requires click + release inside bounding box if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0) flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_; ImGuiWindow* backup_hovered_window = g.HoveredWindow; const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree; if (flatten_hovered_children) g.HoveredWindow = window; #ifdef IMGUI_ENABLE_TEST_ENGINE // Alternate registration spot, for when caller didn't use ItemAdd() if (g.LastItemData.ID != id) IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL); #endif bool pressed = false; bool hovered = ItemHoverable(bb, id, item_flags); // Special mode for Drag and Drop used by openables (tree nodes, tabs etc.) // where holding the button pressed for a long time while drag a payload item triggers the button. if (g.DragDropActive) { if ((flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) { hovered = true; SetHoveredID(id); if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) { pressed = true; g.DragDropHoldJustPressedId = id; FocusWindow(window); } } if (g.DragDropAcceptIdPrev == id && (g.DragDropAcceptFlagsPrev & ImGuiDragDropFlags_AcceptDrawAsHovered)) hovered = true; } if (flatten_hovered_children) g.HoveredWindow = backup_hovered_window; // Mouse handling const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; if (hovered) { IM_ASSERT(id != 0); // Lazily check inside rare path. // Poll mouse buttons // - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId. // - Technically we only need some values in one code path, but since this is gated by hovered test this is fine. int mouse_button_clicked = -1; int mouse_button_released = -1; for (int button = 0; button < 3; button++) if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here. { if (IsMouseClicked(button, ImGuiInputFlags_None, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; } if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; } } // Process initial action const bool mods_ok = !(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt); if (mods_ok) { if (mouse_button_clicked != -1 && g.ActiveId != id) { if (!(flags & ImGuiButtonFlags_NoSetKeyOwner)) SetKeyOwner(MouseButtonToKey(mouse_button_clicked), id); if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere)) { SetActiveID(id, window); g.ActiveIdMouseButton = (ImS8)mouse_button_clicked; if (!(flags & ImGuiButtonFlags_NoNavFocus)) { SetFocusID(id, window); FocusWindow(window); } else if (!(flags & ImGuiButtonFlags_NoFocus)) { FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child } } if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveId) ClearActiveID(); else SetActiveID(id, window); // Hold on ID g.ActiveIdMouseButton = (ImS8)mouse_button_clicked; if (!(flags & ImGuiButtonFlags_NoNavFocus)) { SetFocusID(id, window); FocusWindow(window); } else if (!(flags & ImGuiButtonFlags_NoFocus)) { FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child } } } if (flags & ImGuiButtonFlags_PressedOnRelease) { if (mouse_button_released != -1) { const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior if (!has_repeated_at_least_once) pressed = true; if (!(flags & ImGuiButtonFlags_NoNavFocus)) SetFocusID(id, window); // FIXME: Lack of FocusWindow() call here is inconsistent with other paths. Research why. ClearActiveID(); } } // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat)) if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id)) pressed = true; } if (pressed && g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = false; } // Keyboard/Gamepad navigation handling // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse. if ((item_flags & ImGuiItemFlags_Disabled) == 0) { if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav) if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus)) hovered = true; if (g.NavActivateDownId == id) { bool nav_activated_by_code = (g.NavActivateId == id); bool nav_activated_by_inputs = (g.NavActivatePressedId == id); if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat)) { // Avoid pressing multiple keys from triggering excessive amount of repeat events const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter); const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate); const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration); nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; } if (nav_activated_by_code || nav_activated_by_inputs) { // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. pressed = true; SetActiveID(id, window); g.ActiveIdSource = g.NavInputSource; if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)) SetFocusID(id, window); if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut) g.ActiveIdFromShortcut = true; } } } // Process while held bool held = false; if (g.ActiveId == id) { if (g.ActiveIdSource == ImGuiInputSource_Mouse) { if (g.ActiveIdIsJustActivated) g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; const int mouse_button = g.ActiveIdMouseButton; if (mouse_button == -1) { // Fallback for the rare situation were g.ActiveId was set programmatically or from another widget (e.g. #6304). ClearActiveID(); } else if (IsMouseDown(mouse_button, test_owner_id)) { held = true; } else { bool release_in = hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) != 0; bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0; if ((release_in || release_anywhere) && !g.DragDropActive) { // Report as pressed when releasing the mouse (this is the most common path) bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id); if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned) pressed = true; } ClearActiveID(); } if (!(flags & ImGuiButtonFlags_NoNavFocus) && g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = false; } else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) { // When activated using Nav, we hold on the ActiveID until activation button is released if (g.NavActivateDownId == id) held = true; // hovered == true not true as we are already likely hovered on direct activation. else ClearActiveID(); } if (pressed) g.ActiveIdHasBeenPressedBefore = true; } // Activation highlight (this may be a remote activation) if (g.NavHighlightActivatedId == id && (item_flags & ImGuiItemFlags_Disabled) == 0) hovered = true; if (out_hovered) *out_hovered = hovered; if (out_held) *out_held = held; return pressed; } bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 pos = window->DC.CursorPos; if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; 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(size, style.FramePadding.y); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); if (g.LogEnabled) LogSetNextTextDecoration("[", "]"); RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); // Automatically close popups //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } bool ImGui::Button(const char* label, const ImVec2& size_arg) { return ButtonEx(label, size_arg, ImGuiButtonFlags_None); } // Small buttons fits within text without additional vertical spacing. bool ImGui::SmallButton(const char* label) { ImGuiContext& g = *GImGui; float backup_padding_y = g.Style.FramePadding.y; g.Style.FramePadding.y = 0.0f; bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine); g.Style.FramePadding.y = backup_padding_y; return pressed; } // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; // Ensure zero-size fits to contents ImVec2 size = CalcItemSize(ImVec2(size_arg.x != 0.0f ? size_arg.x : -FLT_MIN, size_arg.y != 0.0f ? size_arg.y : -FLT_MIN), 0.0f, 0.0f); const ImGuiID id = window->GetID(str_id); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); ItemSize(size); if (!ItemAdd(bb, id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); RenderNavCursor(bb, id); IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); return pressed; } bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiID id = window->GetID(str_id); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); const float default_size = GetFrameHeight(); ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); // Render const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); const ImU32 text_col = GetColorU32(ImGuiCol_Text); RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir); IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); return pressed; } bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) { float sz = GetFrameHeight(); return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), ImGuiButtonFlags_None); } // Button to close a window bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825) // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible? const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); ImRect bb_interact = bb; const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea(); if (area_to_visible_ratio < 1.5f) bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f)); // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window. // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer). bool is_clipped = !ItemAdd(bb_interact, id); bool hovered, held; bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held); if (is_clipped) return pressed; // Render ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); if (hovered) window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact); const ImU32 cross_col = GetColorU32(ImGuiCol_Text); const ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f); const float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; const float cross_thickness = 1.0f * (float)(int)g.Style._MainScale; // FIXME-DPI window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, cross_thickness); window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, cross_thickness); return pressed; } // The Collapse button also functions as a Dock Menu button. bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); bool is_clipped = !ItemAdd(bb, id); bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); if (is_clipped) return pressed; // Render //bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed); ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); if (hovered || held) window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact); if (dock_node) RenderArrowDockMenu(window->DrawList, bb.Min, g.FontSize, text_col); else RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold if (IsItemActive() && IsMouseDragging(0)) StartMouseMovingWindowOrNode(window, dock_node, true); // Undock from window/collapse menu button return pressed; } ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis) { return window->GetID(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY"); } // Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set. ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) { ImGuiContext& g = *GImGui; const ImRect outer_rect = window->Rect(); const ImRect inner_rect = window->InnerRect; const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) IM_ASSERT(scrollbar_size >= 0.0f); const float border_size = IM_ROUND(window->WindowBorderSize * 0.5f); const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : 0.0f; if (axis == ImGuiAxis_X) return ImRect(inner_rect.Min.x + border_size, ImMax(outer_rect.Min.y + border_size, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); else return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y + border_top, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); } void ImGui::ExtendHitBoxWhenNearViewportEdge(ImGuiWindow* window, ImRect* bb, float threshold, ImGuiAxis axis) { ImRect window_rect = window->RootWindow->Rect(); ImRect viewport_rect = window->Viewport->GetMainRect(); if (window_rect.Min[axis] == viewport_rect.Min[axis] && bb->Min[axis] > window_rect.Min[axis] && bb->Min[axis] - threshold <= window_rect.Min[axis]) bb->Min[axis] = window_rect.Min[axis]; if (window_rect.Max[axis] == viewport_rect.Max[axis] && bb->Max[axis] < window_rect.Max[axis] && bb->Max[axis] + threshold >= window_rect.Max[axis]) bb->Max[axis] = window_rect.Max[axis]; } void ImGui::Scrollbar(ImGuiAxis axis) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImGuiID id = GetWindowScrollbarID(window, axis); // Calculate scrollbar bounding box ImRect bb = GetWindowScrollbarRect(window, axis); ImRect host_rect = (window->DockIsActive ? window->DockNode->HostWindow : window)->Rect(); ImDrawFlags rounding_corners = CalcRoundingFlagsForRectInRect(bb, host_rect, g.Style.WindowBorderSize); float size_visible = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; ImS64 scroll = (ImS64)window->Scroll[axis]; ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_visible, (ImS64)size_contents, rounding_corners); window->Scroll[axis] = (float)scroll; } // 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. // Still, the code should probably be made simpler.. bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags draw_rounding_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; const float bb_frame_width = bb_frame.GetWidth(); const float bb_frame_height = bb_frame.GetHeight(); if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f) return false; // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab) float alpha = 1.0f; if ((axis == ImGuiAxis_Y) && bb_frame_height < bb_frame_width) alpha = ImSaturate(bb_frame_height / ImMax(bb_frame_width * 2.0f, 1.0f)); if (alpha <= 0.0f) return false; const ImGuiStyle& style = g.Style; const bool allow_interaction = (alpha >= 1.0f); ImRect bb = bb_frame; float padding = IM_TRUNC(ImMin(style.ScrollbarPadding, ImMin(bb_frame_width, bb_frame_height) * 0.5f)); bb.Expand(-padding); // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight(); if (scrollbar_size_v < 1.0f) return false; // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1); const float grab_h_minsize = ImMin(bb.GetSize()[axis], style.GrabMinSize); const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), grab_h_minsize, scrollbar_size_v); const float grab_h_norm = grab_h_pixels / scrollbar_size_v; // As a special thing, we allow scrollbar near the edge of a screen/viewport to be reachable with mouse at the extreme edge (#9276) ImRect bb_hit = bb_frame; ExtendHitBoxWhenNearViewportEdge(window, &bb_hit, g.Style.WindowBorderSize, (ImGuiAxis)(axis ^ 1)); // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). bool held = false; bool hovered = false; ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav); ButtonBehavior(bb_hit, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_visible_v); float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space if (held && allow_interaction && grab_h_norm < 1.0f) { const float scrollbar_pos_v = bb.Min[axis]; const float mouse_pos_v = g.IO.MousePos[axis]; // Click position in scrollbar normalized space (0.0f->1.0f) const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0; if (g.ActiveIdIsJustActivated) { // On initial click when held_dir == 0 (clicked over grab): calculate the distance between mouse and the center of the grab const bool scroll_to_clicked_location = (g.IO.ConfigScrollbarScrollByPage == false || g.IO.KeyShift || held_dir == 0); g.ScrollbarSeekMode = scroll_to_clicked_location ? 0 : (short)held_dir; g.ScrollbarClickDeltaToGrabCenter = (held_dir == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f; } // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position if (g.ScrollbarSeekMode == 0) { // Absolute seeking const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); } else { // Page by page if (IsMouseClicked(ImGuiMouseButton_Left, ImGuiInputFlags_Repeat) && held_dir == g.ScrollbarSeekMode) { float page_dir = (g.ScrollbarSeekMode > 0.0f) ? +1.0f : -1.0f; *p_scroll_v = ImClamp(*p_scroll_v + (ImS64)(page_dir * size_visible_v), (ImS64)0, scroll_max); } } // Update values for rendering scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Update distance to grab now that we have seek'ed and saturated //if (seek_absolute) // g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; } // Render const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg); const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, draw_rounding_flags); ImRect grab_rect; if (axis == ImGuiAxis_X) grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y); else grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels); window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); return held; } // - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; const ImVec2 padding(g.Style.ImageBorderSize, g.Style.ImageBorderSize); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); ItemSize(bb); if (!ItemAdd(bb, 0)) return; // Render float rounding = g.Style.ImageRounding; if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col), rounding); if (rounding > 0.0f) window->DrawList->AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col), rounding); else window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); if (g.Style.ImageBorderSize > 0.0f) window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), rounding, ImDrawFlags_None, g.Style.ImageBorderSize); } void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1) { ImageWithBg(tex_ref, image_size, uv0, uv1); } // 1.91.9 (February 2025) removed 'tint_col' and 'border_col' parameters, made border size not depend on color value. (#8131, #8238) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) { ImGuiContext& g = *GImGui; PushStyleVar(ImGuiStyleVar_ImageBorderSize, (border_col.w > 0.0f) ? ImMax(1.0f, g.Style.ImageBorderSize) : 0.0f); // Preserve legacy behavior where border is always visible when border_col's Alpha is >0.0f PushStyleColor(ImGuiCol_Border, border_col); ImageWithBg(tex_ref, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col); PopStyleColor(); PopStyleVar(); } #endif bool ImGui::ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImVec2 padding = g.Style.FramePadding; const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); ItemSize(bb); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderNavCursor(bb, id); RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); float image_rounding = ImMax(g.Style.FrameRounding - ImMax(padding.x, padding.y), g.Style.ImageRounding); if (image_rounding > 0.0f) window->DrawList->AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col), image_rounding); else window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); return pressed; } // - ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button. // - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. (#8165) // FIXME: Maybe that's not the best design? bool ImGui::ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; return ImageButtonEx(window->GetID(str_id), tex_ref, image_size, uv0, uv1, bg_col, tint_col); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy API obsoleted in 1.89. Two differences with new ImageButton() // - old ImageButton() used ImTextureID as item id (created issue with multiple buttons with same image, transient texture id values, opaque computation of ID) // - new ImageButton() requires an explicit 'const char* str_id' // - old ImageButton() had frame_padding' override argument. // - new ImageButton() always use style.FramePadding. /* bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) { // Default to using texture ID as ID. User can still push string/integer prefixes. PushID((ImTextureID)(intptr_t)user_texture_id); if (frame_padding >= 0) PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding)); bool ret = ImageButton("", user_texture_id, size, uv0, uv1, bg_col, tint_col); if (frame_padding >= 0) PopStyleVar(); PopID(); return ret; } */ #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS bool ImGui::Checkbox(const char* label, bool* v) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); 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); const bool is_visible = ItemAdd(total_bb, id); const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (!is_visible) if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of "no logic clip" for box-select support { IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; } // Range-Selection/Multi-selection support (header) bool checked = *v; if (is_multi_select) MultiSelectItemHeader(id, &checked, NULL); bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); // Range-Selection/Multi-selection support (footer) if (is_multi_select) MultiSelectItemFooter(id, &checked, &pressed); else if (pressed) checked = !checked; if (*v != checked) { *v = checked; pressed = true; // return value MarkItemEdited(id); } const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); const bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0; if (is_visible) { RenderNavCursor(total_bb, id); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); if (mixed_value) { // Undocumented tristate/mixed/indeterminate checkbox (#2644) // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f))); window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); } else if (*v) { const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); } } const ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); if (g.LogEnabled) LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); if (is_visible && label_size.x > 0.0f) RenderText(label_pos, label); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } template bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) { bool all_on = (*flags & flags_value) == flags_value; bool any_on = (*flags & flags_value) != 0; bool pressed; if (!all_on && any_on) { ImGuiContext& g = *GImGui; g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); } else { pressed = Checkbox(label, &all_on); } if (pressed) { if (all_on) *flags |= flags_value; else *flags &= ~flags_value; } return pressed; } bool ImGui::CheckboxFlags(const char* label, int* flags, int flags_value) { return CheckboxFlagsT(label, flags, flags_value); } bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) { return CheckboxFlagsT(label, flags, flags_value); } bool ImGui::CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value) { return CheckboxFlagsT(label, flags, flags_value); } bool ImGui::CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value) { return CheckboxFlagsT(label, flags, flags_value); } bool ImGui::RadioButton(const char* label, bool active) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); 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 = IM_ROUND(center.x); center.y = IM_ROUND(center.y); const float radius = (square_sz - 1.0f) * 0.5f; bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); if (pressed) MarkItemEdited(id); RenderNavCursor(total_bb, id); const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius); window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment); if (active) { const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark)); } if (style.FrameBorderSize > 0.0f) { window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize); window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize); } ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); if (g.LogEnabled) LogRenderedText(&label_pos, active ? "(x)" : "( )"); if (label_size.x > 0.0f) RenderText(label_pos, label); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } // FIXME: This would work nicely if it was a public template, e.g. 'template RadioButton(const char* label, T* v, T v_button)', but I'm not sure how we would expose it.. bool ImGui::RadioButton(const char* label, int* v, int v_button) { const bool pressed = RadioButton(label, *v == v_button); if (pressed) *v = v_button; return pressed; } // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; ImVec2 pos = window->DC.CursorPos; ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f); ImRect bb(pos, pos + size); ItemSize(size, style.FramePadding.y); if (!ItemAdd(bb, 0)) return; // Fraction < 0.0f will display an indeterminate progress bar animation // The value must be animated along with time, so e.g. passing '-1.0f * ImGui::GetTime()' as fraction works. const bool is_indeterminate = (fraction < 0.0f); if (!is_indeterminate) fraction = ImSaturate(fraction); // Out of courtesy we accept a NaN fraction without crashing float fill_n0 = 0.0f; float fill_n1 = (fraction == fraction) ? fraction : 0.0f; if (is_indeterminate) { const float fill_width_n = 0.2f; fill_n0 = ImFmod(-fraction, 1.0f) * (1.0f + fill_width_n) - fill_width_n; fill_n1 = ImSaturate(fill_n0 + fill_width_n); fill_n0 = ImSaturate(fill_n0); } // Render RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); float fill_x0 = ImLerp(bb.Min.x, bb.Max.x, fill_n0); float fill_x1 = ImLerp(bb.Min.x, bb.Max.x, fill_n1); if (fill_x0 < fill_x1) RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_x0, fill_x1, style.FrameRounding); // Default displaying the fraction as percentage string, but user can override it // Don't display text for indeterminate bars by default char overlay_buf[32]; if (!is_indeterminate || overlay != NULL) { if (!overlay) { ImFormatString(overlay_buf, IM_COUNTOF(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); overlay = overlay_buf; } ImVec2 overlay_size = CalcTextSize(overlay, NULL); if (overlay_size.x > 0.0f) { float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : fill_x1 + style.ItemSpacing.x; RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); } } } void ImGui::Bullet() { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), g.FontSize); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); ItemSize(bb); if (!ItemAdd(bb, 0)) { SameLine(0, style.FramePadding.x * 2); return; } // Render and stay on same line ImU32 text_col = GetColorU32(ImGuiCol_Text); RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f), text_col); SameLine(0, style.FramePadding.x * 2.0f); } // This is provided as a convenience for being an often requested feature. // FIXME-STYLE: we delayed adding as there is a larger plan to revamp the styling system. // Because of this we currently don't provide many styling options for this widget // (e.g. hovered/active colors are automatically inferred from a single color). bool ImGui::TextLink(const char* label) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiID id = window->GetID(label); const char* label_end = FindRenderedTextEnd(label); ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); ImVec2 size = CalcTextSize(label, label_end, true); ImRect bb(pos, pos + size); ItemSize(size, 0.0f); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); RenderNavCursor(bb, id); if (hovered) SetMouseCursor(ImGuiMouseCursor_Hand); ImVec4 text_colf = g.Style.Colors[ImGuiCol_TextLink]; ImVec4 line_colf = text_colf; { // FIXME-STYLE: Read comments above. This widget is NOT written in the same style as some earlier widgets, // as we are currently experimenting/planning a different styling system. float h, s, v; ColorConvertRGBtoHSV(text_colf.x, text_colf.y, text_colf.z, h, s, v); if (held || hovered) { v = ImSaturate(v + (held ? 0.4f : 0.3f)); h = ImFmod(h + 0.02f, 1.0f); } ColorConvertHSVtoRGB(h, s, v, text_colf.x, text_colf.y, text_colf.z); v = ImSaturate(v - 0.20f); ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z); } float line_y = bb.Max.y + ImFloor(g.FontBaked->Descent * g.FontBakedScale * 0.20f); window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf), 1.0f * (float)(int)g.Style._MainScale); // FIXME-TEXT: Underline mode // FIXME-DPI PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf)); RenderText(bb.Min, label, label_end); PopStyleColor(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } bool ImGui::TextLinkOpenURL(const char* label, const char* url) { ImGuiContext& g = *GImGui; if (url == NULL) url = label; bool pressed = TextLink(label); if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL) g.PlatformIO.Platform_OpenInShellFn(&g, url); SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label if (BeginPopupContextItem()) { if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink))) SetClipboardText(url); EndPopup(); } return pressed; } //------------------------------------------------------------------------- // [SECTION] Widgets: Low-level Layout helpers //------------------------------------------------------------------------- // - Spacing() // - Dummy() // - NewLine() // - AlignTextToFramePadding() // - SeparatorEx() [Internal] // - Separator() // - SplitterBehavior() [Internal] // - ShrinkWidths() [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(size); 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; window->DC.IsSameLine = false; if (window->DC.CurrLineSize.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.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2); window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y); } // Horizontal/vertical separating line // FIXME: Surprisingly, this seemingly trivial widget is a victim of many different legacy/tricky layout issues. // Note how thickness == 1.0f is handled specifically as not moving CursorPos by 'thickness', but other values are. void ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected IM_ASSERT(thickness > 0.0f); if (flags & ImGuiSeparatorFlags_Vertical) { // Vertical separator, for menu bars (use current line height). float y1 = window->DC.CursorPos.y; float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y; const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness, y2)); ItemSize(ImVec2(thickness, 0.0f)); if (!ItemAdd(bb, 0)) return; // Draw window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator)); if (g.LogEnabled) LogText(" |"); } else if (flags & ImGuiSeparatorFlags_Horizontal) { // Horizontal Separator float x1 = window->DC.CursorPos.x; float x2 = window->WorkRect.Max.x; // Preserve legacy behavior inside Columns() // Before Tables API happened, we relied on Separator() to span all columns of a Columns() set. // We currently don't need to provide the same feature for tables because tables naturally have border features. ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; if (columns) { x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03 x2 = window->Pos.x + window->Size.x; PushColumnsBackground(); } // We don't provide our width to the layout so that it doesn't get feed back into AutoFit // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell) // Between 1.71 and 1.92.7, we maintained a hack where a 1.0f thin Separator() would not impact layout. // This was mostly chosen to allow backward compatibility with user's code assuming zero-height when calculating height for layout (e.g. bottom alignment of a status bar). // In order to handle scaling we need to scale separator thickness and it would not makes sense to have a disparity depending on height. ////float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change. const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness)); ItemSize(ImVec2(0.0f, thickness)); if (ItemAdd(bb, 0)) { // Draw window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator)); if (g.LogEnabled) LogRenderedText(&bb.Min, "--------------------------------\n"); } if (columns) { PopColumnsBackground(); columns->LineMinY = window->DC.CursorPos.y; } } } void ImGui::Separator() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return; // Those flags should eventually be configurable by the user ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; // Only applies to legacy Columns() api as they relied on Separator() a lot. if (window->DC.CurrentColumns) flags |= ImGuiSeparatorFlags_SpanAllColumns; SeparatorEx(flags, g.Style.SeparatorSize); } void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiStyle& style = g.Style; const ImVec2 label_size = CalcTextSize(label, label_end, false); const ImVec2 pos = window->DC.CursorPos; const ImVec2 padding = style.SeparatorTextPadding; const float separator_thickness = style.SeparatorTextBorderSize; const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); ItemSize(min_size, text_baseline_y); if (!ItemAdd(bb, id)) return; const float sep1_x1 = pos.x; const float sep2_x2 = bb.Max.x; const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f); const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f); const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN // This allows using SameLine() to position something in the 'extra_w' window->DC.CursorPosPrevLine.x = label_pos.x + label_size.x; const ImU32 separator_col = GetColorU32(ImGuiCol_Separator); if (label_size.x > 0.0f) { const float sep1_x2 = label_pos.x - style.ItemSpacing.x; const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x; if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f) window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness); if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f) window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); if (g.LogEnabled) LogSetNextTextDecoration("---", NULL); RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size); } else { if (g.LogEnabled) LogText("---"); if (separator_thickness > 0.0f) window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); } } void ImGui::SeparatorText(const char* label) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; // The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want: // - allow separator-text to be draggable items (would require a stable ID + a noticeable highlight) // - this high-level entry point to allow formatting? (which in turns may require ID separate from formatted string) // - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...' // Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item, // and then we can turn this into a format function. SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f); } // 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, ImU32 bg_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav)) return false; // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item. // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item. ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren; #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS button_flags |= ImGuiButtonFlags_AllowOverlap; #endif 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, button_flags); if (hovered) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); ImRect bb_render = bb; if (held) { float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis]; // 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) { *size1 = ImMax(*size1 + mouse_delta, min_size1); *size2 = ImMax(*size2 - mouse_delta, min_size2); bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); MarkItemEdited(id); } } // Render at new position if (bg_col & IM_COL32_A_MASK) window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f); 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, 0.0f); return held; } static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs) { const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs; const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs; if (int d = (int)(b->Width - a->Width)) return d; return b->Index - a->Index; } // Shrink excess width from a set of item, by removing width from the larger items first. // Set items Width to -1.0f to disable shrinking this item. void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min) { if (count == 1) { if (items[0].Width >= 0.0f) items[0].Width = ImMax(items[0].Width - width_excess, width_min); return; } ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); // Sort largest first, smallest last. int count_same_width = 1; while (width_excess > 0.001f && count_same_width < count) { while (count_same_width < count && items[0].Width <= items[count_same_width].Width) count_same_width++; float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f); max_width_to_remove_per_item = ImMin(items[0].Width - width_min, max_width_to_remove_per_item); if (max_width_to_remove_per_item <= 0.0f) break; float base_width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item); for (int item_n = 0; item_n < count_same_width; item_n++) { float width_to_remove_for_this_item = ImMin(base_width_to_remove_per_item, items[item_n].Width - width_min); items[item_n].Width -= width_to_remove_for_this_item; width_excess -= width_to_remove_for_this_item; } } // Round width and redistribute remainder // Ensure that e.g. the right-most tab of a shrunk tab-bar always reaches exactly at the same distance from the right-most edge of the tab bar separator. width_excess = 0.0f; for (int n = 0; n < count; n++) { float width_rounded = ImTrunc(items[n].Width); width_excess += items[n].Width - width_rounded; items[n].Width = width_rounded; } while (width_excess > 0.0f) for (int n = 0; n < count && width_excess > 0.0f; n++) { float width_to_add = ImMin(items[n].InitialWidth - items[n].Width, 1.0f); items[n].Width += width_to_add; width_excess -= width_to_add; } } //------------------------------------------------------------------------- // [SECTION] Widgets: ComboBox //------------------------------------------------------------------------- // - CalcMaxPopupHeightFromItemCount() [Internal] // - BeginCombo() // - BeginComboPopup() [Internal] // - EndCombo() // - BeginComboPreview() [Internal] // - EndComboPreview() [Internal] // - Combo() //------------------------------------------------------------------------- static float CalcMaxPopupHeightFromItemCount(int items_count) { ImGuiContext& g = *GImGui; if (items_count <= 0) return FLT_MAX; return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); } bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.HasFlags; g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together if (flags & ImGuiComboFlags_WidthFitPreview) IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0); const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const ImVec2 label_size = CalcTextSize(label, NULL, true); const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth()); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id, &bb)) return false; // Open on click bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); if (pressed && !popup_open) { OpenPopupEx(popup_id, ImGuiPopupFlags_None); popup_open = true; } // Render shape const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size); RenderNavCursor(bb, id); if (!(flags & ImGuiComboFlags_NoPreview)) window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); if (!(flags & ImGuiComboFlags_NoArrowButton)) { ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x) RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); } RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding); // Custom preview if (flags & ImGuiComboFlags_CustomPreview) { g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); IM_ASSERT(preview_value == NULL || preview_value[0] == 0); preview_value = NULL; } // Render preview and label if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) { if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL); } if (label_size.x > 0) RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label); if (!popup_open) return false; g.NextWindowData.HasFlags = backup_next_window_data_flags; return BeginComboPopup(popup_id, bb, flags); } bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags) { ImGuiContext& g = *GImGui; if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None)) { g.NextWindowData.ClearFlags(); return false; } // Set popup size float w = bb.GetWidth(); if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint) { g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); } else { if ((flags & ImGuiComboFlags_HeightMask_) == 0) flags |= ImGuiComboFlags_HeightRegular; IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one int popup_max_height_in_items = -1; if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; ImVec2 constraint_min(0.0f, 0.0f), constraint_max(FLT_MAX, FLT_MAX); if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.x <= 0.0f) // Don't apply constraints if user specified a size constraint_min.x = w; if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.y <= 0.0f) constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items); SetNextWindowSizeConstraints(constraint_min, constraint_max); } // This is essentially a specialized version of BeginPopupEx() char name[16]; ImFormatString(name, IM_COUNTOF(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth // Set position given a custom constraint (peak into expected window size so we can position it) // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()? if (ImGuiWindow* popup_window = FindWindowByName(name)) if (popup_window->WasActive) { // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us. ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window); popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)" ImRect r_outer = GetPopupAllowedExtentRect(popup_window); ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox); SetNextWindowPos(pos); } // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; PushStyleVarX(ImGuiStyleVar_WindowPadding, g.Style.FramePadding.x); // Horizontally align ourselves with the framed text bool ret = Begin(name, NULL, window_flags); PopStyleVar(); if (!ret) { EndPopup(); if (!g.IO.ConfigDebugBeginReturnValueOnce && !g.IO.ConfigDebugBeginReturnValueLoop) // Begin may only return false with those debug tools activated. IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above return false; } g.BeginComboDepth++; return true; } void ImGui::EndCombo() { ImGuiContext& g = *GImGui; g.BeginComboDepth--; char name[16]; ImFormatString(name, IM_COUNTOF(name), "##Combo_%02d", g.BeginComboDepth); // FIXME: Move those to helpers? if (strcmp(g.CurrentWindow->Name, name) != 0) IM_ASSERT_USER_ERROR_RET(0, "Calling EndCombo() in wrong window!"); EndPopup(); } // Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements // (Experimental, see GitHub issues: #1658, #4168) bool ImGui::BeginComboPreview() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)) return false; IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional) return false; // FIXME: This could be contained in a PushWorkRect() api preview_data->BackupCursorPos = window->DC.CursorPos; preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos; preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; preview_data->BackupLayout = window->DC.LayoutType; window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding; window->DC.CursorMaxPos = window->DC.CursorPos; window->DC.LayoutType = ImGuiLayoutType_Horizontal; window->DC.IsSameLine = false; PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true); return true; } void ImGui::EndComboPreview() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future ImDrawList* draw_list = window->DrawList; if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y) if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command { draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect; draw_list->_TryMergeDrawCmds(); } PopClipRect(); window->DC.CursorPos = preview_data->BackupCursorPos; window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos); window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine; window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset; window->DC.LayoutType = preview_data->BackupLayout; window->DC.IsSameLine = false; preview_data->PreviewRect = ImRect(); } // Getter for the old Combo() API: const char*[] static const char* Items_ArrayGetter(void* data, int idx) { const char* const* items = (const char* const*)data; return items[idx]; } // Getter for the old Combo() API: "item1\0item2\0item3\0" static const char* Items_SingleStringGetter(void* data, int idx) { const char* items_separated_by_zeros = (const char*)data; int items_count = 0; const char* p = items_separated_by_zeros; while (*p) { if (idx == items_count) break; p += ImStrlen(p) + 1; items_count++; } return *p ? p : NULL; } // Old API, prefer using BeginCombo() nowadays if you can. bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items) { ImGuiContext& g = *GImGui; // Call the getter to obtain the preview string which is a parameter to BeginCombo() const char* preview_value = NULL; if (*current_item >= 0 && *current_item < items_count) preview_value = getter(user_data, *current_item); // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. if (popup_max_height_in_items != -1 && !(g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint)) SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); if (!BeginCombo(label, preview_value, ImGuiComboFlags_None)) return false; // Display items bool value_changed = false; ImGuiListClipper clipper; clipper.Begin(items_count); clipper.IncludeItemByIndex(*current_item); while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { const char* item_text = getter(user_data, i); if (item_text == NULL) item_text = "*Unknown item*"; PushID(i); const bool item_selected = (i == *current_item); if (Selectable(item_text, item_selected) && *current_item != i) { value_changed = true; *current_item = i; } if (item_selected) SetItemDefaultFocus(); PopID(); } EndCombo(); if (value_changed) MarkItemEdited(g.LastItemData.ID); return value_changed; } // Combo box helper allowing to pass an array of strings. bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) { const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_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" bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) { int items_count = 0; const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open while (*p) { p += ImStrlen(p) + 1; items_count++; } bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); return value_changed; } //------------------------------------------------------------------------- // [SECTION] Data Type and Data Formatting Helpers [Internal] //------------------------------------------------------------------------- // - DataTypeGetInfo() // - DataTypeFormatString() // - DataTypeApplyOp() // - DataTypeApplyFromText() // - DataTypeCompare() // - DataTypeClamp() // - GetMinimumStepAtDecimalPrecision // - RoundScalarWithFormat<>() //------------------------------------------------------------------------- static const ImU32 GDefaultRgbaColorMarkers[4] = { IM_COL32(240,20,20,255), IM_COL32(20,240,20,255), IM_COL32(20,20,240,255), IM_COL32(140,140,140,255) }; static const ImGuiDataTypeInfo GDataTypeInfo[] = { { sizeof(char), "S8", "%d", "%d" }, // ImGuiDataType_S8 { sizeof(unsigned char), "U8", "%u", "%u" }, { sizeof(short), "S16", "%d", "%d" }, // ImGuiDataType_S16 { sizeof(unsigned short), "U16", "%u", "%u" }, { sizeof(int), "S32", "%d", "%d" }, // ImGuiDataType_S32 { sizeof(unsigned int), "U32", "%u", "%u" }, #ifdef _MSC_VER { sizeof(ImS64), "S64", "%I64d","%I64d" }, // ImGuiDataType_S64 { sizeof(ImU64), "U64", "%I64u","%I64u" }, #else { sizeof(ImS64), "S64", "%lld", "%lld" }, // ImGuiDataType_S64 { sizeof(ImU64), "U64", "%llu", "%llu" }, #endif { sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) { sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double { sizeof(bool), "bool", "%d", "%d" }, // ImGuiDataType_Bool { 0, "char*","%s", "%s" }, // ImGuiDataType_String }; IM_STATIC_ASSERT(IM_COUNTOF(GDataTypeInfo) == ImGuiDataType_COUNT); const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) { IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); return &GDataTypeInfo[data_type]; } int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format) { // 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*)p_data); if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) return ImFormatString(buf, buf_size, format, *(const ImU64*)p_data); if (data_type == ImGuiDataType_Float) return ImFormatString(buf, buf_size, format, *(const float*)p_data); if (data_type == ImGuiDataType_Double) return ImFormatString(buf, buf_size, format, *(const double*)p_data); if (data_type == ImGuiDataType_S8) return ImFormatString(buf, buf_size, format, *(const ImS8*)p_data); if (data_type == ImGuiDataType_U8) return ImFormatString(buf, buf_size, format, *(const ImU8*)p_data); if (data_type == ImGuiDataType_S16) return ImFormatString(buf, buf_size, format, *(const ImS16*)p_data); if (data_type == ImGuiDataType_U16) return ImFormatString(buf, buf_size, format, *(const ImU16*)p_data); IM_ASSERT(0); return 0; } void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const 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 == '+') { *(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 == '+') { *(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 = 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 = 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; } if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; } return; case ImGuiDataType_Double: 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; } IM_ASSERT(0); } // User can input math operators (e.g. +100) to edit a numerical values. // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty) { // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); ImGuiDataTypeStorage data_backup; memcpy(&data_backup, p_data, type_info->Size); while (ImCharIsBlankA(*buf)) buf++; if (!buf[0]) { if (p_data_when_empty != NULL) { memcpy(p_data, p_data_when_empty, type_info->Size); return memcmp(&data_backup, p_data, type_info->Size) != 0; } return false; } // Sanitize format // - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf // - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %. char format_sanitized[32]; if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) format = type_info->ScanFmt; else format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_COUNTOF(format_sanitized)); // Small types need a 32-bit buffer to receive the result from scanf() int v32 = 0; if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1) return false; if (type_info->Size < 4) { if (data_type == ImGuiDataType_S8) *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); else if (data_type == ImGuiDataType_U8) *(ImU8*)p_data = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX); else if (data_type == ImGuiDataType_S16) *(ImS16*)p_data = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX); else if (data_type == ImGuiDataType_U16) *(ImU16*)p_data = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX); else IM_ASSERT(0); } return memcmp(&data_backup, p_data, type_info->Size) != 0; } template static int DataTypeCompareT(const T* lhs, const T* rhs) { if (*lhs < *rhs) return -1; if (*lhs > *rhs) return +1; return 0; } int ImGui::DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2) { switch (data_type) { case ImGuiDataType_S8: return DataTypeCompareT((const ImS8* )arg_1, (const ImS8* )arg_2); case ImGuiDataType_U8: return DataTypeCompareT((const ImU8* )arg_1, (const ImU8* )arg_2); case ImGuiDataType_S16: return DataTypeCompareT((const ImS16* )arg_1, (const ImS16* )arg_2); case ImGuiDataType_U16: return DataTypeCompareT((const ImU16* )arg_1, (const ImU16* )arg_2); case ImGuiDataType_S32: return DataTypeCompareT((const ImS32* )arg_1, (const ImS32* )arg_2); case ImGuiDataType_U32: return DataTypeCompareT((const ImU32* )arg_1, (const ImU32* )arg_2); case ImGuiDataType_S64: return DataTypeCompareT((const ImS64* )arg_1, (const ImS64* )arg_2); case ImGuiDataType_U64: return DataTypeCompareT((const ImU64* )arg_1, (const ImU64* )arg_2); case ImGuiDataType_Float: return DataTypeCompareT((const float* )arg_1, (const float* )arg_2); case ImGuiDataType_Double: return DataTypeCompareT((const double*)arg_1, (const double*)arg_2); case ImGuiDataType_COUNT: break; } IM_ASSERT(0); return 0; } template static bool DataTypeClampT(T* v, const T* v_min, const T* v_max) { // Clamp, both sides are optional, return true if modified if (v_min && *v < *v_min) { *v = *v_min; return true; } if (v_max && *v > *v_max) { *v = *v_max; return true; } return false; } bool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max) { switch (data_type) { case ImGuiDataType_S8: return DataTypeClampT((ImS8* )p_data, (const ImS8* )p_min, (const ImS8* )p_max); case ImGuiDataType_U8: return DataTypeClampT((ImU8* )p_data, (const ImU8* )p_min, (const ImU8* )p_max); case ImGuiDataType_S16: return DataTypeClampT((ImS16* )p_data, (const ImS16* )p_min, (const ImS16* )p_max); case ImGuiDataType_U16: return DataTypeClampT((ImU16* )p_data, (const ImU16* )p_min, (const ImU16* )p_max); case ImGuiDataType_S32: return DataTypeClampT((ImS32* )p_data, (const ImS32* )p_min, (const ImS32* )p_max); case ImGuiDataType_U32: return DataTypeClampT((ImU32* )p_data, (const ImU32* )p_min, (const ImU32* )p_max); case ImGuiDataType_S64: return DataTypeClampT((ImS64* )p_data, (const ImS64* )p_min, (const ImS64* )p_max); case ImGuiDataType_U64: return DataTypeClampT((ImU64* )p_data, (const ImU64* )p_min, (const ImU64* )p_max); case ImGuiDataType_Float: return DataTypeClampT((float* )p_data, (const float* )p_min, (const float* )p_max); case ImGuiDataType_Double: return DataTypeClampT((double*)p_data, (const double*)p_min, (const double*)p_max); case ImGuiDataType_COUNT: break; } IM_ASSERT(0); return false; } bool ImGui::DataTypeIsZero(ImGuiDataType data_type, const void* p_data) { ImGuiContext& g = *GImGui; return DataTypeCompare(data_type, p_data, &g.DataTypeZeroValue) == 0; } 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 < IM_COUNTOF(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); } template TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) { IM_UNUSED(data_type); IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); 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 return v; // Sanitize format char fmt_sanitized[32]; ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_COUNTOF(fmt_sanitized)); fmt_start = fmt_sanitized; // Format value with our rounding, and read back char v_str[64]; ImFormatString(v_str, IM_COUNTOF(v_str), fmt_start, v); const char* p = v_str; while (*p == ' ') p++; v = (TYPE)ImAtof(p); return v; } //------------------------------------------------------------------------- // [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. //------------------------------------------------------------------------- // - DragBehaviorT<>() [Internal] // - DragBehavior() [Internal] // - DragScalar() // - DragScalarN() // - DragFloat() // - DragFloat2() // - DragFloat3() // - DragFloat4() // - DragFloatRange2() // - DragInt() // - DragInt2() // - DragInt3() // - DragInt4() // - DragIntRange2() //------------------------------------------------------------------------- // This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) template bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags) { ImGuiContext& g = *GImGui; const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; const bool is_bounded = (v_min < v_max) || ((v_min == v_max) && (v_min != 0.0f || (flags & ImGuiSliderFlags_ClampZeroRange))); const bool is_wrapped = is_bounded && (flags & ImGuiSliderFlags_WrapAround); const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); // Default tweak speed if (v_speed == 0.0f && is_bounded && (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() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { adjust_delta = g.IO.MouseDelta[axis]; if (g.IO.KeyAlt && !(flags & ImGuiSliderFlags_NoSpeedTweaks)) adjust_delta *= 1.0f / 100.0f; if (g.IO.KeyShift && !(flags & ImGuiSliderFlags_NoSpeedTweaks)) adjust_delta *= 10.0f; } else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) { const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); const float tweak_factor = (flags & ImGuiSliderFlags_NoSpeedTweaks) ? 1.0f : tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f; adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor; 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; // For logarithmic use our range is effectively 0..1 so scale the delta into that range if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f)) // Epsilon to avoid /0 adjust_delta /= (float)(v_max - v_min); // 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. const bool is_just_activated = g.ActiveIdIsJustActivated; const bool is_already_past_limits_and_pushing_outward = is_bounded && !is_wrapped && ((*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) { g.DragCurrentAccum = 0.0f; g.DragCurrentAccumDirty = false; } else if (adjust_delta != 0.0f) { g.DragCurrentAccum += adjust_delta; g.DragCurrentAccumDirty = true; } if (!g.DragCurrentAccumDirty) return false; TYPE v_cur = *v; FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true const float zero_deadzone_halfsize = 0.0f; // Drag widgets have no deadzone (as it doesn't make sense) if (is_logarithmic) { // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); // Convert to parametric space, apply delta, convert back float v_old_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); float v_new_parametric = v_old_parametric + g.DragCurrentAccum; v_cur = ScaleValueFromRatioT(data_type, v_new_parametric, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); v_old_ref_for_accum_remainder = v_old_parametric; } else { v_cur += (SIGNEDTYPE)g.DragCurrentAccum; } // Round to user desired precision based on format string if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) 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; if (is_logarithmic) { // Convert to parametric space, apply delta, convert back float v_new_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder); } else { g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v); } // Lose zero sign for float/double if (v_cur == (TYPE)-0) v_cur = (TYPE)0; if (*v != v_cur && is_bounded) { if (is_wrapped) { // Wrap values if (v_cur < v_min) v_cur += v_max - v_min + (is_floating_point ? 0 : 1); if (v_cur > v_max) v_cur -= v_max - v_min + (is_floating_point ? 0 : 1); } else { // Clamp values + handle overflow/wrap-around for integer types. if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point)) v_cur = v_min; if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point)) v_cur = v_max; } } // Apply result if (*v == v_cur) return false; *v = v_cur; return true; } bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) { // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); ImGuiContext& g = *GImGui; if (g.ActiveId == id) { // Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation. if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) ClearActiveID(); else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ClearActiveID(); } if (g.ActiveId != id) return false; if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) { case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN, p_max ? *(const ImS8*)p_max : IM_S8_MAX, format, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN, p_max ? *(const ImU8*)p_max : IM_U8_MAX, format, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; } case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; } case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; } case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)p_v, v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, flags); case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)p_v, v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, flags); case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)p_v, v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, flags); case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)p_v, v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, flags); case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)p_v, v_speed, p_min ? *(const float* )p_min : -FLT_MAX, p_max ? *(const float* )p_max : FLT_MAX, format, flags); case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX, p_max ? *(const double*)p_max : DBL_MAX, format, flags); case ImGuiDataType_COUNT: break; } IM_ASSERT(0); return false; } // Only clamp Ctrl+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) static bool TempInputIsClampEnabled(ImGuiSliderFlags flags, ImGuiDataType data_type, const void* p_min, const void* p_max) { if ((flags & ImGuiSliderFlags_ClampOnInput) && (p_min != NULL || p_max != NULL)) { const int clamp_range_dir = (p_min != NULL && p_max != NULL) ? ImGui::DataTypeCompare(data_type, p_min, p_max) : 0; // -1 when *p_min < *p_max, == 0 when *p_min == *p_max if (p_min == NULL || p_max == NULL || clamp_range_dir < 0) return true; if (clamp_range_dir == 0) return ImGui::DataTypeIsZero(data_type, p_min) ? ((flags & ImGuiSliderFlags_ClampZeroRange) != 0) : true; } return false; } // Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional. // Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0; 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 total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { // Tabbing or Ctrl+Click on Drag turns it into an InputText const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id)); const bool make_active = (clicked || double_clicked || g.NavActivateId == id); if (make_active && (clicked || double_clicked)) SetKeyOwner(ImGuiKey_MouseLeft, id); if (make_active && temp_input_allowed) if ((clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) temp_input_is_active = true; // (Optional) simple click (without moving) turns Drag into an InputText if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { g.NavActivateId = id; g.NavActivateFlags = ImGuiActivateFlags_PreferInput; temp_input_is_active = true; } // Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert) if (make_active) memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size); if (make_active && !temp_input_is_active) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); } } if (temp_input_is_active) { const bool clamp_enabled = TempInputIsClampEnabled(flags, data_type, p_min, p_max); return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL); } // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding); if (color_marker != 0 && style.ColorMarkerSize > 0.0f) RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding); RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding); // Drag behavior const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags); if (value_changed) MarkItemEdited(id); // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); 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, frame_bb.Min.y + style.FramePadding.y), label); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0)); return value_changed; } bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; bool value_changed = false; BeginGroup(); PushID(label); PushMultiItemsWidths(components, CalcItemWidth()); size_t type_size = GDataTypeInfo[data_type].Size; for (int i = 0; i < components; i++) { PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); if (flags & ImGuiSliderFlags_ColorMarkers) SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]); value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags); PopID(); PopItemWidth(); p_data = (void*)((char*)p_data + type_size); } PopID(); const char* label_end = FindRenderedTextEnd(label); if (label != label_end) { SameLine(0, g.Style.ItemInnerSpacing.x); TextEx(label, label_end); } EndGroup(); return value_changed; } bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags); } bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags); } bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags); } bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags); } // NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this. bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; PushID(label); BeginGroup(); PushMultiItemsWidths(2, CalcItemWidth()); float min_min = (v_min >= v_max) ? -FLT_MAX : v_min; float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0); bool value_changed = DragScalar("##min", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags); PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); float max_max = (v_min >= v_max) ? FLT_MAX : v_max; ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0); value_changed |= DragScalar("##max", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max : format, max_flags); PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); return value_changed; } // NB: v_speed is float to allow adjusting the drag speed with more precision bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags); } bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags); } bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags); } bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags); } // NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this. bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; PushID(label); BeginGroup(); PushMultiItemsWidths(2, CalcItemWidth()); int min_min = (v_min >= v_max) ? INT_MIN : v_min; int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0); bool value_changed = DragInt("##min", v_current_min, v_speed, min_min, min_max, format, min_flags); PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); int max_max = (v_min >= v_max) ? INT_MAX : v_max; ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0); value_changed |= DragInt("##max", v_current_max, v_speed, max_min, max_max, format_max ? format_max : format, max_flags); PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); return value_changed; } //------------------------------------------------------------------------- // [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. //------------------------------------------------------------------------- // - ScaleRatioFromValueT<> [Internal] // - ScaleValueFromRatioT<> [Internal] // - SliderBehaviorT<>() [Internal] // - SliderBehavior() [Internal] // - SliderScalar() // - SliderScalarN() // - SliderFloat() // - SliderFloat2() // - SliderFloat3() // - SliderFloat4() // - SliderAngle() // - SliderInt() // - SliderInt2() // - SliderInt3() // - SliderInt4() // - VSliderScalar() // - VSliderFloat() // - VSliderInt() //------------------------------------------------------------------------- // Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT) template float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) { if (v_min == v_max) return 0.0f; IM_UNUSED(data_type); const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); if (logarithmic_zero_epsilon > 0.0f) // == is_logarithmic from caller { bool flipped = v_max < v_min; if (flipped) // Handle the case where the range is backwards ImSwap(v_min, v_max); // Fudge min/max to avoid getting close to log(0) FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min; FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max; // Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon) if ((v_min == 0.0f) && (v_max < 0.0f)) v_min_fudged = -logarithmic_zero_epsilon; else if ((v_max == 0.0f) && (v_min < 0.0f)) v_max_fudged = -logarithmic_zero_epsilon; float result; if (v_clamped <= v_min_fudged) result = 0.0f; // Workaround for values that are in-range but below our fudge else if (v_clamped >= v_max_fudged) result = 1.0f; // Workaround for values that are in-range but above our fudge else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions { float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space. There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine) float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize; if (v == 0.0f) result = zero_point_center; // Special case for exactly zero else if (v < 0.0f) result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L; else result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R)); } else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged)); else result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged)); return flipped ? (1.0f - result) : result; } else { // Linear slider return (float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min)); } } // Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT) template TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) { // We special-case the extents because otherwise our logarithmic fudging can lead to "mathematically correct" // but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler. if (t <= 0.0f || v_min == v_max) return v_min; if (t >= 1.0f) return v_max; TYPE result = (TYPE)0; if (logarithmic_zero_epsilon > 0.0f) // == is_logarithmic from caller { // Fudge min/max to avoid getting silly results close to zero FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min; FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max; const bool flipped = v_max < v_min; // Check if range is "backwards" if (flipped) ImSwap(v_min_fudged, v_max_fudged); // Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon) if ((v_max == 0.0f) && (v_min < 0.0f)) v_max_fudged = -logarithmic_zero_epsilon; float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts { float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize; if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R) result = (TYPE)0.0f; // Special case to make getting exactly zero possible (the epsilon prevents it otherwise) else if (t_with_flip < zero_point_center) result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L)))); else result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R)))); } else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip))); else result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip)); } else { // Linear slider const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); if (is_floating_point) { result = ImLerp(v_min, v_max, t); } else if (t < 1.0) { // - For integer values we want the clicking position to match the grab box so we round above // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. // - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64 // range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits. FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) * t; result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5))); } } return result; } // FIXME: Try to move more of the code into shared SliderBehavior() template bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) { ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it. // Calculate bounds const float grab_padding = 2.0f; // FIXME: Should be part of style. const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; float grab_sz = style.GrabMinSize; if (!is_floating_point && v_range_f >= 0.0f) // v_range_f < 0 may happen on integer overflows grab_sz = ImMax(slider_sz / (v_range_f + 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 = bb.Min[axis] + grab_padding + grab_sz * 0.5f; const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f; float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true float zero_deadzone_halfsize = 0.0f; // Only valid when is_logarithmic is true if (is_logarithmic) { // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f); } // Process interacting with the slider bool value_changed = false; if (g.ActiveId == id) { bool set_new_value = false; float clicked_t = 0.0f; if (g.ActiveIdSource == ImGuiInputSource_Mouse) { if (!g.IO.MouseDown[0]) { ClearActiveID(); } else { const float mouse_abs_pos = g.IO.MousePos[axis]; if (g.ActiveIdIsJustActivated) { float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); 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); const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here. g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f; } if (slider_usable_sz > 0.0f) clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); if (axis == ImGuiAxis_Y) clicked_t = 1.0f - clicked_t; set_new_value = true; } } else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) { if (g.ActiveIdIsJustActivated) { g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation g.SliderCurrentAccumDirty = false; } float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis); if (input_delta != 0.0f) { const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; if (decimal_precision > 0) { input_delta /= 100.0f; // Keyboard/Gamepad tweak speeds in % of slider bounds if (tweak_slow) input_delta /= 10.0f; } else { if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow) input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Keyboard/Gamepad tweak speeds in integer steps else input_delta /= 100.0f; } if (tweak_fast) input_delta *= 10.0f; g.SliderCurrentAccum += input_delta; g.SliderCurrentAccumDirty = true; } float delta = g.SliderCurrentAccum; if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) { ClearActiveID(); } else if (g.SliderCurrentAccumDirty) { clicked_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits { set_new_value = false; g.SliderCurrentAccum = 0.0f; // If pushing up against the limits, don't continue to accumulate } else { set_new_value = true; float old_clicked_t = clicked_t; clicked_t = ImSaturate(clicked_t + delta); // Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) v_new = RoundScalarWithFormatT(format, data_type, v_new); float new_clicked_t = ScaleRatioFromValueT(data_type, v_new, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); if (delta > 0) g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta); else g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta); } g.SliderCurrentAccumDirty = false; } } if (set_new_value) if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) set_new_value = false; if (set_new_value) { TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); // Round to user desired precision based on format string if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) v_new = RoundScalarWithFormatT(format, data_type, v_new); // Apply result if (*v != v_new) { *v = v_new; value_changed = true; } } } if (slider_sz < 1.0f) { *out_grab_bb = ImRect(bb.Min, bb.Min); } else { // Output grab position so it can be displayed by the caller float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); 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 (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); } return value_changed; } // For 32-bit and larger types, slider bounds are limited to half the natural type range. // So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. // It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) { // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); IM_ASSERT((flags & ImGuiSliderFlags_WrapAround) == 0); // Not supported by SliderXXX(), only by DragXXX() switch (data_type) { case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min, *(const ImU8*)p_max, format, flags, out_grab_bb); if (r) *(ImU8*)p_v = (ImU8)v32; return r; } case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; } case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; } case ImGuiDataType_S32: IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2); return SliderBehaviorT(bb, id, data_type, (ImS32*)p_v, *(const ImS32*)p_min, *(const ImS32*)p_max, format, flags, out_grab_bb); case ImGuiDataType_U32: IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2); return SliderBehaviorT(bb, id, data_type, (ImU32*)p_v, *(const ImU32*)p_min, *(const ImU32*)p_max, format, flags, out_grab_bb); case ImGuiDataType_S64: IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2); return SliderBehaviorT(bb, id, data_type, (ImS64*)p_v, *(const ImS64*)p_min, *(const ImS64*)p_max, format, flags, out_grab_bb); case ImGuiDataType_U64: IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2); return SliderBehaviorT(bb, id, data_type, (ImU64*)p_v, *(const ImU64*)p_min, *(const ImU64*)p_max, format, flags, out_grab_bb); case ImGuiDataType_Float: IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f); return SliderBehaviorT(bb, id, data_type, (float*)p_v, *(const float*)p_min, *(const float*)p_max, format, flags, out_grab_bb); case ImGuiDataType_Double: IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f); return SliderBehaviorT(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, flags, out_grab_bb); case ImGuiDataType_COUNT: break; } IM_ASSERT(0); return false; } // Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required. // Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0; 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 total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { // Tabbing or Ctrl+Click on Slider turns it into an input box const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); const bool make_active = (clicked || g.NavActivateId == id); if (make_active && clicked) SetKeyOwner(ImGuiKey_MouseLeft, id); if (make_active && temp_input_allowed) if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) temp_input_is_active = true; // Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert) if (make_active) memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size); if (make_active && !temp_input_is_active) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); } } if (temp_input_is_active) { // Only clamp Ctrl+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) const bool clamp_enabled = (flags & ImGuiSliderFlags_ClampOnInput) != 0; // Don't use TempInputIsClampEnabled() return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL); } // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding); if (color_marker != 0 && style.ColorMarkerSize > 0.0f) RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding); RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding); // Slider behavior ImRect grab_bb; const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags, &grab_bb); if (value_changed) MarkItemEdited(id); // Render grab if (grab_bb.Max.x > grab_bb.Min.x) window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); 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, frame_bb.Min.y + style.FramePadding.y), label); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0)); return value_changed; } // Add multiple sliders on 1 line for compact edition of multiple components bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, ImGuiSliderFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; bool value_changed = false; BeginGroup(); PushID(label); PushMultiItemsWidths(components, CalcItemWidth()); size_t type_size = GDataTypeInfo[data_type].Size; for (int i = 0; i < components; i++) { PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); if (flags & ImGuiSliderFlags_ColorMarkers) SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]); value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags); PopID(); PopItemWidth(); v = (void*)((char*)v + type_size); } PopID(); const char* label_end = FindRenderedTextEnd(label); if (label != label_end) { SameLine(0, g.Style.ItemInnerSpacing.x); TextEx(label, label_end); } EndGroup(); return value_changed; } bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags); } bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags); } bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags); } bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags); } bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) { 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, format, flags); if (value_changed) *v_rad = v_deg * (2 * IM_PI) / 360.0f; return value_changed; } bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); } bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags); } bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags); } bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags); } bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(bb, style.FramePadding.y); if (!ItemAdd(frame_bb, id)) return false; // Default format string when passing NULL if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); if (clicked || g.NavActivateId == id) { if (clicked) SetKeyOwner(ImGuiKey_MouseLeft, id); SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); } // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); // Slider behavior ImRect grab_bb; const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb); if (value_changed) MarkItemEdited(id); // Render grab if (grab_bb.Max.y > grab_bb.Min.y) window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // For the vertical slider we allow centered text to overlap the frame padding char value_buf[64]; const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); return value_changed; } bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags); } bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); } //------------------------------------------------------------------------- // [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. //------------------------------------------------------------------------- // - ImParseFormatFindStart() [Internal] // - ImParseFormatFindEnd() [Internal] // - ImParseFormatTrimDecorations() [Internal] // - ImParseFormatSanitizeForPrinting() [Internal] // - ImParseFormatSanitizeForScanning() [Internal] // - ImParseFormatPrecision() [Internal] // - TempInputTextScalar() [Internal] // - InputScalar() // - InputScalarN() // - InputFloat() // - InputFloat2() // - InputFloat3() // - InputFloat4() // - InputInt() // - InputInt2() // - InputInt3() // - InputInt4() // - InputDouble() //------------------------------------------------------------------------- // We don't use strchr() because our strings are usually very short and often start with '%' const char* ImParseFormatFindStart(const char* fmt) { while (char c = fmt[0]) { if (c == '%' && fmt[1] != '%') return fmt; else if (c == '%') fmt++; fmt++; } return fmt; } const char* ImParseFormatFindEnd(const char* fmt) { // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format. if (fmt[0] != '%') return fmt; const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A')); const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a')); for (char c; (c = *fmt) != 0; fmt++) { if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0) return fmt + 1; if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0) return fmt + 1; } return fmt; } // Extract the format out of a format string with leading or trailing decorations // fmt = "blah blah" -> return "" // 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, size_t buf_size) { const char* fmt_start = ImParseFormatFindStart(fmt); if (fmt_start[0] != '%') return ""; 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((size_t)(fmt_end - fmt_start) + 1, buf_size)); return buf; } // Sanitize format // - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi // - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size) { const char* fmt_end = ImParseFormatFindEnd(fmt_in); IM_UNUSED(fmt_out_size); IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! while (fmt_in < fmt_end) { char c = *fmt_in++; if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. *(fmt_out++) = c; } *fmt_out = 0; // Zero-terminate } // - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d" const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size) { const char* fmt_end = ImParseFormatFindEnd(fmt_in); const char* fmt_out_begin = fmt_out; IM_UNUSED(fmt_out_size); IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! bool has_type = false; while (fmt_in < fmt_end) { char c = *fmt_in++; if (!has_type && ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '#')) continue; has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. *(fmt_out++) = c; } *fmt_out = 0; // Zero-terminate return fmt_out_begin; } template static const char* ImAtoi(const char* src, TYPE* output) { int negative = 0; if (*src == '-') { negative = 1; src++; } if (*src == '+') { src++; } TYPE v = 0; while (*src >= '0' && *src <= '9') v = (v * 10) + (*src++ - '0'); *output = negative ? -v : v; return src; } // Parse display precision back from the display format string // FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. int ImParseFormatPrecision(const char* fmt, int default_precision) { fmt = ImParseFormatFindStart(fmt); if (fmt[0] != '%') return default_precision; fmt++; while (*fmt >= '0' && *fmt <= '9') fmt++; int precision = INT_MAX; if (*fmt == '.') { fmt = ImAtoi(fmt + 1, &precision); if (precision < 0 || precision > 99) precision = default_precision; } if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation precision = -1; if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) precision = -1; return (precision == INT_MAX) ? default_precision : precision; } // Create text input in place of another active widget (e.g. used when doing a Ctrl+Click on drag/slider widgets) // FIXME: Facilitate using this in variety of other situations. // FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but // the expected relationship between TempInputXXX functions and LastItemData is a little fishy. bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) { // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. // We clear ActiveID on the first frame to allow the InputText() taking it back. ImGuiContext& g = *GImGui; const bool init = (g.TempInputId != id); if (init) ClearActiveID(); g.CurrentWindow->DC.CursorPos = bb.Min; g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); if (init) { // First frame we started displaying the InputText widget, we expect it to take the active id. IM_ASSERT(g.ActiveId == id); g.TempInputId = g.ActiveId; } return value_changed; } // Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set! // This is intended: this way we allow Ctrl+Click manual input to set a value out of bounds, for maximum flexibility. // However this may not be ideal for all uses, as some user code may break on out of bound values. bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) { // FIXME: May need to clarify display behavior if format doesn't contain %. // "%d" -> "%d" / "There are %d items" -> "%d" / "items" -> "%d" (fallback). Also see #6405 ImGuiContext& g = *GImGui; const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); char fmt_buf[32]; char data_buf[32]; format = ImParseFormatTrimDecorations(format, fmt_buf, IM_COUNTOF(fmt_buf)); if (format[0] == 0) format = type_info->PrintFmt; DataTypeFormatString(data_buf, IM_COUNTOF(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; // Because TempInputText() uses ImGuiInputTextFlags_MergedItem it doesn't submit a new item, so we poke LastItemData. if (!TempInputText(bb, id, label, data_buf, IM_COUNTOF(data_buf), flags)) return false; // Backup old value size_t data_type_size = type_info->Size; ImGuiDataTypeStorage data_backup; memcpy(&data_backup, p_data, data_type_size); // Apply new value (or operations) then clamp DataTypeApplyFromText(data_buf, data_type, p_data, format, NULL); if (p_clamp_min || p_clamp_max) { if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) ImSwap(p_clamp_min, p_clamp_max); DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max); } // Only mark as edited if new value is different g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited; bool value_changed = memcmp(&data_backup, p_data, data_type_size) != 0; if (value_changed) MarkItemEdited(id); return value_changed; } void ImGui::SetNextItemRefVal(ImGuiDataType data_type, void* p_data) { ImGuiContext& g = *GImGui; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasRefVal; memcpy(&g.NextItemData.RefVal, p_data, DataTypeGetInfo(data_type)->Size); } // Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional. // Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()! if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; void* p_data_default = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue; char buf[64]; if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0) buf[0] = 0; else DataTypeFormatString(buf, IM_COUNTOF(buf), data_type, p_data, format); // Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited. // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. g.NextItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; bool value_changed = false; if (p_step == NULL) { if (InputText(label, buf, IM_COUNTOF(buf), flags)) value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); } else { const float button_size = GetFrameHeight(); BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() PushID(label); SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); if (InputText("", buf, IM_COUNTOF(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); // Step buttons const ImVec2 backup_frame_padding = style.FramePadding; style.FramePadding.x = style.FramePadding.y; if (flags & ImGuiInputTextFlags_ReadOnly) BeginDisabled(); PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("-", ImVec2(button_size, button_size))) { DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); value_changed = true; } SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("+", ImVec2(button_size, button_size))) { DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); value_changed = true; } PopItemFlag(); if (flags & ImGuiInputTextFlags_ReadOnly) EndDisabled(); const char* label_end = FindRenderedTextEnd(label); if (label != label_end) { SameLine(0, style.ItemInnerSpacing.x); TextEx(label, label_end); } style.FramePadding = backup_frame_padding; PopID(); EndGroup(); } g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited; if (value_changed) MarkItemEdited(g.LastItemData.ID); return value_changed; } bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; bool value_changed = false; BeginGroup(); PushID(label); PushMultiItemsWidths(components, CalcItemWidth()); size_t type_size = GDataTypeInfo[data_type].Size; for (int i = 0; i < components; i++) { PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); value_changed |= InputScalar("", data_type, p_data, p_step, p_step_fast, format, flags); PopID(); PopItemWidth(); p_data = (void*)((char*)p_data + type_size); } PopID(); const char* label_end = FindRenderedTextEnd(label); if (label != label_end) { SameLine(0.0f, g.Style.ItemInnerSpacing.x); TextEx(label, label_end); } EndGroup(); return value_changed; } bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags) { 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 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 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 flags) { return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, 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 = (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 flags) { return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags); } bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags) { return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags); } bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags 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 flags) { 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, InputTextWithHint //------------------------------------------------------------------------- // - imstb_textedit.h include // - InputText() // - InputTextWithHint() // - InputTextMultiline() // - InputTextEx() [Internal] // - DebugNodeInputTextState() [Internal] //------------------------------------------------------------------------- namespace ImStb { #include "imstb_textedit.h" } // If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp! 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, 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, 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() or InputTextEx() manually if you need multi-line + hint. return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); } static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags) { ImGuiContext& g = *ctx; ImGuiInputTextState* obj = &g.InputTextState; IM_ASSERT(text_end_display >= text_begin && text_end_display <= text_end); return ImFontCalcTextSizeEx(g.Font, g.FontSize, FLT_MAX, obj->WrapWidth, text_begin, text_end_display, text_end, out_remaining, out_offset, flags); } // 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) // With our UTF-8 use of stb_textedit: // - STB_TEXTEDIT_GETCHAR is nothing more than a a "GETBYTE". It's only used to compare to ascii or to copy blocks of text so we are fine. // - One exception is the STB_TEXTEDIT_IS_SPACE feature which would expect a full char in order to handle full-width space such as 0x3000 (see ImCharIsBlankW). // - ...but we don't use that feature. namespace ImStb { static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; } static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx >= 0 && idx <= obj->TextLen); return obj->TextSrc[idx]; } static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.FontBaked->GetCharAdvance((ImWchar)c) * g.FontBakedScale; } static char STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { const char* text = obj->TextSrc; const char* text_remaining = NULL; const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, text + obj->TextLen, &text_remaining, NULL, ImDrawTextFlags_StopOnNewLine | ImDrawTextFlags_WrapKeepBlanks); r->x0 = 0.0f; r->x1 = size.x; r->baseline_y_delta = size.y; r->ymin = 0.0f; r->ymax = size.y; r->num_chars = (int)(text_remaining - (text + line_start_idx)); } #define IMSTB_TEXTEDIT_GETNEXTCHARINDEX IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL #define IMSTB_TEXTEDIT_GETPREVCHARINDEX IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx) { if (idx >= obj->TextLen) return obj->TextLen + 1; unsigned int c; return idx + ImTextCharFromUtf8(&c, obj->TextSrc + idx, obj->TextSrc + obj->TextLen); } static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx) { if (idx <= 0) return -1; const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, obj->TextSrc + idx); return (int)(p - obj->TextSrc); } static bool ImCharIsSeparatorW(unsigned int c) { static const unsigned int separator_list[] = { ',', 0x3001, '.', 0x3002, ';', 0xFF1B, '(', 0xFF08, ')', 0xFF09, '{', 0xFF5B, '}', 0xFF5D, '[', 0x300C, ']', 0x300D, '|', 0xFF5C, '!', 0xFF01, '\\', 0xFFE5, '/', 0x30FB, 0xFF0F, '\n', '\r', }; for (unsigned int separator : separator_list) if (c == separator) return true; return false; } static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { // When ImGuiInputTextFlags_Password is set, we don't want actions such as Ctrl+Arrow to leak the fact that underlying data are blanks or separators. if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) return 0; const char* curr_p = obj->TextSrc + idx; const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p); unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextSrc + obj->TextLen); unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextSrc + obj->TextLen); bool prev_white = ImCharIsBlankW(prev_c); bool prev_separ = ImCharIsSeparatorW(prev_c); bool curr_white = ImCharIsBlankW(curr_c); bool curr_separ = ImCharIsSeparatorW(curr_c); return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); } static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) return 0; const char* curr_p = obj->TextSrc + idx; const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p); unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextSrc + obj->TextLen); unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextSrc + obj->TextLen); bool prev_white = ImCharIsBlankW(prev_c); bool prev_separ = ImCharIsSeparatorW(prev_c); bool curr_white = ImCharIsBlankW(curr_c); bool curr_separ = ImCharIsSeparatorW(curr_c); return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); } static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx); while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx); return idx < 0 ? 0 : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { int len = obj->TextLen; idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); while (idx < len && !is_word_boundary_from_left(obj, idx)) idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); return idx > len ? len : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); int len = obj->TextLen; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); return idx > len ? len : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); } #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL // Reimplementation of stb_textedit_move_line_start()/stb_textedit_move_line_end() which supports word-wrapping. static int STB_TEXTEDIT_MOVELINESTART_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor) { if (state->single_line) return 0; if (obj->WrapWidth > 0.0f) { ImGuiContext& g = *obj->Ctx; const char* p_cursor = obj->TextSrc + cursor; const char* p_bol = ImStrbol(p_cursor, obj->TextSrc); const char* p = p_bol; const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough while (p >= p_bol) { const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks); if (p == p_cursor) // If we are already on a visible beginning-of-line, return real beginning-of-line (would be same as regular handler below) return (int)(p_bol - obj->TextSrc); if (p_eol == p_cursor && obj->TextA[cursor] != '\n' && obj->LastMoveDirectionLR == ImGuiDir_Left) return (int)(p_bol - obj->TextSrc); if (p_eol >= p_cursor) return (int)(p - obj->TextSrc); p = (*p_eol == '\n') ? p_eol + 1 : p_eol; } } // Regular handler, same as stb_textedit_move_line_start() while (cursor > 0) { int prev_cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, cursor); if (STB_TEXTEDIT_GETCHAR(obj, prev_cursor) == STB_TEXTEDIT_NEWLINE) break; cursor = prev_cursor; } return cursor; } static int STB_TEXTEDIT_MOVELINEEND_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor) { int n = STB_TEXTEDIT_STRINGLEN(obj); if (state->single_line) return n; if (obj->WrapWidth > 0.0f) { ImGuiContext& g = *obj->Ctx; const char* p_cursor = obj->TextSrc + cursor; const char* p = ImStrbol(p_cursor, obj->TextSrc); const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough while (p < text_end) { const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks); cursor = (int)(p_eol - obj->TextSrc); if (p_eol == p_cursor && obj->LastMoveDirectionLR != ImGuiDir_Left) // If we are already on a visible end-of-line, switch to regular handle break; if (p_eol > p_cursor) return cursor; p = (*p_eol == '\n') ? p_eol + 1 : p_eol; } } // Regular handler, same as stb_textedit_move_line_end() while (cursor < n && STB_TEXTEDIT_GETCHAR(obj, cursor) != STB_TEXTEDIT_NEWLINE) cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, cursor); return cursor; } #define STB_TEXTEDIT_MOVELINESTART STB_TEXTEDIT_MOVELINESTART_IMPL #define STB_TEXTEDIT_MOVELINEEND STB_TEXTEDIT_MOVELINEEND_IMPL static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { // Offset remaining text (+ copy zero terminator) IM_ASSERT(obj->TextSrc == obj->TextA.Data); char* dst = obj->TextA.Data + pos; char* src = obj->TextA.Data + pos + n; memmove(dst, src, obj->TextLen - n - pos + 1); obj->Edited = true; obj->TextLen -= n; } static int STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len) { const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int text_len = obj->TextLen; IM_ASSERT(pos <= text_len); // We support partial insertion (with a mod in stb_textedit.h) const int avail = obj->BufCapacity - 1 - obj->TextLen; if (!is_resizable && new_text_len > avail) new_text_len = (int)(ImTextFindValidUtf8CodepointEnd(new_text, new_text + new_text_len, new_text + avail) - new_text); // Truncate to closest UTF-8 codepoint. Alternative: return 0 to cancel insertion. if (new_text_len == 0) return 0; // Grow internal buffer if needed IM_ASSERT(obj->TextSrc == obj->TextA.Data); if (text_len + new_text_len + 1 > obj->TextA.Size && is_resizable) { obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1); obj->TextSrc = obj->TextA.Data; } char* text = obj->TextA.Data; if (pos != text_len) memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos)); memcpy(text + pos, new_text, (size_t)new_text_len); obj->Edited = true; obj->TextLen += new_text_len; obj->TextA[obj->TextLen] = '\0'; return new_text_len; } // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) #define STB_TEXTEDIT_K_LEFT 0x200000 // keyboard input to move cursor left #define STB_TEXTEDIT_K_RIGHT 0x200001 // keyboard input to move cursor right #define STB_TEXTEDIT_K_UP 0x200002 // keyboard input to move cursor up #define STB_TEXTEDIT_K_DOWN 0x200003 // keyboard input to move cursor down #define STB_TEXTEDIT_K_LINESTART 0x200004 // keyboard input to move cursor to start of line #define STB_TEXTEDIT_K_LINEEND 0x200005 // keyboard input to move cursor to end of line #define STB_TEXTEDIT_K_TEXTSTART 0x200006 // keyboard input to move cursor to start of text #define STB_TEXTEDIT_K_TEXTEND 0x200007 // keyboard input to move cursor to end of text #define STB_TEXTEDIT_K_DELETE 0x200008 // keyboard input to delete selection or character under cursor #define STB_TEXTEDIT_K_BACKSPACE 0x200009 // keyboard input to delete selection or character left of cursor #define STB_TEXTEDIT_K_UNDO 0x20000A // keyboard input to perform undo #define STB_TEXTEDIT_K_REDO 0x20000B // keyboard input to perform redo #define STB_TEXTEDIT_K_WORDLEFT 0x20000C // keyboard input to move cursor left one word #define STB_TEXTEDIT_K_WORDRIGHT 0x20000D // keyboard input to move cursor right one word #define STB_TEXTEDIT_K_PGUP 0x20000E // keyboard input to move cursor up a page #define STB_TEXTEDIT_K_PGDOWN 0x20000F // keyboard input to move cursor down a page #define STB_TEXTEDIT_K_SHIFT 0x400000 #define IMSTB_TEXTEDIT_IMPLEMENTATION #define IMSTB_TEXTEDIT_memmove memmove #include "imstb_textedit.h" // stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling // the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len) { stb_text_makeundo_replace(str, state, 0, str->TextLen, text_len); ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->TextLen); state->cursor = state->select_start = state->select_end = 0; if (text_len <= 0) return; int text_len_inserted = ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len); if (text_len_inserted > 0) { state->cursor = state->select_start = state->select_end = text_len; state->has_preferred_x = 0; return; } IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace() } } // namespace ImStb // We added an extra indirection where 'Stb' is heap-allocated, in order facilitate the work of bindings generators. ImGuiInputTextState::ImGuiInputTextState() { memset((void*)this, 0, sizeof(*this)); Stb = IM_NEW(ImStbTexteditState); memset(Stb, 0, sizeof(*Stb)); } ImGuiInputTextState::~ImGuiInputTextState() { IM_DELETE(Stb); } void ImGuiInputTextState::OnKeyPressed(int key) { stb_textedit_key(this, Stb, key); CursorFollow = true; CursorAnimReset(); const int key_u = (key & ~STB_TEXTEDIT_K_SHIFT); if (key_u == STB_TEXTEDIT_K_LEFT || key_u == STB_TEXTEDIT_K_LINESTART || key_u == STB_TEXTEDIT_K_TEXTSTART || key_u == STB_TEXTEDIT_K_BACKSPACE || key_u == STB_TEXTEDIT_K_WORDLEFT) LastMoveDirectionLR = ImGuiDir_Left; else if (key_u == STB_TEXTEDIT_K_RIGHT || key_u == STB_TEXTEDIT_K_LINEEND || key_u == STB_TEXTEDIT_K_TEXTEND || key_u == STB_TEXTEDIT_K_DELETE || key_u == STB_TEXTEDIT_K_WORDRIGHT) LastMoveDirectionLR = ImGuiDir_Right; } void ImGuiInputTextState::OnCharPressed(unsigned int c) { // Convert the key to a UTF8 byte sequence. // The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great. char utf8[5]; ImTextCharToUtf8(utf8, c); stb_textedit_text(this, Stb, utf8, (int)ImStrlen(utf8)); CursorFollow = true; CursorAnimReset(); } // Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header. void ImGuiInputTextState::CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking void ImGuiInputTextState::CursorClamp() { Stb->cursor = ImMin(Stb->cursor, TextLen); Stb->select_start = ImMin(Stb->select_start, TextLen); Stb->select_end = ImMin(Stb->select_end, TextLen); } bool ImGuiInputTextState::HasSelection() const { return Stb->select_start != Stb->select_end; } void ImGuiInputTextState::ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; } int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; } int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; } int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; } void ImGuiInputTextState::SetSelection(int start, int end) { Stb->select_start = start; Stb->cursor = Stb->select_end = end; } float ImGuiInputTextState::GetPreferredOffsetX() const { return Stb->has_preferred_x ? Stb->preferred_x : -1; } void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; } void ImGuiInputTextState::ReloadUserBufAndSelectAll() { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { WantReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() { memset((void*)this, 0, sizeof(*this)); } // Public API to manipulate UTF-8 text from within a callback. // FIXME: The existence of this rarely exercised code path is a bit of a nuisance. // Historically they existed because STB_TEXTEDIT_INSERTCHARS() etc. worked on our ImWchar // buffer, but nowadays they both work on UTF-8 data. Should aim to merge both. void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) { IM_ASSERT(pos + bytes_count <= BufTextLen); char* dst = Buf + pos; const char* src = Buf + pos + bytes_count; memmove(dst, src, BufTextLen - bytes_count - pos + 1); if (CursorPos >= pos + bytes_count) CursorPos -= bytes_count; else if (CursorPos >= pos) CursorPos = pos; SelectionStart = SelectionEnd = CursorPos; BufDirty = true; BufTextLen -= bytes_count; } void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) { // Accept null ranges if (new_text == new_text_end) return; ImGuiContext& g = *Ctx; ImGuiInputTextState* obj = &g.InputTextState; IM_ASSERT(obj->ID != 0 && g.ActiveId == obj->ID); const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; const bool is_readonly = (Flags & ImGuiInputTextFlags_ReadOnly) != 0; int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text); // We support partial insertion (with a mod in stb_textedit.h) const int avail = BufSize - 1 - BufTextLen; if (!is_resizable && new_text_len > avail) new_text_len = (int)(ImTextFindValidUtf8CodepointEnd(new_text, new_text + new_text_len, new_text + avail) - new_text); // Truncate to closest UTF-8 codepoint. Alternative: return 0 to cancel insertion. if (new_text_len == 0) return; // Grow internal buffer if needed if (new_text_len + BufTextLen + 1 > obj->TextA.Size && is_resizable && !is_readonly) { IM_ASSERT(Buf == obj->TextA.Data); int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; obj->TextA.resize(new_buf_size + 1); obj->TextSrc = obj->TextA.Data; Buf = obj->TextA.Data; BufSize = obj->BufCapacity = new_buf_size; } if (BufTextLen != pos) memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); Buf[BufTextLen + new_text_len] = '\0'; BufDirty = true; BufTextLen += new_text_len; if (CursorPos >= pos) CursorPos += new_text_len; CursorPos = ImClamp(CursorPos, 0, BufTextLen); SelectionStart = SelectionEnd = CursorPos; } void ImGui::PushPasswordFont() { ImGuiContext& g = *GImGui; ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked; IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0); ImFontGlyph* glyph = g.FontBaked->FindGlyph('*'); g.InputTextPasswordFontBackupFlags = g.Font->Flags; backup->FallbackGlyphIndex = g.FontBaked->FallbackGlyphIndex; backup->FallbackAdvanceX = g.FontBaked->FallbackAdvanceX; backup->IndexLookup.swap(g.FontBaked->IndexLookup); backup->IndexAdvanceX.swap(g.FontBaked->IndexAdvanceX); g.Font->Flags |= ImFontFlags_NoLoadGlyphs; g.FontBaked->FallbackGlyphIndex = g.FontBaked->Glyphs.index_from_ptr(glyph); g.FontBaked->FallbackAdvanceX = glyph->AdvanceX; } void ImGui::PopPasswordFont() { ImGuiContext& g = *GImGui; ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked; g.Font->Flags = g.InputTextPasswordFontBackupFlags; g.FontBaked->FallbackGlyphIndex = backup->FallbackGlyphIndex; g.FontBaked->FallbackAdvanceX = backup->FallbackAdvanceX; g.FontBaked->IndexLookup.swap(backup->IndexLookup); g.FontBaked->IndexAdvanceX.swap(backup->IndexAdvanceX); IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0); } // Return false to discard a character. static bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* state, unsigned int* p_char, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) { unsigned int c = *p_char; ImGuiInputTextFlags flags = state->Flags; // Filter non-printable (NB: isprint is unreliable! see #2467) bool apply_named_filters = true; if (c < 0x20) { bool pass = false; pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code) if (c == '\n' && input_source_is_clipboard && (flags & ImGuiInputTextFlags_Multiline) == 0) // In single line mode, replace \n with a space { c = *p_char = ' '; pass = true; } pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; pass |= (c == '\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0; if (!pass) return false; apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. } if (input_source_is_clipboard == false) { // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) if (c == 127) return false; // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) if (c >= 0xE000 && c <= 0xF8FF) return false; } // Filter Unicode ranges we are not handling in this build if (c > IM_UNICODE_CODEPOINT_MAX) return false; // Generic named filters if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint))) { // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. // Change the default decimal_point with: // ImGui::GetPlatformIO()->Platform_LocaleDecimalPoint = *localeconv()->decimal_point; // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. ImGuiContext& g = *ctx; const unsigned c_decimal_point = (unsigned int)g.PlatformIO.Platform_LocaleDecimalPoint; if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)) if (c == '.' || c == ',') c = c_decimal_point; // Full-width -> half-width conversion for numeric fields: https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may // scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font. if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal)) if (c >= 0xFF01 && c <= 0xFF5E) c = c - 0xFF01 + 0x21; // Allow 0-9 . - + * / if (flags & ImGuiInputTextFlags_CharsDecimal) if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/')) return false; // Allow 0-9 . - + * / e E if (flags & ImGuiInputTextFlags_CharsScientific) if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) return false; // Allow 0-9 a-F A-F if (flags & ImGuiInputTextFlags_CharsHexadecimal) if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) return false; // Turn a-z into A-Z if (flags & ImGuiInputTextFlags_CharsUppercase) if (c >= 'a' && c <= 'z') c += (unsigned int)('A' - 'a'); if (flags & ImGuiInputTextFlags_CharsNoBlank) if (ImCharIsBlankW(c)) return false; *p_char = c; } // Custom callback filter if (flags & ImGuiInputTextFlags_CallbackCharFilter) { ImGuiContext& g = *GImGui; ImGuiInputTextCallbackData callback_data; callback_data.Ctx = &g; callback_data.ID = state->ID; callback_data.Flags = flags; callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; callback_data.EventChar = (ImWchar)c; callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.UserData = user_data; if (callback(&callback_data) != 0) return false; *p_char = callback_data.EventChar; if (!callback_data.EventChar) return false; } return true; } // Find the shortest single replacement we can make to get from old_buf to new_buf // Note that this doesn't directly alter state->TextA, state->TextLen. They are expected to be made valid separately. // FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly. static void InputTextReconcileUndoState(ImGuiInputTextState* state, const char* old_buf, int old_length, const char* new_buf, int new_length) { const int shorter_length = ImMin(old_length, new_length); int first_diff; for (first_diff = 0; first_diff < shorter_length; first_diff++) if (old_buf[first_diff] != new_buf[first_diff]) break; if (first_diff == old_length && first_diff == new_length) return; int old_last_diff = old_length - 1; int new_last_diff = new_length - 1; for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--) if (old_buf[old_last_diff] != new_buf[new_last_diff]) break; const int insert_len = new_last_diff - first_diff + 1; const int delete_len = old_last_diff - first_diff + 1; if (insert_len > 0 || delete_len > 0) if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len)) for (int i = 0; i < delete_len; i++) p[i] = old_buf[first_diff + i]; } // As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables) // we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714) // It would be more desirable that we discourage users from taking advantage of the "user not retaining data" trick, // but that more likely be attractive when we do have _NoLiveEdit flag available. void ImGui::InputTextDeactivateHook(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiInputTextState* state = &g.InputTextState; if (id == 0 || state->ID != id) return; g.InputTextDeactivatedState.ID = state->ID; if (state->Flags & ImGuiInputTextFlags_ReadOnly) { g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat. } else { IM_ASSERT(state->TextA.Data != 0); IM_ASSERT(state->TextA[state->TextLen] == 0); g.InputTextDeactivatedState.TextA.resize(state->TextLen + 1); memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->TextLen + 1); } } static int* ImLowerBound(int* in_begin, int* in_end, int v) { int* in_p = in_begin; for (size_t count = (size_t)(in_end - in_p); count > 0; ) { size_t count2 = count >> 1; int* mid = in_p + count2; if (*mid < v) { in_p = ++mid; count -= count2 + 1; } else { count = count2; } } return in_p; } // FIXME-WORDWRAP: Bundle some of this into ImGuiTextIndex and/or extract as a different tool? // 'max_output_buffer_size' happens to be a meaningful optimization to avoid writing the full line_index when not necessarily needed (e.g. very large buffer, scrolled up, inactive) static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, float wrap_width, int max_output_buffer_size, const char** out_buf_end) { ImGuiContext& g = *GImGui; int size = 0; const char* s; if (flags & ImGuiInputTextFlags_WordWrap) { for (s = buf; s < buf_end; s = (*s == '\n') ? s + 1 : s) { if (size++ <= max_output_buffer_size) line_index->Offsets.push_back((int)(s - buf)); s = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, s, buf_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks); } } else if (buf_end != NULL) { for (s = buf; s < buf_end; s = s ? s + 1 : buf_end) { if (size++ <= max_output_buffer_size) line_index->Offsets.push_back((int)(s - buf)); s = (const char*)ImMemchr(s, '\n', buf_end - s); } } else { const char* s_eol; for (s = buf; ; s = s_eol + 1) { if (size++ <= max_output_buffer_size) line_index->Offsets.push_back((int)(s - buf)); if ((s_eol = strchr(s, '\n')) != NULL) continue; s += strlen(s); break; } } if (out_buf_end != NULL) *out_buf_end = buf_end = s; if (size == 0) { line_index->Offsets.push_back(0); size++; } if (buf_end > buf && buf_end[-1] == '\n' && size <= max_output_buffer_size) { line_index->Offsets.push_back((int)(buf_end - buf)); size++; } return size; } static ImVec2 InputTextLineIndexGetPosOffset(ImGuiContext& g, ImGuiInputTextState* state, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, int cursor_n) { const char* cursor_ptr = buf + cursor_n; int* it_begin = line_index->Offsets.begin(); int* it_end = line_index->Offsets.end(); const int* it = ImLowerBound(it_begin, it_end, cursor_n); if (it > it_begin) if (it == it_end || *it != cursor_n || (state != NULL && state->WrapWidth > 0.0f && state->LastMoveDirectionLR == ImGuiDir_Right && cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0)) it--; const int line_no = (it == it_begin) ? 0 : line_index->Offsets.index_from_ptr(it); const char* line_start = line_index->get_line_begin(buf, line_no); ImVec2 offset; offset.x = InputTextCalcTextSize(&g, line_start, cursor_ptr, buf_end, NULL, NULL, ImDrawTextFlags_WrapKeepBlanks).x; offset.y = (line_no + 1) * g.FontSize; return offset; } // 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 // 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 InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp! 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) return false; IM_ASSERT(buf != NULL && buf_size >= 0); IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline))); // Multiline does not not work with left-trimming IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Password) == 0); // WordWrap does not work with Password mode. IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Multiline) != 0); // WordWrap does not work in single-line mode. ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; const ImGuiStyle& style = g.Style; const bool RENDER_SELECTION_WHEN_INACTIVE = false; const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) BeginGroup(); const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size); ImGuiWindow* draw_window = window; ImVec2 inner_size = frame_size; ImGuiLastItemData item_data_backup; if (is_multiline) { ImVec2 backup_pos = window->DC.CursorPos; ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) { EndGroup(); return false; } item_data_backup = g.LastItemData; window->DC.CursorPos = backup_pos; // Prevent NavActivation from explicit Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping. if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && !(g.NavActivateFlags & ImGuiActivateFlags_FromFocusApi) && (flags & ImGuiInputTextFlags_AllowTabInput)) g.NavActivateId = 0; // Prevent NavActivate reactivating in BeginChild() when we are already active. const ImGuiID backup_activate_id = g.NavActivateId; if (g.ActiveId == id) // Prevent reactivation g.NavActivateId = 0; // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove); g.NavActivateId = backup_activate_id; PopStyleVar(3); PopStyleColor(); if (!child_visible) { EndChild(); EndGroup(); return false; } draw_window = g.CurrentWindow; // Child window draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. draw_window->DC.CursorPos += style.FramePadding; inner_size.x -= draw_window->ScrollbarSizes.x; } else { // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) ItemSize(total_bb, style.FramePadding.y); if (!(flags & ImGuiInputTextFlags_MergedItem)) if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) return false; } // Ensure mouse cursor is set even after switching to keyboard/gamepad mode. May generalize further? (#6417) bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags | ImGuiItemFlags_NoNavDisableMouseHover); if (hovered) SetMouseCursor(ImGuiMouseCursor_TextInput); if (hovered && g.NavHighlightItemUnderNav) hovered = false; // We are only allowed to access the state if we are already the active widget. ImGuiInputTextState* state = GetInputTextState(id); if (g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) flags |= ImGuiInputTextFlags_ReadOnly; 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! // Word-wrapping: enforcing a fixed width not altered by vertical scrollbar makes things easier, notably to track cursor reliably and avoid one-frame glitches. // Instead of using ImGuiWindowFlags_AlwaysVerticalScrollbar we account for that space if the scrollbar is not visible. const bool is_wordwrap = (flags & ImGuiInputTextFlags_WordWrap) != 0; float wrap_width = 0.0f; if (is_wordwrap) wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize)); const bool input_requested_by_nav = (g.ActiveId != id) && (g.NavActivateId == id); const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive const bool user_clicked = hovered && io.MouseClicked[0]; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); bool clear_active_id = false; bool select_all = false; float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf); const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state. const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_reactivate); const bool init_state = (init_make_active || user_scroll_active); if (init_reload_from_user_buf) { int new_len = (int)ImStrlen(buf); IM_ASSERT(new_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?"); state->WantReloadUserBuf = false; InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len); state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string. state->TextLen = new_len; memcpy(state->TextA.Data, buf, state->TextLen + 1); state->Stb->select_start = state->ReloadSelectionStart; state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; // will be clamped to bounds below } else if ((init_state && g.ActiveId != id) || init_changed_specs) { // Access state even if we don't own it yet. state = &g.InputTextState; state->CursorAnimReset(); // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714) InputTextDeactivateHook(state->ID); // Take a copy of the initial buffer value. // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode) const int buf_len = (int)ImStrlen(buf); IM_ASSERT(((buf_len + 1 <= buf_size) || (buf_len == 0 && buf_size == 0)) && "Is your input buffer properly zero-terminated?"); state->TextToRevertTo.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->TextToRevertTo.Data, buf, buf_len + 1); // Preserve cursor position and undo/redo stack if we come back to same widget // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate? bool recycle_state = (state->ID == id && !init_changed_specs); if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0))) recycle_state = false; // Start edition state->ID = id; state->TextLen = buf_len; if (!is_readonly) { state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string. memcpy(state->TextA.Data, buf, state->TextLen + 1); } // Find initial scroll position for right alignment state->Scroll = ImVec2(0.0f, 0.0f); if (flags & ImGuiInputTextFlags_ElideLeft) state->Scroll.x += ImMax(0.0f, CalcTextSize(buf).x - frame_size.x + style.FramePadding.x * 2.0f); // 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. if (!recycle_state) stb_textedit_initialize_state(state->Stb, !is_multiline); if (!is_multiline) { if (flags & ImGuiInputTextFlags_AutoSelectAll) select_all = true; if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) select_all = true; if (user_clicked && io.KeyCtrl) select_all = true; } if (flags & ImGuiInputTextFlags_AlwaysOverwrite) state->Stb->insert_mode = 1; // stb field name is indeed incorrect (see #2863) } const bool is_osx = io.ConfigMacOSXBehaviors; if (g.ActiveId != id && init_make_active) { IM_ASSERT(state && state->ID == id); SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); if (input_requested_by_nav) SetNavCursorVisibleAfterMove(); } if (g.ActiveId == id) { // Declare some inputs, the other are registered and polled via Shortcut() routing system. // FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combination into individual shortcuts. const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End }; for (ImGuiKey key : always_owned_keys) SetKeyOwner(key, id); if (user_clicked) SetKeyOwner(ImGuiKey_MouseLeft, id); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory)) { g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); SetKeyOwner(ImGuiKey_UpArrow, id); SetKeyOwner(ImGuiKey_DownArrow, id); } if (is_multiline) { SetKeyOwner(ImGuiKey_PageUp, id); SetKeyOwner(ImGuiKey_PageDown, id); } // FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu if (is_osx) SetKeyOwner(ImGuiMod_Alt, id); // Expose scroll in a manner that is agnostic to us using a child window if (is_multiline && state != NULL) state->Scroll.y = draw_window->Scroll.y; // Read-only mode always ever read from source buffer. Refresh TextLen when active. if (is_readonly && state != NULL) state->TextLen = (int)ImStrlen(buf); if (state != NULL) state->CursorClamp(); //if (is_readonly && state != NULL) // state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation. } if (state != NULL) state->TextSrc = is_readonly ? buf : state->TextA.Data; // 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; // Lock the decision of whether we are going to take the path displaying the cursor or selection bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); bool value_changed = false; bool validated = false; // Select the buffer to render. const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state; 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) PushPasswordFont(); // Word-wrapping: attempt to keep cursor in view while resizing frame/parent // FIXME-WORDWRAP: It would be better to preserve same relative offset. if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width) { state->CursorCenterY = true; state->WrapWidth = wrap_width; render_cursor = true; } // Process mouse inputs and character inputs if (g.ActiveId == id) { IM_ASSERT(state != NULL); state->Edited = false; state->BufCapacity = buf_size; state->Flags = flags; state->WrapWidth = wrap_width; // 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. g.ActiveIdAllowOverlap = !io.MouseDown[0]; // Edit in progress const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->Scroll.x; const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); if (select_all) { state->SelectAll(); state->SelectedAllMouseLock = true; } else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) { stb_textedit_click(state, state->Stb, mouse_x, mouse_y); const int multiclick_count = (io.MouseClickedCount[0] - 2); if ((multiclick_count % 2) == 0) { // Double-click: Select word // We always use the "Mac" word advance for double-click select vs Ctrl+Right which use the platform dependent variant: // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) == '\n'; if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol) state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); if (!STB_TEXT_HAS_SELECTION(state->Stb)) ImStb::stb_textedit_prep_selection_at_cursor(state->Stb); state->Stb->cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb->cursor); state->Stb->select_end = state->Stb->cursor; ImStb::stb_textedit_clamp(state, state->Stb); } else { // Triple-click: Select line const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n'; state->WrapWidth = 0.0f; // Temporarily disable wrapping so we use real line start. state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); state->WrapWidth = wrap_width; if (!is_eol && is_multiline) { ImSwap(state->Stb->select_start, state->Stb->select_end); state->Stb->cursor = state->Stb->select_end; } state->CursorFollow = false; } state->CursorAnimReset(); } else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { if (hovered) { if (io.KeyShift) stb_textedit_drag(state, state->Stb, mouse_x, mouse_y); else stb_textedit_click(state, state->Stb, mouse_x, mouse_y); state->CursorAnimReset(); } } else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) { stb_textedit_drag(state, state->Stb, mouse_x, mouse_y); state->CursorAnimReset(); state->CursorFollow = true; } if (state->SelectedAllMouseLock && !io.MouseDown[0]) state->SelectedAllMouseLock = false; // We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336) // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes) if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly) { if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat, id)) { unsigned int c = '\t'; // Insert TAB if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data)) state->OnCharPressed(c); } // FIXME: Implement Shift+Tab /* if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, ImGuiInputFlags_Repeat, id)) { } */ } // Process regular 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. const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl); if (io.InputQueueCharacters.Size > 0) { if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) for (int n = 0; n < io.InputQueueCharacters.Size; n++) { // Insert character if they pass filtering unsigned int c = (unsigned int)io.InputQueueCharacters[n]; if (c == '\t') // Skip Tab, see above. continue; if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data)) state->OnCharPressed(c); } // Consume characters io.InputQueueCharacters.resize(0); } } // Process other shortcuts/key-presses bool revert_edit = false; if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) { IM_ASSERT(state != NULL); const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1); state->Stb->row_count_per_page = row_count_per_page; const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use Ctrl+A and Ctrl+B: former would be handled by InputText) // Otherwise we could simply assume that we own the keys as we are active. const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat; const bool is_cut = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); const bool is_copy = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, 0, id)) && !is_password && (!is_multiline || state->HasSelection()); const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat, id)) && !is_readonly; const bool is_undo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable; const bool is_redo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable; const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0, id); // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool is_enter = Shortcut(ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiKey_KeypadEnter, f_repeat, id); const bool is_ctrl_enter = Shortcut(ImGuiMod_Ctrl | ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_KeypadEnter, f_repeat, id); const bool is_shift_enter = Shortcut(ImGuiMod_Shift | ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_KeypadEnter, f_repeat, id); const bool is_gamepad_validate = nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false); const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id)); // FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of. // FIXME-OSX: Missing support for Alt(option)+Right/Left = go to end of line, or next line if already in end of line. if (IsKeyPressed(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 (IsKeyPressed(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 (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(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 (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(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 (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; } else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } else if (IsKeyPressed(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } else if (IsKeyPressed(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) { if (!state->HasSelection()) { // OSX doesn't seem to have Super+Delete to delete until end-of-line, so we don't emulate that (as opposed to Super+Backspace) if (is_wordmove_key_down) state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); } state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly) { if (!state->HasSelection()) { if (is_wordmove_key_down) state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT); else if (is_osx && io.KeyCtrl && !io.KeyAlt && !io.KeySuper) state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT); } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } else if (is_enter || is_ctrl_enter || is_shift_enter || is_gamepad_validate) { // Determine if we turn Enter into a \n character bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; bool is_new_line = is_multiline && !is_gamepad_validate && (is_shift_enter || (is_enter && !ctrl_enter_for_new_line) || (is_ctrl_enter && ctrl_enter_for_new_line)); if (!is_new_line) { validated = clear_active_id = true; if (io.ConfigInputTextEnterKeepActive && !is_multiline) { // Queue reactivation, so that e.g. IsItemDeactivatedAfterEdit() will work. (#9001) state->SelectAll(); // No need to scroll g.InputTextReactivateId = id; // Mark for reactivation on next frame } } else if (!is_readonly) { // Insert new line unsigned int c = '\n'; if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data)) state->OnCharPressed(c); } } else if (is_cancel) { if (flags & ImGuiInputTextFlags_EscapeClearsAll) { if (state->TextA.Data[0] != 0) { revert_edit = true; } else { render_cursor = render_selection = false; clear_active_id = true; } } else { clear_active_id = revert_edit = true; render_cursor = render_selection = false; } } else if (is_undo || is_redo) { state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); state->ClearSelection(); } else if (is_select_all) { state->SelectAll(); state->CursorFollow = true; } else if (is_cut || is_copy) { // Cut, Copy if (g.PlatformIO.Platform_SetClipboardTextFn != NULL) { // SetClipboardText() only takes null terminated strings + state->TextSrc may point to read-only user buffer, so we need to make a copy. 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->TextLen; g.TempBuffer.reserve(ie - ib + 1); memcpy(g.TempBuffer.Data, state->TextSrc + ib, ie - ib); g.TempBuffer.Data[ie - ib] = 0; SetClipboardText(g.TempBuffer.Data); } if (is_cut) { if (!state->HasSelection()) state->SelectAll(); state->CursorFollow = true; stb_textedit_cut(state, state->Stb); } } else if (is_paste) { if (const char* clipboard = GetClipboardText()) { // Filter pasted buffer const int clipboard_len = (int)ImStrlen(clipboard); const char* clipboard_end = clipboard + clipboard_len; ImVector clipboard_filtered; clipboard_filtered.reserve(clipboard_len + 1); for (const char* s = clipboard; *s != 0; ) { unsigned int c; int in_len = ImTextCharFromUtf8(&c, s, clipboard_end); s += in_len; if (!InputTextFilterCharacter(&g, state, &c, callback, callback_user_data, true)) continue; char c_utf8[5]; ImTextCharToUtf8(c_utf8, c); int out_len = (int)ImStrlen(c_utf8); clipboard_filtered.resize(clipboard_filtered.Size + out_len); memcpy(clipboard_filtered.Data + clipboard_filtered.Size - out_len, c_utf8, out_len); } if (clipboard_filtered.Size > 0) // If everything was filtered, ignore the pasting operation { clipboard_filtered.push_back(0); stb_textedit_paste(state, state->Stb, clipboard_filtered.Data, clipboard_filtered.Size - 1); state->CursorFollow = true; } } } // Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame. render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); } // Process callbacks and apply result back to user's buffer. const char* apply_new_text = NULL; int apply_new_text_length = 0; if (g.ActiveId == id) { IM_ASSERT(state != NULL); if (revert_edit && !is_readonly) { if (flags & ImGuiInputTextFlags_EscapeClearsAll) { // Clear input IM_ASSERT(state->TextA.Data[0] != 0); apply_new_text = ""; apply_new_text_length = 0; value_changed = true; char empty_string = 0; stb_textedit_replace(state, state->Stb, &empty_string, 0); } else if (strcmp(state->TextA.Data, state->TextToRevertTo.Data) != 0) { apply_new_text = state->TextToRevertTo.Data; apply_new_text_length = state->TextToRevertTo.Size - 1; // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. // Push records into the undo stack so we can Ctrl+Z the revert operation itself value_changed = true; stb_textedit_replace(state, state->Stb, state->TextToRevertTo.Data, state->TextToRevertTo.Size - 1); } } // FIXME-OPT: We always reapply the live buffer back to the input buffer before clearing ActiveId, // even though strictly speaking it wasn't modified on this frame. Should mark dirty state from the stb_textedit callbacks. // If we do that, need to ensure that as special case, 'validated == true' also writes back. // This also allows the user to use InputText() without maintaining any user-side storage. // (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object // unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). const bool apply_edit_back_to_user_buffer = true;// !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); if (apply_edit_back_to_user_buffer) { // Apply current edited text immediately. // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer // User callback if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0) { IM_ASSERT(callback != NULL); // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. ImGuiInputTextFlags event_flag = 0; ImGuiKey event_key = ImGuiKey_None; if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, 0, id)) { event_flag = ImGuiInputTextFlags_CallbackCompletion; event_key = ImGuiKey_Tab; } else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow)) { event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_UpArrow; } else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow)) { event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_DownArrow; } else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited) { event_flag = ImGuiInputTextFlags_CallbackEdit; } else if (flags & ImGuiInputTextFlags_CallbackAlways) { event_flag = ImGuiInputTextFlags_CallbackAlways; } if (event_flag) { ImGuiInputTextCallbackData callback_data; callback_data.Ctx = &g; callback_data.ID = id; callback_data.Flags = flags; callback_data.EventFlag = event_flag; callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.UserData = callback_user_data; // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925 char* callback_buf = is_readonly ? buf : state->TextA.Data; IM_ASSERT(callback_buf == state->TextSrc); state->CallbackTextBackup.resize(state->TextLen + 1); memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1); callback_data.EventKey = event_key; callback_data.Buf = callback_buf; callback_data.BufTextLen = state->TextLen; callback_data.BufSize = state->BufCapacity; callback_data.BufDirty = false; callback_data.CursorPos = state->Stb->cursor; callback_data.SelectionStart = state->Stb->select_start; callback_data.SelectionEnd = state->Stb->select_end; // Call user code callback(&callback_data); // Read back what user may have modified callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == state->BufCapacity); IM_ASSERT(callback_data.Flags == flags); if (callback_data.BufDirty || callback_data.CursorPos != state->Stb->cursor) state->CursorFollow = true; state->Stb->cursor = ImClamp(callback_data.CursorPos, 0, callback_data.BufTextLen); state->Stb->select_start = ImClamp(callback_data.SelectionStart, 0, callback_data.BufTextLen); state->Stb->select_end = ImClamp(callback_data.SelectionEnd, 0, callback_data.BufTextLen); if (callback_data.BufDirty) { // Callback may update buffer and thus set buf_dirty even in read-only mode. IM_ASSERT(callback_data.BufTextLen == (int)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! InputTextReconcileUndoState(state, state->CallbackTextBackup.Data, state->CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen); state->TextLen = 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_readonly && strcmp(state->TextSrc, buf) != 0) { apply_new_text = state->TextSrc; apply_new_text_length = state->TextLen; value_changed = true; } } } // Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details) if (g.InputTextDeactivatedState.ID == id) { if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) { apply_new_text = g.InputTextDeactivatedState.TextA.Data; apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; value_changed = true; //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); } g.InputTextDeactivatedState.ID = 0; } // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) if (apply_new_text != NULL) { //// We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size //// of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used //// without any storage on user's side. IM_ASSERT(apply_new_text_length >= 0); if (is_resizable) { ImGuiInputTextCallbackData callback_data; callback_data.Ctx = &g; callback_data.ID = id; callback_data.Flags = flags; callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.Buf = buf; callback_data.BufTextLen = apply_new_text_length; callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); callback_data.UserData = callback_user_data; callback(&callback_data); buf = callback_data.Buf; buf_size = callback_data.BufSize; apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); IM_ASSERT(apply_new_text_length <= buf_size); } //IMGUI_DEBUG_PRINT("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); // 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, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) // Otherwise request text input ahead for next frame. if (g.ActiveId == id && clear_active_id) ClearActiveID(); // Render frame if (!is_multiline) { RenderNavCursor(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); } ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size if (is_multiline) clip_rect.ClipWith(draw_window->ClipRect); // 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; 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 // Display hint when contents is empty // At this point we need to handle the possibility that a callback could have modified the underlying buffer (#8368) const bool new_is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0); if (new_is_displaying_hint != is_displaying_hint) { if (is_password && !is_displaying_hint) PopPasswordFont(); is_displaying_hint = new_is_displaying_hint; if (is_password && !is_displaying_hint) PushPasswordFont(); } if (is_displaying_hint) { buf_display = hint; buf_display_end = hint + ImStrlen(hint); } else { if (render_cursor || render_selection || g.ActiveId == id) buf_display_end = buf_display + state->TextLen; //-V595 else if (is_multiline && !is_wordwrap) buf_display_end = NULL; // Inactive multi-line: end of buffer will be output by InputTextLineIndexBuild() special strchr() path. else buf_display_end = buf_display + ImStrlen(buf_display); } // Calculate visibility int line_visible_n0 = 0, line_visible_n1 = 1; if (is_multiline) CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1); // Build line index for easy data access (makes code below simpler and faster) ImGuiTextIndex* line_index = &g.InputTextLineIndex; line_index->Offsets.resize(0); int line_count = 1; if (is_multiline) { // If scrolling is expected to change build full index. // FIXME-OPT: Could append to index when new value of line_visible_n1 becomes bigger, see second call to CalcClipRectVisibleItemsY() below. bool will_scroll_y = state && ((state->CursorFollow && render_cursor) || (state->CursorCenterY && (render_cursor || render_selection))); line_count = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, will_scroll_y ? INT_MAX : line_visible_n1 + 1, buf_display_end ? NULL : &buf_display_end); } line_index->EndOffset = (int)(buf_display_end - buf_display); line_visible_n1 = ImMin(line_visible_n1, line_count); // Store text height (we don't need width) float text_size_y = line_count * g.FontSize; //GetForegroundDrawList()->AddRect(draw_pos + ImVec2(0, line_visible_n0 * g.FontSize), draw_pos + ImVec2(frame_size.x, line_visible_n1 * g.FontSize), IM_COL32(255, 0, 0, 255)); // Calculate blinking cursor position const ImVec2 cursor_offset = render_cursor && state ? InputTextLineIndexGetPosOffset(g, state, line_index, buf_display, buf_display_end, state->Stb->cursor) : ImVec2(0.0f, 0.0f); ImVec2 draw_scroll; // Render text. We currently only render selection when the widget is active or while scrolling. const ImU32 text_col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); if (render_cursor || render_selection) { // 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. IM_ASSERT(state != NULL); state->LineCount = line_count; // Scroll float new_scroll_y = scroll_y; if (render_cursor && state->CursorFollow) { // Horizontal scroll in chunks of quarter width if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) { const float scroll_increment_x = inner_size.x * 0.25f; const float visible_width = inner_size.x - style.FramePadding.x; if (cursor_offset.x < state->Scroll.x) state->Scroll.x = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); else if (cursor_offset.x - visible_width >= state->Scroll.x) state->Scroll.x = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x); } else { state->Scroll.x = 0.0f; } // Vertical scroll if (is_multiline) { // Test if cursor is vertically visible if (cursor_offset.y - g.FontSize < scroll_y) new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; } state->CursorFollow = false; } if (state->CursorCenterY) { if (is_multiline) new_scroll_y = cursor_offset.y - g.FontSize - (inner_size.y * 0.5f - style.FramePadding.y); state->CursorCenterY = false; render_cursor = false; } if (new_scroll_y != scroll_y) { const float scroll_max_y = ImMax((text_size_y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y); draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag draw_window->Scroll.y = scroll_y; CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1); line_visible_n1 = ImMin(line_visible_n1, line_count); } // Draw selection draw_scroll.x = state->Scroll.x; if (render_selection) { const 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. const 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. const float bg_offy_dn = is_multiline ? 0.0f : 2.0f; const float bg_eol_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines const char* text_selected_begin = buf_display + ImMin(state->Stb->select_start, state->Stb->select_end); const char* text_selected_end = buf_display + ImMax(state->Stb->select_start, state->Stb->select_end); for (int line_n = line_visible_n0; line_n < line_visible_n1; line_n++) { const char* p = line_index->get_line_begin(buf_display, line_n); const char* p_eol = line_index->get_line_end(buf_display, line_n); const bool p_eol_is_wrap = (p_eol < buf_display_end && *p_eol != '\n'); if (p_eol_is_wrap) p_eol++; const char* line_selected_begin = (text_selected_begin > p) ? text_selected_begin : p; const char* line_selected_end = (text_selected_end < p_eol) ? text_selected_end : p_eol; float rect_width = 0.0f; if (line_selected_begin < line_selected_end) rect_width += CalcTextSize(line_selected_begin, line_selected_end).x; if (text_selected_begin <= p_eol && text_selected_end > p_eol && !p_eol_is_wrap) rect_width += bg_eol_width; // So we can see selected empty lines if (rect_width == 0.0f) continue; ImRect rect; rect.Min.x = draw_pos.x - draw_scroll.x + CalcTextSize(p, line_selected_begin).x; rect.Min.y = draw_pos.y - draw_scroll.y + line_n * g.FontSize; rect.Max.x = rect.Min.x + rect_width; rect.Max.y = rect.Min.y + bg_offy_dn + g.FontSize; rect.Min.y -= bg_offy_up; rect.ClipWith(clip_rect); draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); } } } // Find render position for right alignment (single-line only) if (g.ActiveId != id && (flags & ImGuiInputTextFlags_ElideLeft) && !render_cursor && !render_selection) draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x); //draw_scroll.x = state->Scroll.x; // Preserve scroll when inactive? // Render text if ((is_multiline || (buf_display_end - buf_display) < buf_display_max_length) && (text_col & IM_COL32_A_MASK) && (line_visible_n0 < line_visible_n1)) g.Font->RenderText(draw_window->DrawList, g.FontSize, draw_pos - draw_scroll + ImVec2(0.0f, line_visible_n0 * g.FontSize), text_col, clip_rect.AsVec4(), line_index->get_line_begin(buf_display, line_visible_n0), line_index->get_line_end(buf_display, line_visible_n1 - 1), wrap_width, ImDrawTextFlags_WrapKeepBlanks | ImDrawTextFlags_CpuFineClip); // Render blinking cursor 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 = ImTrunc(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_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031) // 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.) // This is required for some backends (SDL3) to start emitting character/text inputs. // As per #6341, make sure we don't set that on the deactivating frame. if (!is_readonly && g.ActiveId == id) { ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler) ime_data->WantVisible = true; ime_data->WantTextInput = true; ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); ime_data->InputLineHeight = g.FontSize; ime_data->ViewportId = window->Viewport->ID; } } if (is_password && !is_displaying_hint) PopPasswordFont(); if (is_multiline) { // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)... Dummy(ImVec2(0.0f, text_size_y + style.FramePadding.y)); g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... // FIXME: This quite messy/tricky, should attempt to get rid of the child window. EndGroup(); if (g.LastItemData.ID == 0 || g.LastItemData.ID != GetWindowScrollbarID(draw_window, ImGuiAxis_Y)) { g.LastItemData.ID = id; g.LastItemData.ItemFlags = item_data_backup.ItemFlags; g.LastItemData.StatusFlags = item_data_backup.StatusFlags; } } if (state && is_readonly) state->TextSrc = NULL; // Log as text if (g.LogEnabled && (!is_password || is_displaying_hint)) { LogSetNextTextDecoration("{", "}"); 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); if (value_changed) MarkItemEdited(id); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) return validated; else return value_changed; } void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) { #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; ImStb::STB_TexteditState* stb_state = state->Stb; ImStb::StbUndoState* undo_state = &stb_state->undostate; Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); DebugLocateItemOnHover(state->ID); Text("TextLen: %d, Cursor: %d%s, Selection: %d..%d", state->TextLen, stb_state->cursor, (state->Flags & ImGuiInputTextFlags_WordWrap) ? (state->LastMoveDirectionLR == ImGuiDir_Left ? " (L)" : " (R)") : "", stb_state->select_start, stb_state->select_end); Text("BufCapacity: %d, LineCount: %d", state->BufCapacity, state->LineCount); Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity); Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state { PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); for (int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++) { ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n]; const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' '; if (undo_rec_type == ' ') BeginDisabled(); const int buf_preview_len = (undo_rec_type != ' ' && undo_rec->char_storage != -1) ? undo_rec->insert_length : 0; const char* buf_preview_str = undo_state->undo_char + undo_rec->char_storage; Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%.*s\"", undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf_preview_len, buf_preview_str); if (undo_rec_type == ' ') EndDisabled(); } PopStyleVar(); } EndChild(); #else IM_UNUSED(state); #endif } //------------------------------------------------------------------------- // [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. //------------------------------------------------------------------------- // - ColorEdit3() // - ColorEdit4() // - ColorPicker3() // - RenderColorRectWithAlphaCheckerboard() [Internal] // - ColorPicker4() // - ColorButton() // - SetColorEditOptions() // - ColorTooltip() [Internal] // - ColorEditOptionsPopup() [Internal] // - ColorPickerOptionsPopup() [Internal] //------------------------------------------------------------------------- bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) { return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); } static void ColorEditRestoreH(const float* col, float* H) { ImGuiContext& g = *GImGui; IM_ASSERT(g.ColorEditCurrentID != 0); if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) return; *H = g.ColorEditSavedHue; } // ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. // Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) { ImGuiContext& g = *GImGui; IM_ASSERT(g.ColorEditCurrentID != 0); if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) return; // When S == 0, H is undefined. // When H == 1 it wraps around to 0. if (*S == 0.0f || (*H == 0.0f && g.ColorEditSavedHue == 1)) *H = g.ColorEditSavedHue; // When V == 0, S is undefined. if (*V == 0.0f) *S = g.ColorEditSavedSat; } // Edit colors components (each component in 0.0f..1.0f range). // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. // With typical options: Left-click on color square to open color picker. Right-click to open option menu. Ctrl+Click over input fields to edit them and TAB to go to next item. bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const float square_sz = GetFrameHeight(); const char* label_display_end = FindRenderedTextEnd(label); float w_full = CalcItemWidth(); g.NextItemData.ClearFlags(); BeginGroup(); PushID(label); const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0); if (set_current_color_edit_id) g.ColorEditCurrentID = window->IDStack.back(); // 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_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_DisplayMask_)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_); if (!(flags & ImGuiColorEditFlags_DataTypeMask_)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_); if (!(flags & ImGuiColorEditFlags_PickerMask_)) flags |= (g.ColorEditOptions & 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; const int components = alpha ? 4 : 3; const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); const float w_inputs = ImMax(w_full - w_button, 1.0f); w_full = w_inputs + w_button; // Convert to the formats we need float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; 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)) { // Hue is lost when converting from grayscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); ColorEditRestoreHS(col, &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; const ImVec2 pos = window->DC.CursorPos; const float inputs_offset_x = (style.ColorButtonPosition == ImGuiDir_Left) ? w_button : 0.0f; window->DC.CursorPos.x = pos.x + inputs_offset_x; if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) { // RGB/HSV 0..255 Sliders const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1); const float w_per_component = IM_TRUNC(w_items / components); const bool draw_color_marker = (flags & (ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_NoColorMarkers)) == 0; const bool hide_prefix = draw_color_marker || (w_per_component <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); 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 }; 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_DisplayHSV) ? 2 : 1; const ImGuiSliderFlags drag_flags = draw_color_marker ? ImGuiSliderFlags_ColorMarkers : ImGuiSliderFlags_None; float prev_split = 0.0f; for (int n = 0; n < components; n++) { if (n > 0) SameLine(0, style.ItemInnerSpacing.x); float next_split = IM_TRUNC(w_items * (n + 1) / components); SetNextItemWidth(ImMax(next_split - prev_split, 1.0f)); prev_split = next_split; if (draw_color_marker) SetNextItemColorMarker(GDefaultRgbaColorMarkers[n]); // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0. if (flags & ImGuiColorEditFlags_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], drag_flags); 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], drag_flags); } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } } else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) { // RGB Hexadecimal Input char buf[64]; if (alpha) ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); else ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); SetNextItemWidth(w_inputs); if (InputText("##Text", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_CharsUppercase)) { value_changed = true; char* p = buf; while (*p == '#' || ImCharIsBlankA(*p)) p++; i[0] = i[1] = i[2] = 0; i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha) int r; if (alpha) r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) else r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } ImGuiWindow* picker_active_window = NULL; if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) { const float button_offset_x = ((flags & ImGuiColorEditFlags_NoInputs) || (style.ColorButtonPosition == ImGuiDir_Left)) ? 0.0f : w_inputs + style.ItemInnerSpacing.x; window->DC.CursorPos = ImVec2(pos.x + button_offset_x, pos.y); const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); if (ColorButton("##ColorButton", col_v4, flags)) { if (!(flags & ImGuiColorEditFlags_NoPicker)) { // Store current color and open a picker g.ColorPickerRef = col_v4; OpenPopup("picker"); SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y)); } } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); if (BeginPopup("picker")) { if (g.CurrentWindow->BeginCount == 1) { picker_active_window = g.CurrentWindow; if (label != label_display_end) { TextEx(label, label_display_end); Spacing(); } 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_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); } EndPopup(); } } if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) { // Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left), // but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this. SameLine(0.0f, style.ItemInnerSpacing.x); window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x); TextEx(label, label_display_end); } // Convert back 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_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB)) { g.ColorEditSavedHue = f[0]; g.ColorEditSavedSat = f[1]; ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); g.ColorEditSavedID = g.ColorEditCurrentID; g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); } 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]; } if (set_current_color_edit_id) g.ColorEditCurrentID = 0; PopID(); EndGroup(); // Drag and Drop Target // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) && !(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); // Preserve alpha if any //-V512 //-V1086 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 = 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(); } // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) g.LastItemData.ID = g.ActiveId; if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId MarkItemEdited(g.LastItemData.ID); return value_changed; } bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) { float col4[4] = { col[0], col[1], col[2], 1.0f }; if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) return false; col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; return true; } // Helper for ColorPicker4() static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w, float alpha) { ImU32 alpha8 = IM_F32_TO_INT8_SAT(alpha); ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32(0,0,0,alpha8)); ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32(255,255,255,alpha8)); ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32(0,0,0,alpha8)); ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32(255,255,255,alpha8)); } // 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..) // FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0) bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImDrawList* draw_list = window->DrawList; ImGuiStyle& style = g.Style; ImGuiIO& io = g.IO; const float width = CalcItemWidth(); const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0; g.NextItemData.ClearFlags(); PushID(label); const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0); if (set_current_color_edit_id) g.ColorEditCurrentID = window->IDStack.back(); BeginGroup(); if (!(flags & ImGuiColorEditFlags_NoSidePreview)) flags |= ImGuiColorEditFlags_NoSmallPreview; // Context menu: display and store options. if (!(flags & ImGuiColorEditFlags_NoOptions)) ColorPickerOptionsPopup(col, flags); // Read stored options if (!(flags & ImGuiColorEditFlags_PickerMask_)) flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; if (!(flags & ImGuiColorEditFlags_InputMask_)) flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & 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); // Setup int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); ImVec2 picker_pos = window->DC.CursorPos; float square_sz = GetFrameHeight(); float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f); float backup_initial_col[4]; memcpy(backup_initial_col, col, components * sizeof(float)); float wheel_thickness = sv_picker_size * 0.08f; float wheel_r_outer = sv_picker_size * 0.50f; float wheel_r_inner = wheel_r_outer - wheel_thickness; ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f); // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. 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 = col[0], S = col[1], V = col[2]; float R = col[0], G = col[1], B = col[2]; if (flags & ImGuiColorEditFlags_InputRGB) { // Hue is lost when converting from grayscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(R, G, B, H, S, V); ColorEditRestoreHS(col, &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; PushItemFlag(ImGuiItemFlags_NoNav, true); if (flags & ImGuiColorEditFlags_PickerHueWheel) { // Hue wheel + SV triangle logic InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); if (IsItemActive() && !is_readonly) { ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; ImVec2 current_off = g.IO.MousePos - wheel_center; float initial_dist2 = ImLengthSqr(initial_off); if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1)) { // Interactive with Hue wheel H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f; if (H < 0.0f) H += 1.0f; value_changed = value_changed_h = true; } float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) { // Interacting with SV triangle ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); float uu, vv, ww; ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); V = ImClamp(1.0f - vv, 0.0001f, 1.0f); S = ImClamp(uu / V, 0.0001f, 1.0f); value_changed = value_changed_sv = true; } } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } else if (flags & ImGuiColorEditFlags_PickerHueBar) { // SV rectangle logic InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); if (IsItemActive() && !is_readonly) { S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); ColorEditRestoreH(col, &H); // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. value_changed = value_changed_sv = true; } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); // Hue bar logic SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); if (IsItemActive() && !is_readonly) { H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); value_changed = value_changed_h = true; } } // Alpha bar logic if (alpha_bar) { SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); if (IsItemActive()) { col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); value_changed = true; } } PopItemFlag(); // ImGuiItemFlags_NoNav if (!(flags & ImGuiColorEditFlags_NoSidePreview)) { SameLine(0, style.ItemInnerSpacing.x); BeginGroup(); } if (!(flags & ImGuiColorEditFlags_NoLabel)) { const char* label_display_end = FindRenderedTextEnd(label); if (label != label_display_end) { if ((flags & ImGuiColorEditFlags_NoSidePreview)) SameLine(0, style.ItemInnerSpacing.x); TextEx(label, label_display_end); } } if (!(flags & ImGuiColorEditFlags_NoSidePreview)) { PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | 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 & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2))) { memcpy(col, ref_col, components * sizeof(float)); value_changed = true; } } PopItemFlag(); EndGroup(); } // Convert back color to RGB if (value_changed_h || value_changed_sv) { if (flags & ImGuiColorEditFlags_InputRGB) { ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); g.ColorEditSavedHue = H; g.ColorEditSavedSat = S; g.ColorEditSavedID = g.ColorEditCurrentID; g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); } 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_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) { // FIXME: Hackily differentiating 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_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 && (flags & ImGuiColorEditFlags_InputRGB)) { float new_H, new_S, new_V; ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); if (new_H <= 0 && H > 0) { if (new_V <= 0 && V != new_V) ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); else if (new_S <= 0) ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); } } if (value_changed) { if (flags & ImGuiColorEditFlags_InputRGB) { R = col[0]; G = col[1]; B = col[2]; ColorConvertRGBtoHSV(R, G, B, H, S, V); ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. } else if (flags & ImGuiColorEditFlags_InputHSV) { H = col[0]; S = col[1]; V = col[2]; ColorConvertHSVtoRGB(H, S, V, R, G, B); } } const int style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha); const ImU32 col_black = IM_COL32(0,0,0,style_alpha8); const ImU32 col_white = IM_COL32(255,255,255,style_alpha8); const ImU32 col_midgrey = IM_COL32(128,128,128,style_alpha8); const ImU32 col_hues[6 + 1] = { IM_COL32(255,0,0,style_alpha8), IM_COL32(255,255,0,style_alpha8), IM_COL32(0,255,0,style_alpha8), IM_COL32(0,255,255,style_alpha8), IM_COL32(0,0,255,style_alpha8), IM_COL32(255,0,255,style_alpha8), IM_COL32(255,0,0,style_alpha8) }; ImVec4 hue_color_f(1, 1, 1, style.Alpha); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); ImU32 user_col32_striped_of_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha)); // Important: this is still including the main rendering/style alpha!! ImVec2 sv_cursor_pos; if (flags & ImGuiColorEditFlags_PickerHueWheel) { // Render Hue Wheel const float aeps = 0.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); for (int n = 0; n < 6; n++) { const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; const int vert_start_idx = draw_list->VtxBuffer.Size; draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); draw_list->PathStroke(col_white, 0, wheel_thickness); const int vert_end_idx = draw_list->VtxBuffer.Size; // Paint colors over existing vertices ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner); ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner); ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]); } // Render Cursor + preview on Hue Wheel float cos_hue_angle = ImCos(H * 2.0f * IM_PI); float sin_hue_angle = ImSin(H * 2.0f * IM_PI); ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f); float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad); // Lock segment count so the +1 one matches others. draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments); draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments); // Render SV triangle (rotated according to hue) ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); ImVec2 uv_white = GetFontTexUvWhitePixel(); draw_list->PrimReserve(3, 3); draw_list->PrimVtx(tra, uv_white, hue_color32); draw_list->PrimVtx(trb, uv_white, col_black); draw_list->PrimVtx(trc, uv_white, col_white); draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f); sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); } else if (flags & ImGuiColorEditFlags_PickerHueBar) { // Render SV Square draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white); draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black); RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f); sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(S) * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); // Render Hue Bar for (int i = 0; i < 6; ++i) draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1]); float bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size); RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha); } // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) float sv_cursor_rad = value_changed_sv ? wheel_thickness * 0.55f : wheel_thickness * 0.40f; int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others. draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments); draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments); draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments); // Render alpha bar if (alpha_bar) { float alpha = ImSaturate(col[3]); ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, 0, bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, user_col32_striped_of_alpha & ~IM_COL32_A_MASK, user_col32_striped_of_alpha & ~IM_COL32_A_MASK); float bar1_line_y = IM_ROUND(picker_pos.y + (1.0f - alpha) * sv_picker_size); RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha); } EndGroup(); if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) value_changed = false; if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId MarkItemEdited(g.LastItemData.ID); if (set_current_color_edit_id) g.ColorEditCurrentID = 0; PopID(); return value_changed; } // A little color 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, const ImVec2& size_arg) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiID id = window->GetID(desc_id); const float default_size = GetFrameHeight(); const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); if (!ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); if (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque)) flags &= ~(ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf); 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.0f; if ((flags & ImGuiColorEditFlags_NoBorder) == 0) { 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_rgb.w < 1.0f) { float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f); if ((flags & ImGuiColorEditFlags_AlphaNoBg) == 0) RenderColorRectWithAlphaCheckerboard(window->DrawList, 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, ImDrawFlags_RoundCornersRight); else window->DrawList->AddRectFilled(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), rounding, ImDrawFlags_RoundCornersRight); window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft); } 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_AlphaOpaque) ? col_rgb_without_alpha : col_rgb; if (col_source.w < 1.0f && (flags & ImGuiColorEditFlags_AlphaNoBg) == 0) RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); else window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding); } RenderNavCursor(bb, id); if ((flags & ImGuiColorEditFlags_NoBorder) == 0) { if (g.Style.FrameBorderSize > 0.0f) RenderFrameBorder(bb.Min, bb.Max, rounding); else window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding, 0, 1.0f * (float)(int)g.Style._MainScale); // Color buttons are often in need of some sort of border // FIXME-DPI } // Drag and Drop Source // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test. if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) { if (flags & ImGuiColorEditFlags_NoAlpha) SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once); else SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once); ColorButton(desc_id, col, flags); SameLine(); TextEx("Color"); EndDragDropSource(); } // Tooltip if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_)); return pressed; } // Initialize/override default color options // FIXME: Could be moved to a simple IO field. void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; if ((flags & ImGuiColorEditFlags_InputMask_) == 0) flags |= ImGuiColorEditFlags_DefaultOptions_ & 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; } // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) return; const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) { TextEx(text, text_end); Separator(); } ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); 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]); ImGuiColorEditFlags flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_; ColorButton("##preview", cf, (flags & flags_to_forward) | ImGuiColorEditFlags_NoTooltip, sz); SameLine(); 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; PushItemFlag(ImGuiItemFlags_NoMarkEdited, true); ImGuiColorEditFlags opts = g.ColorEditOptions; if (allow_opt_inputs) { 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) { if (allow_opt_inputs) Separator(); if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8; if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float; } if (allow_opt_inputs || allow_opt_datatype) Separator(); if (Button("Copy as..", ImVec2(-1, 0))) OpenPopup("Copy"); if (BeginPopup("Copy")) { 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]); char buf[64]; ImFormatString(buf, IM_COUNTOF(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); if (Selectable(buf)) SetClipboardText(buf); ImFormatString(buf, IM_COUNTOF(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); if (Selectable(buf)) SetClipboardText(buf); ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X", cr, cg, cb); if (Selectable(buf)) SetClipboardText(buf); if (!(flags & ImGuiColorEditFlags_NoAlpha)) { ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca); if (Selectable(buf)) SetClipboardText(buf); } EndPopup(); } g.ColorEditOptions = opts; PopItemFlag(); EndPopup(); } void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) { 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) || !BeginPopup("context")) return; ImGuiContext& g = *GImGui; PushItemFlag(ImGuiItemFlags_NoMarkEdited, true); if (allow_opt_picker) { 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) 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 = GetCursorScreenPos(); if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); SetCursorScreenPos(backup_pos); ImVec4 previewing_ref_col; memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); ColorPicker4("##previewing_picker", &previewing_ref_col.x, picker_flags); PopID(); } PopItemWidth(); } if (allow_opt_alpha_bar) { if (allow_opt_picker) Separator(); CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); } PopItemFlag(); EndPopup(); } //------------------------------------------------------------------------- // [SECTION] Widgets: TreeNode, CollapsingHeader, etc. //------------------------------------------------------------------------- // - TreeNode() // - TreeNodeV() // - TreeNodeEx() // - TreeNodeExV() // - TreeNodeStoreStackData() [Internal] // - TreeNodeBehavior() [Internal] // - TreePush() // - TreePop() // - GetTreeNodeToLabelSpacing() // - SetNextItemOpen() // - CollapsingHeader() //------------------------------------------------------------------------- bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) { va_list args; va_start(args, fmt); bool is_open = TreeNodeExV(str_id, 0, fmt, args); va_end(args); return is_open; } bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) { va_list args; va_start(args, fmt); bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); va_end(args); return is_open; } bool ImGui::TreeNode(const char* label) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiID id = window->GetID(label); return TreeNodeBehavior(id, ImGuiTreeNodeFlags_None, label, NULL); } bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) { return TreeNodeExV(str_id, 0, fmt, args); } bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) { return TreeNodeExV(ptr_id, 0, fmt, args); } bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiID id = window->GetID(label); return TreeNodeBehavior(id, flags, label, NULL); } bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) { va_list args; va_start(args, fmt); bool is_open = TreeNodeExV(str_id, flags, fmt, args); va_end(args); return is_open; } bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) { va_list args; va_start(args, fmt); bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); va_end(args); return is_open; } bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiID id = window->GetID(str_id); const char* label, *label_end; ImFormatStringToTempBufferV(&label, &label_end, fmt, args); return TreeNodeBehavior(id, flags, label, label_end); } bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiID id = window->GetID(ptr_id); const char* label, *label_end; ImFormatStringToTempBufferV(&label, &label_end, fmt, args); return TreeNodeBehavior(id, flags, label, label_end); } // The reason those two functions are not yet in public API is because I would like to design a more feature-full and generic API for this. // They are otherwise function (cc: #3823, #9251, #7553, #6754, #5423, #2958, #2079, #1947, #1131, #722) bool ImGui::TreeNodeGetOpen(ImGuiID storage_id) { ImGuiContext& g = *GImGui; ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; return storage->GetInt(storage_id, 0) != 0; } void ImGui::TreeNodeSetOpen(ImGuiID storage_id, bool is_open) { ImGuiContext& g = *GImGui; ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; storage->SetInt(storage_id, is_open ? 1 : 0); } bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags) { // Leaf node always open a new tree/id scope. If you never use it, add ImGuiTreeNodeFlags_NoTreePushOnOpen. if (flags & ImGuiTreeNodeFlags_Leaf) return true; // We only write to the tree storage if the user clicks, or explicitly use the SetNextItemOpen function ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiStorage* storage = window->DC.StateStorage; bool is_open; if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasOpen) { if (g.NextItemData.OpenCond & ImGuiCond_Always) { is_open = g.NextItemData.OpenVal; TreeNodeSetOpen(storage_id, is_open); } else { // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. const int stored_value = storage->GetInt(storage_id, -1); if (stored_value == -1) { is_open = g.NextItemData.OpenVal; TreeNodeSetOpen(storage_id, is_open); } else { is_open = stored_value != 0; } } } else { is_open = storage->GetInt(storage_id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; } // 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.LogDepthRef) < g.LogDepthToExpand) is_open = true; return is_open; } // Store ImGuiTreeNodeStackData for just submitted node. // Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags, float x1) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1); ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1]; tree_node_data->ID = g.LastItemData.ID; tree_node_data->TreeFlags = flags; tree_node_data->ItemFlags = g.LastItemData.ItemFlags; tree_node_data->NavRect = g.LastItemData.NavRect; // Initially I tried to latch value for GetColorU32(ImGuiCol_TreeLines) but it's not a good trade-off for very large trees. const bool draw_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) != 0; tree_node_data->DrawLinesX1 = draw_lines ? (x1 + g.FontSize * 0.5f + g.Style.FramePadding.x) : +FLT_MAX; tree_node_data->DrawLinesTableColumn = (draw_lines && g.CurrentTable) ? (ImGuiTableColumnIdx)g.CurrentTable->CurrentColumn : -1; tree_node_data->DrawLinesToNodesY2 = -FLT_MAX; window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth); if (flags & ImGuiTreeNodeFlags_DrawLinesToNodes) window->DC.TreeRecordsClippedNodesY2Mask |= (1 << window->DC.TreeDepth); } bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; // When not framed, we vertically increase height up to typical framed widget height const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; const bool use_frame_padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)); const ImVec2 padding = use_frame_padding ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); if (!label_end) label_end = FindRenderedTextEnd(label); const ImVec2 label_size = CalcTextSize(label, label_end, false); const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing const float text_offset_y = use_frame_padding ? ImMax(style.FramePadding.y, window->DC.CurrLineTextBaseOffset) : window->DC.CurrLineTextBaseOffset; // Latch before ItemSize changes it const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow const float frame_height = label_size.y + padding.y * 2; const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL); const bool span_all_columns_label = (flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) != 0 && (g.CurrentTable != NULL); ImRect frame_bb; frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; frame_bb.Min.y = window->DC.CursorPos.y + (text_offset_y - padding.y); frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanLabelWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x; frame_bb.Max.y = window->DC.CursorPos.y + (text_offset_y - padding.y) + frame_height; if (display_frame) { const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits frame_bb.Min.x -= outer_extend; frame_bb.Max.x += outer_extend; } ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); ItemSize(ImVec2(text_width, frame_height), padding.y); // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing ImRect interact_bb = frame_bb; if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f); // Compute open and multi-select states before ItemAdd() as it clear NextItem data. ImGuiID storage_id = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasStorageID) ? g.NextItemData.StorageId : id; bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); bool is_visible; if (span_all_columns || span_all_columns_label) { // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. const float backup_clip_rect_min_x = window->ClipRect.Min.x; const float backup_clip_rect_max_x = window->ClipRect.Max.x; window->ClipRect.Min.x = window->ParentWorkRect.Min.x; window->ClipRect.Max.x = window->ParentWorkRect.Max.x; is_visible = ItemAdd(interact_bb, id); window->ClipRect.Min.x = backup_clip_rect_min_x; window->ClipRect.Max.x = backup_clip_rect_max_x; } else { is_visible = ItemAdd(interact_bb, id); } g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; g.LastItemData.DisplayRect = frame_bb; // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsToParent enabled: // Store data for the current depth to allow returning to this node from any child item. // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsToParent by default or move it to ImGuiStyle. bool store_tree_node_stack_data = false; if ((flags & ImGuiTreeNodeFlags_DrawLinesMask_) == 0) flags |= g.Style.TreeLinesFlags; const bool draw_tree_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) && (frame_bb.Min.y < window->ClipRect.Max.y) && (g.Style.TreeLinesSize > 0.0f); if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) { store_tree_node_stack_data = draw_tree_lines; if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsToParent) && !g.NavIdIsAlive) if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) store_tree_node_stack_data = true; } const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; if (!is_visible) { if ((flags & ImGuiTreeNodeFlags_DrawLinesToNodes) && (window->DC.TreeRecordsClippedNodesY2Mask & (1 << (window->DC.TreeDepth - 1)))) { ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1]; parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, window->DC.CursorPos.y); // Don't need to aim to mid Y position as we are clipped anyway. if (frame_bb.Min.y >= window->ClipRect.Max.y) window->DC.TreeRecordsClippedNodesY2Mask &= ~(1 << (window->DC.TreeDepth - 1)); // Done } if (is_open && store_tree_node_stack_data) TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } if (span_all_columns || span_all_columns_label) { TablePushBackgroundChannel(); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; g.LastItemData.ClipRect = window->ClipRect; } ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None; if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) button_flags |= ImGuiButtonFlags_AllowOverlap; if (!is_leaf) button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; // We allow clicking on the arrow section with keyboard modifiers held, in order to easily // allow browsing a tree while preserving selection with code implementing multi-selection patterns. // When clicking on the rest of the tree node we always disallow keyboard modifiers. const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x; const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes by default flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick : ImGuiTreeNodeFlags_OpenOnArrow; // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support. // - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0) // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=0) // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1) // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1) // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0) // It is rather standard that arrow click react on Down rather than Up. // We set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work. if (is_mouse_x_over_arrow) button_flags |= ImGuiButtonFlags_PressedOnClick; else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; else button_flags |= ImGuiButtonFlags_PressedOnClickRelease; if (flags & ImGuiTreeNodeFlags_NoNavFocus) button_flags |= ImGuiButtonFlags_NoNavFocus; bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; const bool was_selected = selected; // Multi-selection support (header) if (is_multi_select) { // Handle multi-select + alter button flags for it MultiSelectItemHeader(id, &selected, &button_flags); if (is_mouse_x_over_arrow) button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; } else { if (window != g.HoveredWindow || !is_mouse_x_over_arrow) button_flags |= ImGuiButtonFlags_NoKeyModsAllowed; } bool hovered, held; bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); bool toggled = false; if (!is_leaf) { if (pressed && g.DragDropHoldJustPressedId != id) { if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || (g.NavActivateId == id && !is_multi_select)) toggled = true; // Single click if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavHighlightItemUnderNav; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) toggled = true; // Double click } else if (pressed && g.DragDropHoldJustPressedId == id) { IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold); if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. toggled = true; else pressed = false; // Cancel press so it doesn't trigger selection. } if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) { toggled = true; NavClearPreferredPosForAxis(ImGuiAxis_X); NavMoveRequestCancel(); } if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? { toggled = true; NavClearPreferredPosForAxis(ImGuiAxis_X); NavMoveRequestCancel(); } if (toggled) { is_open = !is_open; window->DC.StateStorage->SetInt(storage_id, is_open); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } // Multi-selection support (footer) if (is_multi_select) { bool pressed_copy = pressed && !toggled; MultiSelectItemFooter(id, &selected, &pressed_copy); if (pressed) SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb); } if (selected != was_selected) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render { const ImU32 text_col = GetColorU32(ImGuiCol_Text); ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact; if (is_multi_select) nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle if (display_frame) { // Framed type const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); RenderNavCursor(frame_bb, id, nav_render_cursor_flags); if (span_all_columns && !span_all_columns_label) TablePopBackgroundChannel(); if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f); else // Leaf without bullet, left-adjusted text text_pos.x -= text_offset_x - padding.x; if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) frame_bb.Max.x -= g.FontSize + style.FramePadding.x; if (g.LogEnabled) LogSetNextTextDecoration("###", "###"); } else { // Unframed typed for tree nodes if (hovered || selected) { const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); } RenderNavCursor(frame_bb, id, nav_render_cursor_flags); if (span_all_columns && !span_all_columns_label) TablePopBackgroundChannel(); if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f); if (g.LogEnabled) LogSetNextTextDecoration(">", NULL); } if (draw_tree_lines) TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.5f)); // Label if (display_frame) RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); else RenderText(text_pos, label, label_end, false); if (span_all_columns_label) TablePopBackgroundChannel(); } if (is_open && store_tree_node_stack_data) TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } // Draw horizontal line from our parent node // This is only called for visible child nodes so we are not too fussy anymore about performances void ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->DC.TreeDepth == 0 || (window->DC.TreeHasStackDataDepthMask & (1 << (window->DC.TreeDepth - 1))) == 0) return; ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1]; float x1 = ImTrunc(parent_data->DrawLinesX1); float x2 = ImTrunc(target_pos.x - g.Style.ItemInnerSpacing.x); float y = ImTrunc(target_pos.y); float rounding = (g.Style.TreeLinesRounding > 0.0f) ? ImMin(x2 - x1, g.Style.TreeLinesRounding) : 0.0f; parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, y - rounding); if (x1 >= x2) return; if (rounding > 0.0f) { x1 += 0.5f + rounding; window->DrawList->PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3); if (x1 < x2) window->DrawList->PathLineTo(ImVec2(x2, y)); window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), ImDrawFlags_None, g.Style.TreeLinesSize); } else { window->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize); } } // Draw vertical line of the hierarchy void ImGui::TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float y1 = ImMax(data->NavRect.Max.y, window->ClipRect.Min.y); float y2 = data->DrawLinesToNodesY2; if (data->TreeFlags & ImGuiTreeNodeFlags_DrawLinesFull) { float y2_full = window->DC.CursorPos.y; if (g.CurrentTable) y2_full = ImMax(g.CurrentTable->RowPosY2, y2_full); y2_full = ImTrunc(y2_full - g.Style.ItemSpacing.y - g.FontSize * 0.5f); if (y2 + (g.Style.ItemSpacing.y + g.Style.TreeLinesRounding) < y2_full) // FIXME: threshold to use ToNodes Y2 instead of Full Y2 when close by ItemSpacing.y y2 = y2_full; } y2 = ImMin(y2, window->ClipRect.Max.y); if (y2 <= y1) return; float x = ImTrunc(data->DrawLinesX1); if (data->DrawLinesTableColumn != -1) TablePushColumnChannel(data->DrawLinesTableColumn); window->DrawList->AddLine(ImVec2(x, y1), ImVec2(x, y2), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize); if (data->DrawLinesTableColumn != -1) TablePopColumnChannel(); } void ImGui::TreePush(const char* str_id) { ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; PushID(str_id); } void ImGui::TreePush(const void* ptr_id) { ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; PushID(ptr_id); } void ImGui::TreePushOverrideID(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; Indent(); window->DC.TreeDepth++; PushOverrideID(id); } void ImGui::TreePop() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; Unindent(); window->DC.TreeDepth--; ImU32 tree_depth_mask = (1 << window->DC.TreeDepth); if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask) { const ImGuiTreeNodeStackData* data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1]; IM_ASSERT(data->ID == window->IDStack.back()); // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsToParent is enabled) if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsToParent) if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data); // Draw hierarchy lines if (data->DrawLinesX1 != +FLT_MAX && window->DC.CursorPos.y >= window->ClipRect.Min.y) TreeNodeDrawLineToTreePop(data); g.TreeNodeStack.pop_back(); window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask; window->DC.TreeRecordsClippedNodesY2Mask &= ~tree_depth_mask; } IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. PopID(); } // Horizontal distance preceding label when using TreeNode() or Bullet() float ImGui::GetTreeNodeToLabelSpacing() { ImGuiContext& g = *GImGui; return g.FontSize + (g.Style.FramePadding.x * 2.0f); } // Set next TreeNode/CollapsingHeader open state. void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond) { ImGuiContext& g = *GImGui; if (g.CurrentWindow->SkipItems) return; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasOpen; g.NextItemData.OpenVal = is_open; g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always); } // Set next TreeNode/CollapsingHeader storage id. void ImGui::SetNextItemStorageID(ImGuiID storage_id) { ImGuiContext& g = *GImGui; if (g.CurrentWindow->SkipItems) return; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasStorageID; g.NextItemData.StorageId = storage_id; } // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiID id = window->GetID(label); return TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label); } // p_visible == NULL : regular collapsing header // p_visible != NULL && *p_visible == true : show a small close button on the corner of the header, clicking the button will set *p_visible = false // p_visible != NULL && *p_visible == false : do not show the header at all // Do not mistake this with the Open state of the header itself, which you can adjust with SetNextItemOpen() or ImGuiTreeNodeFlags_DefaultOpen. bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; if (p_visible && !*p_visible) return false; ImGuiID id = window->GetID(label); flags |= ImGuiTreeNodeFlags_CollapsingHeader; if (p_visible) flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton; bool is_open = TreeNodeBehavior(id, flags, label); if (p_visible != NULL) { // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. ImGuiContext& g = *GImGui; ImGuiLastItemData last_item_backup = g.LastItemData; float button_size = g.FontSize; float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size); float button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y; ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id); if (CloseButton(close_button_id, ImVec2(button_x, button_y))) *p_visible = false; g.LastItemData = last_item_backup; } return is_open; } //------------------------------------------------------------------------- // [SECTION] Widgets: Selectable //------------------------------------------------------------------------- // - Selectable() //------------------------------------------------------------------------- // Tip: pass a non-visible label (e.g. "##hello") 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. // With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags. // FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported. bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. ImGuiID id = window->GetID(label); ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); ImVec2 pos = window->DC.CursorPos; pos.y += window->DC.CurrLineTextBaseOffset; ItemSize(size, 0.0f); // Fill horizontal space // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets. const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) size.x = ImMax(label_size.x, max_x - min_x); // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos. ImRect bb(min_x, pos.y, min_x + size.x, pos.y + size.y); if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; const float spacing_y = style.ItemSpacing.y; const float spacing_L = IM_TRUNC(spacing_x * 0.50f); const float spacing_U = IM_TRUNC(spacing_y * 0.50f); bb.Min.x -= spacing_L; bb.Min.y -= spacing_U; bb.Max.x += (spacing_x - spacing_L); bb.Max.y += (spacing_y - spacing_U); } //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; const ImGuiItemFlags extra_item_flags = disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None; bool is_visible; if (span_all_columns) { // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. const float backup_clip_rect_min_x = window->ClipRect.Min.x; const float backup_clip_rect_max_x = window->ClipRect.Max.x; window->ClipRect.Min.x = window->ParentWorkRect.Min.x; window->ClipRect.Max.x = window->ParentWorkRect.Max.x; is_visible = ItemAdd(bb, id, NULL, extra_item_flags); window->ClipRect.Min.x = backup_clip_rect_min_x; window->ClipRect.Max.x = backup_clip_rect_max_x; } else { is_visible = ItemAdd(bb, id, NULL, extra_item_flags); } const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (!is_visible) if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) // Extra layer of "no logic clip" for box-select support (would be more overhead to add to ItemAdd) return false; const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; if (disabled_item && !disabled_global) // Only testing this as an optimization BeginDisabled(); // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, // which would be advantageous since most selectable are not selected. if (span_all_columns) { if (g.CurrentTable) TablePushBackgroundChannel(); else if (window->DC.CurrentColumns) PushColumnsBackground(); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; g.LastItemData.ClipRect = window->ClipRect; } // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries ImGuiButtonFlags button_flags = 0; if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } if (flags & ImGuiSelectableFlags_NoSetKeyOwner) { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; } if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } // Multi-selection support (header) const bool was_selected = selected; if (is_multi_select) { // Handle multi-select + alter button flags for it MultiSelectItemHeader(id, &selected, &button_flags); } bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); bool auto_selected = false; // Multi-selection support (footer) if (is_multi_select) { MultiSelectItemFooter(id, &selected, &pressed); } else { // Auto-select when moved into // - This will be more fully fleshed in the range-select branch // - This is not exposed as it won't nicely work with some user side handling of shift/control // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) // - (2) usage will fail with clipped items // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId) if (g.NavJustMovedToId == id && (g.NavJustMovedToKeyMods & ImGuiMod_Ctrl) == 0) selected = pressed = auto_selected = true; } // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) { if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect) if (g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = false; } } if (pressed) MarkItemEdited(id); if (selected != was_selected) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render if (is_visible) { const bool highlighted = hovered || (flags & ImGuiSelectableFlags_Highlight); if (highlighted || selected) { // Between 1.91.0 and 1.91.4 we made selected Selectable use an arbitrary lerp between _Header and _HeaderHovered. Removed that now. (#8106) ImU32 col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); } if (g.NavId == id) { ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding; if (is_multi_select) nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle RenderNavCursor(bb, id, nav_render_cursor_flags); } } if (span_all_columns) { if (g.CurrentTable) TablePopBackgroundChannel(); else if (window->DC.CurrentColumns) PopColumnsBackground(); } // Text stays at the submission position. Alignment/clipping extents ignore SpanAllColumns. if (is_visible) RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups if (pressed && !auto_selected && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) CloseCurrentPopup(); if (disabled_item && !disabled_global) EndDisabled(); // Selectable() always returns a pressed state! // Users of BeginMultiSelect()/EndMultiSelect() scope: you may call ImGui::IsItemToggledSelection() to retrieve // selection toggle, only useful if you need that state updated (e.g. for rendering purpose) before reaching EndMultiSelect(). IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; //-V1020 } bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) { if (Selectable(label, *p_selected, flags, size_arg)) { *p_selected = !*p_selected; return true; } return false; } //------------------------------------------------------------------------- // [SECTION] Widgets: Typing-Select support //------------------------------------------------------------------------- // [Experimental] Currently not exposed in public API. // Consume character inputs and return search request, if any. // This would typically only be called on the focused window or location you want to grab inputs for, e.g. // if (ImGui::IsWindowFocused(...)) // if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest()) // focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1); // However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer). ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags) { ImGuiContext& g = *GImGui; ImGuiTypingSelectState* data = &g.TypingSelectState; ImGuiTypingSelectRequest* out_request = &data->Request; // Clear buffer const float TYPING_SELECT_RESET_TIMER = 1.80f; // FIXME: Potentially move to IO config. const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times if (data->SearchBuffer[0] != 0) { bool clear_buffer = false; clear_buffer |= (g.NavFocusScopeId != data->FocusScope); clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time); clear_buffer |= g.NavAnyRequest; clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter); clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0; //if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); } if (clear_buffer) data->Clear(); } // Append to buffer const int buffer_max_len = IM_COUNTOF(data->SearchBuffer) - 1; int buffer_len = (int)ImStrlen(data->SearchBuffer); bool select_request = false; for (ImWchar w : g.IO.InputQueueCharacters) { const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1); if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks continue; char w_buf[5]; ImTextCharToUtf8(w_buf, (unsigned int)w); if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0) { select_request = true; // Same character: don't need to append to buffer. continue; } if (data->SingleCharModeLock) { data->Clear(); // Different character: clear buffer_len = 0; } memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append buffer_len += w_len; select_request = true; } g.IO.InputQueueCharacters.resize(0); // Handle backspace if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, ImGuiInputFlags_Repeat)) { char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len); *p = 0; buffer_len = (int)(p - data->SearchBuffer); } // Return request if any if (buffer_len == 0) return NULL; if (select_request) { data->FocusScope = g.NavFocusScopeId; data->LastRequestFrame = g.FrameCount; data->LastRequestTime = (float)g.Time; } out_request->Flags = flags; out_request->SearchBufferLen = buffer_len; out_request->SearchBuffer = data->SearchBuffer; out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount); out_request->SingleCharMode = false; out_request->SingleCharSize = 0; // Calculate if buffer contains the same character repeated. // - This can be used to implement a special search mode on first character. // - Performed on UTF-8 codepoint for correctness. // - SingleCharMode is always set for first input character, because it usually leads to a "next". if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode) { const char* buf_begin = out_request->SearchBuffer; const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen; const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end); const char* p = buf_begin + c0_len; for (; p < buf_end; p += c0_len) if (memcmp(buf_begin, p, (size_t)c0_len) != 0) break; const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0; out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock); out_request->SingleCharSize = (ImS8)c0_len; data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode. } return out_request; } static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2) { int match_len = 0; while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++)) match_len++; return match_len; } // Default handler for finding a result for typing-select. You may implement your own. // You might want to display a tooltip to visualize the current request SearchBuffer // When SingleCharMode is set: // - it is better to NOT display a tooltip of other on-screen display indicator. // - the index of the currently focused item is required. // if your SetNextItemSelectionUserData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData. int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) { if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot. return -1; int idx = -1; if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode)) idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx); else idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data); if (idx != -1) SetNavCursorVisibleAfterMove(); return idx; } // Special handling when a single character is repeated: perform search on a single letter and goes to next. int ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) { // FIXME: Assume selection user data is index. Would be extremely practical. //if (nav_item_idx == -1) // nav_item_idx = (int)g.NavLastValidSelectionUserData; int first_match_idx = -1; bool return_next_match = false; for (int idx = 0; idx < items_count; idx++) { const char* item_name = get_item_name_func(user_data, idx); if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize) continue; if (return_next_match) // Return next matching item after current item. return idx; if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value. return idx; if (first_match_idx == -1) // Record first match for wrapping. first_match_idx = idx; if (nav_item_idx == idx) // Record that we encountering nav_item so we can return next match. return_next_match = true; } return first_match_idx; // First result } int ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data) { int longest_match_idx = -1; int longest_match_len = 0; for (int idx = 0; idx < items_count; idx++) { const char* item_name = get_item_name_func(user_data, idx); const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name); if (match_len <= longest_match_len) continue; longest_match_idx = idx; longest_match_len = match_len; if (match_len == req->SearchBufferLen) break; } return longest_match_idx; } void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) { #ifndef IMGUI_DISABLE_DEBUG_TOOLS Text("SearchBuffer = \"%s\"", data->SearchBuffer); Text("SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock); Text("LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame); #else IM_UNUSED(data); #endif } //------------------------------------------------------------------------- // [SECTION] Widgets: Box-Select support // This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet. //------------------------------------------------------------------------- // Extra logic in MultiSelectItemFooter() and ImGuiListClipper::Step() //------------------------------------------------------------------------- // - BoxSelectPreStartDrag() [Internal] // - BoxSelectActivateDrag() [Internal] // - BoxSelectDeactivateDrag() [Internal] // - BoxSelectScrollWithMouseDrag() [Internal] // - BeginBoxSelect() [Internal] // - EndBoxSelect() [Internal] //------------------------------------------------------------------------- // Call on the initial click. static void BoxSelectPreStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item) { ImGuiContext& g = *GImGui; ImGuiBoxSelectState* bs = &g.BoxSelectState; bs->ID = id; bs->IsStarting = true; // Consider starting box-select. bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); bs->IsStartedSetNavIdOnce = bs->IsStartedFromVoid; bs->KeyMods = g.IO.KeyMods; bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); bs->ScrollAccum = ImVec2(0.0f, 0.0f); } static void BoxSelectActivateDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window) { ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Activate\n", bs->ID); bs->IsActive = true; bs->Window = window; bs->IsStarting = false; ImGui::SetActiveID(bs->ID, window); ImGui::SetActiveIdUsingAllKeyboardKeys(); if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) bs->RequestClear = true; } static void BoxSelectDeactivateDrag(ImGuiBoxSelectState* bs) { ImGuiContext& g = *GImGui; bs->IsActive = bs->IsStarting = false; if (g.ActiveId == bs->ID) { IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Deactivate\n", bs->ID); ImGui::ClearActiveID(); } bs->ID = 0; } static void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window, const ImRect& inner_r) { ImGuiContext& g = *GImGui; IM_ASSERT(bs->Window == window); for (int n = 0; n < 2; n++) // each axis { const float mouse_pos = g.IO.MousePos[n]; const float dist = (mouse_pos > inner_r.Max[n]) ? mouse_pos - inner_r.Max[n] : (mouse_pos < inner_r.Min[n]) ? mouse_pos - inner_r.Min[n] : 0.0f; const float scroll_curr = window->Scroll[n]; if (dist == 0.0f || (dist < 0.0f && scroll_curr < 0.0f) || (dist > 0.0f && scroll_curr >= window->ScrollMax[n])) continue; const float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance const float scroll_step = g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime; bs->ScrollAccum[n] += scroll_step; // Accumulate into a stored value so we can handle high-framerate const float scroll_step_i = ImFloor(bs->ScrollAccum[n]); if (scroll_step_i == 0.0f) continue; if (n == 0) ImGui::SetScrollX(window, scroll_curr + scroll_step_i); else ImGui::SetScrollY(window, scroll_curr + scroll_step_i); bs->ScrollAccum[n] -= scroll_step_i; } } bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags) { ImGuiContext& g = *GImGui; ImGuiBoxSelectState* bs = &g.BoxSelectState; KeepAliveID(box_select_id); if (bs->ID != box_select_id) return false; // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. bs->UnclipMode = false; bs->RequestClear = false; if (bs->IsStarting && IsMouseDragPastThreshold(0)) BoxSelectActivateDrag(bs, window); else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false) BoxSelectDeactivateDrag(bs); if (!bs->IsActive) return false; // Current frame absolute prev/current rectangles are used to toggle selection. // They are derived from positions relative to scrolling space. ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already ImVec2 curr_end_pos_abs = g.IO.MousePos; if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) // Storing an extra rect used by widgets supporting box-select. if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) { bs->UnclipMode = true; bs->UnclipRect = bs->BoxSelectRectPrev; // FIXME-OPT: UnclipRect x coordinates could be intersection of Prev and Curr rect on X axis. bs->UnclipRect.Add(bs->BoxSelectRectCurr); } //GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); //GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); //GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f); return true; } void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiBoxSelectState* bs = &g.BoxSelectState; IM_ASSERT(bs->IsActive); bs->UnclipMode = false; // Render selection rectangle bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view ImRect box_select_r = bs->BoxSelectRectCurr; box_select_r.ClipWith(scope_rect); window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling // Scroll const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0; if (enable_scroll) { ImRect scroll_r = scope_rect; scroll_r.Expand(-g.FontSize); //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255)); if (!scroll_r.Contains(g.IO.MousePos)) BoxSelectScrollWithMouseDrag(bs, window, scroll_r); } } //------------------------------------------------------------------------- // [SECTION] Widgets: Multi-Select support //------------------------------------------------------------------------- // - DebugLogMultiSelectRequests() [Internal] // - CalcScopeRect() [Internal] // - BeginMultiSelect() // - EndMultiSelect() // - SetNextItemSelectionUserData() // - MultiSelectItemHeader() [Internal] // - MultiSelectItemFooter() [Internal] // - DebugNodeMultiSelectState() [Internal] //------------------------------------------------------------------------- static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) { ImGuiContext& g = *GImGui; IM_UNUSED(function); for (const ImGuiSelectionRequest& req : io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear"); if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d (dir %d)\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected, req.RangeDirection); } } static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) { // Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)); } else { // When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970) ImRect scope_rect = window->InnerClipRect; if (g.CurrentTable != NULL) scope_rect = g.CurrentTable->HostClipRect; // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect? scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max); return scope_rect; } } // Return ImGuiMultiSelectIO structure. // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). // Passing 'selection_size' and 'items_count' parameters is currently optional. // - 'selection_size' is useful to disable some shortcut routing: e.g. ImGuiMultiSelectFlags_ClearOnEscape won't claim Escape key when selection_size 0, // allowing a first press to clear selection THEN the second press to leave child window and return to parent. // - 'items_count' is stored in ImGuiMultiSelectIO which makes it a convenient way to pass the information to your ApplyRequest() handler (but you may pass it differently). // - If they are costly for you to compute (e.g. external intrusive selection without maintaining size), you may avoid them and pass -1. // - If you can easily tell if your selection is empty or not, you may pass 0/1, or you may enable ImGuiMultiSelectFlags_ClearOnEscape flag dynamically. ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size, int items_count) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (++g.MultiSelectTempDataStacked > g.MultiSelectTempData.Size) g.MultiSelectTempData.resize(g.MultiSelectTempDataStacked, ImGuiMultiSelectTempData()); ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1]; IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that. g.CurrentMultiSelect = ms; if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) flags |= ImGuiMultiSelectFlags_ScopeWindow; if (flags & ImGuiMultiSelectFlags_SingleSelect) flags &= ~(ImGuiMultiSelectFlags_BoxSelect2d | ImGuiMultiSelectFlags_BoxSelect1d); if (flags & ImGuiMultiSelectFlags_BoxSelect2d) flags &= ~ImGuiMultiSelectFlags_BoxSelect1d; // FIXME: Workaround to the fact we override CursorMaxPos, meaning size measurement are lost. (#8250) // They should perhaps be stacked properly? if (ImGuiTable* table = g.CurrentTable) if (table->CurrentColumn != -1) TableEndCell(table); // This is currently safe to call multiple time. If that properly is lost we can extract the "save measurement" part of it. // FIXME: BeginFocusScope() const ImGuiID id = window->IDStack.back(); ms->Clear(); ms->FocusScopeId = id; ms->Flags = flags; ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); ms->BackupCursorMaxPos = window->DC.CursorMaxPos; ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; PushFocusScope(ms->FocusScopeId); if (flags & ImGuiMultiSelectFlags_ScopeWindow) // Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items. window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. ms->KeyMods = g.NavJustMovedToId ? (g.NavJustMovedToIsTabbing ? 0 : g.NavJustMovedToKeyMods) : g.IO.KeyMods; if (flags & ImGuiMultiSelectFlags_NoRangeSelect) ms->KeyMods &= ~ImGuiMod_Shift; // Bind storage ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id); storage->ID = id; storage->LastFrameActive = g.FrameCount; storage->LastSelectionSize = selection_size; storage->Window = window; ms->Storage = storage; // Output to user ms->IO.Requests.resize(0); ms->IO.RangeSrcItem = storage->RangeSrcItem; ms->IO.NavIdItem = storage->NavIdItem; ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; ms->IO.ItemsCount = items_count; // Clear when using Navigation to move within the scope // (we compare FocusScopeId so it possible to use multiple selections inside a same window) bool request_clear = false; bool request_select_all = false; if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { if (ms->KeyMods & ImGuiMod_Shift) ms->IsKeyboardSetRange = true; if (ms->IsKeyboardSetRange) IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear? if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0) request_clear = true; } else if (g.NavJustMovedFromFocusScopeId == ms->FocusScopeId) { // Also clear on leaving scope (may be optional?) if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0) request_clear = true; } // Box-select handling: update active state. ImGuiBoxSelectState* bs = &g.BoxSelectState; if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) { ms->BoxSelectId = GetID("##BoxSelect"); if (BeginBoxSelect(CalcScopeRect(ms, window), window, ms->BoxSelectId, flags)) request_clear |= bs->RequestClear; } if (ms->IsFocused) { // Shortcut: Clear selection (Escape) // - Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window. // - Box select also handle Escape and needs to pass an id to bypass ActiveIdUsingAllKeyboardKeys lock. if (flags & ImGuiMultiSelectFlags_ClearOnEscape) { if (selection_size != 0 || bs->IsActive) if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_None, bs->IsActive ? bs->ID : 0)) { request_clear = true; if (bs->IsActive) BoxSelectDeactivateDrag(bs); } } // Shortcut: Select all (Ctrl+A) if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) request_select_all = true; } if (request_clear || request_select_all) { MultiSelectAddSetAll(ms, request_select_all); if (!request_select_all) storage->LastSelectionSize = 0; } ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO); return &ms->IO; } // Return updated ImGuiMultiSelectIO structure. // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). ImGuiMultiSelectIO* ImGui::EndMultiSelect() { ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; ImGuiMultiSelectState* storage = ms->Storage; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT_USER_ERROR(ms->FocusScopeId == g.CurrentFocusScopeId, "EndMultiSelect() FocusScope mismatch!"); IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect); ImRect scope_rect = CalcScopeRect(ms, window); if (ms->IsFocused) { // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at beginning of the scope (see tests for easy failure) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; } if (ms->NavIdPassedBy == false && storage->NavIdItem != ImGuiSelectionUserData_Invalid) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n"); storage->NavIdItem = ImGuiSelectionUserData_Invalid; storage->NavIdSelected = -1; } if ((ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) && GetBoxSelectState(ms->BoxSelectId)) EndBoxSelect(scope_rect, ms->Flags); } if (ms->IsEndIO == false) ms->IO.Requests.resize(0); // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! // The InnerRect test is necessary for non-child/decorated windows. bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(g.IO.MousePos); if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)) scope_hovered &= scope_rect.Contains(g.IO.MousePos); if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) { if (ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) { if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) { BoxSelectPreStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal); SetHoveredID(ms->BoxSelectId); if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(g.IO.MousePos, g.IO.MousePos)); // Automatically switch FocusScope for initial click from void to box-select. } } if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) MultiSelectAddSetAll(ms, false); } // Courtesy nav wrapping helper flag if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX) { IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow); // Only supported at window scope ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); } // Unwind window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); PopFocusScope(); if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) DebugLogMultiSelectRequests("EndMultiSelect", &ms->IO); ms->FocusScopeId = 0; ms->Flags = ImGuiMultiSelectFlags_None; g.CurrentMultiSelect = (--g.MultiSelectTempDataStacked > 0) ? &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] : NULL; return &ms->IO; } void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) { // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code! // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api. ImGuiContext& g = *GImGui; g.NextItemData.SelectionUserData = selection_user_data; g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) { // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; if (ms->IO.RangeSrcItem == selection_user_data) ms->RangeSrcPassedBy = true; } else { g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; } } // In charge of: // - Applying SetAll for submitted items. // - Applying SetRange for submitted items and record end points. // - Altering button behavior flags to facilitate use with drag and drop. void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags) { ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; bool selected = *p_selected; if (ms->IsFocused) { ImGuiMultiSelectState* storage = ms->Storage; ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); // Apply SetAll (Clear/SelectAll) requests requested by BeginMultiSelect(). // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. // If you are using a clipper you need to process the SetAll request after calling BeginMultiSelect() if (ms->LoopRequestSetAll != -1) selected = (ms->LoopRequestSetAll == 1); // When using Shift+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) if (ms->IsKeyboardSetRange) { IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) ms->RangeDstPassedBy = true; if (is_range_dst && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst { storage->RangeSrcItem = item_data; storage->RangeSelected = selected ? 1 : 0; } const bool is_range_src = storage->RangeSrcItem == item_data; if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) { // Apply range-select value to visible items IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); selected = (storage->RangeSelected != 0); } else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0 && (ms->Flags & ImGuiMultiSelectFlags_NoAutoClear) == 0) { // Clear other items selected = false; } } *p_selected = selected; } // Alter button behavior flags // To handle drag and drop of multiple items we need to avoid clearing selection on click. // Enabling this test makes actions using Ctrl+Shift delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. if (p_button_flags != NULL) { ImGuiButtonFlags button_flags = *p_button_flags; button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; else button_flags |= ImGuiButtonFlags_PressedOnClickRelease; *p_button_flags = button_flags; } } // In charge of: // - Auto-select on navigation. // - Box-select toggle handling. // - Right-click handling. // - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse. // - Record current selection state for RangeSrc // This is all rather complex, best to run and refer to "widgets_multiselect_xxx" tests in imgui_test_suite. void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; bool selected = *p_selected; bool pressed = *p_pressed; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; ImGuiMultiSelectState* storage = ms->Storage; if (pressed) ms->IsFocused = true; bool hovered = false; if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); if (!ms->IsFocused && !hovered) return; ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; ImGuiMultiSelectFlags flags = ms->Flags; const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0; bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; bool apply_to_range_src = false; if (g.NavId == id && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) apply_to_range_src = true; if (ms->IsEndIO == false) { ms->IO.Requests.resize(0); ms->IsEndIO = true; } // Auto-select as you navigate a list if (g.NavJustMovedToId == id) { if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) { if (is_ctrl && is_shift) pressed = true; else if (!is_ctrl) selected = pressed = true; } else { // With NoAutoSelect, using Shift+keyboard performs a write/copy if (is_shift) pressed = true; else if (!is_ctrl) apply_to_range_src = true; // Since if (pressed) {} main block is not running we update this } } if (apply_to_range_src) { storage->RangeSrcItem = item_data; storage->RangeSelected = selected; // Will be updated at the end of this function anyway. } // Box-select toggle handling if (ms->BoxSelectId != 0) if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId)) { const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) { if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce) { pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway) bs->IsStartedSetNavIdOnce = false; } else { selected = !selected; MultiSelectAddSetRange(ms, selected, +1, item_data, item_data); } storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1); } } // Right-click handling. // FIXME-MULTISELECT: Maybe should be moved to Selectable()? Also see #5816, #8200, #9015 if (hovered && IsMouseClicked(1) && (flags & (ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoSelectOnRightClick)) == 0) { if (g.ActiveId != 0 && g.ActiveId != id) ClearActiveID(); SetFocusID(id, window); if (!pressed && !selected) { pressed = true; is_ctrl = is_shift = false; } } // Unlike Space, Enter doesn't alter selection (but can still return a press) unless current item is not selected. // The later, "unless current item is not select", may become optional? It seems like a better default if Enter doesn't necessarily open something // (unlike e.g. Windows explorer). For use case where Enter always open something, we might decide to make this optional? const bool enter_pressed = pressed && (g.NavActivateId == id) && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput); // Alter selection if (pressed && (!enter_pressed || !selected)) { // Box-select ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) BoxSelectPreStartDrag(ms->BoxSelectId, item_data); //---------------------------------------------------------------------------------------- // ACTION | Begin | Pressed/Activated | End //---------------------------------------------------------------------------------------- // Keys Navigated: | Clear | Src=item, Sel=1 SetRange 1 // Keys Navigated: Ctrl | n/a | n/a // Keys Navigated: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1 // Keys Navigated: Ctrl+Shift | n/a | Dst=item, Sel=Src => Clear + SetRange Src-Dst // Keys Activated: | n/a | Src=item, Sel=1 => Clear + SetRange 1 // Keys Activated: Ctrl | n/a | Src=item, Sel=!Sel => SetSange 1 // Keys Activated: Shift | n/a | Dst=item, Sel=1 => Clear + SetSange 1 //---------------------------------------------------------------------------------------- // Mouse Pressed: | n/a | Src=item, Sel=1, => Clear + SetRange 1 // Mouse Pressed: Ctrl | n/a | Src=item, Sel=!Sel => SetRange 1 // Mouse Pressed: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1 // Mouse Pressed: Ctrl+Shift | n/a | Dst=item, Sel=!Sel => SetRange Src-Dst //---------------------------------------------------------------------------------------- if ((flags & ImGuiMultiSelectFlags_NoAutoClear) == 0) { bool request_clear = false; if (is_singleselect) request_clear = true; else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) request_clear = (flags & ImGuiMultiSelectFlags_NoAutoClearOnReselect) ? !selected : true; else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. if (request_clear) MultiSelectAddSetAll(ms, false); } int range_direction; bool range_selected; if (is_shift && !is_singleselect) { //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) storage->RangeSrcItem = item_data; if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) { // Shift+Arrow always select // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected) range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; } else { // Shift+Arrow copy source selection state // Shift+Click always copy from target selection state if (ms->IsKeyboardSetRange) range_selected = (storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; else range_selected = !selected; } range_direction = ms->RangeSrcPassedBy ? +1 : -1; } else { // Ctrl inverts selection, otherwise always select if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) selected = is_ctrl ? !selected : true; else selected = !selected; storage->RangeSrcItem = item_data; range_selected = selected; range_direction = +1; } MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data); } // Update/store the selection state of the Source item (used by Ctrl+Shift, when Source is unselected we perform a range unselect) if (storage->RangeSrcItem == item_data) storage->RangeSelected = selected ? 1 : 0; // Update/store the selection state of focused item if (g.NavId == id) { storage->NavIdItem = item_data; storage->NavIdSelected = selected ? 1 : 0; } if (storage->NavIdItem == item_data) ms->NavIdPassedBy = true; ms->LastSubmittedItem = item_data; *p_selected = selected; *p_pressed = pressed; } void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected) { ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; ms->IO.Requests.resize(0); // Can always clear previous requests ms->IO.Requests.push_back(req); // Add new request } void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item) { // Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges) if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0) { ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1]; if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected) { prev->RangeLastItem = last_item; return; } } ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item }; ms->IO.Requests.push_back(req); // Add new request } void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) { #ifndef IMGUI_DISABLE_DEBUG_TOOLS const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } bool open = TreeNode((void*)(intptr_t)storage->ID, "MultiSelect 0x%08X in '%s'%s", storage->ID, storage->Window ? storage->Window->Name : "N/A", is_active ? "" : " *Inactive*"); if (!is_active) { PopStyleColor(); } if (!open) return; Text("RangeSrcItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected); Text("NavIdItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected); Text("LastSelectionSize = %d", storage->LastSelectionSize); // Provided by user TreePop(); #else IM_UNUSED(storage); #endif } //------------------------------------------------------------------------- // [SECTION] Widgets: Multi-Select helpers //------------------------------------------------------------------------- // - ImGuiSelectionBasicStorage // - ImGuiSelectionExternalStorage //------------------------------------------------------------------------- ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage() { Size = 0; PreserveOrder = false; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; _SelectionOrder = 1; // Always >0 } void ImGuiSelectionBasicStorage::Clear() { Size = 0; _SelectionOrder = 1; // Always >0 _Storage.Data.resize(0); } void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r) { ImSwap(Size, r.Size); ImSwap(_SelectionOrder, r._SelectionOrder); _Storage.Data.swap(r._Storage.Data); } bool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const { return _Storage.GetInt(id, 0) != 0; } static int IMGUI_CDECL PairComparerByValueInt(const void* lhs, const void* rhs) { int lhs_v = ((const ImGuiStoragePair*)lhs)->val_i; int rhs_v = ((const ImGuiStoragePair*)rhs)->val_i; return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0); } // GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user. // (e.g. store unselected vs compact down, compact down on demand, use raw ImVector instead of ImGuiStorage...) bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id) { ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it; ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size; if (PreserveOrder && it == NULL && it_end != NULL) ImQsort(_Storage.Data.Data, (size_t)_Storage.Data.Size, sizeof(ImGuiStoragePair), PairComparerByValueInt); // ~ImGuiStorage::BuildSortByValueInt() if (it == NULL) it = _Storage.Data.Data; IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); if (it != it_end) while (it->val_i == 0 && it < it_end) it++; const bool has_more = (it != it_end); *opaque_it = has_more ? (void**)(it + 1) : (void**)(it); *out_id = has_more ? it->key : 0; if (PreserveOrder && !has_more) _Storage.BuildSortByKey(); return has_more; } void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) { int* p_int = _Storage.GetIntRef(id, 0); if (selected && *p_int == 0) { *p_int = _SelectionOrder++; Size++; } else if (!selected && *p_int != 0) { *p_int = 0; Size--; } } // Optimized for batch edits (with same value of 'selected') static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends, int selection_order) { ImGuiStorage* storage = &selection->_Storage; ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key == id); if (selected == (is_contained && it->val_i != 0)) return; if (selected && !is_contained) storage->Data.push_back(ImGuiStoragePair(id, selection_order)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() else if (is_contained) it->val_i = selected ? selection_order : 0; // Modify in-place. selection->Size += selected ? +1 : -1; } static void ImGuiSelectionBasicStorage_BatchFinish(ImGuiSelectionBasicStorage* selection, bool selected, int size_before_amends) { ImGuiStorage* storage = &selection->_Storage; if (selected && selection->Size != size_before_amends) storage->BuildSortByKey(); // When done selecting: sort everything } // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). // - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. // - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. // - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection. // - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform // a lookup in order to have some way to iterate/interpolate between two items. // - A full-featured application is likely to allow search/filtering which is likely to lead to using indices // and constructing a view index <> object id/ptr data structure anyway. // WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ImGuiSelectionBasicStorage' INDIRECTION LOGIC. // Notice that with the simplest adapter (using indices everywhere), all functions return their parameters. // The most simple implementation (using indices everywhere) would look like: // for (ImGuiSelectionRequest& req : ms_io->Requests) // { // if (req.Type == ImGuiSelectionRequestType_SetAll) { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { SetItemSelected(n, true); } } // if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { SetItemSelected(n, ms_io->Selected); } } // } void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) { // For convenience we obtain ItemsCount as passed to BeginMultiSelect(), which is optional. // It makes sense when using ImGuiSelectionBasicStorage to simply pass your items count to BeginMultiSelect(). // Other scheme may handle SetAll differently. IM_ASSERT(ms_io->ItemsCount != -1 && "Missing value for items_count in BeginMultiSelect() call!"); IM_ASSERT(AdapterIndexToStorageId != NULL); // This is optimized/specialized to cope with very large selections (e.g. 100k+ items) // - A simpler version could call SetItemSelected() directly instead of ImGuiSelectionBasicStorage_BatchSetItemSelected() + ImGuiSelectionBasicStorage_BatchFinish(). // - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass. // - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector to reduce bandwidth, but this is a reasonable trade off to reuse code. // - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling // left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each. // FIXME-OPT: For each block of consecutive SetRange request: // - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage. // - rewrite sorted storage a single time. for (ImGuiSelectionRequest& req : ms_io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) { Clear(); if (req.Selected) { _Storage.Data.reserve(ms_io->ItemsCount); const int size_before_amends = _Storage.Data.Size; for (int idx = 0; idx < ms_io->ItemsCount; idx++, _SelectionOrder++) ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, _SelectionOrder); ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); } } else if (req.Type == ImGuiSelectionRequestType_SetRange) { const int selection_changes = (int)req.RangeLastItem - (int)req.RangeFirstItem + 1; //ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_SELECTION("Req %d/%d: set %d to %d\n", ms_io->Requests.index_from_ptr(&req), ms_io->Requests.Size, selection_changes, req.Selected); if (selection_changes == 1 || (selection_changes < Size / 100)) { // Multiple sorted insertion + copy likely to be faster. // Technically we could do a single copy with a little more work (sort sequential SetRange requests) for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) SetItemSelected(GetStorageIdFromIndex(idx), req.Selected); } else { // Append insertion + single sort likely be faster. // Use req.RangeDirection to set order field so that Shift+Clicking from 1 to 5 is different than Shift+Clicking from 5 to 1 const int size_before_amends = _Storage.Data.Size; int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? selection_changes - 1 : 0); for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection) ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order); if (req.Selected) _SelectionOrder += selection_changes; ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); } } } } //------------------------------------------------------------------------- ImGuiSelectionExternalStorage::ImGuiSelectionExternalStorage() { UserData = NULL; AdapterSetItemSelected = NULL; } // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). // We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage // This makes no assumption about underlying storage. void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) { IM_ASSERT(AdapterSetItemSelected); for (ImGuiSelectionRequest& req : ms_io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) for (int idx = 0; idx < ms_io->ItemsCount; idx++) AdapterSetItemSelected(this, idx, req.Selected); if (req.Type == ImGuiSelectionRequestType_SetRange) for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) AdapterSetItemSelected(this, idx, req.Selected); } } //------------------------------------------------------------------------- // [SECTION] Widgets: ListBox //------------------------------------------------------------------------- // - BeginListBox() // - EndListBox() // - ListBox() //------------------------------------------------------------------------- // This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. // This handle some subtleties with capturing info from the label. // If you don't need a label you can pretty much directly use ImGui::BeginChild() with ImGuiChildFlags_FrameStyle. // Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty" // Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.5f * item_height). bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); // Size default to hold ~7.25 items. // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f)); ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); g.NextItemData.ClearFlags(); if (!IsRectVisible(bb.Min, bb.Max)) { ItemSize(bb.GetSize(), style.FramePadding.y); ItemAdd(bb, 0, &frame_bb); g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values return false; } // FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well. BeginGroup(); if (label_size.x > 0.0f) { ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y); RenderText(label_pos, label); window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size); AlignTextToFramePadding(); } BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle); return true; } void ImGui::EndListBox() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?"); IM_UNUSED(window); EndChild(); EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label } bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) { const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); return value_changed; } // This is merely a helper around BeginListBox(), EndListBox(). // Considering using those directly to submit custom data or store selection differently. bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items) { ImGuiContext& g = *GImGui; // Calculate size from "height_in_items" if (height_in_items < 0) height_in_items = ImMin(items_count, 7); float height_in_items_f = height_in_items + 0.25f; ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f)); if (!BeginListBox(label, size)) return false; // Assume all items have even height (= 1 line of text). If you need items of different height, // you can create a custom version of ListBox() in your code without using the clipper. bool value_changed = false; ImGuiListClipper clipper; clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. clipper.IncludeItemByIndex(*current_item); while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { const char* item_text = getter(user_data, i); if (item_text == NULL) item_text = "*Unknown item*"; PushID(i); const bool item_selected = (i == *current_item); if (Selectable(item_text, item_selected)) { *current_item = i; value_changed = true; } if (item_selected) SetItemDefaultFocus(); PopID(); } EndListBox(); if (value_changed) MarkItemEdited(g.LastItemData.ID); return value_changed; } //------------------------------------------------------------------------- // [SECTION] Widgets: PlotLines, PlotHistogram //------------------------------------------------------------------------- // - PlotEx() [Internal] // - PlotLines() // - PlotHistogram() //------------------------------------------------------------------------- // Plot/Graph widgets are not very good. // Consider writing your own, or using a third-party one, see: // - ImPlot https://github.com/epezent/implot // - others https://github.com/ocornut/imgui/wiki/Useful-Extensions //------------------------------------------------------------------------- int 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, const ImVec2& size_arg) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return -1; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f); 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, id, &frame_bb, ImGuiItemFlags_NoNav)) return -1; bool hovered; ButtonBehavior(frame_bb, id, &hovered, NULL); // Determine scale from values if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) { float v_min = FLT_MAX; float v_max = -FLT_MAX; for (int i = 0; i < values_count; i++) { const float v = values_getter(data, i); if (v != v) // Ignore NaN values continue; v_min = ImMin(v_min, v); v_max = ImMax(v_max, v); } if (scale_min == FLT_MAX) scale_min = v_min; if (scale_max == FLT_MAX) scale_max = v_max; } RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; int idx_hovered = -1; if (values_count >= values_count_min) { 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 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); IM_ASSERT(v_idx >= 0 && v_idx < values_count); const float v0 = values_getter(data, (v_idx + values_offset) % values_count); const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); if (plot_type == ImGuiPlotType_Lines) SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1); else if (plot_type == ImGuiPlotType_Histogram) SetTooltip("%d: %8.4g", v_idx, v0); idx_hovered = v_idx; } const float t_step = 1.0f / (float)res_w; const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); float v0 = values_getter(data, (0 + values_offset) % values_count); float t0 = 0.0f; ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); for (int n = 0; n < res_w; n++) { const float t1 = t0 + t_step; const int v1_idx = (int)(t0 * item_count + 0.5f); IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); if (plot_type == ImGuiPlotType_Lines) { window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base); } else if (plot_type == ImGuiPlotType_Histogram) { if (pos1.x >= pos0.x + 2.0f) pos1.x -= 1.0f; window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base); } t0 = t1; tp0 = tp1; } } // Text overlay if (overlay_text) RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f)); if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); // Return hovered index or -1 if none are hovered. // This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx(). return idx_hovered; } struct ImGuiPlotArrayGetterData { const float* Values; int Stride; ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } }; static float Plot_ArrayGetter(void* data, int idx) { ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); return v; } void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) { ImGuiPlotArrayGetterData data(values, stride); PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); } void ImGui::PlotLines(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) { PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); } void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) { ImGuiPlotArrayGetterData data(values, stride); PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); } void ImGui::PlotHistogram(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) { PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); } //------------------------------------------------------------------------- // [SECTION] Widgets: Value helpers // Those is not very useful, legacy API. //------------------------------------------------------------------------- // - Value() //------------------------------------------------------------------------- void ImGui::Value(const char* prefix, bool b) { Text("%s: %s", prefix, (b ? "true" : "false")); } void ImGui::Value(const char* prefix, int v) { Text("%s: %d", prefix, v); } void ImGui::Value(const char* prefix, unsigned int v) { Text("%s: %d", prefix, v); } void ImGui::Value(const char* prefix, float v, const char* float_format) { if (float_format) { char fmt[64]; ImFormatString(fmt, IM_COUNTOF(fmt), "%%s: %s", float_format); Text(fmt, prefix, v); } else { Text("%s: %.3f", prefix, v); } } //------------------------------------------------------------------------- // [SECTION] MenuItem, BeginMenu, EndMenu, etc. //------------------------------------------------------------------------- // - ImGuiMenuColumns [Internal] // - BeginMenuBar() // - EndMenuBar() // - BeginMainMenuBar() // - EndMainMenuBar() // - BeginMenu() // - EndMenu() // - MenuItemEx() [Internal] // - MenuItem() //------------------------------------------------------------------------- // Helpers for internal use void ImGuiMenuColumns::Update(float spacing, bool window_reappearing) { if (window_reappearing) memset(Widths, 0, sizeof(Widths)); Spacing = (ImU16)spacing; CalcNextTotalWidth(true); memset(Widths, 0, sizeof(Widths)); TotalWidth = NextTotalWidth; NextTotalWidth = 0; } void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) { ImU16 offset = 0; bool want_spacing = false; for (int i = 0; i < IM_COUNTOF(Widths); i++) { ImU16 width = Widths[i]; if (want_spacing && width > 0) offset += Spacing; want_spacing |= (width > 0); if (update_offsets) { if (i == 1) { OffsetLabel = offset; } if (i == 2) { OffsetShortcut = offset; } if (i == 3) { OffsetMark = offset; } } offset += width; } NextTotalWidth = offset; } float ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark) { Widths[0] = ImMax(Widths[0], (ImU16)w_icon); Widths[1] = ImMax(Widths[1], (ImU16)w_label); Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut); Widths[3] = ImMax(Widths[3], (ImU16)w_mark); CalcNextTotalWidth(false); return (float)ImMax(TotalWidth, NextTotalWidth); } // FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere.. // Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer. // Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags_MenuBar will become unnecessary. // Then later the same system could be used for multiple menu-bars, scrollbars, side-bars. bool ImGui::BeginMenuBar() { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; if (!(window->Flags & ImGuiWindowFlags_MenuBar)) return false; IM_ASSERT(!window->DC.MenuBarAppending); BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore PushID("##MenuBar"); // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. const float border_top = ImMax(IM_ROUND(window->WindowBorderSize * 0.5f - window->TitleBarHeight), 0.0f); const float border_half = IM_ROUND(window->WindowBorderSize * 0.5f); ImRect bar_rect = window->MenuBarRect(); ImRect clip_rect(ImFloor(bar_rect.Min.x + border_half), ImFloor(bar_rect.Min.y + border_top), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, border_half))), ImFloor(bar_rect.Max.y)); clip_rect.ClipWith(window->OuterRectClipped); PushClipRect(clip_rect.Min, clip_rect.Max, false); // We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags). window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); window->DC.LayoutType = ImGuiLayoutType_Horizontal; window->DC.IsSameLine = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; window->DC.MenuBarAppending = true; AlignTextToFramePadding(); return true; } void ImGui::EndMenuBar() { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); IM_ASSERT(window->DC.MenuBarAppending); // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { // Try to find out if the request is for one of our child menu ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) nav_earliest_child = nav_earliest_child->ParentWindow; if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) { // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) const ImGuiNavLayer layer = ImGuiNavLayer_Menu; IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary) FocusWindow(window); SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); // FIXME-NAV: How to deal with this when not using g.IO.ConfigNavCursorVisibleAuto? if (g.NavCursorVisible) { g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection. Will be set again g.NavCursorHideFrames = 2; } g.NavHighlightItemUnderNav = g.NavMousePosDirty = true; NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat } } else { NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_WrapX); } PopClipRect(); PopID(); IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. // FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here. ImGuiGroupData& group_data = g.GroupStack.back(); group_data.EmitItem = false; ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos; window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent. EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.IsSameLine = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.MenuBarAppending = false; window->DC.CursorMaxPos = restore_cursor_max_pos; } // Important: calling order matters! // FIXME: Somehow overlapping with docking tech. // FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts) bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags) { IM_ASSERT(dir != ImGuiDir_None); ImGuiWindow* bar_window = FindWindowByName(name); ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport()); if (bar_window == NULL || bar_window->BeginCount == 0) { // Calculate and set window size/position ImRect avail_rect = viewport->GetBuildWorkRect(); ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; ImVec2 pos = avail_rect.Min; if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) pos[axis] = avail_rect.Max[axis] - axis_size; ImVec2 size = avail_rect.GetSize(); size[axis] = axis_size; SetNextWindowPos(pos); SetNextWindowSize(size); // Report our size into work area (for next frame) using actual window size if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) viewport->BuildWorkInsetMin[axis] += axis_size; else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) viewport->BuildWorkInsetMax[axis] += axis_size; } window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; // Create window SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set. PushStyleColor(ImGuiCol_WindowShadow, ImVec4(0, 0, 0, 0)); PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint bool is_open = Begin(name, NULL, window_flags); PopStyleVar(2); PopStyleColor(); return is_open; } bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); // Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change SetCurrentViewport(NULL, viewport); // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; float height = GetFrameHeight(); bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags); g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); if (!is_open) { End(); return false; } // Temporarily disable _NoSavedSettings, in the off-chance that tables or child windows submitted within the menu-bar may want to use settings. (#8356) g.CurrentWindow->Flags &= ~ImGuiWindowFlags_NoSavedSettings; BeginMenuBar(); return is_open; } void ImGui::EndMainMenuBar() { ImGuiContext& g = *GImGui; IM_ASSERT_USER_ERROR_RET(g.CurrentWindow->DC.MenuBarAppending, "Calling EndMainMenuBar() not from a menu-bar!"); // Not technically testing that it is the main menu bar EndMenuBar(); g.CurrentWindow->Flags |= ImGuiWindowFlags_NoSavedSettings; // Restore _NoSavedSettings (#8356) // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window // FIXME: With this strategy we won't be able to restore a NULL focus. if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest && g.ActiveId == 0) FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild); End(); } static bool IsRootOfOpenMenuSet() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) return false; // Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others // (e.g. inside menu bar vs loose menu items) based on parent ID. // This would however prevent the use of e.g. PushID() user code submitting menus. // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu. // In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer. // This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still // open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart // it likely won't be a problem anyone runs into. const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer) return false; return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true, false); } bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) // The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; if (window->Flags & ImGuiWindowFlags_ChildMenu) window_flags |= ImGuiWindowFlags_ChildWindow; // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used. if (g.MenusIdSubmittedThisFrame.contains(id)) { if (menu_is_open) menu_is_open = BeginPopupMenuEx(id, label, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) else g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values return menu_is_open; } // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu g.MenusIdSubmittedThisFrame.push_back(id); ImVec2 label_size = CalcTextSize(label, NULL, true); // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) // This is only done for items for the menu set and not the full parent window. const bool menuset_is_open = IsRootOfOpenMenuSet(); if (menuset_is_open) PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); // 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 chosen by FindBestWindowPosForPopup(). // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. ImVec2 popup_pos; ImVec2 pos = window->DC.CursorPos; PushID(label); if (!enabled) BeginDisabled(); const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; bool pressed; // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside a 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() window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); float w = label_size.x; ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y)); LogSetNextTextDecoration("[", "]"); RenderText(text_pos, label); PopStyleVar(); window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), text_pos.y - style.FramePadding.y + window->MenuBarHeight); } else { // Menu inside a regular/vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.) float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); ImVec2 text_pos(window->DC.CursorPos.x, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); LogSetNextTextDecoration("", ">"); RenderText(ImVec2(text_pos.x + offsets->OffsetLabel, text_pos.y), label); if (icon_w > 0.0f) RenderText(ImVec2(text_pos.x + offsets->OffsetIcon, text_pos.y), icon); RenderArrow(window->DrawList, ImVec2(text_pos.x + offsets->OffsetMark + extra_w + g.FontSize * 0.30f, text_pos.y), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); popup_pos = ImVec2(pos.x, text_pos.y - style.WindowPadding.y); } if (!enabled) EndDisabled(); const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav; if (menuset_is_open) PopItemFlag(); bool want_open = false; bool want_open_nav_init = false; bool want_close = false; if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) { // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_toward_child_menu = false; ImGuiPopupData* child_popup = (g.BeginPopupStack.Size < g.OpenPopupStack.Size) ? &g.OpenPopupStack[g.BeginPopupStack.Size] : NULL; // Popup candidate (testing below) ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL; if (g.HoveredWindow == window && child_menu_window != NULL) { const float ref_unit = g.FontSize; // FIXME-DPI const float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f; const ImRect next_window_rect = child_menu_window->Rect(); ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR(); ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR(); const float pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // Add a bit of extra slack. ta.x += child_dir * -0.5f; tb.x += child_dir * ref_unit; tc.x += child_dir * ref_unit; tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0f); // Triangle has maximum height to limit the slope and the bias toward large sub-menus tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, +ref_unit * 8.0f); moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] } // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not) // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon. // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.) if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavHighlightItemUnderNav && g.ActiveId == 0) want_close = true; // Open // (note: at this point 'hovered' actually includes the NavDisableMouseHover == false test) if (!menu_is_open && pressed) // Click/activate to open want_open = true; else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open want_open = true; else if (!menu_is_open && hovered && g.HoveredIdTimer >= 0.30f && g.MouseStationaryTimer >= 0.30f) // Hover to open (timer fallback) want_open = true; if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { want_open = want_open_nav_init = true; NavMoveRequestCancel(); SetNavCursorVisibleAfterMove(); } } else { // Menu bar if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it { want_close = true; want_open = menu_is_open = false; } else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others { want_open = true; } else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open { want_open = true; NavMoveRequestCancel(); } } 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, ImGuiPopupFlags_None)) ClosePopupToLevel(g.BeginPopupStack.Size, true); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); PopID(); if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { // Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame. OpenPopup(label); } else if (want_open) { menu_is_open = true; OpenPopup(label, ImGuiPopupFlags_NoReopen);// | (want_open_nav_init ? ImGuiPopupFlags_NoReopenAlwaysNavInit : 0)); } if (menu_is_open) { ImGuiLastItemData last_item_in_parent = g.LastItemData; SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos. PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding menu_is_open = BeginPopupMenuEx(id, label, window_flags); // menu_is_open may be 'false' when the popup is completely clipped (e.g. zero size display) PopStyleVar(); if (menu_is_open) { // Implement what ImGuiPopupFlags_NoReopenAlwaysNavInit would do: // Perform an init request in the case the popup was already open (via a previous mouse hover) if (want_open && want_open_nav_init && !g.NavInitRequest) { FocusWindow(g.CurrentWindow, ImGuiFocusRequestFlags_UnlessBelowModal); NavInitWindow(g.CurrentWindow, false); } // Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu() // (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck) g.LastItemData = last_item_in_parent; if (g.HoveredWindow == window) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; } } else { g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values } return menu_is_open; } bool ImGui::BeginMenu(const char* label, bool enabled) { return BeginMenuEx(label, NULL, enabled); } void ImGui::EndMenu() { // Nav: When a left move request our menu failed, close ourselves. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT_USER_ERROR_RET((window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu), "Calling EndMenu() in wrong window!"); ImGuiWindow* parent_window = window->ParentWindow; // Should always be != NULL is we passed assert. if (window->BeginCount == window->BeginCountPreviousFrame) if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet()) if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical) { ClosePopupToLevel(g.BeginPopupStack.Size - 1, true); NavMoveRequestCancel(); } EndPopup(); } bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; ImVec2 pos = window->DC.CursorPos; ImVec2 label_size = CalcTextSize(label, NULL, true); // See BeginMenuEx() for comments about this. const bool menuset_is_open = IsRootOfOpenMenuSet(); if (menuset_is_open) PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. bool pressed; PushID(label); if (!enabled) BeginDisabled(); // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. float w = label_size.x; window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) RenderText(text_pos, label); window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else { // Menu item inside a vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.) float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); ImVec2 text_pos(pos.x, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) { RenderText(text_pos + ImVec2(offsets->OffsetLabel, 0.0f), label); if (icon_w > 0.0f) RenderText(text_pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); if (shortcut_w > 0.0f) { PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); LogSetNextTextDecoration("(", ")"); RenderText(text_pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); PopStyleColor(); } if (selected) RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } } IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); if (!enabled) EndDisabled(); PopID(); if (menuset_is_open) PopItemFlag(); return pressed; } bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) { return MenuItemEx(label, NULL, shortcut, selected, enabled); } bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) { if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled)) { if (p_selected) *p_selected = !*p_selected; return true; } return false; } //------------------------------------------------------------------------- // [SECTION] Widgets: BeginTabBar, EndTabBar, etc. //------------------------------------------------------------------------- // - BeginTabBar() // - BeginTabBarEx() [Internal] // - EndTabBar() // - TabBarLayout() [Internal] // - TabBarCalcTabID() [Internal] // - TabBarCalcMaxTabWidth() [Internal] // - TabBarFindTabById() [Internal] // - TabBarFindTabByOrder() [Internal] // - TabBarFindMostRecentlySelectedTabForActiveWindow() [Internal] // - TabBarGetCurrentTab() [Internal] // - TabBarGetTabName() [Internal] // - TabBarAddTab() [Internal] // - TabBarRemoveTab() [Internal] // - TabBarCloseTab() [Internal] // - TabBarScrollClamp() [Internal] // - TabBarScrollToTab() [Internal] // - TabBarQueueFocus() [Internal] // - TabBarQueueReorder() [Internal] // - TabBarProcessReorderFromMousePos() [Internal] // - TabBarProcessReorder() [Internal] // - TabBarScrollingButtons() [Internal] // - TabBarTabListPopupButton() [Internal] //------------------------------------------------------------------------- struct ImGuiTabBarSection { int TabCount; // Number of tabs in this section. float Width; // Sum of width of tabs in this section (after shrinking down) float WidthAfterShrinkMinWidth; float Spacing; // Horizontal spacing at the end of the section. ImGuiTabBarSection() { memset((void*)this, 0, sizeof(*this)); } }; namespace ImGui { static void TabBarLayout(ImGuiTabBar* tab_bar); static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window); static float TabBarCalcMaxTabWidth(); static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections); static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar); static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar); } ImGuiTabBar::ImGuiTabBar() { memset((void*)this, 0, sizeof(*this)); CurrFrameVisible = PrevFrameVisible = -1; LastTabItemIdx = -1; } static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab) { return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; } static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs) { const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; const int a_section = TabItemGetSectionIdx(a); const int b_section = TabItemGetSectionIdx(b); if (a_section != b_section) return a_section - b_section; return (int)(a->IndexDuringLayout - b->IndexDuringLayout); } static int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs) { const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; return (int)(a->BeginOrder - b->BeginOrder); } static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiPtrOrIndex& ref) { ImGuiContext& g = *GImGui; return ref.Ptr ? (ImGuiTabBar*)ref.Ptr : g.TabBars.GetByIndex(ref.Index); } static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar) { ImGuiContext& g = *GImGui; if (g.TabBars.Contains(tab_bar)) return ImGuiPtrOrIndex(g.TabBars.GetIndex(tab_bar)); return ImGuiPtrOrIndex(tab_bar); } ImGuiTabBar* ImGui::TabBarFindByID(ImGuiID id) { ImGuiContext& g = *GImGui; return g.TabBars.GetByKey(id); } // Remove TabBar data (currently only used by TestEngine) void ImGui::TabBarRemove(ImGuiTabBar* tab_bar) { ImGuiContext& g = *GImGui; g.TabBars.Remove(tab_bar->ID, 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->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); tab_bar->ID = id; tab_bar->SeparatorMinX = tab_bar_bb.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f); tab_bar->SeparatorMaxX = tab_bar_bb.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f); //if (g.NavWindow && IsWindowChildOf(g.NavWindow, window, false, false)) flags |= ImGuiTabBarFlags_IsFocused; return BeginTabBarEx(tab_bar, tab_bar_bb, flags); } 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; IM_ASSERT(tab_bar->ID != 0); if ((flags & ImGuiTabBarFlags_DockNode) == 0) // Already done PushOverrideID(tab_bar->ID); // Add to stack g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); g.CurrentTabBar = tab_bar; tab_bar->Window = window; // Append with multiple BeginTabBar()/EndTabBar() pairs. tab_bar->BackupCursorPos = window->DC.CursorPos; if (tab_bar->CurrFrameVisible == g.FrameCount) { window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); tab_bar->BeginCount++; return true; } // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) if ((flags & ImGuiTabBarFlags_DockNode) == 0) // FIXME: TabBar with DockNode can now be hybrid ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); tab_bar->TabsAddedNew = false; // 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->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight; tab_bar->CurrTabsContentsHeight = 0.0f; tab_bar->ItemSpacingY = g.Style.ItemSpacing.y; tab_bar->FramePadding = g.Style.FramePadding; tab_bar->TabsActiveCount = 0; tab_bar->LastTabItemIdx = -1; tab_bar->BeginCount = 1; // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); // Draw separator // (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable) const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected); if (g.Style.TabBarBorderSize > 0.0f) { const float y = tab_bar->BarRect.Max.y; window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col); } return true; } void ImGui::EndTabBar() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return; ImGuiTabBar* tab_bar = g.CurrentTabBar; IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!"); // Fallback in case no TabItem have been submitted 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->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight); window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight; } else { window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight; } if (tab_bar->BeginCount > 1) window->DC.CursorPos = tab_bar->BackupCursorPos; tab_bar->LastTabItemIdx = -1; if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) // Already done PopID(); g.CurrentTabBarStack.pop_back(); g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); } // Scrolling happens only in the central section (leading/trailing sections are not scrolling) static float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections) { return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; } // 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; // Track selected tab when resizing our parent down const bool scroll_to_selected_tab = (tab_bar->BarRectPrevWidth > tab_bar->BarRect.GetWidth()); tab_bar->BarRectPrevWidth = tab_bar->BarRect.GetWidth(); // Garbage collect by compacting list // Detect if we need to sort out tab list (e.g. in rare case where a tab changed section) int tab_dst_n = 0; bool need_sort_by_section = false; ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing 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 || tab->WantClose) { // Remove 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; } continue; } if (tab_dst_n != tab_src_n) tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n]; tab = &tab_bar->Tabs[tab_dst_n]; tab->IndexDuringLayout = (ImS16)tab_dst_n; // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) int curr_tab_section_n = TabItemGetSectionIdx(tab); if (tab_dst_n > 0) { ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; int prev_tab_section_n = TabItemGetSectionIdx(prev_tab); if (curr_tab_section_n == 0 && prev_tab_section_n != 0) need_sort_by_section = true; if (prev_tab_section_n == 2 && curr_tab_section_n != 2) need_sort_by_section = true; } sections[curr_tab_section_n].TabCount++; tab_dst_n++; } if (tab_bar->Tabs.Size != tab_dst_n) tab_bar->Tabs.resize(tab_dst_n); if (need_sort_by_section) ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection); // Calculate spacing between sections const float tab_spacing = g.Style.ItemInnerSpacing.x; sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? tab_spacing : 0.0f; sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? tab_spacing : 0.0f; // Setup next selected tab ImGuiID scroll_to_tab_id = 0; if (tab_bar->NextScrollToTabId) { scroll_to_tab_id = tab_bar->NextScrollToTabId; tab_bar->NextScrollToTabId = 0; } if (tab_bar->NextSelectedTabId) { tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; tab_bar->NextSelectedTabId = 0; scroll_to_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 (TabBarProcessReorder(tab_bar)) if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId) scroll_to_tab_id = tab_bar->ReorderRequestTabId; 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.Min.x! scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; // Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central // (whereas our tabs are stored as: leading, central, trailing) int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount }; g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size); // Minimum shrink width const float shrink_min_width = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed) ? g.Style.TabMinWidthShrink : 1.0f; // Compute ideal tabs widths + store them into shrink buffer ImGuiTabItem* most_recently_selected_tab = NULL; int curr_section_n = -1; 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) && !(tab->Flags & ImGuiTabItemFlags_Button)) most_recently_selected_tab = tab; if (tab->ID == tab_bar->SelectedTabId) found_selected_tab_id = true; if (scroll_to_tab_id == 0 && g.NavJustMovedToId == tab->ID) scroll_to_tab_id = tab->ID; // 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 = TabBarGetTabName(tab_bar, tab); const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument); tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x; if ((tab->Flags & ImGuiTabItemFlags_Button) == 0) tab->ContentWidth = ImMax(tab->ContentWidth, g.Style.TabMinWidthBase); int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; section->Width += tab->ContentWidth + (section_n == curr_section_n ? tab_spacing : 0.0f); section->WidthAfterShrinkMinWidth += ImMin(tab->ContentWidth, shrink_min_width) + (section_n == curr_section_n ? tab_spacing : 0.0f); curr_section_n = section_n; // Store data so we can build an array sorted by width if we need to shrink tabs down IM_MSVC_WARNING_SUPPRESS(6385); ImGuiShrinkWidthItem* shrink_width_item = &g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++]; shrink_width_item->Index = tab_n; shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth; tab->Width = ImMax(tab->ContentWidth, 1.0f); } // Compute total ideal width (used for e.g. auto-resizing a window) float width_all_tabs_after_min_width_shrink = 0.0f; tab_bar->WidthAllTabsIdeal = 0.0f; for (int section_n = 0; section_n < 3; section_n++) { tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing; width_all_tabs_after_min_width_shrink += sections[section_n].WidthAfterShrinkMinWidth + sections[section_n].Spacing; } // Horizontal scrolling buttons // Important: note that TabBarScrollButtons() will alter BarRect.Max.x. const bool can_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed); const float width_all_tabs_to_use_for_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) ? tab_bar->WidthAllTabs : width_all_tabs_after_min_width_shrink; tab_bar->ScrollButtonEnabled = ((width_all_tabs_to_use_for_scroll > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && can_scroll); if (tab_bar->ScrollButtonEnabled) if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar)) { scroll_to_tab_id = scroll_and_select_tab->ID; if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0) tab_bar->SelectedTabId = scroll_to_tab_id; } if (scroll_to_tab_id == 0 && scroll_to_selected_tab) scroll_to_tab_id = tab_bar->SelectedTabId; // Shrink widths if full tabs don't fit in their allocated space float section_0_w = sections[0].Width + sections[0].Spacing; float section_1_w = sections[1].Width + sections[1].Spacing; float section_2_w = sections[2].Width + sections[2].Spacing; bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth(); float width_excess; if (central_section_is_visible) width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f); // Excess used to shrink central section else width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore const bool can_shrink = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyShrink) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed); if (width_excess >= 1.0f && (can_shrink || !central_section_is_visible)) { int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount); int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0); ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess, shrink_min_width); // Apply shrunk values into tabs and sections for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index]; float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width); if (shrinked_width < 0.0f) continue; shrinked_width = ImMax(1.0f, shrinked_width); int section_n = TabItemGetSectionIdx(tab); sections[section_n].Width -= (tab->Width - shrinked_width); tab->Width = shrinked_width; } } // Layout all active tabs int section_tab_index = 0; float tab_offset = 0.0f; tab_bar->WidthAllTabs = 0.0f; for (int section_n = 0; section_n < 3; section_n++) { ImGuiTabBarSection* section = §ions[section_n]; if (section_n == 2) tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset); for (int tab_n = 0; tab_n < section->TabCount; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n]; tab->Offset = tab_offset; tab->NameOffset = -1; tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f); } tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f); tab_offset += section->Spacing; section_tab_index += section->TabCount; } // Clear name buffers tab_bar->TabsNames.Buf.resize(0); // If we have lost the selected tab, select the next most recently active one const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); if (found_selected_tab_id == false && !tab_bar_appearing) tab_bar->SelectedTabId = 0; if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; // Lock in visible tab tab_bar->VisibleTabId = tab_bar->SelectedTabId; tab_bar->VisibleTabWasSubmitted = false; // CTRL+TAB can override visible tab temporarily if (g.NavWindowingTarget != NULL && g.NavWindowingTarget->DockNode && g.NavWindowingTarget->DockNode->TabBar == tab_bar) tab_bar->VisibleTabId = scroll_to_tab_id = g.NavWindowingTarget->TabId; // Apply request requests if (scroll_to_tab_id != 0) TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); else if (tab_bar->ScrollButtonEnabled && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow)) { const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH; const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX; if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f) { const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f; tab_bar->ScrollingTargetDistToVisibility = 0.0f; tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step); } SetKeyOwner(wheel_key, tab_bar->ID); } // Update scrolling 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; } tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing; tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing; // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame) ImGuiWindow* window = g.CurrentWindow; window->DC.CursorPos = tab_bar->BarRect.Min; ItemSize(ImVec2(tab_bar->WidthAllTabs, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y); window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal); } // Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack. static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window) { if (docked_window != NULL) { IM_UNUSED(tab_bar); IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); ImGuiID id = docked_window->TabId; 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; } // Order = visible order, not submission order! (which is tab->BeginOrder) ImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order) { if (order < 0 || order >= tab_bar->Tabs.Size) return NULL; return &tab_bar->Tabs[order]; } // FIXME: See references to #2304 in TODO.txt ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar) { ImGuiTabItem* most_recently_selected_tab = NULL; for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) if (tab->Window && tab->Window->WasActive) most_recently_selected_tab = tab; } return most_recently_selected_tab; } ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar) { if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size) return NULL; return &tab_bar->Tabs[tab_bar->LastTabItemIdx]; } const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { if (tab->Window) return tab->Window->Name; if (tab->NameOffset == -1) return "N/A"; IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size); return tab_bar->TabsNames.Buf.Data + tab->NameOffset; } // The purpose of this call is to register tab in advance so we can control their order at the time they appear. // Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function. void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL); IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame) if (!window->HasCloseButton) tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation. ImGuiTabItem new_tab; new_tab.ID = window->TabId; new_tab.Flags = tab_flags; new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab if (new_tab.LastFrameVisible == -1) new_tab.LastFrameVisible = g.FrameCount - 1; new_tab.Window = window; // Required so tab bar layout can compute the tab width before tab submission tab_bar->Tabs.push_back(new_tab); } // The *TabId fields are 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->Flags & ImGuiTabItemFlags_Button) return; // A button appended with TabItemButton(). if ((tab->Flags & (ImGuiTabItemFlags_UnsavedDocument | ImGuiTabItemFlags_NoAssumedClosure)) == 0) { // 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->WantClose = true; if (tab_bar->VisibleTabId == tab->ID) { tab->LastFrameVisible = -1; tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0; } } else { // Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup) if (tab_bar->VisibleTabId != tab->ID) TabBarQueueFocus(tab_bar, tab); } } static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) { scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth()); return ImMax(scrolling, 0.0f); } // Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections) { ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); if (tab == NULL) return; if (tab->Flags & ImGuiTabItemFlags_SectionMask_) return; 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 = TabBarGetTabOrder(tab_bar, tab); // Scrolling happens only in the central section (leading/trailing sections are not scrolling) float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections); // We make all tabs positions all relative Sections[0].Width to make code simpler float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f); float tab_x2 = tab->Offset - sections[0].Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].TabCount ? margin : 1.0f); tab_bar->ScrollingTargetDistToVisibility = 0.0f; if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width)) { // Scroll to the left tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f); tab_bar->ScrollingTarget = tab_x1; } else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width) { // Scroll to the right tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f); tab_bar->ScrollingTarget = tab_x2 - scrollable_width; } } void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { tab_bar->NextSelectedTabId = tab->ID; } void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name) { IM_ASSERT((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0); // Only supported for manual/explicit tab bars ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name, NULL); tab_bar->NextSelectedTabId = tab_id; } void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset) { IM_ASSERT(offset != 0); IM_ASSERT(tab_bar->ReorderRequestTabId == 0); tab_bar->ReorderRequestTabId = tab->ID; tab_bar->ReorderRequestOffset = (ImS16)offset; } void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* src_tab, ImVec2 mouse_pos) { ImGuiContext& g = *GImGui; IM_ASSERT(tab_bar->ReorderRequestTabId == 0); if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) return; const float tab_spacing = g.Style.ItemInnerSpacing.x; const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); // Count number of contiguous tabs we are crossing over const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1; const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab); int dst_idx = src_idx; for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir) { // Reordered tabs must share the same section const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i]; if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder) break; if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_)) break; dst_idx = i; // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. const float x1 = bar_offset + dst_tab->Offset - tab_spacing; const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + tab_spacing; //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) break; } if (dst_idx != src_idx) TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx); } bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) { ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId); if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder)) return false; //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools int tab2_order = TabBarGetTabOrder(tab_bar, tab1) + tab_bar->ReorderRequestOffset; if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) return false; // Reordered tabs must share the same section // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too) ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; if (tab2->Flags & ImGuiTabItemFlags_NoReorder) return false; if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_)) return false; ImGuiTabItem item_tmp = *tab1; ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2; ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1; const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset; memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem)); *tab2 = item_tmp; if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) MarkIniSettingsDirty(); return true; } 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)); 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)); PushItemFlag(ImGuiItemFlags_ButtonRepeat | ImGuiItemFlags_NoNav, true); 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; float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width); window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y); if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick)) select_dir = -1; window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y); if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick)) select_dir = +1; PopItemFlag(); PopStyleColor(2); g.IO.KeyRepeatRate = backup_repeat_rate; g.IO.KeyRepeatDelay = backup_repeat_delay; ImGuiTabItem* tab_to_scroll_to = NULL; if (select_dir != 0) if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) { int selected_order = TabBarGetTabOrder(tab_bar, tab_item); int target_order = selected_order + select_dir; // Skip tab item buttons until another tab item is found or end is reached while (tab_to_scroll_to == NULL) { // If we are at the end of the list, still scroll to make our tab visible tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; // Cross through buttons // (even if first/last item is a button, return it so we can update the scroll) if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button) { target_order += select_dir; selected_order += select_dir; tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL; } } } window->DC.CursorPos = backup_cursor_pos; tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f; return tab_to_scroll_to; } 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 | ImGuiComboFlags_HeightLargest); 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]; if (tab->Flags & ImGuiTabItemFlags_Button) continue; const char* tab_name = TabBarGetTabName(tab_bar, 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. //------------------------------------------------------------------------- // - BeginTabItem() // - EndTabItem() // - TabItemButton() // - TabItemEx() [Internal] // - SetTabItemClosed() // - TabItemCalcSize() [Internal] // - TabItemBackground() [Internal] // - TabItemLabelAndCloseButton() [Internal] //------------------------------------------------------------------------- bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; ImGuiTabBar* tab_bar = g.CurrentTabBar; IM_ASSERT_USER_ERROR_RETV(tab_bar != NULL, false, "Needs to be called between BeginTabBar() and EndTabBar()!"); IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; PushOverrideID(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; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return; ImGuiTabBar* tab_bar = g.CurrentTabBar; IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); IM_ASSERT(tab_bar->LastTabItemIdx >= 0); ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) PopID(); } bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; ImGuiTabBar* tab_bar = g.CurrentTabBar; IM_ASSERT_USER_ERROR_RETV(tab_bar != NULL, false, "Needs to be called between BeginTabBar() and EndTabBar()!"); return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); } void ImGui::TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return; ImGuiTabBar* tab_bar = g.CurrentTabBar; IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); SetNextItemWidth(width); TabItemEx(tab_bar, str_id, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder | ImGuiTabItemFlags_Invisible, NULL); } bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) { // Layout whole tab bar if not already done ImGuiContext& g = *GImGui; if (tab_bar->WantLayout) { ImGuiNextItemData backup_next_item_data = g.NextItemData; TabBarLayout(tab_bar); g.NextItemData = backup_next_item_data; } ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; const ImGuiStyle& style = g.Style; const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window); // If the user called us with *p_open == false, we early out and don't render. // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if (p_open && !*p_open) { ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav); return false; } IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button)); IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)); // Can't use both Leading and Trailing // Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented) if (flags & ImGuiTabItemFlags_NoCloseButton) p_open = NULL; else if (p_open == NULL) flags |= ImGuiTabItemFlags_NoCloseButton; // 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_bar->TabsAddedNew = tab_is_new = true; } tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab); // Calculate tab contents size ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument)); tab->RequestedWidth = -1.0f; if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth) size.x = tab->RequestedWidth = g.NextItemData.Width; if (tab_is_new) tab->Width = ImMax(1.0f, size.x); tab->ContentWidth = size.x; tab->BeginOrder = tab_bar->TabsActiveCount++; 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); const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument); const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0; tab->LastFrameVisible = g.FrameCount; tab->Flags = flags; tab->Window = docked_window; // Append name _WITH_ the zero-terminator // (regular tabs are permitted in a DockNode tab bar, but window tabs not permitted in a non-DockNode tab bar) if (docked_window != NULL) { IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); tab->NameOffset = -1; } else { tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); tab_bar->TabsNames.append(label, label + ImStrlen(label) + 1); } // Update selected tab if (!is_tab_button) { if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) TabBarQueueFocus(tab_bar, tab); // New tabs gets activated if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // _SetSelected can only be passed on explicit tab bar TabBarQueueFocus(tab_bar, tab); } // Lock visibility // (Note: tab_contents_visible != tab_selected... because Ctrl+Tab operations may preview some tabs without selecting them!) 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 && docked_window == NULL) if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) tab_contents_visible = true; // Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. if (tab_appearing && (!tab_bar_appearing || tab_is_new)) { ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav); if (is_tab_button) return false; 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 const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; size.x = tab->Width; if (is_central_section) window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f); else window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 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) const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX); if (want_clip_rect) PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true); ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos; ItemSize(bb.GetSize(), style.FramePadding.y); window->DC.CursorMaxPos = backup_cursor_max_pos; 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 = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; bool hovered, held, pressed; if (flags & ImGuiTabItemFlags_Invisible) hovered = held = pressed = false; else pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (pressed && !is_tab_button) TabBarQueueFocus(tab_bar, tab); // Transfer active id window so the active id is not owned by the dock host (as StartMouseMovingWindow() // will only do it on the drag). This allows FocusWindow() to be more conservative in how it clears active id. if (held && docked_window && g.ActiveId == id && g.ActiveIdIsJustActivated) g.ActiveIdWindow = docked_window; // Drag and drop a single floating window node moves it ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL; const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1); if (held && single_floating_window_node && IsMouseDragging(0, 0.0f)) { // Move StartMouseMovingWindow(docked_window); } else if (held && !tab_appearing && IsMouseDragging(0)) { // Drag and drop: re-order tabs int drag_dir = 0; float drag_distance_from_edge_x = 0.0f; if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL))) { // 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) { drag_dir = -1; drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { drag_dir = +1; drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } } // Extract a Dockable window out of it's tab bar const bool can_undock = docked_window != NULL && !(docked_window->Flags & ImGuiWindowFlags_NoMove) && !(node->MergedFlags & ImGuiDockNodeFlags_NoUndocking); if (can_undock) { // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); if (!undocking_tab) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) { float threshold_base = g.FontSize; float threshold_x = (threshold_base * 2.2f); float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); //GetForegroundDrawList()->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y); if (distance_from_edge_y >= threshold_y) undocking_tab = true; if (drag_distance_from_edge_x > threshold_x) if ((drag_dir < 0 && TabBarGetTabOrder(tab_bar, tab) == 0) || (drag_dir > 0 && TabBarGetTabOrder(tab_bar, tab) == tab_bar->Tabs.Size - 1)) undocking_tab = true; } if (undocking_tab) { // Undock // FIXME: refactor to share more code with e.g. StartMouseMovingWindow DockContextQueueUndockWindow(&g, docked_window); g.MovingWindow = docked_window; SetActiveID(g.MovingWindow->MoveId, g.MovingWindow); g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; g.ActiveIdNoClearOnFocusLoss = true; SetActiveIdUsingAllKeyboardKeys(); } } } #if 0 if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth) { // Enlarge tab display when hovering bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, 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 const bool is_visible = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) && !(flags & ImGuiTabItemFlags_Invisible); if (is_visible) { ImDrawList* display_draw_list = window->DrawList; const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); TabItemBackground(display_draw_list, bb, flags, tab_col); if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) { // Might be moved to TabItemBackground() ? ImVec2 tl = bb.GetTL() + ImVec2(0, 1.0f * g.CurrentDpiScale); ImVec2 tr = bb.GetTR() + ImVec2(0, 1.0f * g.CurrentDpiScale); ImU32 overline_col = GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline); if (style.TabRounding > 0.0f) { float rounding = style.TabRounding; display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9); display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11); display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize); } else { display_draw_list->AddLine(tl - ImVec2(0.5f, 0.5f), tr - ImVec2(0.5f, 0.5f), overline_col, style.TabBarOverlineSize); } } RenderNavCursor(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 (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) TabBarQueueFocus(tab_bar, tab); if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; // Render tab label, process close button const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; bool just_closed; bool text_clipped; TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); if (just_closed && p_open != NULL) { *p_open = false; TabBarCloseTab(tab_bar, tab); } // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) // That state is copied to window->DockTabItemStatusFlags by our caller. if (docked_window && (hovered || g.HoveredId == close_button_id)) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Tooltip // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) // FIXME: This is a mess. // FIXME: We may want disabled tab to still display the tooltip? if (text_clipped && g.HoveredId == id && !held) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); } // Restore main window position so user can draw there if (want_clip_rect) PopClipRect(); window->DC.CursorPos = backup_main_cursor_pos; IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button) return pressed; 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() between BeginTabBar() and EndTabBar(). // Tabs closed by the close button will automatically be flagged to avoid this issue. 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; ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL); if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) tab->WantClose = true; // Will be processed by next call to TabBarLayout() } else if (ImGuiWindow* window = FindWindowByName(label)) { if (window->DockIsActive) if (ImGuiDockNode* node = window->DockNode) { ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label, window); TabBarRemoveTab(node->TabBar, tab_id); window->DockTabWantClose = true; } } } ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker) { 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_or_unsaved_marker) 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); } ImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window) { return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument)); } 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((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f)); const float y1 = bb.Min.y + 1.0f; const float y2 = bb.Max.y - g.Style.TabBarBorderSize; 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), 0, 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. void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped) { ImGuiContext& g = *GImGui; ImVec2 label_size = CalcTextSize(label, NULL, true); if (out_just_closed) *out_just_closed = false; if (out_text_clipped) *out_text_clipped = false; if (bb.GetWidth() <= 1.0f) return; // In Style V2 we'll have full override of all colors per state (e.g. focused, selected) // But right now if you want to alter text color of tabs this is what you need to do. #if 0 const float backup_alpha = g.Style.Alpha; if (!is_contents_visible) g.Style.Alpha *= 0.7f; #endif // Render text label (with clipping + alpha gradient) + unsaved marker ImRect text_ellipsis_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); // Return clipped state ignoring the close button if (out_text_clipped) { *out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_ellipsis_clip_bb.Max.x; //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); } const float button_sz = g.FontSize; const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y); // Close Button & Unsaved Marker // 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; bool is_hovered = g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id; // Any interaction account for this too. if (close_button_id != 0) { if (is_contents_visible) close_button_visible = (g.Style.TabCloseButtonMinWidthSelected < 0.0f) ? true : (is_hovered && bb.GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthSelected)); else close_button_visible = (g.Style.TabCloseButtonMinWidthUnselected < 0.0f) ? true : (is_hovered && bb.GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthUnselected)); } // When tabs/document is unsaved, the unsaved marker takes priority over the close button. const bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x) && (!close_button_visible || !is_hovered); if (unsaved_marker_visible) { ImVec2 bullet_pos = button_pos + ImVec2(button_sz, button_sz) * 0.5f; RenderBullet(draw_list, bullet_pos, GetColorU32(ImGuiCol_UnsavedMarker)); } else if (close_button_visible) { ImGuiLastItemData last_item_backup = g.LastItemData; if (CloseButton(close_button_id, button_pos)) close_button_pressed = true; g.LastItemData = last_item_backup; // Close with middle mouse button if (is_hovered && !(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) close_button_pressed = true; } // This is all rather complicated // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. float ellipsis_max_x = text_ellipsis_clip_bb.Max.x; if (close_button_visible || unsaved_marker_visible) { const bool visible_without_hover = unsaved_marker_visible || (is_contents_visible ? g.Style.TabCloseButtonMinWidthSelected : g.Style.TabCloseButtonMinWidthUnselected) < 0.0f; if (visible_without_hover) { text_ellipsis_clip_bb.Max.x -= button_sz * 0.90f; ellipsis_max_x -= button_sz * 0.90f; } else { text_ellipsis_clip_bb.Max.x -= button_sz * 1.00f; } } LogSetNextTextDecoration("/", "\\"); RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, NULL, &label_size); #if 0 if (!is_contents_visible) g.Style.Alpha = backup_alpha; #endif if (out_just_closed) *out_just_closed = close_button_pressed; } #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui/source/misc/freetype/imgui_freetype.cpp ================================================ // dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder) // (code) // Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype // Original code by @vuhdo (Aleksei Skriabin) in 2017, with improvements by @mikesart. // Maintained since 2019 by @ocornut. // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2025/06/11: refactored for the new ImFontLoader architecture, and ImGuiBackendFlags_RendererHasTextures support. // 2024/10/17: added plutosvg support for SVG Fonts (seems faster/better than lunasvg). Enable by using '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG'. (#7927) // 2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics. // 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'. (#6591) // 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly. // 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns nullptr. // 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs. // 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format. // 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+). // 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas(). // 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails. // 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!) // 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions(). // 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding. // 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX. // 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club) // 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member. // 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement). // 2017/09/26: fixes for imgui internal changes. // 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply. // 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks. // About Gamma Correct Blending: // - FreeType assumes blending in linear space rather than gamma space. // - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph // - For correct results you need to be using sRGB and convert to linear space in the pixel shader output. // - The default dear imgui styles will be impacted by this change (alpha values will need tweaking). // FIXME: cfg.OversampleH, OversampleV are not supported, but generally not necessary with this rasterizer because Hinting makes everything look better. #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_freetype.h" #include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*, #include #include #include FT_FREETYPE_H // #include FT_MODULE_H // #include FT_GLYPH_H // #include FT_SIZES_H // #include FT_SYNTHESIS_H // // Handle LunaSVG and PlutoSVG #if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) && defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) #error "Cannot enable both IMGUI_ENABLE_FREETYPE_LUNASVG and IMGUI_ENABLE_FREETYPE_PLUTOSVG" #endif #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG #include FT_OTSVG_H // #include FT_BBOX_H // #include #endif #ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG #include #endif #if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined (IMGUI_ENABLE_FREETYPE_PLUTOSVG) #if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) #error IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12 #endif #endif #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #ifndef __clang__ #pragma GCC diagnostic ignored "-Wsubobject-linkage" // warning: 'xxxx' has a field 'xxxx' whose type uses the anonymous namespace #endif #endif //------------------------------------------------------------------------- // Data //------------------------------------------------------------------------- // Default memory allocators static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); } static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); } // Current memory allocators static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc; static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc; static void* GImGuiFreeTypeAllocatorUserData = nullptr; // Lunasvg support #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state); static void ImGuiLunasvgPortFree(FT_Pointer* state); static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state); static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state); #endif //------------------------------------------------------------------------- // Code //------------------------------------------------------------------------- #define FT_CEIL(X) (((X + 63) & -64) / 64) // From SDL_ttf: Handy routines for converting from fixed point #define FT_SCALEFACTOR 64.0f // Glyph metrics: // -------------- // // xmin xmax // | | // |<-------- width -------->| // | | // | +-------------------------+----------------- ymax // | | ggggggggg ggggg | ^ ^ // | | g:::::::::ggg::::g | | | // | | g:::::::::::::::::g | | | // | | g::::::ggggg::::::gg | | | // | | g:::::g g:::::g | | | // offsetX -|-------->| g:::::g g:::::g | offsetY | // | | g:::::g g:::::g | | | // | | g::::::g g:::::g | | | // | | g:::::::ggggg:::::g | | | // | | g::::::::::::::::g | | height // | | gg::::::::::::::g | | | // baseline ---*---------|---- gggggggg::::::g-----*-------- | // / | | g:::::g | | // origin | | gggggg g:::::g | | // | | g:::::gg gg:::::g | | // | | g::::::ggg:::::::g | | // | | gg:::::::::::::g | | // | | ggg::::::ggg | | // | | gggggg | v // | +-------------------------+----------------- ymin // | | // |------------- advanceX ----------->| // Stored in ImFontAtlas::FontLoaderData. ALLOCATED BY US. struct ImGui_ImplFreeType_Data { FT_Library Library; FT_MemoryRec_ MemoryManager; ImGui_ImplFreeType_Data() { memset((void*)this, 0, sizeof(*this)); } }; // Stored in ImFontConfig::FontLoaderData. ALLOCATED BY US. struct ImGui_ImplFreeType_FontSrcData { // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime. bool InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_user_flags); void CloseFont(); ImGui_ImplFreeType_FontSrcData() { memset((void*)this, 0, sizeof(*this)); } ~ImGui_ImplFreeType_FontSrcData() { CloseFont(); } // Members FT_Face FtFace; ImGuiFreeTypeLoaderFlags UserFlags; // = ImFontConfig::FontLoaderFlags FT_Int32 LoadFlags; ImFontBaked* BakedLastActivated; }; // Stored in ImFontBaked::FontLoaderDatas: pointer to SourcesCount instances of this. ALLOCATED BY CORE. struct ImGui_ImplFreeType_FontSrcBakedData { FT_Size FtSize; // This represent a FT_Face with a given size. ImGui_ImplFreeType_FontSrcBakedData() { memset((void*)this, 0, sizeof(*this)); } }; bool ImGui_ImplFreeType_FontSrcData::InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_font_loader_flags) { FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)src->FontData, (FT_Long)src->FontDataSize, (FT_Long)src->FontNo, &FtFace); if (error != 0) return false; error = FT_Select_Charmap(FtFace, FT_ENCODING_UNICODE); if (error != 0) return false; // Convert to FreeType flags (NB: Bold and Oblique are processed separately) UserFlags = (ImGuiFreeTypeLoaderFlags)(src->FontLoaderFlags | extra_font_loader_flags); LoadFlags = 0; if ((UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) == 0) LoadFlags |= FT_LOAD_NO_BITMAP; if (UserFlags & ImGuiFreeTypeLoaderFlags_NoHinting) LoadFlags |= FT_LOAD_NO_HINTING; if (UserFlags & ImGuiFreeTypeLoaderFlags_NoAutoHint) LoadFlags |= FT_LOAD_NO_AUTOHINT; if (UserFlags & ImGuiFreeTypeLoaderFlags_ForceAutoHint) LoadFlags |= FT_LOAD_FORCE_AUTOHINT; if (UserFlags & ImGuiFreeTypeLoaderFlags_LightHinting) LoadFlags |= FT_LOAD_TARGET_LIGHT; else if (UserFlags & ImGuiFreeTypeLoaderFlags_MonoHinting) LoadFlags |= FT_LOAD_TARGET_MONO; else LoadFlags |= FT_LOAD_TARGET_NORMAL; if (UserFlags & ImGuiFreeTypeLoaderFlags_LoadColor) LoadFlags |= FT_LOAD_COLOR; // IMHEX PATCH BEGIN if (UserFlags & ImGuiFreeTypeLoaderFlags_SubPixel) LoadFlags |= FT_LOAD_TARGET_LCD; // IMHEX PATCH END return true; } void ImGui_ImplFreeType_FontSrcData::CloseFont() { if (FtFace) { FT_Done_Face(FtFace); FtFace = nullptr; } } static const FT_Glyph_Metrics* ImGui_ImplFreeType_LoadGlyph(ImGui_ImplFreeType_FontSrcData* src_data, uint32_t codepoint) { uint32_t glyph_index = FT_Get_Char_Index(src_data->FtFace, codepoint); if (glyph_index == 0) return nullptr; // If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts. // - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076 // - https://github.com/ocornut/imgui/issues/4567 // - https://github.com/ocornut/imgui/issues/4566 // You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version. FT_Error error = FT_Load_Glyph(src_data->FtFace, glyph_index, src_data->LoadFlags); if (error) return nullptr; // Need an outline for this to work FT_GlyphSlot slot = src_data->FtFace->glyph; #if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG); #else #if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font"); #endif IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP); #endif // IMGUI_ENABLE_FREETYPE_LUNASVG // Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting) if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bold) FT_GlyphSlot_Embolden(slot); if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Oblique) { FT_GlyphSlot_Oblique(slot); //FT_BBox bbox; //FT_Outline_Get_BBox(&slot->outline, &bbox); //slot->metrics.width = bbox.xMax - bbox.xMin; //slot->metrics.height = bbox.yMax - bbox.yMin; } return &slot->metrics; } static void ImGui_ImplFreeType_BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch) { IM_ASSERT(ft_bitmap != nullptr); const uint32_t w = ft_bitmap->width; const uint32_t h = ft_bitmap->rows; const uint8_t* src = ft_bitmap->buffer; const uint32_t src_pitch = ft_bitmap->pitch; switch (ft_bitmap->pixel_mode) { case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel. { for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) for (uint32_t x = 0; x < w; x++) dst[x] = IM_COL32(255, 255, 255, src[x]); break; } case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB. { for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) { uint8_t bits = 0; const uint8_t* bits_ptr = src; for (uint32_t x = 0; x < w; x++, bits <<= 1) { if ((x & 7) == 0) bits = *bits_ptr++; dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? 255 : 0); } } break; } case FT_PIXEL_MODE_BGRA: { // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good. #define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u) for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) for (uint32_t x = 0; x < w; x++) { uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3]; dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a); } #undef DE_MULTIPLY break; } // IMHEX PATCH BEGIN case FT_PIXEL_MODE_LCD: { memset(dst, 0x00, (size_t)w * h * sizeof(uint32_t)); for (uint32_t y = 0; y < h; y++) { for (uint32_t x = 0; x < w / 3; x++) { dst[x] = IM_COL32(src[x * 3 + 0], src[x * 3 + 1], src[x * 3 + 2], (src[x * 3 + 0] + src[x * 3 + 1] + src[x * 3 + 2]) / 3); if (dst[x] == 0x00FFFFFF) dst[x] = 0x00000000; } src += src_pitch; dst += dst_pitch; } break; } // IMHEX PATCH END default: IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!"); } } // FreeType memory allocation callbacks static void* FreeType_Alloc(FT_Memory /*memory*/, long size) { return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData); } static void FreeType_Free(FT_Memory /*memory*/, void* block) { GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); } static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block) { // Implement realloc() as we don't ask user to provide it. if (block == nullptr) return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); if (new_size == 0) { GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); return nullptr; } if (new_size > cur_size) { void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); memcpy(new_block, block, (size_t)cur_size); GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); return new_block; } return block; } static bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) { IM_ASSERT(atlas->FontLoaderData == nullptr); ImGui_ImplFreeType_Data* bd = IM_NEW(ImGui_ImplFreeType_Data)(); // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html bd->MemoryManager.user = nullptr; bd->MemoryManager.alloc = &FreeType_Alloc; bd->MemoryManager.free = &FreeType_Free; bd->MemoryManager.realloc = &FreeType_Realloc; // https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library FT_Error error = FT_New_Library(&bd->MemoryManager, &bd->Library); if (error != 0) { IM_DELETE(bd); return false; } // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator. FT_Add_Default_Modules(bd->Library); #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG // Install svg hooks for FreeType // https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks // https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot }; FT_Property_Set(bd->Library, "ot-svg", "svg-hooks", &hooks); #endif // IMGUI_ENABLE_FREETYPE_LUNASVG #ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG // With plutosvg, use provided hooks FT_Property_Set(bd->Library, "ot-svg", "svg-hooks", plutosvg_ft_svg_hooks()); #endif // IMGUI_ENABLE_FREETYPE_PLUTOSVG // Store our data atlas->FontLoaderData = (void*)bd; return true; } static void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas) { ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; IM_ASSERT(bd != nullptr); FT_Done_Library(bd->Library); IM_DELETE(bd); atlas->FontLoaderData = nullptr; } static bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) { ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; ImGui_ImplFreeType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplFreeType_FontSrcData); IM_ASSERT(src->FontLoaderData == nullptr); src->FontLoaderData = bd_font_data; if (!bd_font_data->InitFont(bd->Library, src, (ImGuiFreeTypeLoaderFlags)atlas->FontLoaderFlags)) { IM_DELETE(bd_font_data); src->FontLoaderData = nullptr; return false; } return true; } static void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) { IM_UNUSED(atlas); ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; IM_DELETE(bd_font_data); src->FontLoaderData = nullptr; } static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) { IM_UNUSED(atlas); float size = baked->Size; if (src->MergeMode && src->SizePixels != 0.0f) size *= (src->SizePixels / baked->OwnerFont->Sources[0]->SizePixels); size *= src->ExtraSizeScale; ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; bd_font_data->BakedLastActivated = baked; // We use one FT_Size per (source + baked) combination. ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; IM_ASSERT(bd_baked_data != nullptr); IM_PLACEMENT_NEW(bd_baked_data) ImGui_ImplFreeType_FontSrcBakedData(); FT_New_Size(bd_font_data->FtFace, &bd_baked_data->FtSize); FT_Activate_Size(bd_baked_data->FtSize); // Vuhdo 2017: "I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height' // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me. // FT_Set_Pixel_Sizes() doesn't seem to get us the same result." // (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL) const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; FT_Size_RequestRec req; req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM; req.width = 0; req.height = (uint32_t)(size * 64 * rasterizer_density); req.horiResolution = 0; req.vertResolution = 0; FT_Request_Size(bd_font_data->FtFace, &req); // Output if (src->MergeMode == false) { // Read metrics FT_Size_Metrics metrics = bd_baked_data->FtSize->metrics; const float scale = 1.0f / (rasterizer_density * src->ExtraSizeScale); baked->Ascent = (float)FT_CEIL(metrics.ascender) * scale; // The pixel extents above the baseline in pixels (typically positive). baked->Descent = (float)FT_CEIL(metrics.descender) * scale; // The extents below the baseline in pixels (typically negative). //LineSpacing = (float)FT_CEIL(metrics.height) * scale; // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate. //LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender) * scale; // The spacing in pixels between one row's descent and the next row's ascent. //MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance) * scale; // This field gives the maximum horizontal cursor advance for all glyphs in the font. } return true; } static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) { IM_UNUSED(atlas); IM_UNUSED(baked); IM_UNUSED(src); ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; IM_ASSERT(bd_baked_data != nullptr); FT_Done_Size(bd_baked_data->FtSize); bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE() } static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x) { ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); if (glyph_index == 0) return false; if (bd_font_data->BakedLastActivated != baked) // <-- could use id { // Activate current size ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; FT_Activate_Size(bd_baked_data->FtSize); bd_font_data->BakedLastActivated = baked; } const FT_Glyph_Metrics* metrics = ImGui_ImplFreeType_LoadGlyph(bd_font_data, codepoint); if (metrics == nullptr) return false; FT_Face face = bd_font_data->FtFace; FT_GlyphSlot slot = face->glyph; const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; // Load metrics only mode const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density; if (out_advance_x != NULL) { IM_ASSERT(out_glyph == NULL); *out_advance_x = advance_x; return true; } // Render glyph into a bitmap (currently held by FreeType) // IMHEX PATCH BEGIN FT_Render_Mode render_mode; if (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome) render_mode = FT_RENDER_MODE_MONO; else if (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_SubPixel) render_mode = FT_RENDER_MODE_LCD; else render_mode = FT_RENDER_MODE_NORMAL; // IMHEX PATCH END FT_Error error = FT_Render_Glyph(slot, render_mode); const FT_Bitmap* ft_bitmap = &slot->bitmap; if (error != 0 || ft_bitmap == nullptr) return false; const int w = (int)ft_bitmap->width; const int h = (int)ft_bitmap->rows; const bool is_visible = (w != 0 && h != 0); // Prepare glyph out_glyph->Codepoint = codepoint; out_glyph->AdvanceX = advance_x; // Pack and retrieve position inside texture atlas if (is_visible) { ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h); if (pack_id == ImFontAtlasRectId_Invalid) { // Pathological out of memory case (TexMaxWidth/TexMaxHeight set too small?) IM_ASSERT(pack_id != ImFontAtlasRectId_Invalid && "Out of texture memory."); return false; } ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id); // Render pixels to our temporary buffer atlas->Builder->TempBuffer.resize(w * h * 4); uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data; ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, w); const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset. float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f) + baked->Ascent; float recip_h = 1.0f / rasterizer_density; float recip_v = 1.0f / rasterizer_density; // Register glyph float glyph_off_x = (float)face->glyph->bitmap_left; float glyph_off_y = (float)-face->glyph->bitmap_top; out_glyph->X0 = glyph_off_x * recip_h + font_off_x; out_glyph->Y0 = glyph_off_y * recip_v + font_off_y; out_glyph->X1 = (glyph_off_x + w) * recip_h + font_off_x; out_glyph->Y1 = (glyph_off_y + h) * recip_v + font_off_y; out_glyph->Visible = true; out_glyph->Colored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA); out_glyph->PackId = pack_id; ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, (const unsigned char*)temp_buffer, ImTextureFormat_RGBA32, w * 4); } return true; } static bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint) { IM_UNUSED(atlas); ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; int glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); return glyph_index != 0; } const ImFontLoader* ImGuiFreeType::GetFontLoader() { static ImFontLoader loader; loader.Name = "FreeType"; loader.LoaderInit = ImGui_ImplFreeType_LoaderInit; loader.LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown; loader.FontSrcInit = ImGui_ImplFreeType_FontSrcInit; loader.FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy; loader.FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph; loader.FontBakedInit = ImGui_ImplFreeType_FontBakedInit; loader.FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy; loader.FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph; loader.FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData); return &loader; } void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) { GImGuiFreeTypeAllocFunc = alloc_func; GImGuiFreeTypeFreeFunc = free_func; GImGuiFreeTypeAllocatorUserData = user_data; } bool ImGuiFreeType::DebugEditFontLoaderFlags(unsigned int* p_font_loader_flags) { bool edited = false; edited |= ImGui::CheckboxFlags("NoHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoHinting); edited |= ImGui::CheckboxFlags("NoAutoHint", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoAutoHint); edited |= ImGui::CheckboxFlags("ForceAutoHint",p_font_loader_flags, ImGuiFreeTypeLoaderFlags_ForceAutoHint); edited |= ImGui::CheckboxFlags("LightHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LightHinting); edited |= ImGui::CheckboxFlags("MonoHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_MonoHinting); edited |= ImGui::CheckboxFlags("Bold", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bold); edited |= ImGui::CheckboxFlags("Oblique", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Oblique); edited |= ImGui::CheckboxFlags("Monochrome", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Monochrome); edited |= ImGui::CheckboxFlags("LoadColor", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LoadColor); edited |= ImGui::CheckboxFlags("Bitmap", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bitmap); return edited; } #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG // For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c // The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT) struct LunasvgPortState { FT_Error err = FT_Err_Ok; lunasvg::Matrix matrix; std::unique_ptr svg = nullptr; }; static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state) { *_state = IM_NEW(LunasvgPortState)(); return FT_Err_Ok; } static void ImGuiLunasvgPortFree(FT_Pointer* _state) { IM_DELETE(*(LunasvgPortState**)_state); } static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state) { LunasvgPortState* state = *(LunasvgPortState**)_state; // If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error. if (state->err != FT_Err_Ok) return state->err; // rows is height, pitch (or stride) equals to width * sizeof(int32) lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch); #if LUNASVG_VERSION_MAJOR >= 3 state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated #else state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated #endif state->err = FT_Err_Ok; return state->err; } static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state) { FT_SVG_Document document = (FT_SVG_Document)slot->other; LunasvgPortState* state = *(LunasvgPortState**)_state; FT_Size_Metrics& metrics = document->metrics; // This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender(). // If it's the latter, don't do anything because it's // already done in the former. if (cache) return state->err; state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length); if (state->svg == nullptr) { state->err = FT_Err_Invalid_SVG_Document; return state->err; } #if LUNASVG_VERSION_MAJOR >= 3 lunasvg::Box box = state->svg->boundingBox(); #else lunasvg::Box box = state->svg->box(); #endif double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h); double xx = (double)document->transform.xx / (1 << 16); double xy = -(double)document->transform.xy / (1 << 16); double yx = -(double)document->transform.yx / (1 << 16); double yy = (double)document->transform.yy / (1 << 16); double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem; double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem; #if LUNASVG_VERSION_MAJOR >= 3 // Scale, transform and pre-translate the matrix for the rendering step state->matrix = lunasvg::Matrix::translated(-box.x, -box.y); state->matrix.multiply(lunasvg::Matrix(xx, xy, yx, yy, x0, y0)); state->matrix.scale(scale, scale); // Apply updated transformation to the bounding box box.transform(state->matrix); #else // Scale and transform, we don't translate the svg yet state->matrix.identity(); state->matrix.scale(scale, scale); state->matrix.transform(xx, xy, yx, yy, x0, y0); state->svg->setMatrix(state->matrix); // Pre-translate the matrix for the rendering step state->matrix.translate(-box.x, -box.y); // Get the box again after the transformation box = state->svg->box(); #endif // Calculate the bitmap size slot->bitmap_left = FT_Int(box.x); slot->bitmap_top = FT_Int(-box.y); slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h)); slot->bitmap.width = (unsigned int)(ImCeil((float)box.w)); slot->bitmap.pitch = slot->bitmap.width * 4; slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; // Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box. double metrics_width = box.w; double metrics_height = box.h; double horiBearingX = box.x; double horiBearingY = -box.y; double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0; double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0; slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0)); // Using IM_ROUND() assume width and height are positive slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0)); slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64); slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64); slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64); slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64); if (slot->metrics.vertAdvance == 0) slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0); state->err = FT_Err_Ok; return state->err; } #endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG //----------------------------------------------------------------------------- #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #ifdef _MSC_VER #pragma warning (pop) #endif #endif // #ifndef IMGUI_DISABLE ================================================ FILE: lib/third_party/imgui/imgui_test_engine/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) # https://github.com/ocornut/imgui_test_engine project(imgui_test_engine) set(CMAKE_CXX_STANDARD 23) if (IMHEX_ENABLE_IMGUI_TEST_ENGINE AND NOT IMHEX_EXTERNAL_PLUGIN_BUILD) add_library(imgui_test_engine OBJECT source/imgui_capture_tool.cpp source/imgui_te_context.cpp source/imgui_te_coroutine.cpp source/imgui_te_engine.cpp source/imgui_te_exporters.cpp source/imgui_te_perftool.cpp source/imgui_te_ui.cpp source/imgui_te_utils.cpp ) target_include_directories(imgui_test_engine PUBLIC include ) target_link_libraries(imgui_test_engine PRIVATE imgui_includes imgui_implot) target_compile_definitions(imgui_test_engine PUBLIC IMGUI_TEST_ENGINE=1) target_compile_definitions(imgui_test_engine PRIVATE EXPORT_SYMBOLS=1) if (MSVC) target_compile_options(imgui_test_engine PUBLIC /wd4251) endif() endif() add_library(imgui_test_engine_includes INTERFACE) target_include_directories(imgui_test_engine_includes INTERFACE include) target_include_directories(imgui_all_includes INTERFACE include) ================================================ FILE: lib/third_party/imgui/imgui_test_engine/LICENSE.txt ================================================ Dear ImGui Test Engine License (v1.04) Copyright (c) 2018-2025 Omar Cornut This document is a legal agreement ("License") between you ("Licensee") and DISCO HELLO ("Licensor") that governs your use of Dear ImGui Test Engine ("Software"). 1. LICENSE MODELS 1.1. Free license The Licensor grants you a free license ("Free License") if you meet ANY of the following criterion: - You are a natural person; - You are not a legal entity, or you are a not-for-profit legal entity; - You are using this Software for educational purposes; - You are using this Software to create Derivative Software released publicly and under an Open Source license, as defined by the Open Source Initiative; - You are a legal entity with a turnover inferior to 2 million USD (or equivalent) during your last fiscal year. 1.2. Paid license If you do not meet any criterion of Article 1.1, Licensor grants you a trial period of a maximum of 45 days, at no charge. Following this trial period, you must subscribe to a paid license ("Paid License") with the Licensor to continue using the Software. Paid Licenses are exclusively sold by DISCO HELLO. Paid Licenses and the associated information are available at the following URL: http://www.dearimgui.com/licenses 2. GRANT OF LICENSE 2.1. License scope A limited and non-exclusive worldwide license is hereby granted, to the Licensee, to reproduce, execute, publicly perform, and display, use, copy, modify, merge, distribute, or create derivative works based on and/or derived from the Software ("Derivative Software"). 2.2. Right of distribution License holders may also publish and/or distribute the Software or any Derivative Software. The above copyright notice and this license shall be included in all copies or substantial portions of the Software and/or Derivative Software. License holders may add their own attribution notices within the Derivative Software that they distribute. Such attribution notices must not directly or indirectly imply a modification of the License. License holders may provide to their modifications their own copyright and/or additional or different terms and conditions, providing such conditions complies with this License. 3. DISCLAIMER THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: lib/third_party/imgui/imgui_test_engine/README.md ================================================ # Dear ImGui Test Engine + Test Suite Automation system for [Dear ImGui](https://github.com/ocornut/imgui) and applications/games/engines using Dear ImGui. Because Dear ImGui tends to be wired in many low-level subsystems within your codebase, you can leverage that to automate actions interacting with them. It may be a very efficient solution to automate and tests many things (engine systems etc.). ## Contents - [imgui_test_engine/](https://github.com/ocornut/imgui_test_engine/tree/main/imgui_test_engine): dear imgui test engine / automation system (library) - [imgui_test_suite/](https://github.com/ocornut/imgui_test_engine/tree/main/imgui_test_suite): dear imgui test suite (app) - [app_minimal/](https://github.com/ocornut/imgui_test_engine/tree/main/app_minimal): minimal demo app showcasing how to integrate the test engine (app) - [shared/](https://github.com/ocornut/imgui_test_engine/tree/main/shared): shared C++ helpers for apps ## Don't blink Running Dear ImGui Test Suite, in Fast Mode, with full rendering enabled: https://user-images.githubusercontent.com/8225057/182409619-cd3bf990-b383-4a6c-a6ba-c5afe7557d6c.mp4 Quick overview of what automation code may look like like: ```cpp ImGuiTest* test = IM_REGISTER_TEST(e, "demo_test", "test1"); test->TestFunc = [](ImGuiTestContext* ctx) { ctx->SetRef("My Window"); // Set a base path so we don't have to specify full path afterwards ctx->ItemClick("My Button"); // Click "My Button" inside "My Window" ctx->ItemCheck("Node/Checkbox"); // Open "Node", find "Checkbox", ensure it is checked if not checked already. ctx->ItemInputValue("Slider", 123); // Find "Slider" and set the value to 123 IM_CHECK_EQ(app->SliderValue, 123); // Check value on app side ctx->MenuCheck("//Dear ImGui Demo/Tools/About Dear ImGui"); // Show Dear ImGui About Window (assume Demo window is open) }; ``` ## Overview - Designed to **automate and test Dear ImGui applications**. - We also use it to **self-test Dear ImGui itself**, reduce regression and facilitate contributions. - **Test Engine interacts mostly from the point of view of an end-user, by injecting mouse/keyboard/gamepad** inputs into Dear ImGui's IO. It means it tries to "find its way" toward accomplishing an action. Opening an item may mean CTRL+Tabbing into a given widow, moving things out of the way, scrolling to locate the item, querying its open status, etc. - It can be used for a variety of testing (smoke testing, integration/functional testing) or automation purposes (running tasks, capturing videos, etc.). - Your app can be controlled from Dear ImGui + You can now automate your app = **You can test anything exposed in your app/engine**! (not only UI). - It **can run in your windowed application**. **It can also run headless** (e.g. running GUI tests from a console or on a CI server without rendering). - It **can run at simulated human speed** (for watching or exporting videos) or **can run in fast mode** (e.g. teleporting mouse). - It **can export screenshots and videos/gifs**. They can be leveraged for some forms of testing, but also to generate assets for documentation, or notify teams of certain changes. Assets that often need to be updated are best generated from a script inside of manually recreated/cropped/exported. - You can use it to program high-level commands e.g. `MenuCheck("Edit/Options/Enable Grid")` or run more programmatic queries ("list openable items in that section, then open them all"). So from your POV it could be used for simple smoke testing ("open all our tools") or for more elaborate testing ("interact with xxx and xxx, check result"). - It **can be used as a form of "live tutorial / demo"** where a script can run on an actual user application to showcase features. - It includes a performance tool and viewer which we used to record/compare performances between builds and branches (optional, requires ImPlot). ## Status - It is currently a C++ API but down the line we can expect that the commands will be better standardized, stored in data files, called from other languages. - It has been in use and development since 2018 and only made public at the end of 2022. We'll provide best effort to make it suitable for user needs. - You will run into problems and shortcomings. We are happy to hear about them and improve the software and documentation accordingly. ## Documentation See [Wiki](https://github.com/ocornut/imgui_test_engine/wiki) sections: - [Overview](https://github.com/ocornut/imgui_test_engine/wiki/Overview) - [Setting Up](https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up) - [Automation API](https://github.com/ocornut/imgui_test_engine/wiki/Automation-API) (ImGuiTestContext interface) - [Named References](https://github.com/ocornut/imgui_test_engine/wiki/Named-References) (all nice uses of ImGuiTestRef) - [Exporting Results](https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results) - [Screen & Video Captures](https://github.com/ocornut/imgui_test_engine/wiki/Screen-and-Video-Captures) ## Licenses - The [imgui_test_engine/](https://github.com/ocornut/imgui_test_engine/tree/main/imgui_test_engine) folder is under the [Dear ImGui Test Engine License](https://github.com/ocornut/imgui_test_engine/blob/main/imgui_test_engine/LICENSE.txt).
TL;DR: free for individuals, educational, open-source and small businesses uses. Paid for larger businesses. Read license for details. License sales to larger businesses are used to fund and sustain the development of Dear ImGui. - The [imgui_test_suite/](https://github.com/ocornut/imgui_test_engine/tree/main/imgui_test_suite) folder and all other folders are all under the [MIT License](https://github.com/ocornut/imgui_test_engine/blob/main/imgui_test_suite/LICENSE.txt). ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_capture_tool.h ================================================ // dear imgui test engine // (screen/video capture tool) // This is usable as a standalone applet or controlled by the test engine. // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #pragma once // Need "imgui_te_engine.h" included for ImFuncPtr //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- // Our types struct ImGuiCaptureArgs; // Parameters for Capture struct ImGuiCaptureContext; // State of an active capture tool struct ImGuiCaptureImageBuf; // Simple helper to store an RGBA image in memory struct ImGuiCaptureToolUI; // Capture tool instance + UI window typedef unsigned int ImGuiCaptureFlags; // See enum: ImGuiCaptureFlags_ // Capture function which needs to be provided by user application typedef bool (ImGuiScreenCaptureFunc)(ImGuiID viewport_id, int x, int y, int w, int h, unsigned int* pixels, void* user_data); // External types struct ImGuiWindow; // imgui.h //----------------------------------------------------------------------------- // [Internal] // Helper class for simple bitmap manipulation (not particularly efficient!) struct IMGUI_API ImGuiCaptureImageBuf { int Width; int Height; unsigned int* Data; // RGBA8 ImGuiCaptureImageBuf() { Width = Height = 0; Data = nullptr; } ~ImGuiCaptureImageBuf() { Clear(); } void Clear(); // Free allocated memory buffer if such exists. void CreateEmpty(int w, int h); // Reallocate buffer for pixel data and zero it. bool SaveFile(const char* filename); // Save pixel data to specified image file. void RemoveAlpha(); // Clear alpha channel from all pixels. }; enum ImGuiCaptureFlags_ : unsigned int { ImGuiCaptureFlags_None = 0, ImGuiCaptureFlags_StitchAll = 1 << 0, // Capture entire window scroll area (by scrolling and taking multiple screenshot). Only works for a single window. ImGuiCaptureFlags_IncludeOtherWindows = 1 << 1, // Disable hiding other windows (when CaptureAddWindow has been called by default other windows are hidden) ImGuiCaptureFlags_IncludePopups = 1 << 2, // Expand capture area to automatically include visible popups (Unused if ImGuiCaptureFlags_IncludeOtherWindows is set) ImGuiCaptureFlags_HideMouseCursor = 1 << 3, // Hide render software mouse cursor during capture. ImGuiCaptureFlags_Instant = 1 << 4, // Perform capture on very same frame. Only works when capturing a rectangular region. Unsupported features: content stitching, window hiding, window relocation. ImGuiCaptureFlags_NoSave = 1 << 5 // Do not save output image. }; // Defines input and output arguments for capture process. // When capturing from tests you can usually use the ImGuiTestContext::CaptureXXX() helpers functions. struct ImGuiCaptureArgs { // [Input] ImGuiCaptureFlags InFlags = 0; // Flags for customizing behavior of screenshot tool. ImVector InCaptureWindows; // Windows to capture. All other windows will be hidden. May be used with InCaptureRect to capture only some windows in specified rect. ImRect InCaptureRect; // Screen rect to capture. Does not include padding. float InPadding = 16.0f; // Extra padding at the edges of the screenshot. Ensure that there is available space around capture rect horizontally, also vertically if ImGuiCaptureFlags_StitchFullContents is not used. char InOutputFile[256] = ""; // Output will be saved to a file if InOutputImageBuf is nullptr. ImGuiCaptureImageBuf* InOutputImageBuf = nullptr; // _OR_ Output will be saved to image buffer if specified. int InRecordFPSTarget = 30; // FPS target for recording videos. int InSizeAlign = 0; // Resolution alignment (0 = auto, 1 = no alignment, >= 2 = align width/height to be multiple of given value) // [Output] ImVec2 OutImageSize; // Produced image size. }; enum ImGuiCaptureStatus { ImGuiCaptureStatus_InProgress, ImGuiCaptureStatus_Done, ImGuiCaptureStatus_Error }; struct ImGuiCaptureWindowData { ImGuiWindow* Window; ImRect BackupRect; ImVec2 PosDuringCapture; }; // Implements functionality for capturing images struct IMGUI_API ImGuiCaptureContext { // IO ImFuncPtr(ImGuiScreenCaptureFunc) ScreenCaptureFunc = nullptr; // Graphics backend specific function that captures specified portion of framebuffer and writes RGBA data to `pixels` buffer. void* ScreenCaptureUserData = nullptr; // Custom user pointer which is passed to ScreenCaptureFunc. (Optional) char* VideoCaptureEncoderPath = nullptr; // Video encoder path (not owned, stored externally). int VideoCaptureEncoderPathSize = 0; // Optional. Set in order to edit this parameter from UI. char* VideoCaptureEncoderParams = nullptr; // Video encoder params (not owned, stored externally). int VideoCaptureEncoderParamsSize = 0; // Optional. Set in order to edit this parameter from UI. char* GifCaptureEncoderParams = nullptr; // Video encoder params for GIF output (not owned, stored externally). int GifCaptureEncoderParamsSize = 0; // Optional. Set in order to edit this parameter from UI. // [Internal] ImRect _CaptureRect; // Viewport rect that is being captured. ImRect _CapturedWindowRect; // Top-left corner of region that covers all windows included in capture. This is not same as _CaptureRect.Min when capturing explicitly specified rect. int _ChunkNo = 0; // Number of chunk that is being captured when capture spans multiple frames. int _FrameNo = 0; // Frame number during capture process that spans multiple frames. ImVec2 _MouseRelativeToWindowPos; // Mouse cursor position relative to captured window (when _StitchAll is in use). ImGuiWindow* _HoveredWindow = nullptr; // Window which was hovered at capture start. ImGuiCaptureImageBuf _CaptureBuf; // Output image buffer. const ImGuiCaptureArgs* _CaptureArgs = nullptr; // Current capture args. Set only if capture is in progress. ImVector _WindowsData; // Backup windows that will have their rect modified and restored. args->InCaptureWindows can not be used because popups may get closed during capture and no longer appear in that list. // [Internal] Video recording bool _VideoRecording = false; // Flag indicating that video recording is in progress. double _VideoLastFrameTime = 0; // Time when last video frame was recorded. FILE* _VideoEncoderPipe = nullptr; // File writing to stdin of video encoder process. // [Internal] Backups bool _BackupMouseDrawCursor = false; // Initial value of g.IO.MouseDrawCursor ImVec2 _BackupDisplayWindowPadding; // Backup padding. We set it to {0, 0} during capture. ImVec2 _BackupDisplaySafeAreaPadding; // Backup padding. We set it to {0, 0} during capture. //------------------------------------------------------------------------- // Functions //------------------------------------------------------------------------- ImGuiCaptureContext(ImGuiScreenCaptureFunc capture_func = nullptr) { ScreenCaptureFunc = capture_func; _MouseRelativeToWindowPos = ImVec2(-FLT_MAX, -FLT_MAX); } // These functions should be called from appropriate context hooks. See ImGui::AddContextHook() for more info. // (ImGuiTestEngine automatically calls that for you, so this only apply to independently created instance) void PreNewFrame(); void PreRender(); void PostRender(); // Update capturing. If this function returns true then it should be called again with same arguments on the next frame. ImGuiCaptureStatus CaptureUpdate(ImGuiCaptureArgs* args); void RestoreBackedUpData(); void ClearState(); // Begin video capture. Call CaptureUpdate() every frame afterwards until it returns false. void BeginVideoCapture(ImGuiCaptureArgs* args); void EndVideoCapture(); bool IsCapturingVideo(); bool IsCapturing(); }; //----------------------------------------------------------------------------- // ImGuiCaptureToolUI //----------------------------------------------------------------------------- // Implements UI for capturing images // (when using ImGuiTestEngine scripting API you may not need to use this at all) struct IMGUI_API ImGuiCaptureToolUI { float SnapGridSize = 32.0f; // Size of the grid cell for "snap to grid" functionality. char OutputLastFilename[256] = ""; // File name of last captured file. char* VideoCaptureExtension = nullptr; // Video file extension (e.g. ".gif" or ".mp4") int VideoCaptureExtensionSize = 0; // Optional. Set in order to edit this parameter from UI. ImGuiCaptureArgs _CaptureArgs; // Capture args bool _StateIsPickingWindow = false; bool _StateIsCapturing = false; ImVector _SelectedWindows; char _OutputFileTemplate[256] = ""; // int _FileCounter = 0; // Counter which may be appended to file name when saving. By default, counting starts from 1. When done this field holds number of saved files. // Public ImGuiCaptureToolUI(); void ShowCaptureToolWindow(ImGuiCaptureContext* context, bool* p_open = nullptr); // Render a capture tool window with various options and utilities. // [Internal] void _CaptureWindowPicker(ImGuiCaptureArgs* args); // Render a window picker that captures picked window to file specified in file_name. void _CaptureWindowsSelector(ImGuiCaptureContext* context, ImGuiCaptureArgs* args); // Render a selector for selecting multiple windows for capture. void _SnapWindowsToGrid(float cell_size); // Snap edges of all visible windows to a virtual grid. bool _InitializeOutputFile(); // Format output file template into capture args struct and ensure target directory exists. bool _ShowEncoderConfigFields(ImGuiCaptureContext* context); }; #define IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG "-hide_banner -loglevel error -r $FPS -f rawvideo -pix_fmt rgba -s $WIDTHx$HEIGHT -i - -threads 0 -y -preset ultrafast -pix_fmt yuv420p -crf 20 $OUTPUT" #define IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG "-hide_banner -loglevel error -r $FPS -f rawvideo -pix_fmt rgba -s $WIDTHx$HEIGHT -i - -threads 0 -y -filter_complex \"split=2 [a] [b]; [a] palettegen [pal]; [b] [pal] paletteuse\" $OUTPUT" ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_te_context.h ================================================ // dear imgui test engine // (context when a running test + end user automation API) // This is the main (if not only) interface that your Tests will be using. // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #pragma once #include "imgui.h" #include "imgui_internal.h" // ImGuiAxis, ImGuiItemStatusFlags, ImGuiInputSource, ImGuiWindow #include "imgui_te_engine.h" // ImGuiTestStatus, ImGuiTestRunFlags, ImGuiTestActiveFunc, ImGuiTestItemInfo, ImGuiTestLogFlags /* Index of this file: // [SECTION] Header mess, warnings // [SECTION] Forward declarations // [SECTION] ImGuiTestRef // [SECTION] Helper keys // [SECTION] ImGuiTestContext related Flags/Enumerations // [SECTION] ImGuiTestGenericVars, ImGuiTestGenericItemStatus // [SECTION] ImGuiTestContext // [SECTION] Debugging macros: IM_SUSPEND_TESTFUNC() // [SECTION] Testing/Checking macros: IM_CHECK(), IM_ERRORF() etc. */ //------------------------------------------------------------------------- // [SECTION] Header mess, warnings //------------------------------------------------------------------------- // Undo some of the damage done by #ifdef Yield #undef Yield #endif #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif //------------------------------------------------------------------------- // [SECTION] Forward declarations //------------------------------------------------------------------------- // This file typedef int ImGuiTestOpFlags; // Flags: See ImGuiTestOpFlags_ // External: imgui struct ImGuiDockNode; struct ImGuiTabBar; struct ImGuiWindow; // External: test engine struct ImGuiTest; // A test registered with IM_REGISTER_TEST() struct ImGuiTestEngine; // Test Engine Instance (opaque) struct ImGuiTestEngineIO; // Test Engine IO structure (configuration flags, state) struct ImGuiTestItemInfo; // Information gathered about an item: label, status, bounding box etc. struct ImGuiTestItemList; // Result of an GatherItems() query struct ImGuiTestInputs; // Test Engine Simulated Inputs structure (opaque) struct ImGuiTestGatherTask; // Test Engine task for scanning/finding items struct ImGuiCaptureArgs; // Parameters for ctx->CaptureXXX functions enum ImGuiTestVerboseLevel : int; //------------------------------------------------------------------------- // [SECTION] ImGuiTestRef //------------------------------------------------------------------------- // Weak reference to an Item/Window given an hashed ID _or_ a string path ID. // This is most often passed as argument to function and generally has a very short lifetime. // Documentation: https://github.com/ocornut/imgui_test_engine/wiki/Named-References // (SUGGESTION: add those constructors to "VA Step Filter" (Visual Assist) or a .natstepfilter file (Visual Studio) so they are skipped by F11 (StepInto) struct IMGUI_API ImGuiTestRef { ImGuiID ID; // Pre-hashed ID const char* Path; // Relative or absolute path (string pointed to, not owned, as our lifetime is very short) ImGuiTestRef() { ID = 0; Path = nullptr; } ImGuiTestRef(ImGuiID id) { ID = id; Path = nullptr; } ImGuiTestRef(const char* path) { ID = 0; Path = path; } bool IsEmpty() const { return ID == 0 && (Path == nullptr || Path[0] == 0); } }; // Debug helper to output a string showing the Path, ID or Debug Label based on what is available (some items only have ID as we couldn't find/store a Path) // (The size is arbitrary, this is only used for logging info the user/debugger) struct IMGUI_API ImGuiTestRefDesc { char Buf[80]; const char* c_str() { return Buf; } ImGuiTestRefDesc(const ImGuiTestRef& ref); ImGuiTestRefDesc(const ImGuiTestRef& ref, const ImGuiTestItemInfo& item); }; //------------------------------------------------------------------------- // [SECTION] ImGuiTestContext related Flags/Enumerations //------------------------------------------------------------------------- // Named actions. Generally you will call the named helpers e.g. ItemClick(). This is used by shared/low-level functions such as ItemAction(). enum ImGuiTestAction { ImGuiTestAction_Unknown = 0, ImGuiTestAction_Hover, // Move mouse ImGuiTestAction_Click, // Move mouse and click ImGuiTestAction_DoubleClick, // Move mouse and double-click ImGuiTestAction_Check, // Check item if unchecked (Checkbox, MenuItem or any widget reporting ImGuiItemStatusFlags_Checkable) ImGuiTestAction_Uncheck, // Uncheck item if checked ImGuiTestAction_Open, // Open item if closed (TreeNode, BeginMenu or any widget reporting ImGuiItemStatusFlags_Openable) ImGuiTestAction_Close, // Close item if opened ImGuiTestAction_Input, // Start text inputing into a field (e.g. CTRL+Click on Drags/Slider, click on InputText etc.) ImGuiTestAction_NavActivate, // Activate item with navigation ImGuiTestAction_COUNT }; // Generic flags for many ImGuiTestContext functions // Some flags are only supported by a handful of functions. Check function headers for list of supported flags. enum ImGuiTestOpFlags_ { ImGuiTestOpFlags_None = 0, ImGuiTestOpFlags_NoCheckHoveredId = 1 << 1, // Don't check for HoveredId after aiming for a widget. A few situations may want this: while e.g. dragging or another items prevents hovering, or for items that don't use ItemHoverable() ImGuiTestOpFlags_NoError = 1 << 2, // Don't abort/error e.g. if the item cannot be found or the operation doesn't succeed. ImGuiTestOpFlags_NoFocusWindow = 1 << 3, // Don't focus window when aiming at an item ImGuiTestOpFlags_NoAutoUncollapse = 1 << 4, // Disable automatically uncollapsing windows (useful when specifically testing Collapsing behaviors) ImGuiTestOpFlags_NoAutoOpenFullPath = 1 << 5, // Disable automatically opening intermediaries (e.g. ItemClick("Hello/OK") will automatically first open "Hello" if "OK" isn't found. Only works if ref is a string path. ImGuiTestOpFlags_NoYield = 1 << 6, // Don't yield (only supported by a few functions), in case you need to manage rigorous per-frame timing. ImGuiTestOpFlags_IsSecondAttempt = 1 << 7, // Used by recursing functions to indicate a second attempt ImGuiTestOpFlags_MoveToEdgeL = 1 << 8, // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities. ImGuiTestOpFlags_MoveToEdgeR = 1 << 9, ImGuiTestOpFlags_MoveToEdgeU = 1 << 10, ImGuiTestOpFlags_MoveToEdgeD = 1 << 11, }; // Advanced filtering for ItemActionAll() struct IMGUI_API ImGuiTestActionFilter { int MaxDepth; int MaxPasses; const int* MaxItemCountPerDepth; ImGuiItemStatusFlags RequireAllStatusFlags; ImGuiItemStatusFlags RequireAnyStatusFlags; ImGuiTestActionFilter() { MaxDepth = -1; MaxPasses = -1; MaxItemCountPerDepth = nullptr; RequireAllStatusFlags = RequireAnyStatusFlags = 0; } }; //------------------------------------------------------------------------- // [SECTION] ImGuiTestGenericVars, ImGuiTestGenericItemStatus //------------------------------------------------------------------------- // Helper struct to store various query-able state of an item. // This facilitate interactions between GuiFunc and TestFunc, since those state are frequently used. struct IMGUI_API ImGuiTestGenericItemStatus { int RetValue; // return value int Hovered; // result of IsItemHovered() int HoveredAllowDisabled; // result of IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) int Active; // result of IsItemActive() int Focused; // result of IsItemFocused() int Clicked; // result of IsItemClicked() int Visible; // result of IsItemVisible() int Edited; // result of IsItemEdited() int Activated; // result of IsItemActivated() int Deactivated; // result of IsItemDeactivated() int DeactivatedAfterEdit; // result of IsItemDeactivatedAfterEdit() ImGuiTestGenericItemStatus() { Clear(); } void Clear() { memset(this, 0, sizeof(*this)); } void QuerySet(bool ret_val = false) { Clear(); QueryInc(ret_val); } void QueryInc(bool ret_val = false) { RetValue += ret_val; Hovered += ImGui::IsItemHovered(); HoveredAllowDisabled += ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled); Active += ImGui::IsItemActive(); Focused += ImGui::IsItemFocused(); Clicked += ImGui::IsItemClicked(); Visible += ImGui::IsItemVisible(); Edited += ImGui::IsItemEdited(); Activated += ImGui::IsItemActivated(); Deactivated += ImGui::IsItemDeactivated(); DeactivatedAfterEdit += ImGui::IsItemDeactivatedAfterEdit(); } void Draw() { ImGui::Text("Ret: %d, Hovered: %d, Active: %d, Focused: %d\nClicked: %d, Visible: %d, Edited: %d\nActivated: %d, Deactivated: %d, DeactivatedAfterEdit: %d", RetValue, Hovered, Active, Focused, Clicked, Visible, Edited, Activated, Deactivated, DeactivatedAfterEdit); } }; // Generic structure with various storage fields. // This is useful for tests to quickly share data between GuiFunc and TestFunc without creating custom data structure. // If those fields are not enough: using test->SetVarsDataType<>() + ctx->GetVars<>() it is possible to store custom data. struct IMGUI_API ImGuiTestGenericVars { // Generic storage with a bit of semantic to make user/test code look neater int Step; int Count; ImGuiID DockId; ImGuiID OwnerId; ImVec2 WindowSize; ImGuiWindowFlags WindowFlags; ImGuiTableFlags TableFlags; ImGuiPopupFlags PopupFlags; ImGuiInputTextFlags InputTextFlags; ImGuiTestGenericItemStatus Status; bool ShowWindow1, ShowWindow2; bool UseClipper; bool UseViewports; float Width; ImVec2 Pos; ImVec2 Pivot; ImVec2 ItemSize; ImVec4 Color1, Color2; // Generic unnamed storage int Int1, Int2, IntArray[10]; float Float1, Float2, FloatArray[10]; bool Bool1, Bool2, BoolArray[10]; ImGuiID Id, IdArray[10]; char Str1[256], Str2[256]; ImGuiTestGenericVars() { Clear(); } void Clear() { memset(this, 0, sizeof(*this)); } }; //------------------------------------------------------------------------- // [SECTION] ImGuiTestContext // Context for a running ImGuiTest // This is the interface that most tests will interact with. //------------------------------------------------------------------------- struct IMGUI_API ImGuiTestContext { // User variables ImGuiTestGenericVars GenericVars; // Generic variables holder for convenience. void* UserVars = nullptr; // Access using ctx->GetVars(). Setup with test->SetVarsDataType<>(). // Public fields ImGuiContext* UiContext = nullptr; // UI context ImGuiTestEngineIO* EngineIO = nullptr; // Test Engine IO/settings ImGuiTest* Test = nullptr; // Test currently running ImGuiTestOutput* TestOutput = nullptr; // Test output (generally == &Test->Output) ImGuiTestOpFlags OpFlags = ImGuiTestOpFlags_None; // Flags affecting all operation (supported: ImGuiTestOpFlags_NoAutoUncollapse) int PerfStressAmount = 0; // Convenience copy of engine->IO.PerfStressAmount int FrameCount = 0; // Test frame count (restarts from zero every time) int FirstTestFrameCount = 0; // First frame where TestFunc is running (after warm-up frame). This is generally -1 or 0 depending on whether we have warm up enabled bool FirstGuiFrame = false; bool HasDock = false; // #ifdef IMGUI_HAS_DOCK expressed in an easier to test value ImGuiCaptureArgs* CaptureArgs = nullptr; // Capture settings used by ctx->Capture*() functions //------------------------------------------------------------------------- // [Internal Fields] //------------------------------------------------------------------------- ImGuiTestEngine* Engine = nullptr; ImGuiTestInputs* Inputs = nullptr; ImGuiTestRunFlags RunFlags = ImGuiTestRunFlags_None; ImGuiTestActiveFunc ActiveFunc = ImGuiTestActiveFunc_None; // None/GuiFunc/TestFunc double RunningTime = 0.0; // Amount of wall clock time the Test has been running. Used by safety watchdog. int ActionDepth = 0; // Nested depth of ctx-> function calls (used to decorate log) int CaptureCounter = 0; // Number of captures int ErrorCounter = 0; // Number of errors (generally this maxxes at 1 as most functions will early out) bool Abort = false; double PerfRefDt = -1.0; int PerfIterations = 400; // Number of frames for PerfCapture() measurements char RefStr[256] = { 0 }; // Reference window/path over which all named references are based ImGuiID RefID = 0; // Reference ID over which all named references are based ImGuiID RefWindowID = 0; // ID of a window that contains RefID item ImGuiInputSource InputMode = ImGuiInputSource_Mouse; // Prefer interacting with mouse/keyboard/gamepad ImVector TempString; ImVector Clipboard; // Private clipboard for the test instance ImVector ForeignWindowsToHide; ImGuiTestItemInfo DummyItemInfoNull; // Storage for ItemInfoNull() bool CachedLinesPrintedToTTY = false; //------------------------------------------------------------------------- // Public API //------------------------------------------------------------------------- // Main control void Finish(ImGuiTestStatus status = ImGuiTestStatus_Success); // Set test status and stop running. Usually called when running test logic from GuiFunc() only. ImGuiTestStatus RunChildTest(const char* test_name, ImGuiTestRunFlags flags = 0); // [Experimental] Run another test from the current test. template T& GetVars() { IM_ASSERT(UserVars != nullptr); return *(T*)(UserVars); }// Campanion to using t->SetVarsDataType<>(). FIXME: Assert to compare sizes // Main status queries bool IsError() const { return TestOutput->Status == ImGuiTestStatus_Error || Abort; } bool IsWarmUpGuiFrame() const { return FrameCount < FirstTestFrameCount; } // Unless test->Flags has ImGuiTestFlags_NoGuiWarmUp, we run GuiFunc() twice before running TestFunc(). Those frames are called "WarmUp" frames. bool IsFirstGuiFrame() const { return FirstGuiFrame; } bool IsFirstTestFrame() const { return FrameCount == FirstTestFrameCount; } // First frame where TestFunc is running (after warm-up frame). bool IsGuiFuncOnly() const { return (RunFlags & ImGuiTestRunFlags_GuiFuncOnly) != 0; } // Debugging bool SuspendTestFunc(const char* file = nullptr, int line = 0); // [DEBUG] Generally called via IM_SUSPEND_TESTFUNC // Logging void LogEx(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, ...) IM_FMTARGS(4); void LogExV(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, va_list args) IM_FMTLIST(4); void LogToTTY(ImGuiTestVerboseLevel level, const char* message, const char* message_end = nullptr); void LogToDebugger(ImGuiTestVerboseLevel level, const char* message); void LogDebug(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Debug or ImGuiTestVerboseLevel_Trace depending on context depth void LogInfo(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Info void LogWarning(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Warning void LogError(const char* fmt, ...) IM_FMTARGS(2); // ImGuiTestVerboseLevel_Error void LogBasicUiState(); void LogItemList(ImGuiTestItemList* list); // Yield, Timing void Yield(int count = 1); void Sleep(float time_in_second); // Sleep for a given simulation time, unless in Fast mode void SleepShort(); // Standard short delay of io.ActionDelayShort (~0.15f), unless in Fast mode. void SleepStandard(); // Standard regular delay of io.ActionDelayStandard (~0.40f), unless in Fast mode. void SleepNoSkip(float time_in_second, float framestep_in_second); // Base Reference // - ItemClick("Window/Button") --> click "Window/Button" // - SetRef("Window"), ItemClick("Button") --> click "Window/Button" // - SetRef("Window"), ItemClick("/Button") --> click "Window/Button" // - SetRef("Window"), ItemClick("//Button") --> click "/Button" // - SetRef("//$FOCUSED"), ItemClick("Button") --> click "Button" in focused window. // See https://github.com/ocornut/imgui_test_engine/wiki/Named-References about using ImGuiTestRef in all ImGuiTestContext functions. // Note: SetRef() may take multiple frames to complete if specified ref is an item id. // Note: SetRef() ignores current reference, so they are always absolute path. void SetRef(ImGuiTestRef ref); void SetRef(ImGuiWindow* window); // Shortcut to SetRef(window->Name) which works for ChildWindow (see code) ImGuiTestRef GetRef(); // Windows // - Use WindowInfo() to access path to child windows, since the paths are internally mangled. // - SetRef(WindowInfo("Parent/Child")->Window) --> set ref to child window. ImGuiTestItemInfo WindowInfo(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void WindowClose(ImGuiTestRef window_ref); void WindowCollapse(ImGuiTestRef window_ref, bool collapsed); void WindowFocus(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void WindowBringToFront(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void WindowMove(ImGuiTestRef window_ref, ImVec2 pos, ImVec2 pivot = ImVec2(0.0f, 0.0f), ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void WindowResize(ImGuiTestRef window_ref, ImVec2 sz); bool WindowTeleportToMakePosVisible(ImGuiTestRef window_ref, ImVec2 pos_in_window); ImGuiWindow*GetWindowByRef(ImGuiTestRef window_ref); // Popups void PopupCloseOne(); void PopupCloseAll(); ImGuiID PopupGetWindowID(ImGuiTestRef ref); // Get hash for a decorated ID Path. // Note: for windows you may use WindowInfo() ImGuiID GetID(ImGuiTestRef ref); ImGuiID GetID(ImGuiTestRef ref, ImGuiTestRef seed_ref); // Miscellaneous helpers ImVec2 GetPosOnVoid(ImGuiViewport* viewport); // Find a point that has no windows // FIXME: This needs error return and flag to enable/disable forcefully finding void. ImVec2 GetWindowTitlebarPoint(ImGuiTestRef window_ref); // Return a clickable point on window title-bar (window tab for docked windows). ImVec2 GetMainMonitorWorkPos(); // Work pos and size of main viewport when viewports are disabled, or work pos and size of monitor containing main viewport when viewports are enabled. ImVec2 GetMainMonitorWorkSize(); // Screenshot/Video Captures void CaptureReset(); // Reset state (use when doing multiple captures) void CaptureSetExtension(const char* ext); // Set capture file format (otherwise for video this default to EngineIO->VideoCaptureExtension) bool CaptureAddWindow(ImGuiTestRef ref); // Add window to be captured (default to capture everything) void CaptureScreenshotWindow(ImGuiTestRef ref, int capture_flags = 0); // Trigger a screen capture of a single window (== CaptureAddWindow() + CaptureScreenshot()) bool CaptureScreenshot(int capture_flags = 0); // Trigger a screen capture bool CaptureBeginVideo(); // Start a video capture bool CaptureEndVideo(); // Mouse inputs void MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void MouseMoveToPos(ImVec2 pos); void MouseTeleportToPos(ImVec2 pos, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void MouseClick(ImGuiMouseButton button = 0); void MouseClickMulti(ImGuiMouseButton button, int count); void MouseDoubleClick(ImGuiMouseButton button = 0); void MouseDown(ImGuiMouseButton button = 0); void MouseUp(ImGuiMouseButton button = 0); void MouseLiftDragThreshold(ImGuiMouseButton button = 0); void MouseDragWithDelta(ImVec2 delta, ImGuiMouseButton button = 0); void MouseWheel(ImVec2 delta); void MouseWheelX(float dx) { MouseWheel(ImVec2(dx, 0.0f)); } void MouseWheelY(float dy) { MouseWheel(ImVec2(0.0f, dy)); } void MouseMoveToVoid(ImGuiViewport* viewport = nullptr); void MouseClickOnVoid(ImGuiMouseButton button = 0, ImGuiViewport* viewport = nullptr); ImGuiWindow*FindHoveredWindowAtPos(const ImVec2& pos); bool FindExistingVoidPosOnViewport(ImGuiViewport* viewport, ImVec2* out); // Mouse inputs: Viewports // - This is automatically called by SetRef() and any mouse action taking an item reference (e.g. ItemClick("button"), MouseClick("button")) // - But when using raw position directy e.g. MouseMoveToPos() / MouseTeleportToPos() without referring to the parent window before, this needs to be set. void MouseSetViewport(ImGuiWindow* window); void MouseSetViewportID(ImGuiID viewport_id); // Keyboard inputs void KeyDown(ImGuiKeyChord key_chord); void KeyUp(ImGuiKeyChord key_chord); void KeyPress(ImGuiKeyChord key_chord, int count = 1); void KeyHold(ImGuiKeyChord key_chord, float time); void KeySetEx(ImGuiKeyChord key_chord, bool is_down, float time); void KeyChars(const char* chars); // Input characters void KeyCharsAppend(const char* chars); // Input characters at end of field void KeyCharsAppendEnter(const char* chars); // Input characters at end of field, press Enter void KeyCharsReplace(const char* chars); // Delete existing field then input characters void KeyCharsReplaceEnter(const char* chars); // Delete existing field then input characters, press Enter // Navigation inputs // FIXME: Need some redesign/refactoring: // - This was initially intended to: replace mouse action with keyboard/gamepad // - Abstract keyboard vs gamepad actions // However this is widely inconsistent and unfinished at this point. void SetInputMode(ImGuiInputSource input_mode); // Mouse or Keyboard or Gamepad. In Keyboard or Gamepad mode, actions such as ItemClick or ItemInput are using nav facilities instead of Mouse. void NavMoveTo(ImGuiTestRef ref); void NavActivate(); // Activate current selected item: activate button, tweak sliders/drags. Equivalent of pressing Space on keyboard, ImGuiKey_GamepadFaceUp on a gamepad. void NavInput(); // Input into select item: input sliders/drags. Equivalent of pressing Enter on keyboard, ImGuiKey_GamepadFaceDown on a gamepad. // Scrolling void ScrollTo(ImGuiTestRef ref, ImGuiAxis axis, float scroll_v, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void ScrollToX(ImGuiTestRef ref, float scroll_x) { ScrollTo(ref, ImGuiAxis_X, scroll_x); } void ScrollToY(ImGuiTestRef ref, float scroll_y) { ScrollTo(ref, ImGuiAxis_Y, scroll_y); } void ScrollToTop(ImGuiTestRef ref); void ScrollToBottom(ImGuiTestRef ref); void ScrollToPos(ImGuiTestRef window_ref, float pos_v, ImGuiAxis axis, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void ScrollToPosX(ImGuiTestRef window_ref, float pos_x); void ScrollToPosY(ImGuiTestRef window_ref, float pos_y); void ScrollToItem(ImGuiTestRef ref, ImGuiAxis axis, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void ScrollToItemX(ImGuiTestRef ref); void ScrollToItemY(ImGuiTestRef ref); void ScrollToTabItem(ImGuiTabBar* tab_bar, ImGuiID tab_id); bool ScrollErrorCheck(ImGuiAxis axis, float expected, float actual, int* remaining_attempts); void ScrollVerifyScrollMax(ImGuiTestRef ref); // Low-level queries // - ItemInfo queries never returns nullptr! Instead they return an empty instance (info->IsEmpty(), info->ID == 0) and set contexted as errored. // - You can use ImGuiTestOpFlags_NoError to do a query without marking context as errored. This is what ItemExists() does. ImGuiTestItemInfo ItemInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); ImGuiTestItemInfo ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); ImGuiID ItemInfoHandleWildcardSearch(const char* wildcard_prefix_start, const char* wildcard_prefix_end, const char* wildcard_suffix_start); ImGuiTestItemInfo ItemInfoNull() { return ImGuiTestItemInfo(); } void GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth = -1); // Item/Widgets manipulation void ItemAction(ImGuiTestAction action, ImGuiTestRef ref, ImGuiTestOpFlags flags = 0, void* action_arg = nullptr); void ItemClick(ImGuiTestRef ref, ImGuiMouseButton button = 0, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Click, ref, flags, (void*)(size_t)button); } void ItemDoubleClick(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_DoubleClick, ref, flags); } void ItemCheck(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Check, ref, flags); } void ItemUncheck(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Uncheck, ref, flags); } void ItemOpen(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Open, ref, flags); } void ItemClose(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Close, ref, flags); } void ItemInput(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Input, ref, flags); } void ItemNavActivate(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_NavActivate, ref, flags); } // Item/Widgets: Batch actions over an entire scope void ItemActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent, const ImGuiTestActionFilter* filter = nullptr); void ItemOpenAll(ImGuiTestRef ref_parent, int depth = -1, int passes = -1); void ItemCloseAll(ImGuiTestRef ref_parent, int depth = -1, int passes = -1); // Item/Widgets: Helpers to easily set a value void ItemInputValue(ImGuiTestRef ref, int v); void ItemInputValue(ImGuiTestRef ref, float f); void ItemInputValue(ImGuiTestRef ref, const char* str); // Item/Widgets: Helpers to easily read a value by selecting Slider/Drag/Input text, copying it and parsing it. // - This requires the item to be selectable (we will later provide helpers that works in more general manner) // - (this temporarily use the internal test clipboard, but original clipboard value is restored afterwards) // See https://github.com/ocornut/imgui_test_engine/wiki/Automation-API#accessing-your-data int ItemReadAsInt(ImGuiTestRef ref); float ItemReadAsFloat(ImGuiTestRef ref); bool ItemReadAsScalar(ImGuiTestRef ref, ImGuiDataType data_type, void* out_data, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); const char* ItemReadAsString(ImGuiTestRef ref); size_t ItemReadAsString(ImGuiTestRef ref, char* out_buf, size_t out_buf_size); // Item/Widgets: Status query bool ItemExists(ImGuiTestRef ref); bool ItemIsChecked(ImGuiTestRef ref); bool ItemIsOpened(ImGuiTestRef ref); void ItemVerifyCheckedIfAlive(ImGuiTestRef ref, bool checked); // Item/Widgets: Drag and Mouse operations void ItemHold(ImGuiTestRef ref, float time); void ItemHoldForFrames(ImGuiTestRef ref, int frames); void ItemDragOverAndHold(ImGuiTestRef ref_src, ImGuiTestRef ref_dst); void ItemDragAndDrop(ImGuiTestRef ref_src, ImGuiTestRef ref_dst, ImGuiMouseButton button = 0); void ItemDragWithDelta(ImGuiTestRef ref_src, ImVec2 pos_delta); // Helpers for Tab Bars widgets void TabClose(ImGuiTestRef ref); bool TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order); // Helpers for MenuBar and Menus widgets // - e.g. MenuCheck("File/Options/Enable grid"); // Access menu in current ref window. // - e.g. MenuClick("//Window/File/Quit"); // Access menu in another window. void MenuAction(ImGuiTestAction action, ImGuiTestRef ref); void MenuActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent); void MenuClick(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Click, ref); } void MenuCheck(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Check, ref); } void MenuUncheck(ImGuiTestRef ref) { MenuAction(ImGuiTestAction_Uncheck, ref); } void MenuCheckAll(ImGuiTestRef ref_parent) { MenuActionAll(ImGuiTestAction_Check, ref_parent); } void MenuUncheckAll(ImGuiTestRef ref_parent) { MenuActionAll(ImGuiTestAction_Uncheck, ref_parent); } // Helpers for Combo Boxes void ComboClick(ImGuiTestRef ref); void ComboClickAll(ImGuiTestRef ref); // Helpers for Tables void TableOpenContextMenu(ImGuiTestRef ref, int column_n = -1); ImGuiSortDirection TableClickHeader(ImGuiTestRef ref, const char* label, ImGuiKeyChord key_mods = 0); void TableSetColumnEnabled(ImGuiTestRef ref, const char* label, bool enabled); void TableResizeColumn(ImGuiTestRef ref, int column_n, float width); const ImGuiTableSortSpecs* TableGetSortSpecs(ImGuiTestRef ref); // Viewports // IMPORTANT: Those function may alter Platform state (unless using the "Mock Viewport" backend). Use carefully. // Those are mostly useful to simulate OS actions and testing of viewport-specific features, may not be useful to most users. #ifdef IMGUI_HAS_VIEWPORT void ViewportPlatform_SetWindowPos(ImGuiViewport* viewport, const ImVec2& pos); void ViewportPlatform_SetWindowSize(ImGuiViewport* viewport, const ImVec2& size); void ViewportPlatform_SetWindowFocus(ImGuiViewport* viewport); void ViewportPlatform_CloseWindow(ImGuiViewport* viewport); #endif // Docking #ifdef IMGUI_HAS_DOCK void DockClear(const char* window_name, ...); void DockInto(ImGuiTestRef src_id, ImGuiTestRef dst_id, ImGuiDir split_dir = ImGuiDir_None, bool is_outer_docking = false, ImGuiTestOpFlags flags = 0); void UndockNode(ImGuiID dock_id); void UndockWindow(const char* window_name); bool WindowIsUndockedOrStandalone(ImGuiWindow* window); bool DockIdIsUndockedOrStandalone(ImGuiID dock_id); void DockNodeHideTabBar(ImGuiDockNode* node, bool hidden); #endif // Performances Measurement (use along with Dear ImGui Perf Tool) void PerfCalcRef(); void PerfCapture(const char* category = nullptr, const char* test_name = nullptr, const char* csv_file = nullptr); // Obsolete functions #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Obsoleted 2025/01/20 bool ItemSelectAndReadValue(ImGuiTestRef ref, ImGuiDataType data_type, void* out_data, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None) { return ItemReadAsScalar(ref, data_type, out_data, flags); } void ItemSelectAndReadValue(ImGuiTestRef ref, int* out_v) { int v = ItemReadAsInt(ref); *out_v = v; } void ItemSelectAndReadValue(ImGuiTestRef ref, float* out_v) { float v = ItemReadAsFloat(ref); *out_v = v; } // Obsoleted 2024/05/21 void YieldUntil(int frame_count) { while (FrameCount < frame_count) { Yield(); } } // Obsoleted 2022/10/11 ImGuiID GetIDByInt(int n); // Prefer using "$$123" ImGuiID GetIDByInt(int n, ImGuiTestRef seed_ref); ImGuiID GetIDByPtr(void* p); // Prefer using "$$(ptr)0xFFFFFFFF" ImGuiID GetIDByPtr(void* p, ImGuiTestRef seed_ref); // Obsoleted 2022/09/26 //void KeyModDown(ImGuiModFlags mods) { KeyDown(mods); } //void KeyModUp(ImGuiModFlags mods) { KeyUp(mods); } //void KeyModPress(ImGuiModFlags mods) { KeyPress(mods); } #endif // [Internal] void _MakeAimingSpaceOverPos(ImGuiViewport* viewport, ImGuiWindow* over_window, const ImVec2& over_pos); // Move windows covering 'window' at pos. void _ForeignWindowsHideOverPos(const ImVec2& pos, ImGuiWindow** ignore_list); // FIXME: Aim to remove this system... void _ForeignWindowsUnhideAll(); // FIXME: Aim to remove this system... }; //------------------------------------------------------------------------- // [SECTION] Debugging macros (IM_SUSPEND_TESTFUNC) //------------------------------------------------------------------------- // Debug: Temporarily suspend TestFunc to let user interactively inspect the GUI state (user will need to press the "Continue" button to resume TestFunc execution) #define IM_SUSPEND_TESTFUNC() do { if (ctx->SuspendTestFunc(__FILE__, __LINE__)) return; } while (0) //------------------------------------------------------------------------- // [SECTION] Testing/Checking macros: IM_CHECK(), IM_ERRORF() etc. //------------------------------------------------------------------------- // Helpers used by IM_CHECK_OP() macros. // ImGuiTestEngine_GetTempStringBuilder() returns a shared instance of ImGuiTextBuffer to recycle memory allocations template void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, T v) { buf->append("???"); IM_UNUSED(v); } // FIXME-TESTS: Could improve with some template magic template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, const char* v) { buf->appendf("\"%s\"", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, bool v) { buf->append(v ? "true" : "false"); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS8 v) { buf->appendf("%d", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU8 v) { buf->appendf("%u", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS16 v) { buf->appendf("%hd", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU16 v) { buf->appendf("%hu", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS32 v) { buf->appendf("%d", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU32 v) { buf->appendf("0x%08X", v); } // Assuming ImGuiID template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS64 v) { buf->appendf("%lld", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU64 v) { buf->appendf("%llu", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, float v) { buf->appendf("%.3f", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, double v) { buf->appendf("%f", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImVec2 v) { buf->appendf("(%.3f, %.3f)", v.x, v.y); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, const void* v) { buf->appendf("%p", v); } template<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImGuiWindow* v){ if (v) buf->appendf("\"%s\"", v->Name); else buf->append("nullptr"); } // We embed every macro in a do {} while(0) statement as a trick to allow using them as regular single statement, e.g. if (XXX) IM_CHECK(A); else IM_CHECK(B) // We leave the IM_DEBUG_BREAK() outside of the check function to step out faster when using a debugger. It also has the benefit of being lighter than an IM_ASSERT(). #define IM_CHECK(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return; } while (0) #define IM_CHECK_NO_RET(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } } while (0) #define IM_CHECK_SILENT(_EXPR) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_SilentSuccess, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return; } while (0) #define IM_CHECK_RETV(_EXPR,_RETV) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return _RETV; } while (0) #define IM_CHECK_SILENT_RETV(_EXPR,_RETV) do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_SilentSuccess, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return _RETV; } while (0) #define IM_ERRORF(_FMT,...) do { if (ImGuiTestEngine_Error(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, _FMT, __VA_ARGS__)) { IM_DEBUG_BREAK(); } } while (0) #define IM_ERRORF_NOHDR(_FMT,...) do { if (ImGuiTestEngine_Error(nullptr, nullptr, 0, ImGuiTestCheckFlags_None, _FMT, __VA_ARGS__)) { IM_DEBUG_BREAK(); } } while (0) // Those macros allow us to print out the values of both LHS and RHS expressions involved in a check. #define IM_CHECK_OP(_LHS, _RHS, _OP, _RETURN) \ do \ { \ auto __lhs = _LHS; /* Cache to avoid side effects */ \ auto __rhs = _RHS; \ bool __res = __lhs _OP __rhs; \ ImGuiTextBuffer* expr_buf = ImGuiTestEngine_GetTempStringBuilder(); \ expr_buf->append(#_LHS " ["); \ ImGuiTestEngineUtil_appendf_auto(expr_buf, __lhs); \ expr_buf->append("] " #_OP " " #_RHS " ["); \ ImGuiTestEngineUtil_appendf_auto(expr_buf, __rhs); \ expr_buf->append("]"); \ if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, __res, expr_buf->c_str())) \ IM_ASSERT(__res); \ if (_RETURN && !__res) \ return; \ } while (0) #define IM_CHECK_STR_OP(_LHS, _RHS, _OP, _RETURN, _FLAGS) \ do \ { \ bool __res; \ if (ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS, &__res)) \ IM_ASSERT(__res); \ if (_RETURN && !__res) \ return; \ } while (0) // Scalar compares #define IM_CHECK_EQ(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, ==, true) // Equal #define IM_CHECK_NE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, !=, true) // Not Equal #define IM_CHECK_LT(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, < , true) // Less Than #define IM_CHECK_LE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, <=, true) // Less or Equal #define IM_CHECK_GT(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, > , true) // Greater Than #define IM_CHECK_GE(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, >=, true) // Greater or Equal // Scalar compares, without return on failure #define IM_CHECK_EQ_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, ==, false) // Equal #define IM_CHECK_NE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, !=, false) // Not Equal #define IM_CHECK_LT_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, < , false) // Less Than #define IM_CHECK_LE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, <=, false) // Less or Equal #define IM_CHECK_GT_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, > , false) // Greater Than #define IM_CHECK_GE_NO_RET(_LHS, _RHS) IM_CHECK_OP(_LHS, _RHS, >=, false) // Greater or Equal // String compares #define IM_CHECK_STR_EQ(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None) #define IM_CHECK_STR_NE(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, !=, true, ImGuiTestCheckFlags_None) #define IM_CHECK_STR_EQ_NO_RET(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, false, ImGuiTestCheckFlags_None) #define IM_CHECK_STR_NE_NO_RET(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, !=, false, ImGuiTestCheckFlags_None) #define IM_CHECK_STR_EQ_SILENT(_LHS, _RHS) IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_SilentSuccess) // Floating point compares #define IM_CHECK_FLOAT_EQ_EPS(_LHS, _RHS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), FLT_EPSILON) // Float Equal #define IM_CHECK_FLOAT_NE_EPS(_LHS, _RHS) IM_CHECK_GT(ImFabs(_LHS - (_RHS)), FLT_EPSILON) // Float Not Equal #define IM_CHECK_FLOAT_NEAR(_LHS, _RHS, _EPS) IM_CHECK_LE(ImFabs(_LHS - (_RHS)), _EPS) #define IM_CHECK_FLOAT_NEAR_NO_RET(_LHS, _RHS, _E) IM_CHECK_LE_NO_RET(ImFabs(_LHS - (_RHS)), _E) //------------------------------------------------------------------------- #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_te_coroutine.h ================================================ // dear imgui test engine // (coroutine interface + optional implementation) // Read https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #pragma once #ifndef IMGUI_VERSION #include "imgui.h" #endif //------------------------------------------------------------------------ // Coroutine abstraction //------------------------------------------------------------------------ // Coroutines should be used like this: // ImGuiTestCoroutineHandle handle = CoroutineCreate(, , ); // name being for debugging, and ctx being an arbitrary user context pointer // while (CoroutineRun(handle)) { , ImRect, ImGuiItemStatusFlags, ImFormatString #if defined(__clang__) #pragma clang diagnostic push #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif #ifdef Status // X11 headers #undef Status #endif //----------------------------------------------------------------------------- // Function Pointers //----------------------------------------------------------------------------- #if IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION #include #define ImFuncPtr(FUNC_TYPE) std::function #else #define ImFuncPtr(FUNC_TYPE) FUNC_TYPE* #endif #include "imgui_capture_tool.h" // ImGuiScreenCaptureFunc //------------------------------------------------------------------------- // Forward Declarations //------------------------------------------------------------------------- struct ImGuiTest; // Data for a test registered with IM_REGISTER_TEST() struct ImGuiTestContext; // Context while a test is running struct ImGuiTestCoroutineInterface; // Interface to expose coroutine functions (imgui_te_coroutine provides a default implementation for C++11 using std::thread, but you may use your own) struct ImGuiTestEngine; // Test engine instance struct ImGuiTestEngineIO; // Test engine public I/O struct ImGuiTestEngineResultSummary;// Output of ImGuiTestEngine_GetResultSummary() struct ImGuiTestItemInfo; // Info queried from item (id, geometry, status flags, debug label) struct ImGuiTestItemList; // A list of items struct ImGuiTestInputs; // Simulated user inputs (will be fed into ImGuiIO by the test engine) struct ImGuiTestRunTask; // A queued test (test + runflags) typedef int ImGuiTestFlags; // Flags: See ImGuiTestFlags_ typedef int ImGuiTestCheckFlags; // Flags: See ImGuiTestCheckFlags_ typedef int ImGuiTestLogFlags; // Flags: See ImGuiTestLogFlags_ typedef int ImGuiTestRunFlags; // Flags: See ImGuiTestRunFlags_ enum ImGuiTestActiveFunc : int; enum ImGuiTestGroup : int; enum ImGuiTestRunSpeed : int; enum ImGuiTestStatus : int; enum ImGuiTestVerboseLevel : int; enum ImGuiTestEngineExportFormat : int; //------------------------------------------------------------------------- // Types //------------------------------------------------------------------------- // Stored in ImGuiTestContext: where we are currently running GuiFunc or TestFunc enum ImGuiTestActiveFunc : int { ImGuiTestActiveFunc_None, ImGuiTestActiveFunc_GuiFunc, ImGuiTestActiveFunc_TestFunc }; enum ImGuiTestRunSpeed : int { ImGuiTestRunSpeed_Fast = 0, // Run tests as fast as possible (teleport mouse, skip delays, etc.) ImGuiTestRunSpeed_Normal = 1, // Run tests at human watchable speed (for debugging) ImGuiTestRunSpeed_Cinematic = 2, // Run tests with pauses between actions (for e.g. tutorials) ImGuiTestRunSpeed_COUNT }; enum ImGuiTestVerboseLevel : int { ImGuiTestVerboseLevel_Silent = 0, // -v0 ImGuiTestVerboseLevel_Error = 1, // -v1 ImGuiTestVerboseLevel_Warning = 2, // -v2 ImGuiTestVerboseLevel_Info = 3, // -v3 ImGuiTestVerboseLevel_Debug = 4, // -v4 ImGuiTestVerboseLevel_Trace = 5, ImGuiTestVerboseLevel_COUNT }; // Test status (stored in ImGuiTest) enum ImGuiTestStatus : int { ImGuiTestStatus_Unknown = 0, ImGuiTestStatus_Success = 1, ImGuiTestStatus_Queued = 2, ImGuiTestStatus_Running = 3, ImGuiTestStatus_Error = 4, ImGuiTestStatus_Suspended = 5, ImGuiTestStatus_COUNT }; // Test group: this is mostly used to categorize tests in our testing UI. (Stored in ImGuiTest) enum ImGuiTestGroup : int { ImGuiTestGroup_Unknown = -1, ImGuiTestGroup_Tests = 0, ImGuiTestGroup_Perfs = 1, ImGuiTestGroup_COUNT }; // Flags (stored in ImGuiTest) enum ImGuiTestFlags_ { ImGuiTestFlags_None = 0, ImGuiTestFlags_NoGuiWarmUp = 1 << 0, // Disable running the GUI func for 2 frames before starting test code. For tests which absolutely need to start before GuiFunc. ImGuiTestFlags_NoAutoFinish = 1 << 1, // By default, tests with no TestFunc (only a GuiFunc) will end after warmup. Setting this require test to call ctx->Finish(). ImGuiTestFlags_NoRecoveryWarnings = 1 << 2 // Error/recovery warnings (missing End/Pop calls etc.) will be displayed as normal debug entries, for tests which may rely on those. //ImGuiTestFlags_RequireViewports = 1 << 10 }; // Flags for IM_CHECK* macros. enum ImGuiTestCheckFlags_ { ImGuiTestCheckFlags_None = 0, ImGuiTestCheckFlags_SilentSuccess = 1 << 0 }; // Flags for ImGuiTestContext::Log* functions. enum ImGuiTestLogFlags_ { ImGuiTestLogFlags_None = 0, ImGuiTestLogFlags_NoHeader = 1 << 0 // Do not display frame count and depth padding }; enum ImGuiTestRunFlags_ { ImGuiTestRunFlags_None = 0, ImGuiTestRunFlags_GuiFuncDisable = 1 << 0, // Used internally to temporarily disable the GUI func (at the end of a test, etc) ImGuiTestRunFlags_GuiFuncOnly = 1 << 1, // Set when user selects "Run GUI func" ImGuiTestRunFlags_NoSuccessMsg = 1 << 2, ImGuiTestRunFlags_EnableRawInputs = 1 << 3, // Disable input submission to let test submission raw input event (in order to test e.g. IO queue) ImGuiTestRunFlags_RunFromGui = 1 << 4, // Test ran manually from GUI, will disable watchdog. ImGuiTestRunFlags_RunFromCommandLine= 1 << 5, // Test queued from command-line. // Flags for ImGuiTestContext::RunChildTest() ImGuiTestRunFlags_NoError = 1 << 10, ImGuiTestRunFlags_ShareVars = 1 << 11, // Share generic vars and custom vars between child and parent tests (custom vars need to be same type) ImGuiTestRunFlags_ShareTestContext = 1 << 12, // Share ImGuiTestContext instead of creating a new one (unsure what purpose this may be useful for yet) // TODO: Add GuiFunc options }; struct ImGuiTestEngineResultSummary { int CountTested = 0; // Number of tests executed int CountSuccess = 0; // Number of tests succeeded int CountInQueue = 0; // Number of tests remaining in queue (e.g. aborted, crashed) }; //------------------------------------------------------------------------- // Functions //------------------------------------------------------------------------- // Hooks for core imgui/ library (generally called via macros) extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data); #if IMGUI_VERSION_NUM < 18934 extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, const ImRect& bb, ImGuiID id); #endif #ifdef IMGUI_HAS_IMSTR extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, ImStrv label, ImGuiItemStatusFlags flags); #else extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); #endif extern void ImGuiTestEngineHook_Log(ImGuiContext* ui_ctx, const char* fmt, ...); extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ui_ctx, ImGuiID id); // Functions (generally called via IM_CHECK() macros) IMGUI_API bool ImGuiTestEngine_Check(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, bool result, const char* expr); IMGUI_API bool ImGuiTestEngine_CheckStrOp(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* op, const char* lhs_var, const char* lhs_value, const char* rhs_var, const char* rhs_value, bool* out_result); IMGUI_API bool ImGuiTestEngine_Error(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* fmt, ...); IMGUI_API void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* function, int line); IMGUI_API ImGuiTextBuffer* ImGuiTestEngine_GetTempStringBuilder(); //------------------------------------------------------------------------- // ImGuiTestEngine API //------------------------------------------------------------------------- // Functions: Initialization IMGUI_API ImGuiTestEngine* ImGuiTestEngine_CreateContext(); // Create test engine IMGUI_API void ImGuiTestEngine_DestroyContext(ImGuiTestEngine* engine); // Destroy test engine. Call after ImGui::DestroyContext() so test engine specific ini data gets saved. IMGUI_API void ImGuiTestEngine_Start(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); // Bind to a dear imgui context. Start coroutine. IMGUI_API void ImGuiTestEngine_Stop(ImGuiTestEngine* engine); // Stop coroutine and export if any. (Unbind will lazily happen on context shutdown) IMGUI_API void ImGuiTestEngine_PostSwap(ImGuiTestEngine* engine); // Call every frame after framebuffer swap, will process screen capture and call test_io.ScreenCaptureFunc() IMGUI_API ImGuiTestEngineIO& ImGuiTestEngine_GetIO(ImGuiTestEngine* engine); // Macros: Register Test #define IM_REGISTER_TEST(_ENGINE, _CATEGORY, _NAME) ImGuiTestEngine_RegisterTest(_ENGINE, _CATEGORY, _NAME, __FILE__, __LINE__) IMGUI_API ImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* category, const char* name, const char* src_file = nullptr, int src_line = 0); // Prefer calling IM_REGISTER_TEST() IMGUI_API void ImGuiTestEngine_UnregisterTest(ImGuiTestEngine* engine, ImGuiTest* test); IMGUI_API void ImGuiTestEngine_UnregisterAllTests(ImGuiTestEngine* engine); // Functions: Main IMGUI_API void ImGuiTestEngine_QueueTest(ImGuiTestEngine* engine, ImGuiTest* test, ImGuiTestRunFlags run_flags = 0); IMGUI_API void ImGuiTestEngine_QueueTests(ImGuiTestEngine* engine, ImGuiTestGroup group, const char* filter = nullptr, ImGuiTestRunFlags run_flags = 0); IMGUI_API bool ImGuiTestEngine_TryAbortEngine(ImGuiTestEngine* engine); IMGUI_API void ImGuiTestEngine_AbortCurrentTest(ImGuiTestEngine* engine); IMGUI_API ImGuiTest* ImGuiTestEngine_FindTestByName(ImGuiTestEngine* engine, const char* category, const char* name); // Functions: Status Queries // FIXME: Clarify API to avoid function calls vs raw bools in ImGuiTestEngineIO IMGUI_API bool ImGuiTestEngine_IsTestQueueEmpty(ImGuiTestEngine* engine); IMGUI_API bool ImGuiTestEngine_IsUsingSimulatedInputs(ImGuiTestEngine* engine); IMGUI_API void ImGuiTestEngine_GetResultSummary(ImGuiTestEngine* engine, ImGuiTestEngineResultSummary* out_results); IMGUI_API void ImGuiTestEngine_GetTestList(ImGuiTestEngine* engine, ImVector* out_tests); IMGUI_API void ImGuiTestEngine_GetTestQueue(ImGuiTestEngine* engine, ImVector* out_tests); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Obsoleted 2025/03/17 static inline void ImGuiTestEngine_GetResult(ImGuiTestEngine* engine, int& out_count_tested, int& out_count_success) { ImGuiTestEngineResultSummary summary; ImGuiTestEngine_GetResultSummary(engine, &summary); out_count_tested = summary.CountTested; out_count_success = summary.CountSuccess; } #endif // Functions: Crash Handling // Ensure past test results are properly exported even if application crash during a test. IMGUI_API void ImGuiTestEngine_InstallDefaultCrashHandler(); // Install default crash handler (if you don't have one) IMGUI_API void ImGuiTestEngine_CrashHandler(); // Default crash handler, should be called from a custom crash handler if such exists //----------------------------------------------------------------------------- // IO structure to configure the test engine //----------------------------------------------------------------------------- // Function bound to right-clicking on a test and selecting "Open source" in the UI // - Easy: you can make this function call OS shell to "open" the file (e.g. ImOsOpenInShell() helper). // - Better: bind this function to a custom setup which can pass line number to a text editor (e.g. see 'imgui_test_suite/tools/win32_open_with_sublime.cmd' example) typedef void (ImGuiTestEngineSrcFileOpenFunc)(const char* filename, int line_no, void* user_data); struct IMGUI_API ImGuiTestEngineIO { //------------------------------------------------------------------------- // Functions //------------------------------------------------------------------------- // Options: Functions ImGuiTestCoroutineInterface* CoroutineFuncs = nullptr; // (Required) Coroutine functions (see imgui_te_coroutines.h) ImFuncPtr(ImGuiTestEngineSrcFileOpenFunc) SrcFileOpenFunc = nullptr; // (Optional) To open source files from test engine UI (otherwise default to open file in shell) ImFuncPtr(ImGuiScreenCaptureFunc) ScreenCaptureFunc = nullptr; // (Optional) To capture graphics output (application _MUST_ call ImGuiTestEngine_PostSwap() function after swapping is framebuffer) void* SrcFileOpenUserData = nullptr; // (Optional) User data for SrcFileOpenFunc void* ScreenCaptureUserData = nullptr; // (Optional) User data for ScreenCaptureFunc // Options: Main bool ConfigSavedSettings = true; // Load/Save settings in main context .ini file. ImGuiTestRunSpeed ConfigRunSpeed = ImGuiTestRunSpeed_Fast; // Run tests in fast/normal/cinematic mode bool ConfigStopOnError = false; // Stop queued tests on test error bool ConfigBreakOnError = false; // Break debugger on test error by calling IM_DEBUG_BREAK() bool ConfigKeepGuiFunc = false; // Keep test GUI running at the end of the test ImGuiTestVerboseLevel ConfigVerboseLevel = ImGuiTestVerboseLevel_Warning; ImGuiTestVerboseLevel ConfigVerboseLevelOnError = ImGuiTestVerboseLevel_Info; bool ConfigLogToTTY = false; bool ConfigLogToDebugger = false; bool ConfigRestoreFocusAfterTests = true;// Restore focus back after running tests bool ConfigCaptureEnabled = true; // Master enable flags for capturing and saving captures. Disable to avoid e.g. lengthy saving of large PNG files. bool ConfigCaptureOnError = false; bool ConfigNoThrottle = false; // Disable vsync for performance measurement or fast test running bool ConfigMouseDrawCursor = true; // Enable drawing of Dear ImGui software mouse cursor when running tests float ConfigFixedDeltaTime = 0.0f; // Use fixed delta time instead of calculating it from wall clock int PerfStressAmount = 1; // Integer to scale the amount of items submitted in test char GitBranchName[64] = ""; // e.g. fill in branch name (e.g. recorded in perf samples .csv) // Options: Speed of user simulation float MouseSpeed = 600.0f; // Mouse speed (pixel/second) when not running in fast mode float MouseWobble = 0.25f; // (0.0f..1.0f) How much wobble to apply to the mouse (pixels per pixel of move distance) when not running in fast mode float ScrollSpeed = 1400.0f; // Scroll speed (pixel/second) when not running in fast mode float TypingSpeed = 20.0f; // Char input speed (characters/second) when not running in fast mode float ActionDelayShort = 0.15f; // Time between short actions float ActionDelayStandard = 0.40f; // Time between most actions // Options: Screen/video capture char VideoCaptureEncoderPath[256] = ""; // Video encoder executable path, e.g. "path/to/ffmpeg.exe". char VideoCaptureEncoderParams[256] = "";// Video encoder parameters for .MP4 captures, e.g. see IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG char GifCaptureEncoderParams[512] = ""; // Video encoder parameters for .GIF captures, e.g. see IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG char VideoCaptureExtension[8] = ".mp4"; // Video file extension (default, may be overridden by test). // Options: Watchdog. Set values to FLT_MAX to disable. // Interactive GUI applications that may be slower tend to use higher values. float ConfigWatchdogWarning = 30.0f; // Warn when a test exceed this time (in second) float ConfigWatchdogKillTest = 60.0f; // Attempt to stop running a test when exceeding this time (in second) float ConfigWatchdogKillApp = FLT_MAX; // Stop application when exceeding this time (in second) // Options: Export // While you can manually call ImGuiTestEngine_Export(), registering filename/format here ensure the crash handler will always export if application crash. const char* ExportResultsFilename = nullptr; ImGuiTestEngineExportFormat ExportResultsFormat = (ImGuiTestEngineExportFormat)0; // Options: Sanity Checks bool CheckDrawDataIntegrity = false; // Check ImDrawData integrity (buffer count, etc.). Currently cheap but may become a slow operation. //------------------------------------------------------------------------- // Output //------------------------------------------------------------------------- // Output: State of test engine bool IsRunningTests = false; bool IsRequestingMaxAppSpeed = false; // When running in fast mode: request app to skip vsync or even skip rendering if it wants bool IsCapturing = false; // Capture is in progress }; //------------------------------------------------------------------------- // ImGuiTestItemInfo //------------------------------------------------------------------------- // Information about a given item or window, result of an ItemInfo() or WindowInfo() query struct ImGuiTestItemInfo { ImGuiID ID = 0; // Item ID char DebugLabel[32] = {}; // Shortened/truncated label for debugging and convenience purpose ImGuiWindow* Window = nullptr; // Item Window unsigned int NavLayer : 1; // Nav layer of the item (ImGuiNavLayer) int Depth : 16; // Depth from requested parent id. 0 == ID is immediate child of requested parent id. int TimestampMain; // Timestamp of main result (all fields) int TimestampStatus; // Timestamp of StatusFlags ImGuiID ParentID = 0; // Item Parent ID (value at top of the ID stack) ImRect RectFull = ImRect(); // Item Rectangle ImRect RectClipped = ImRect(); // Item Rectangle (clipped with window->ClipRect at time of item submission) ImGuiItemFlags ItemFlags = 0; // Item flags //ImGuiItemFlags InFlags = 0; // Item flags (OBSOLETE: before 2024/10/17 ItemFlags was called InFlags) ImGuiItemStatusFlags StatusFlags = 0; // Item Status flags (fully updated for some items only, compare TimestampStatus to FrameCount) ImGuiTestItemInfo() { memset(this, 0, sizeof(*this)); } }; // Result of an GatherItems() query struct IMGUI_API ImGuiTestItemList { ImPool Pool; void Clear() { Pool.Clear(); } void Reserve(int capacity) { Pool.Reserve(capacity); } int GetSize() const { return Pool.GetMapSize(); } const ImGuiTestItemInfo* GetByIndex(int n) { return Pool.GetByIndex(n); } const ImGuiTestItemInfo* GetByID(ImGuiID id) { return Pool.GetByKey(id); } // For range-for size_t size() const { return (size_t)Pool.GetMapSize(); } const ImGuiTestItemInfo* begin() const { return Pool.Buf.begin(); } const ImGuiTestItemInfo* end() const { return Pool.Buf.end(); } const ImGuiTestItemInfo* operator[] (size_t n) { return &Pool.Buf[(int)n]; } }; //------------------------------------------------------------------------- // ImGuiTestLog: store textual output of one given Test. //------------------------------------------------------------------------- struct IMGUI_API ImGuiTestLogLineInfo { ImGuiTestVerboseLevel Level; int LineOffset; }; struct IMGUI_API ImGuiTestLog { ImGuiTextBuffer Buffer; ImVector LineInfo; int CountPerLevel[ImGuiTestVerboseLevel_COUNT] = {}; // Functions ImGuiTestLog() {} bool IsEmpty() const { return Buffer.empty(); } void Clear(); // Extract log contents filtered per log-level. // Output: // - If 'buffer != nullptr': all extracted lines are appended to 'buffer'. Use 'buffer->c_str()' on your side to obtain the text. // - Return value: number of lines extracted (should be equivalent to number of '\n' inside buffer->c_str()). // - You may call the function with buffer == nullptr to only obtain a count without getting the data. // Verbose levels are inclusive: // - To get ONLY Error: Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Error // - To get ONLY Error and Warnings: Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Warning // - To get All Errors, Warnings, Debug... Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Trace int ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel level_min, ImGuiTestVerboseLevel level_max, ImGuiTextBuffer* out_buffer); // [Internal] void UpdateLineOffsets(ImGuiTestEngineIO* engine_io, ImGuiTestVerboseLevel level, const char* start); }; //------------------------------------------------------------------------- // ImGuiTest //------------------------------------------------------------------------- typedef void (ImGuiTestGuiFunc)(ImGuiTestContext* ctx); typedef void (ImGuiTestTestFunc)(ImGuiTestContext* ctx); // Wraps a placement new of a given type (where 'buffer' is the allocated memory) typedef void (ImGuiTestVarsConstructor)(void* buffer); typedef void (ImGuiTestVarsPostConstructor)(ImGuiTestContext* ctx, void* ptr, void* fn); typedef void (ImGuiTestVarsDestructor)(void* ptr); // Storage for the output of a test run struct IMGUI_API ImGuiTestOutput { ImGuiTestStatus Status = ImGuiTestStatus_Unknown; ImGuiTestLog Log; ImU64 StartTime = 0; ImU64 EndTime = 0; }; // Storage for one test struct IMGUI_API ImGuiTest { // Test Definition const char* Category = nullptr; // Literal, not owned const char* Name = nullptr; // Literal, generally not owned unless NameOwned=true ImGuiTestGroup Group = ImGuiTestGroup_Unknown; // Coarse groups: 'Tests' or 'Perf' bool NameOwned = false; // int ArgVariant = 0; // User parameter. Generally we use it to run variations of a same test by sharing GuiFunc/TestFunc ImGuiTestFlags Flags = ImGuiTestFlags_None; // See ImGuiTestFlags_ ImFuncPtr(ImGuiTestGuiFunc) GuiFunc = nullptr; // GUI function (optional if your test are running over an existing GUI application) ImFuncPtr(ImGuiTestTestFunc) TestFunc = nullptr; // Test function void* UserData = nullptr; // General purpose user data (if assigning capturing lambdas on GuiFunc/TestFunc you may not need to use this) //ImVector Dependencies; // Registered via AddDependencyTest(), ran automatically before our test. This is a simpler wrapper to calling ctx->RunChildTest() // Sources information (exposed in UI) const char* SourceFile = nullptr; // __FILE__ int SourceLine = 0; // __LINE__ int SourceLineEnd = 0; // end of line (when calculated by ImGuiTestEngine_StartCalcSourceLineEnds()) // Last Test Output/Status // (this is the only part that may change after registration) ImGuiTestOutput Output; // User variables (which are instantiated when running the test) // Setup after test registration with SetVarsDataType<>(), access instance during test with GetVars<>(). // This is mostly useful to communicate between GuiFunc and TestFunc. If you don't use both you may not want to use it! size_t VarsSize = 0; ImGuiTestVarsConstructor* VarsConstructor = nullptr; ImGuiTestVarsPostConstructor* VarsPostConstructor = nullptr; // To override constructor default (in case the default are problematic on the first GuiFunc frame) void* VarsPostConstructorUserFn = nullptr; ImGuiTestVarsDestructor* VarsDestructor = nullptr; // Functions ImGuiTest() {} ~ImGuiTest(); void SetOwnedName(const char* name); template void SetVarsDataType(void(*post_initialize)(ImGuiTestContext* ctx, T& vars) = nullptr) { VarsSize = sizeof(T); VarsConstructor = [](void* ptr) { IM_PLACEMENT_NEW(ptr) T; }; VarsDestructor = [](void* ptr) { IM_UNUSED(ptr); reinterpret_cast(ptr)->~T(); }; if (post_initialize != nullptr) { VarsPostConstructorUserFn = (void*)post_initialize; VarsPostConstructor = [](ImGuiTestContext* ctx, void* ptr, void* fn) { ((void (*)(ImGuiTestContext*, T&))(fn))(ctx, *(T*)ptr); }; } } }; // Stored in test queue struct IMGUI_API ImGuiTestRunTask { ImGuiTest* Test = nullptr; ImGuiTestRunFlags RunFlags = ImGuiTestRunFlags_None; }; //------------------------------------------------------------------------- #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_te_exporters.h ================================================ // dear imgui test engine // (result exporters) // Read https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #pragma once //------------------------------------------------------------------------- // Description //------------------------------------------------------------------------- // // Test results may be exported in one of supported formats. // To enable result exporting please configure test engine as follows: // // ImGuiTestEngineIO& test_io = ImGuiTestEngine_GetIO(engine); // test_io.ExportResultsFile = "output_file.xml"; // test_io.ExportResultsFormat = ImGuiTestEngineExportFormat_<...>; // // JUnit XML format //------------------ // JUnit XML format described at https://llg.cubic.org/docs/junit/. Many // third party applications support consumption of this format. Some of // of them are listed here: // - Jenkins // - Installation guide: https://www.jenkins.io/doc/book/installing/docker/ // - JUnit plugin: https://plugins.jenkins.io/junit/ // - xunit-viewer // - Project: https://github.com/lukejpreston/xunit-viewer // - Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm // - Install viewer and view test results: // npm install xunit-viewer // imgui_test_suite -nopause -v2 -ve4 -nogui -export-file junit.xml tests // node_modules/xunit-viewer/bin/xunit-viewer -r junit.xml -o junit.html // - Open junit.html // //------------------------------------------------------------------------- // Forward Declarations //------------------------------------------------------------------------- struct ImGuiTestEngine; //------------------------------------------------------------------------- // Types //------------------------------------------------------------------------- enum ImGuiTestEngineExportFormat : int { ImGuiTestEngineExportFormat_None = 0, ImGuiTestEngineExportFormat_JUnitXml, }; //------------------------------------------------------------------------- // Functions //------------------------------------------------------------------------- void ImGuiTestEngine_PrintResultSummary(ImGuiTestEngine* engine); void ImGuiTestEngine_Export(ImGuiTestEngine* engine); void ImGuiTestEngine_ExportEx(ImGuiTestEngine* engine, ImGuiTestEngineExportFormat format, const char* filename); ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_te_imconfig.h ================================================ // dear imgui test engine // (template for compile-time configuration) // Replicate or #include this file in your imconfig.h to enable test engine. // Compile Dear ImGui with test engine hooks // (Important: This is a value-less define, to be consistent with other defines used in core dear imgui.) #define IMGUI_ENABLE_TEST_ENGINE // [Optional, default 0] Enable plotting of perflog data for comparing performance of different runs. // This feature requires ImPlot to be linked in the application. #ifndef IMGUI_TEST_ENGINE_ENABLE_IMPLOT #define IMGUI_TEST_ENGINE_ENABLE_IMPLOT 1 #endif // [Optional, default 1] Enable screen capture and PNG/GIF saving functionalities // There's not much point to disable this but we provide it to reassure user that the dependencies on imstb_image_write.h and ffmpeg are technically optional. #ifndef IMGUI_TEST_ENGINE_ENABLE_CAPTURE #define IMGUI_TEST_ENGINE_ENABLE_CAPTURE 1 #endif // [Optional, default 0] Using std::function and for function pointers such as ImGuiTest::TestFunc and ImGuiTest::GuiFunc #ifndef IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION #define IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION 1 #endif // [Optional, default 0] Automatically fill ImGuiTestEngineIO::CoroutineFuncs with a default implementation using std::thread #ifndef IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL #define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 1 #endif // [Optional, default 0] Disable calls that do not make sense on game consoles // (Disable: system(), popen(), sigaction(), colored TTY output) #ifndef IMGUI_TEST_ENGINE_IS_GAME_CONSOLE #if defined(__ORBIS__) || defined(__PROSPERO__) || defined(_GAMING_XBOX) || defined(_DURANGO) #define IMGUI_TEST_ENGINE_IS_GAME_CONSOLE 1 #else #define IMGUI_TEST_ENGINE_IS_GAME_CONSOLE 0 #endif #endif // Define IM_DEBUG_BREAK macros so it is accessible in imgui.h // (this is a conveniance for app using test engine may define an IM_ASSERT() that uses this instead of an actual assert) // (this is a copy of the block in imgui_internal.h. if the one in imgui_internal.h were to be defined at the top of imgui.h we wouldn't need this) #ifndef IM_DEBUG_BREAK #if defined (_MSC_VER) #define IM_DEBUG_BREAK() __debugbreak() #elif defined(__clang__) #define IM_DEBUG_BREAK() __builtin_debugtrap() #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #define IM_DEBUG_BREAK() __asm__ volatile("int $0x03") #elif defined(__GNUC__) && defined(__thumb__) #define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") #elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) #define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0"); #else #define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! #endif #endif // #ifndef IMGUI_DEBUG_BREAK #ifndef IMGUI_API #define IMGUI_API #endif // [Options] We provide custom assert macro used by our our test suite, which you may use: // - Calling IM_DEBUG_BREAK() instead of an actual assert, so we can easily recover and step over (compared to many assert implementations). // - If a test is running, test name will be included in the log. // - Macro is calling IM_DEBUG_BREAK() inline to get debugger to break in the calling function (instead of a deeper callstack level). // - Macro is using comma operator instead of an if() to avoid "conditional expression is constant" warnings. IMGUI_API extern void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* func, int line); #define IM_TEST_ENGINE_ASSERT(_EXPR) do { if ((void)0, !(_EXPR)) { ImGuiTestEngine_AssertLog(#_EXPR, __FILE__, __func__, __LINE__); IM_DEBUG_BREAK(); } } while (0) // V_ASSERT_CONTRACT, assertMacro:IM_ASSERT ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_te_internal.h ================================================ // dear imgui test engine // (internal api) // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #pragma once #include "imgui_te_coroutine.h" #include "imgui_te_utils.h" // ImMovingAverage #include "imgui_capture_tool.h" // ImGuiCaptureTool // FIXME //------------------------------------------------------------------------- // FORWARD DECLARATIONS //------------------------------------------------------------------------- class Str; // Str<> from thirdparty/Str/Str.h struct ImGuiPerfTool; //------------------------------------------------------------------------- // DATA STRUCTURES //------------------------------------------------------------------------- // Query item position/window/state given ID. struct ImGuiTestInfoTask { // Input ImGuiID ID = 0; int FrameCount = -1; // Timestamp of request char DebugName[64] = ""; // Debug string representing the queried ID // Output ImGuiTestItemInfo Result; }; // Gather item list in given parent ID. struct ImGuiTestGatherTask { // Input ImGuiID InParentID = 0; int InMaxDepth = 0; short InLayerMask = 0; // Output/Temp ImGuiTestItemList* OutList = nullptr; ImGuiTestItemInfo* LastItemInfo = nullptr; void Clear() { memset(this, 0, sizeof(*this)); } }; // Find item ID given a label and a parent id // Usually used by queries with wildcards such as ItemInfo("hello/**/foo/bar") struct ImGuiTestFindByLabelTask { // Input ImGuiID InPrefixId = 0; // A known base ID which appears BEFORE the wildcard ID (for "hello/**/foo/bar" it would be hash of "hello") int InSuffixDepth = 0; // Number of labels in a path, after unknown base ID (for "hello/**/foo/bar" it would be 2) const char* InSuffix = nullptr; // A label string which appears on ID stack after unknown base ID (for "hello/**/foo/bar" it would be "foo/bar") const char* InSuffixLastItem = nullptr; // A last label string (for "hello/**/foo/bar" it would be "bar") ImGuiID InSuffixLastItemHash = 0; ImGuiItemStatusFlags InFilterItemStatusFlags = 0; // Flags required for item to be returned // Output ImGuiID OutItemId = 0; // Result item ID }; enum ImGuiTestInputType { ImGuiTestInputType_None, ImGuiTestInputType_Key, ImGuiTestInputType_Char, ImGuiTestInputType_ViewportFocus, ImGuiTestInputType_ViewportSetPos, ImGuiTestInputType_ViewportSetSize, ImGuiTestInputType_ViewportClose }; // FIXME: May want to strip further now that core imgui is using its own input queue struct ImGuiTestInput { ImGuiTestInputType Type = ImGuiTestInputType_None; ImGuiKeyChord KeyChord = ImGuiKey_None; ImWchar Char = 0; bool Down = false; ImGuiID ViewportId = 0; ImVec2 ViewportPosSize; static ImGuiTestInput ForKeyChord(ImGuiKeyChord key_chord, bool down) { ImGuiTestInput inp; inp.Type = ImGuiTestInputType_Key; inp.KeyChord = key_chord; inp.Down = down; return inp; } static ImGuiTestInput ForChar(ImWchar v) { ImGuiTestInput inp; inp.Type = ImGuiTestInputType_Char; inp.Char = v; return inp; } static ImGuiTestInput ForViewportFocus(ImGuiID viewport_id) { ImGuiTestInput inp; inp.Type = ImGuiTestInputType_ViewportFocus; inp.ViewportId = viewport_id; return inp; } static ImGuiTestInput ForViewportSetPos(ImGuiID viewport_id, const ImVec2& pos) { ImGuiTestInput inp; inp.Type = ImGuiTestInputType_ViewportSetPos; inp.ViewportId = viewport_id; inp.ViewportPosSize = pos; return inp; } static ImGuiTestInput ForViewportSetSize(ImGuiID viewport_id, const ImVec2& size) { ImGuiTestInput inp; inp.Type = ImGuiTestInputType_ViewportSetSize; inp.ViewportId = viewport_id; inp.ViewportPosSize = size; return inp; } static ImGuiTestInput ForViewportClose(ImGuiID viewport_id) { ImGuiTestInput inp; inp.Type = ImGuiTestInputType_ViewportClose; inp.ViewportId = viewport_id; return inp; } }; struct ImGuiTestInputs { ImVec2 MousePosValue; // Own non-rounded copy of MousePos in order facilitate simulating mouse movement very slow speed and high-framerate ImVec2 MouseWheel; ImGuiID MouseHoveredViewport = 0; int MouseButtonsValue = 0x00; // FIXME-TESTS: Use simulated_io.MouseDown[] ? ImVector Queue; bool HostEscDown = false; float HostEscDownDuration = -1.0f; // Maintain our own DownDuration for host/backend ESC key so we can abort. }; // [Internal] Test Engine Context struct ImGuiTestEngine { ImGuiTestEngineIO IO; ImGuiContext* UiContextTarget = nullptr; // imgui context for testing ImGuiContext* UiContextActive = nullptr; // imgui context for testing == UiContextTarget or nullptr bool Started = false; bool UiContextHasHooks = false; ImU64 BatchStartTime = 0; ImU64 BatchEndTime = 0; int FrameCount = 0; float OverrideDeltaTime = -1.0f; // Inject custom delta time into imgui context to simulate clock passing faster than wall clock time. ImVector TestsAll; ImVector TestsQueue; ImGuiTestContext* TestContext = nullptr; // Running test context bool TestsSourceLinesDirty = false; ImVectorInfoTasks; ImGuiTestGatherTask GatherTask; ImGuiTestFindByLabelTask FindByLabelTask; ImGuiTestCoroutineHandle TestQueueCoroutine = nullptr; // Coroutine to run the test queue bool TestQueueCoroutineShouldExit = false; // Flag to indicate that we are shutting down and the test queue coroutine should stop // Inputs ImGuiTestInputs Inputs; // UI support bool Abort = false; ImGuiTest* UiSelectAndScrollToTest = nullptr; ImGuiTest* UiSelectedTest = nullptr; Str* UiFilterTests; Str* UiFilterPerfs; ImU32 UiFilterByStatusMask = ~0u; bool UiMetricsOpen = false; bool UiDebugLogOpen = false; bool UiCaptureToolOpen = false; bool UiStackToolOpen = false; bool UiPerfToolOpen = false; float UiLogHeight = 150.0f; // Performance Monitor double PerfRefDeltaTime; ImMovingAverage PerfDeltaTime100; ImMovingAverage PerfDeltaTime500; ImGuiPerfTool* PerfTool = nullptr; // Screen/Video Capturing ImGuiCaptureToolUI CaptureTool; // Capture tool UI ImGuiCaptureContext CaptureContext; // Capture context used in tests ImGuiCaptureArgs* CaptureCurrentArgs = nullptr; // Tools bool PostSwapCalled = false; bool ToolDebugRebootUiContext = false; // Completely shutdown and recreate the dear imgui context in place bool ToolSlowDown = false; int ToolSlowDownMs = 100; ImGuiTestRunSpeed BackupConfigRunSpeed = ImGuiTestRunSpeed_Fast; bool BackupConfigNoThrottle = false; // Functions ImGuiTestEngine(); ~ImGuiTestEngine(); }; //------------------------------------------------------------------------- // INTERNAL FUNCTIONS //------------------------------------------------------------------------- ImGuiTestItemInfo* ImGuiTestEngine_FindItemInfo(ImGuiTestEngine* engine, ImGuiID id, const char* debug_id); void ImGuiTestEngine_Yield(ImGuiTestEngine* engine); void ImGuiTestEngine_SetDeltaTime(ImGuiTestEngine* engine, float delta_time); int ImGuiTestEngine_GetFrameCount(ImGuiTestEngine* engine); bool ImGuiTestEngine_PassFilter(ImGuiTest* test, const char* filter); void ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* ctx, ImGuiTest* test, ImGuiTestRunFlags run_flags); void ImGuiTestEngine_BindImGuiContext(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); void ImGuiTestEngine_UnbindImGuiContext(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); void ImGuiTestEngine_RebootUiContext(ImGuiTestEngine* engine); ImGuiPerfTool* ImGuiTestEngine_GetPerfTool(ImGuiTestEngine* engine); void ImGuiTestEngine_UpdateTestsSourceLines(ImGuiTestEngine* engine); // Screen/Video Capturing bool ImGuiTestEngine_CaptureScreenshot(ImGuiTestEngine* engine, ImGuiCaptureArgs* args); bool ImGuiTestEngine_CaptureBeginVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args); bool ImGuiTestEngine_CaptureEndVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args); // Helper functions const char* ImGuiTestEngine_GetStatusName(ImGuiTestStatus v); const char* ImGuiTestEngine_GetRunSpeedName(ImGuiTestRunSpeed v); const char* ImGuiTestEngine_GetVerboseLevelName(ImGuiTestVerboseLevel v); //------------------------------------------------------------------------- ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_te_perftool.h ================================================ // dear imgui test engine // (performance tool) // Browse and visualize samples recorded by ctx->PerfCapture() calls. // User access via 'Test Engine UI -> Tools -> Perf Tool' // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #pragma once #include "imgui.h" // Forward Declaration struct ImGuiPerfToolColumnInfo; struct ImGuiTestEngine; struct ImGuiCsvParser; // Configuration #define IMGUI_PERFLOG_DEFAULT_FILENAME "output/imgui_perflog.csv" // [Internal] Perf log entry. Changes to this struct should be reflected in ImGuiTestContext::PerfCapture() and ImGuiTestEngine_Start(). // This struct assumes strings stored here will be available until next ImGuiPerfTool::Clear() call. Fortunately we do not have to actively // manage lifetime of these strings. New entries are created only in two cases: // 1. ImGuiTestEngine_PerfToolAppendToCSV() call after perf test has run. This call receives ImGuiPerfToolEntry with const strings stored indefinitely by application. // 2. As a consequence of ImGuiPerfTool::LoadCSV() call, we persist the ImGuiCSVParser instance, which keeps parsed CSV text, from which strings are referenced. // As a result our solution also doesn't make many allocations. struct IMGUI_API ImGuiPerfToolEntry { ImU64 Timestamp = 0; // Title of a particular batch of perftool entries. const char* Category = nullptr; // Name of category perf test is in. const char* TestName = nullptr; // Name of perf test. double DtDeltaMs = 0.0; // Result of perf test. double DtDeltaMsMin = +FLT_MAX; // May be used by perftool. double DtDeltaMsMax = -FLT_MAX; // May be used by perftool. int NumSamples = 1; // Number aggregated samples. int PerfStressAmount = 0; // const char* GitBranchName = nullptr; // Build information. const char* BuildType = nullptr; // const char* Cpu = nullptr; // const char* OS = nullptr; // const char* Compiler = nullptr; // const char* Date = nullptr; // Date of this entry or min date of combined entries. //const char* DateMax = nullptr; // Max date of combined entries, or nullptr. double VsBaseline = 0.0; // Percent difference vs baseline. int LabelIndex = 0; // Index of TestName in ImGuiPerfTool::_LabelsVisible. ImGuiPerfToolEntry() { } ImGuiPerfToolEntry(const ImGuiPerfToolEntry& rhs) { Set(rhs); } ImGuiPerfToolEntry& operator=(const ImGuiPerfToolEntry& rhs){ Set(rhs); return *this; } void Set(const ImGuiPerfToolEntry& rhs); }; // [Internal] Perf log batch. struct ImGuiPerfToolBatch { ImU64 BatchID = 0; // Timestamp of the batch, or unique ID of the build in combined mode. int NumSamples = 0; // A number of unique batches aggregated. int BranchIndex = 0; // For per-branch color mapping. ImVector Entries; // Aggregated perf test entries. Order follows ImGuiPerfTool::_LabelsVisible order. ~ImGuiPerfToolBatch() { Entries.clear_destruct(); } // FIXME: Misleading: nothing to destruct in that struct? }; enum ImGuiPerfToolDisplayType : int { ImGuiPerfToolDisplayType_Simple, // Each run will be displayed individually. ImGuiPerfToolDisplayType_PerBranchColors, // Use one bar color per branch. ImGuiPerfToolDisplayType_CombineByBuildInfo, // Entries with same build information will be averaged. }; // struct IMGUI_API ImGuiPerfTool { ImVector_SrcData; // Raw entries from CSV file (with string pointer into CSV data). ImVector _Labels; ImVector _LabelsVisible; // ImPlot requires a pointer of all labels beforehand. Always contains a dummy "" entry at the end! ImVector _Batches; ImGuiStorage _LabelBarCounts; // Number bars each label will render. int _NumVisibleBuilds = 0; // Cached number of visible builds. int _NumUniqueBuilds = 0; // Cached number of unique builds. ImGuiPerfToolDisplayType _DisplayType = ImGuiPerfToolDisplayType_CombineByBuildInfo; int _BaselineBatchIndex = 0; // Index of baseline build. ImU64 _BaselineTimestamp = 0; ImU64 _BaselineBuildId = 0; char _Filter[128]; // Context menu filtering substring. char _FilterDateFrom[11] = {}; char _FilterDateTo[11] = {}; float _InfoTableHeight = 180.0f; int _AlignStress = 0; // Alignment values for build info components, so they look aligned in the legend. int _AlignType = 0; int _AlignOs = 0; int _AlignCpu = 0; int _AlignCompiler = 0; int _AlignBranch = 0; int _AlignSamples = 0; bool _InfoTableSortDirty = false; ImVector _InfoTableSort; // _InfoTableSort[_LabelsVisible.Size * _Batches.Size]. Contains sorted batch indices for each label. const ImGuiTableSortSpecs* _InfoTableSortSpecs = nullptr; // Current table sort specs. ImGuiStorage _TempSet; // Used as a set int _TableHoveredTest = -1; // Index within _VisibleLabelPointers array. int _TableHoveredBatch = -1; int _PlotHoverTest = -1; int _PlotHoverBatch = -1; bool _PlotHoverTestLabel = false; bool _ReportGenerating = false; ImGuiStorage _Visibility; ImGuiCsvParser* _CsvParser = nullptr; // We keep this around and point to its fields ImGuiPerfTool(); ~ImGuiPerfTool(); void Clear(); bool LoadCSV(const char* filename = nullptr); void AddEntry(ImGuiPerfToolEntry* entry); void ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open); void ViewOnly(const char* perf_name); void ViewOnly(const char** perf_names); ImGuiPerfToolEntry* GetEntryByBatchIdx(int idx, const char* perf_name = nullptr); bool SaveHtmlReport(const char* file_name, const char* image_file = nullptr); inline bool Empty() { return _SrcData.empty(); } void _Rebuild(); bool _IsVisibleBuild(ImGuiPerfToolBatch* batch); bool _IsVisibleBuild(ImGuiPerfToolEntry* batch); bool _IsVisibleTest(const char* test_name); void _CalculateLegendAlignment(); void _ShowEntriesPlot(); void _ShowEntriesTable(); void _SetBaseline(int batch_index); void _AddSettingsHandler(); void _UnpackSortedKey(ImU64 key, int* batch_index, int* entry_index, int* monotonic_index = nullptr); }; IMGUI_API void ImGuiTestEngine_PerfToolAppendToCSV(ImGuiPerfTool* perf_log, ImGuiPerfToolEntry* entry, const char* filename = nullptr); ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_te_ui.h ================================================ // dear imgui test engine // (ui) // If you run tests in an interactive or visible application, you may want to call ImGuiTestEngine_ShowTestEngineWindows() // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. // Provide access to: // - "Dear ImGui Test Engine" main interface // - "Dear ImGui Capture Tool" // - "Dear ImGui Perf Tool" // - other core debug functions: Metrics, Debug Log #pragma once #ifndef IMGUI_VERSION #include "imgui.h" // IMGUI_API #endif // Forward declarations struct ImGuiTestEngine; // Functions IMGUI_API void ImGuiTestEngine_ShowTestEngineWindows(ImGuiTestEngine* engine, bool* p_open); IMGUI_API void ImGuiTestEngine_OpenSourceFile(ImGuiTestEngine* engine, const char* source_filename, int source_line_no); ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/imgui_te_utils.h ================================================ // dear imgui test engine // (helpers/utilities. do NOT use this as a general purpose library) // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #pragma once //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- #include // fabsf #include // uint64_t #include // FILE* #include "imgui.h" // ImGuiID, ImGuiKey class Str; // Str<> from thirdparty/Str/Str.h //----------------------------------------------------------------------------- // Hashing Helpers //----------------------------------------------------------------------------- ImGuiID ImHashDecoratedPath(const char* str, const char* str_end = nullptr, ImGuiID seed = 0); const char* ImFindNextDecoratedPartInPath(const char* str, const char* str_end = nullptr); //----------------------------------------------------------------------------- // File/Directory Helpers //----------------------------------------------------------------------------- bool ImFileExist(const char* filename); bool ImFileDelete(const char* filename); bool ImFileCreateDirectoryChain(const char* path, const char* path_end = nullptr); bool ImFileFindInParents(const char* sub_path, int max_parent_count, Str* output); bool ImFileLoadSourceBlurb(const char* filename, int line_no_start, int line_no_end, ImGuiTextBuffer* out_buf); //----------------------------------------------------------------------------- // Path Helpers //----------------------------------------------------------------------------- // Those are strictly string manipulation functions const char* ImPathFindFilename(const char* path, const char* path_end = nullptr); // Return value always between path and path_end const char* ImPathFindExtension(const char* path, const char* path_end = nullptr); // Return value always between path and path_end void ImPathFixSeparatorsForCurrentOS(char* buf); //----------------------------------------------------------------------------- // String Helpers //----------------------------------------------------------------------------- void ImStrReplace(Str* s, const char* find, const char* repl); const char* ImStrchrRangeWithEscaping(const char* str, const char* str_end, char find_c); void ImStrXmlEscape(Str* s); int ImStrBase64Encode(const unsigned char* src, char* dst, int length); //----------------------------------------------------------------------------- // Parsing Helpers //----------------------------------------------------------------------------- void ImParseExtractArgcArgvFromCommandLine(int* out_argc, char const*** out_argv, const char* cmd_line); bool ImParseFindIniSection(const char* ini_config, const char* header, ImVector* result); //----------------------------------------------------------------------------- // Time Helpers //----------------------------------------------------------------------------- uint64_t ImTimeGetInMicroseconds(); void ImTimestampToISO8601(uint64_t timestamp, Str* out_date); //----------------------------------------------------------------------------- // Threading Helpers //----------------------------------------------------------------------------- void ImThreadSleepInMilliseconds(int ms); void ImThreadSetCurrentThreadDescription(const char* description); //----------------------------------------------------------------------------- // Build Info helpers //----------------------------------------------------------------------------- // All the pointers are expect to be literals/persistent struct ImBuildInfo { const char* Type = ""; const char* Cpu = ""; const char* OS = ""; const char* Compiler = ""; char Date[32]; // "YYYY-MM-DD" const char* Time = ""; }; const ImBuildInfo* ImBuildGetCompilationInfo(); bool ImBuildFindGitBranchName(const char* git_repo_path, Str* branch_name); //----------------------------------------------------------------------------- // Operating System Helpers //----------------------------------------------------------------------------- enum ImOsConsoleStream { ImOsConsoleStream_StandardOutput, ImOsConsoleStream_StandardError, }; enum ImOsConsoleTextColor { ImOsConsoleTextColor_Black, ImOsConsoleTextColor_White, ImOsConsoleTextColor_BrightWhite, ImOsConsoleTextColor_BrightRed, ImOsConsoleTextColor_BrightGreen, ImOsConsoleTextColor_BrightBlue, ImOsConsoleTextColor_BrightYellow, }; bool ImOsCreateProcess(const char* cmd_line); FILE* ImOsPOpen(const char* cmd_line, const char* mode); void ImOsPClose(FILE* fp); void ImOsOpenInShell(const char* path); bool ImOsIsDebuggerPresent(); void ImOsOutputDebugString(const char* message); void ImOsConsoleSetTextColor(ImOsConsoleStream stream, ImOsConsoleTextColor color); //----------------------------------------------------------------------------- // Miscellaneous functions //----------------------------------------------------------------------------- // Tables functions struct ImGuiTable; ImGuiID TableGetHeaderID(ImGuiTable* table, const char* column, int instance_no = 0); ImGuiID TableGetHeaderID(ImGuiTable* table, int column_n, int instance_no = 0); void TableDiscardInstanceAndSettings(ImGuiID table_id); // DrawData functions void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data); //----------------------------------------------------------------------------- // Helper: maintain/calculate moving average //----------------------------------------------------------------------------- template struct ImMovingAverage { // Internal Fields ImVector Samples; TYPE Accum; int Idx; int FillAmount; // Functions ImMovingAverage() { Accum = (TYPE)0; Idx = FillAmount = 0; } void Init(int count) { Samples.resize(count); memset(Samples.Data, 0, (size_t)Samples.Size * sizeof(TYPE)); Accum = (TYPE)0; Idx = FillAmount = 0; } void AddSample(TYPE v) { Accum += v - Samples[Idx]; Samples[Idx] = v; if (++Idx == Samples.Size) Idx = 0; if (FillAmount < Samples.Size) FillAmount++; } TYPE GetAverage() const { return Accum / (TYPE)FillAmount; } int GetSampleCount() const { return Samples.Size; } bool IsFull() const { return FillAmount == Samples.Size; } }; //----------------------------------------------------------------------------- // Helper: Simple/dumb CSV parser //----------------------------------------------------------------------------- struct ImGuiCsvParser { // Public fields int Columns = 0; // Number of columns in CSV file. int Rows = 0; // Number of rows in CSV file. // Internal fields char* _Data = nullptr; // CSV file data. ImVector _Index; // CSV table: _Index[row * _Columns + col]. // Functions ImGuiCsvParser(int columns = -1) { Columns = columns; } ~ImGuiCsvParser() { Clear(); } bool Load(const char* file_name); // Open and parse a CSV file. void Clear(); // Free allocated buffers. const char* GetCell(int row, int col) { IM_ASSERT(0 <= row && row < Rows && 0 <= col && col < Columns); return _Index[row * Columns + col]; } }; //----------------------------------------------------------------------------- // Misc Dear ImGui extensions //----------------------------------------------------------------------------- #if IMGUI_VERSION_NUM < 18924 struct ImGuiTabBar; struct ImGuiTabItem; #endif namespace ImGui { IMGUI_API void ItemErrorFrame(ImU32 col); #if IMGUI_VERSION_NUM < 18927 ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no = 0); #endif // Str support for InputText() IMGUI_API bool InputText(const char* label, Str* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr); IMGUI_API bool InputTextWithHint(const char* label, const char* hint, Str* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr); IMGUI_API bool InputTextMultiline(const char* label, Str* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr); // Splitter IMGUI_API bool Splitter(const char* id, float* value_1, float* value_2, int axis, int anchor = 0, float min_size_0 = -1.0f, float min_size_1 = -1.0f); // Misc IMGUI_API ImFont* FindFontByPrefix(const char* name); // Legacy version support #if IMGUI_VERSION_NUM < 18924 IMGUI_API const char* TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); #endif } ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/thirdparty/README.md ================================================ ## Third party libraries used by Test Engine Always used: - `Str/Str.h` simple string type, used by `imgui_test_engine` (Public Domain) Used if `IMGUI_TEST_ENGINE_ENABLE_CAPTURE` is defined to 1 (default: 1) - `stb/imstb_image_write.h` image writer, used by `imgui_capture_tool` (MIT Licence OR Public Domain) ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/thirdparty/Str/.editorconfig ================================================ [*.{h,cpp}] indent_style = space indent_size = 4 ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/thirdparty/Str/README.md ================================================ ``` Str Simple C++ string type with an optional local buffer, by Omar Cornut https://github.com/ocornut/str LICENSE This software is in the public domain. Where that dedication is not recognized, you are granted a perpetual, irrevocable license to copy, distribute, and modify this file as you see fit. USAGE Include Str.h in whatever places need to refer to it. In ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include. This expands out the actual implementation into that C/C++ file. NOTES - This isn't a fully featured string class. - It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans!). - String are mutable. We don't maintain size so length() is not-constant time. - Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity). - Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size). - We could easily raise those limits if we are ok to increase the structure overhead in 32-bits mode. - In "non-owned" mode for literals/reference we don't do any tracking/counting of references. - Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment). - I'm using this code but it hasn't been tested thoroughly. The idea is that you can provide an arbitrary sized local buffer if you expect string to fit most of the time, and then you avoid using costly heap. No local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits) Str s = "hey"; // use heap With a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes. Str16 s = "filename.h"; // copy into local buffer Str16 s = "long_filename_not_very_long_but_longer_than_expected.h"; // use heap With a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes. Str256 s = "long_filename_not_very_long_but_longer_than_expected.h"; // copy into local buffer Common sizes are defined at the bottom of Str.h, you may define your own. Functions: Str256 s; s.set("hello sailor"); // set (copy) s.setf("%s/%s.tmp", folder, filename); // set (w/format) s.append("hello"); // append. cost a length() calculation! s.appendf("hello %d", 42); // append (w/format). cost a length() calculation! s.set_ref("Hey!"); // set (literal/reference, just copy pointer, no tracking) Constructor helper for format string: add a trailing 'f' to the type. Underlying type is the same. Str256f filename("%s/%s.tmp", folder, filename); // construct (w/format) fopen(Str256f("%s/%s.tmp, folder, filename).c_str(), "rb"); // construct (w/format), use as function param, destruct Constructor helper for reference/literal: StrRef ref("literal"); // copy pointer, no allocation, no string copy StrRef ref2(GetDebugName()); // copy pointer. no tracking of anything whatsoever, know what you are doing! All StrXXX types derives from Str and instance hold the local buffer capacity. So you can pass e.g. Str256* to a function taking base type Str* and it will be functional! void MyFunc(Str& s) { s = "Hello"; } // will use local buffer if available in Str instance (Using a template e.g. Str we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky. Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.) ``` ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/thirdparty/Str/Str.h ================================================ // Str v0.33 // Simple C++ string type with an optional local buffer, by Omar Cornut // https://github.com/ocornut/str // LICENSE // This software is in the public domain. Where that dedication is not // recognized, you are granted a perpetual, irrevocable license to copy, // distribute, and modify this file as you see fit. // USAGE // Include this file in whatever places need to refer to it. // In ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include of this file. // This expands out the actual implementation into that C/C++ file. /* - This isn't a fully featured string class. - It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans). - String are mutable. We don't maintain size so length() is not-constant time. - Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity). - Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size). - In "non-owned" mode for literals/reference we don't do any tracking/counting of references. - Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment). - This code hasn't been tested very much. it is probably incomplete or broken. Made it for my own use. The idea is that you can provide an arbitrary sized local buffer if you expect string to fit most of the time, and then you avoid using costly heap. No local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits) Str s = "hey"; With a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes. Str16 s = "filename.h"; // copy into local buffer Str16 s = "long_filename_not_very_long_but_longer_than_expected.h"; // use heap With a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes. Str256 s = "long_filename_not_very_long_but_longer_than_expected.h"; // copy into local buffer Common sizes are defined at the bottom of Str.h, you may define your own. Functions: Str256 s; s.set("hello sailor"); // set (copy) s.setf("%s/%s.tmp", folder, filename); // set (w/format) s.append("hello"); // append. cost a length() calculation! s.appendf("hello %d", 42); // append (w/format). cost a length() calculation! s.set_ref("Hey!"); // set (literal/reference, just copy pointer, no tracking) Constructor helper for format string: add a trailing 'f' to the type. Underlying type is the same. Str256f filename("%s/%s.tmp", folder, filename); // construct (w/format) fopen(Str256f("%s/%s.tmp, folder, filename).c_str(), "rb"); // construct (w/format), use as function param, destruct Constructor helper for reference/literal: StrRef ref("literal"); // copy pointer, no allocation, no string copy StrRef ref2(GetDebugName()); // copy pointer. no tracking of anything whatsoever, know what you are doing! All StrXXX types derives from Str and instance hold the local buffer capacity. So you can pass e.g. Str256* to a function taking base type Str* and it will be functional. void MyFunc(Str& s) { s = "Hello"; } // will use local buffer if available in Str instance (Using a template e.g. Str we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky. Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.) */ /* CHANGELOG 0.33 - fixed capacity() return value to match standard. e.g. a Str256's capacity() now returns 255, not 256. 0.32 - added owned() accessor. 0.31 - fixed various warnings. 0.30 - turned into a single header file, removed Str.cpp. 0.29 - fixed bug when calling reserve on non-owned strings (ie. when using StrRef or set_ref), and fixed include. 0.28 - breaking change: replaced Str32 by Str30 to avoid collision with Str32 from MacTypes.h . 0.27 - added STR_API and basic .natvis file. 0.26 - fixed set(cont char* src, const char* src_end) writing null terminator to the wrong position. 0.25 - allow set(const char* NULL) or operator= NULL to clear the string. note that set() from range or other types are not allowed. 0.24 - allow set_ref(const char* NULL) to clear the string. include fixes for linux. 0.23 - added append(char). added append_from(int idx, XXX) functions. fixed some compilers warnings. 0.22 - documentation improvements, comments. fixes for some compilers. 0.21 - added StrXXXf() constructor to construct directly from a format string. */ /* TODO - Since we lose 4-bytes of padding on 64-bits architecture, perhaps just spread the header to 8-bytes and lift size limits? - More functions/helpers. */ #ifndef STR_INCLUDED #define STR_INCLUDED //------------------------------------------------------------------------- // CONFIGURATION //------------------------------------------------------------------------- #ifndef STR_MEMALLOC #define STR_MEMALLOC malloc #include #endif #ifndef STR_MEMFREE #define STR_MEMFREE free #include #endif #ifndef STR_ASSERT #define STR_ASSERT assert #include #endif #ifndef STR_API #define STR_API #endif #include // for va_list #include // for strlen, strcmp, memcpy, etc. // Configuration: #define STR_SUPPORT_STD_STRING 0 to disable setters variants using const std::string& (on by default) #ifndef STR_SUPPORT_STD_STRING #define STR_SUPPORT_STD_STRING 1 #endif // Configuration: #define STR_DEFINE_STR32 1 to keep defining Str32/Str32f, but be warned: on macOS/iOS, MacTypes.h also defines a type named Str32. #ifndef STR_DEFINE_STR32 #define STR_DEFINE_STR32 0 #endif #if STR_SUPPORT_STD_STRING #include #endif //------------------------------------------------------------------------- // HEADERS //------------------------------------------------------------------------- // This is the base class that you can pass around // Footprint is 8-bytes (32-bits arch) or 16-bytes (64-bits arch) class STR_API Str { char* Data; // Point to LocalBuf() or heap allocated int Capacity : 21; // Max 2 MB. Exclude zero terminator. int LocalBufSize : 10; // Max 1023 bytes unsigned int Owned : 1; // Set when we have ownership of the pointed data (most common, unless using set_ref() method or StrRef constructor) public: inline char* c_str() { return Data; } inline const char* c_str() const { return Data; } inline bool empty() const { return Data[0] == 0; } inline int length() const { return (int)strlen(Data); } // by design, allow user to write into the buffer at any time inline int capacity() const { return Capacity; } inline bool owned() const { return Owned ? true : false; } inline void set_ref(const char* src); int setf(const char* fmt, ...); int setfv(const char* fmt, va_list args); int setf_nogrow(const char* fmt, ...); int setfv_nogrow(const char* fmt, va_list args); int append(char c); int append(const char* s, const char* s_end = NULL); int appendf(const char* fmt, ...); int appendfv(const char* fmt, va_list args); int append_from(int idx, char c); int append_from(int idx, const char* s, const char* s_end = NULL); // If you know the string length or want to append from a certain point int appendf_from(int idx, const char* fmt, ...); int appendfv_from(int idx, const char* fmt, va_list args); void clear(); void reserve(int cap); void reserve_discard(int cap); void shrink_to_fit(); inline char& operator[](size_t i) { return Data[i]; } inline char operator[](size_t i) const { return Data[i]; } //explicit operator const char*() const{ return Data; } inline Str(); inline Str(const char* rhs); inline void set(const char* src); inline void set(const char* src, const char* src_end); inline Str& operator=(const char* rhs) { set(rhs); return *this; } inline bool operator==(const char* rhs) const { return strcmp(c_str(), rhs) == 0; } inline Str(const Str& rhs); inline void set(const Str& src); inline Str& operator=(const Str& rhs) { set(rhs); return *this; } inline bool operator==(const Str& rhs) const { return strcmp(c_str(), rhs.c_str()) == 0; } #if STR_SUPPORT_STD_STRING inline Str(const std::string& rhs); inline void set(const std::string& src); inline Str& operator=(const std::string& rhs) { set(rhs); return *this; } inline bool operator==(const std::string& rhs)const { return strcmp(c_str(), rhs.c_str()) == 0; } #endif // Destructor for all variants inline ~Str() { if (Owned && !is_using_local_buf()) STR_MEMFREE(Data); } static char* EmptyBuffer; protected: inline char* local_buf() { return (char*)this + sizeof(Str); } inline const char* local_buf() const { return (char*)this + sizeof(Str); } inline bool is_using_local_buf() const { return Data == local_buf() && LocalBufSize != 0; } // Constructor for StrXXX variants with local buffer Str(unsigned short local_buf_size) { STR_ASSERT(local_buf_size < 1024); Data = local_buf(); Data[0] = '\0'; Capacity = local_buf_size ? local_buf_size - 1 : 0; LocalBufSize = local_buf_size; Owned = 1; } }; void Str::set(const char* src) { // We allow set(NULL) or via = operator to clear the string. if (src == NULL) { clear(); return; } int buf_len = (int)strlen(src); if (Capacity < buf_len) reserve_discard(buf_len); memcpy(Data, src, (size_t)(buf_len + 1)); Owned = 1; } void Str::set(const char* src, const char* src_end) { STR_ASSERT(src != NULL && src_end >= src); int buf_len = (int)(src_end - src); if ((int)Capacity < buf_len) reserve_discard(buf_len); memcpy(Data, src, (size_t)buf_len); Data[buf_len] = 0; Owned = 1; } void Str::set(const Str& src) { int buf_len = (int)strlen(src.c_str()); if ((int)Capacity < buf_len) reserve_discard(buf_len); memcpy(Data, src.c_str(), (size_t)(buf_len + 1)); Owned = 1; } #if STR_SUPPORT_STD_STRING void Str::set(const std::string& src) { int buf_len = (int)src.length(); if ((int)Capacity < buf_len) reserve_discard(buf_len); memcpy(Data, src.c_str(), (size_t)(buf_len + 1)); Owned = 1; } #endif inline void Str::set_ref(const char* src) { if (Owned && !is_using_local_buf()) STR_MEMFREE(Data); Data = src ? (char*)src : EmptyBuffer; Capacity = 0; Owned = 0; } Str::Str() { Data = EmptyBuffer; // Shared READ-ONLY initial buffer for 0 capacity Capacity = 0; LocalBufSize = 0; Owned = 0; } Str::Str(const Str& rhs) : Str() { set(rhs); } Str::Str(const char* rhs) : Str() { set(rhs); } #if STR_SUPPORT_STD_STRING Str::Str(const std::string& rhs) : Str() { set(rhs); } #endif // Literal/reference string class StrRef : public Str { public: StrRef(const char* s) : Str() { set_ref(s); } }; // Types embedding a local buffer // NB: we need to override the constructor and = operator for both Str& and TYPENAME (without the later compiler will call a default copy operator) #if STR_SUPPORT_STD_STRING #define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \ class TYPENAME : public Str \ { \ char local_buf[LOCALBUFSIZE]; \ public: \ TYPENAME() : Str(LOCALBUFSIZE) {} \ TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \ TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); } \ TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \ TYPENAME(const std::string& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \ TYPENAME& operator=(const char* rhs) { set(rhs); return *this; } \ TYPENAME& operator=(const Str& rhs) { set(rhs); return *this; } \ TYPENAME& operator=(const TYPENAME& rhs) { set(rhs); return *this; } \ TYPENAME& operator=(const std::string& rhs) { set(rhs); return *this; } \ }; #else #define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \ class TYPENAME : public Str \ { \ char local_buf[LOCALBUFSIZE]; \ public: \ TYPENAME() : Str(LOCALBUFSIZE) {} \ TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \ TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); } \ TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \ TYPENAME& operator=(const char* rhs) { set(rhs); return *this; } \ TYPENAME& operator=(const Str& rhs) { set(rhs); return *this; } \ TYPENAME& operator=(const TYPENAME& rhs) { set(rhs); return *this; } \ }; #endif // Disable PVS-Studio warning V730: Not all members of a class are initialized inside the constructor (local_buf is not initialized and that is fine) // -V:STR_DEFINETYPE:730 // Helper to define StrXXXf constructors #define STR_DEFINETYPE_F(TYPENAME, TYPENAME_F) \ class TYPENAME_F : public TYPENAME \ { \ public: \ TYPENAME_F(const char* fmt, ...) : TYPENAME() { va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \ }; #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-private-field" // warning : private field 'local_buf' is not used #endif // Declaring types for common sizes here STR_DEFINETYPE(Str16, 16) STR_DEFINETYPE(Str30, 30) STR_DEFINETYPE(Str64, 64) STR_DEFINETYPE(Str128, 128) STR_DEFINETYPE(Str256, 256) STR_DEFINETYPE(Str512, 512) // Declaring helper constructors to pass in format strings in one statement STR_DEFINETYPE_F(Str16, Str16f) STR_DEFINETYPE_F(Str30, Str30f) STR_DEFINETYPE_F(Str64, Str64f) STR_DEFINETYPE_F(Str128, Str128f) STR_DEFINETYPE_F(Str256, Str256f) STR_DEFINETYPE_F(Str512, Str512f) #if STR_DEFINE_STR32 STR_DEFINETYPE(Str32, 32) STR_DEFINETYPE_F(Str32, Str32f) #endif #ifdef __clang__ #pragma clang diagnostic pop #endif #endif // #ifndef STR_INCLUDED //------------------------------------------------------------------------- // IMPLEMENTATION //------------------------------------------------------------------------- #ifdef STR_IMPLEMENTATION #include // for vsnprintf // On some platform vsnprintf() takes va_list by reference and modifies it. // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. #ifndef va_copy #define va_copy(dest, src) (dest = src) #endif // Static empty buffer we can point to for empty strings // Pointing to a literal increases the like-hood of getting a crash if someone attempts to write in the empty string buffer. char* Str::EmptyBuffer = (char*)"\0NULL"; // Clear void Str::clear() { if (Owned && !is_using_local_buf()) STR_MEMFREE(Data); if (LocalBufSize) { Data = local_buf(); Data[0] = '\0'; Capacity = LocalBufSize - 1; Owned = 1; } else { Data = EmptyBuffer; Capacity = 0; Owned = 0; } } // Reserve memory, preserving the current of the buffer // Capacity doesn't include the zero terminator, so reserve(5) is enough to store "hello". void Str::reserve(int new_capacity) { if (new_capacity <= Capacity) return; char* new_data; if (new_capacity <= LocalBufSize - 1) { // Disowned -> LocalBuf new_data = local_buf(); new_capacity = LocalBufSize - 1; } else { // Disowned or LocalBuf -> Heap new_data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char)); } // string in Data might be longer than new_capacity if it wasn't owned, don't copy too much #ifdef _MSC_VER strncpy_s(new_data, (size_t)new_capacity + 1, Data, (size_t)new_capacity); #else strncpy(new_data, Data, (size_t)new_capacity); #endif new_data[new_capacity] = 0; if (Owned && !is_using_local_buf()) STR_MEMFREE(Data); Data = new_data; Capacity = new_capacity; Owned = 1; } // Reserve memory, discarding the current of the buffer (if we expect to be fully rewritten) void Str::reserve_discard(int new_capacity) { if (new_capacity <= Capacity) return; if (Owned && !is_using_local_buf()) STR_MEMFREE(Data); if (new_capacity <= LocalBufSize - 1) { // Disowned -> LocalBuf Data = local_buf(); Capacity = LocalBufSize - 1; } else { // Disowned or LocalBuf -> Heap Data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char)); Capacity = new_capacity; } Owned = 1; } void Str::shrink_to_fit() { if (!Owned || is_using_local_buf()) return; int new_capacity = length(); if (Capacity <= new_capacity) return; char* new_data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char)); memcpy(new_data, Data, (size_t)(new_capacity + 1)); STR_MEMFREE(Data); Data = new_data; Capacity = new_capacity; } // FIXME: merge setfv() and appendfv()? int Str::setfv(const char* fmt, va_list args) { // Needed for portability on platforms where va_list are passed by reference and modified by functions va_list args2; va_copy(args2, args); // MSVC returns -1 on overflow when writing, which forces us to do two passes // FIXME-OPT: Find a way around that. #ifdef _MSC_VER int len = vsnprintf(NULL, 0, fmt, args); STR_ASSERT(len >= 0); if (Capacity < len) reserve_discard(len); len = vsnprintf(Data, (size_t)len + 1, fmt, args2); #else // First try int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)(Capacity + 1): 0, fmt, args); STR_ASSERT(len >= 0); if (Capacity < len) { reserve_discard(len); len = vsnprintf(Data, (size_t)len + 1, fmt, args2); } #endif STR_ASSERT(Owned); return len; } int Str::setf(const char* fmt, ...) { va_list args; va_start(args, fmt); int len = setfv(fmt, args); va_end(args); return len; } int Str::setfv_nogrow(const char* fmt, va_list args) { STR_ASSERT(Owned); if (Capacity == 0) return 0; int w = vsnprintf(Data, (size_t)(Capacity + 1), fmt, args); Data[Capacity] = 0; Owned = 1; return (w == -1) ? Capacity : w; } int Str::setf_nogrow(const char* fmt, ...) { va_list args; va_start(args, fmt); int len = setfv_nogrow(fmt, args); va_end(args); return len; } int Str::append_from(int idx, char c) { int add_len = 1; if (Capacity < idx + add_len) reserve(idx + add_len); Data[idx] = c; Data[idx + add_len] = 0; STR_ASSERT(Owned); return add_len; } int Str::append_from(int idx, const char* s, const char* s_end) { if (!s_end) s_end = s + strlen(s); int add_len = (int)(s_end - s); if (Capacity < idx + add_len) reserve(idx + add_len); memcpy(Data + idx, (const void*)s, (size_t)add_len); Data[idx + add_len] = 0; // Our source data isn't necessarily zero terminated STR_ASSERT(Owned); return add_len; } // FIXME: merge setfv() and appendfv()? int Str::appendfv_from(int idx, const char* fmt, va_list args) { // Needed for portability on platforms where va_list are passed by reference and modified by functions va_list args2; va_copy(args2, args); // MSVC returns -1 on overflow when writing, which forces us to do two passes // FIXME-OPT: Find a way around that. #ifdef _MSC_VER int add_len = vsnprintf(NULL, 0, fmt, args); STR_ASSERT(add_len >= 0); if (Capacity < idx + add_len) reserve(idx + add_len); add_len = vsnprintf(Data + idx, add_len + 1, fmt, args2); #else // First try int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity + 1 - idx) : 0, fmt, args); STR_ASSERT(add_len >= 0); if (Capacity < idx + add_len) { reserve(idx + add_len); add_len = vsnprintf(Data + idx, (size_t)add_len + 1, fmt, args2); } #endif STR_ASSERT(Owned); return add_len; } int Str::appendf_from(int idx, const char* fmt, ...) { va_list args; va_start(args, fmt); int len = appendfv_from(idx, fmt, args); va_end(args); return len; } int Str::append(char c) { int cur_len = length(); return append_from(cur_len, c); } int Str::append(const char* s, const char* s_end) { int cur_len = length(); return append_from(cur_len, s, s_end); } int Str::appendfv(const char* fmt, va_list args) { int cur_len = length(); return appendfv_from(cur_len, fmt, args); } int Str::appendf(const char* fmt, ...) { va_list args; va_start(args, fmt); int len = appendfv(fmt, args); va_end(args); return len; } #endif // #define STR_IMPLEMENTATION //------------------------------------------------------------------------- ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/thirdparty/Str/Str.natvis ================================================ { Data, na } Data, na strlen(Data) Capacity ================================================ FILE: lib/third_party/imgui/imgui_test_engine/include/thirdparty/stb/imstb_image_write.h ================================================ /* stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk Before #including, #define STB_IMAGE_WRITE_IMPLEMENTATION in the file that you want to have the implementation. Will probably not work correctly with strict-aliasing optimizations. ABOUT: This header file is a library for writing images to C stdio or a callback. The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation; though providing a custom zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. This library is designed for source code compactness and simplicity, not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. You can #define STBIW_MEMMOVE() to replace memmove() You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function for PNG compression (instead of the builtin one), it must have the following signature: unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); The returned data will be freed with STBIW_FREE() (free() by default), so it must be heap allocated with STBIW_MALLOC() (malloc() by default), UNICODE: If compiling for Windows and you wish to use Unicode filenames, compile with #define STBIW_WINDOWS_UTF8 and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert Windows wchar_t filenames to utf8. USAGE: There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically There are also five equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); You can configure it with these global variables: int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. Each function returns 0 on failure and non-0 on success. The functions create an image file defined by the parameters. The image is a rectangle of pixels stored from left-to-right, top-to-bottom. Each pixel contains 'comp' channels of data stored interleaved with 8-bits per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. The *data pointer points to the first byte of the top-left-most pixel. For PNG, "stride_in_bytes" is the distance in bytes from the first byte of a row of pixels to the first byte of the next row of pixels. PNG creates output files with the same number of components as the input. The BMP format expands Y to RGB in the file format and does not output alpha. PNG supports writing rectangles of data even when the bytes storing rows of data are not consecutive in memory (e.g. sub-rectangles of a larger image), by supplying the stride between the beginning of adjacent rows. The other formats do not. (Thus you cannot write a native-format BMP through the BMP writer, both because it is in BGR order and because it may have padding at the end of the line.) PNG allows you to set the deflate compression level by setting the global variable 'stbi_write_png_compression_level' (it defaults to 8). HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. JPEG does ignore alpha channels in input data; quality is between 1 and 100. Higher quality looks better but results in a bigger image. JPEG baseline (no JPEG progressive). CREDITS: Sean Barrett - PNG/BMP/TGA Baldur Karlsson - HDR Jean-Sebastien Guay - TGA monochrome Tim Kelsey - misc enhancements Alan Hickman - TGA RLE Emmanuel Julien - initial file IO callback implementation Jon Olick - original jo_jpeg.cpp code Daniel Gibson - integrate JPEG, allow external zlib Aarni Koskela - allow choosing PNG filter bugfixes: github:Chribba Guillaume Chereau github:jry2 github:romigrou Sergio Gonzalez Jonas Karlsson Filip Wasil Thatcher Ulrich github:poppolopoppo Patrick Boettcher github:xeekworx Cap Petschulat Simon Rodriguez Ivan Tikhonov github:ignotion Adam Schackart LICENSE See end of file for license information. */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H #include // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' #ifndef STBIWDEF #ifdef STB_IMAGE_WRITE_STATIC #define STBIWDEF static #else #ifdef __cplusplus #define STBIWDEF extern "C" #else #define STBIWDEF extern #endif #endif #endif #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations extern int stbi_write_tga_with_rle; extern int stbi_write_png_compression_level; extern int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #ifdef STBI_WINDOWS_UTF8 STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif #endif typedef void stbi_write_func(void *context, void *data, int size); STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #endif//INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif #ifndef STBI_WRITE_NO_STDIO #include #endif // STBI_WRITE_NO_STDIO #include #include #include #include #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) // ok #else #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." #endif #ifndef STBIW_MALLOC #define STBIW_MALLOC(sz) malloc(sz) #define STBIW_REALLOC(p,newsz) realloc(p,newsz) #define STBIW_FREE(p) free(p) #endif #ifndef STBIW_REALLOC_SIZED #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) #endif #ifndef STBIW_MEMMOVE #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) #endif #ifndef STBIW_ASSERT #include #define STBIW_ASSERT(x) assert(x) #endif #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) #ifdef STB_IMAGE_WRITE_STATIC static int stbi__flip_vertically_on_write=0; static int stbi_write_png_compression_level = 8; static int stbi_write_tga_with_rle = 1; static int stbi_write_force_png_filter = -1; #else int stbi_write_png_compression_level = 8; int stbi__flip_vertically_on_write=0; int stbi_write_tga_with_rle = 1; int stbi_write_force_png_filter = -1; #endif STBIWDEF void stbi_flip_vertically_on_write(int flag) { stbi__flip_vertically_on_write = flag; } typedef struct { stbi_write_func *func; void *context; } stbi__write_context; // initialize a callback-based context static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) { s->func = c; s->context = context; } #ifndef STBI_WRITE_NO_STDIO static void stbi__stdio_write(void *context, void *data, int size) { fwrite(data,1,size,(FILE*) context); } #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #ifdef __cplusplus #define STBIW_EXTERN extern "C" #else #define STBIW_EXTERN extern #endif STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbiw__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) return 0; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) return 0; #if _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else f = fopen(filename, mode); #endif return f; } static int stbi__start_write_file(stbi__write_context *s, const char *filename) { FILE *f = stbiw__fopen(filename, "wb"); stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } static void stbi__end_write_file(stbi__write_context *s) { fclose((FILE *)s->context); } #endif // !STBI_WRITE_NO_STDIO typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { case ' ': break; case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); s->func(s->context,&x,1); break; } case '2': { int x = va_arg(v,int); unsigned char b[2]; b[0] = STBIW_UCHAR(x); b[1] = STBIW_UCHAR(x>>8); s->func(s->context,b,2); break; } case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; b[0]=STBIW_UCHAR(x); b[1]=STBIW_UCHAR(x>>8); b[2]=STBIW_UCHAR(x>>16); b[3]=STBIW_UCHAR(x>>24); s->func(s->context,b,4); break; } default: STBIW_ASSERT(0); return; } } } static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); } static void stbiw__putc(stbi__write_context *s, unsigned char c) { s->func(s->context, &c, 1); } static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { unsigned char arr[3]; arr[0] = a; arr[1] = b; arr[2] = c; s->func(s->context, arr, 3); } static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) { unsigned char bg[3] = { 255, 0, 255}, px[3]; int k; if (write_alpha < 0) s->func(s->context, &d[comp - 1], 1); switch (comp) { case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case case 1: if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp else s->func(s->context, d, 1); // monochrome TGA break; case 4: if (!write_alpha) { // composite against pink background for (k = 0; k < 3; ++k) px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); break; } /* FALLTHROUGH */ case 3: stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); break; } if (write_alpha > 0) s->func(s->context, &d[comp - 1], 1); } static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) { stbiw_uint32 zero = 0; int i,j, j_end; if (y <= 0) return; if (stbi__flip_vertically_on_write) vdir *= -1; if (vdir < 0) { j_end = -1; j = y-1; } else { j_end = y; j = 0; } for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } s->func(s->context, &zero, scanline_pad); } } static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) { if (y < 0 || x < 0) { return 0; } else { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); return 1; } } static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { int pad = (-x*3) & 3; return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, "11 4 22 4" "4 44 22 444444", 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_bmp_core(&s, x, y, comp, data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_bmp_core(&s, x, y, comp, data); stbi__end_write_file(&s); return r; } else return 0; } #endif //!STBI_WRITE_NO_STDIO static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { int has_alpha = (comp == 2 || comp == 4); int colorbytes = has_alpha ? comp-1 : comp; int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 if (y < 0 || x < 0) return 0; if (!stbi_write_tga_with_rle) { return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); } else { int i,j,k; int jend, jdir; stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); if (stbi__flip_vertically_on_write) { j = 0; jend = y; jdir = 1; } else { j = y-1; jend = -1; jdir = -1; } for (; j != jend; j += jdir) { unsigned char *row = (unsigned char *) data + j * x * comp; int len; for (i = 0; i < x; i += len) { unsigned char *begin = row + i * comp; int diff = 1; len = 1; if (i < x - 1) { ++len; diff = memcmp(begin, row + (i + 1) * comp, comp); if (diff) { const unsigned char *prev = begin; for (k = i + 2; k < x && len < 128; ++k) { if (memcmp(prev, row + k * comp, comp)) { prev += comp; ++len; } else { --len; break; } } } else { for (k = i + 2; k < x && len < 128; ++k) { if (!memcmp(begin, row + k * comp, comp)) { ++len; } else { break; } } } } if (diff) { unsigned char header = STBIW_UCHAR(len - 1); s->func(s->context, &header, 1); for (k = 0; k < len; ++k) { stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); } } else { unsigned char header = STBIW_UCHAR(len - 129); s->func(s->context, &header, 1); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); } } } } return 1; } STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_tga_core(&s, x, y, comp, (void *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); if (maxcomp < 1e-32f) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; rgbe[0] = (unsigned char)(linear[0] * normalize); rgbe[1] = (unsigned char)(linear[1] * normalize); rgbe[2] = (unsigned char)(linear[2] * normalize); rgbe[3] = (unsigned char)(exponent + 128); } } static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); s->func(s->context, &lengthbyte, 1); s->func(s->context, &databyte, 1); } static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code s->func(s->context, &lengthbyte, 1); s->func(s->context, data, length); } static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; float linear[3]; int x; scanlineheader[2] = (width&0xff00)>>8; scanlineheader[3] = (width&0x00ff); /* skip RLE for images too small or large */ if (width < 8 || width >= 32768) { for (x=0; x < width; x++) { switch (ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); s->func(s->context, rgbe, 4); } } else { int c,r; /* encode into scratch buffer */ for (x=0; x < width; x++) { switch(ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); scratch[x + width*0] = rgbe[0]; scratch[x + width*1] = rgbe[1]; scratch[x + width*2] = rgbe[2]; scratch[x + width*3] = rgbe[3]; } s->func(s->context, scanlineheader, 4); /* RLE each component separately */ for (c=0; c < 4; c++) { unsigned char *comp = &scratch[width*c]; x = 0; while (x < width) { // find first run r = x; while (r+2 < width) { if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) break; ++r; } if (r+2 >= width) r = width; // dump up to first run while (x < r) { int len = r-x; if (len > 128) len = 128; stbiw__write_dump_data(s, len, &comp[x]); x += len; } // if there's a run, output it if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd // find next byte after run while (r < width && comp[r] == comp[x]) ++r; // output run up to r while (x < r) { int len = r-x; if (len > 127) len = 127; stbiw__write_run_data(s, len, comp[x]); x += len; } } } } } } static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { if (y <= 0 || x <= 0 || data == NULL) return 0; else { // Each component is stored separately. Allocate scratch space for full output scanline. unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); int i, len; char buffer[128]; char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); #ifdef __STDC_WANT_SECURE_LIB__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #endif s->func(s->context, buffer, len); for(i=0; i < y; i++) stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); STBIW_FREE(scratch); return 1; } } STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // STBI_WRITE_NO_STDIO ////////////////////////////////////////////////////////////////////////////// // // PNG writer // #ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] #define stbiw__sbn(a) stbiw__sbraw(a)[1] #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); STBIW_ASSERT(p); if (p) { if (!*arr) ((int *) p)[1] = 0; *arr = (void *) ((int *) p + 2); stbiw__sbm(*arr) = m; } return *arr; } static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { while (*bitcount >= 8) { stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); *bitbuffer >>= 8; *bitcount -= 8; } return data; } static int stbiw__zlib_bitrev(int code, int codebits) { int res=0; while (codebits--) { res = (res << 1) | (code & 1); code >>= 1; } return res; } static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) { int i; for (i=0; i < limit && i < 258; ++i) if (a[i] != b[i]) break; return i; } static unsigned int stbiw__zhash(unsigned char *data) { stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) #define stbiw__zlib_add(code,codebits) \ (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) // default huffman tables #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) #define stbiw__ZHASH 16384 #endif // STBIW_ZLIB_COMPRESS STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { #ifdef STBIW_ZLIB_COMPRESS // user provided a zlib compress implementation, use that return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); #else // use builtin static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); if (hash_table == NULL) return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window stbiw__sbpush(out, 0x5e); // FLEVEL = 1 stbiw__zlib_add(1,1); // BFINAL = 1 stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman for (i=0; i < stbiw__ZHASH; ++i) hash_table[i] = NULL; i=0; while (i < data_len-3) { // hash next 3 bytes of data to be compressed int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; unsigned char *bestloc = 0; unsigned char **hlist = hash_table[h]; int n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32768) { // if entry lies within window int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); if (d >= best) { best=d; bestloc=hlist[j]; } } } // when hash table entry is too long, delete half the entries if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); stbiw__sbn(hash_table[h]) = quality; } stbiw__sbpush(hash_table[h],data+i); if (bestloc) { // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); hlist = hash_table[h]; n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32767) { int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); if (e > best) { // if next match is better, bail on current match bestloc = NULL; break; } } } } if (bestloc) { int d = (int) (data+i - bestloc); // distance back STBIW_ASSERT(d <= 32767 && best <= 258); for (j=0; best > lengthc[j+1]-1; ++j); stbiw__zlib_huff(j+257); if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); for (j=0; d > distc[j+1]-1; ++j); stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); i += best; } else { stbiw__zlib_huffb(data[i]); ++i; } } // write out final bytes for (;i < data_len; ++i) stbiw__zlib_huffb(data[i]); stbiw__zlib_huff(256); // end of block // pad with 0 bits to byte boundary while (bitcount) stbiw__zlib_add(0,1); for (i=0; i < stbiw__ZHASH; ++i) (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); { // compute adler32 on input unsigned int s1=1, s2=0; int blocklen = (int) (data_len % 5552); j=0; while (j < data_len) { for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } s1 %= 65521; s2 %= 65521; j += blocklen; blocklen = 5552; } stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s2)); stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s1)); } *out_len = stbiw__sbn(out); // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); #endif // STBIW_ZLIB_COMPRESS } static unsigned int stbiw__crc32(unsigned char *buffer, int len) { #ifdef STBIW_CRC32 return STBIW_CRC32(buffer, len); #else static unsigned int crc_table[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 }; unsigned int crc = ~0u; int i; for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; #endif } #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) static void stbiw__wpcrc(unsigned char **data, int len) { unsigned int crc = stbiw__crc32(*data - len - 4, len+4); stbiw__wp32(*data, crc); } static unsigned char stbiw__paeth(int a, int b, int c) { int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); if (pb <= pc) return STBIW_UCHAR(b); return STBIW_UCHAR(c); } // @OPTIMIZE: provide an option that always forces left-predict or paeth predict static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) { static int mapping[] = { 0,1,2,3,4 }; static int firstmap[] = { 0,1,0,5,6 }; int *mymap = (y != 0) ? mapping : firstmap; int i; int type = mymap[filter_type]; unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; if (type==0) { memcpy(line_buffer, z, width*n); return; } // first loop isn't optimized since it's just one pixel for (i = 0; i < n; ++i) { switch (type) { case 1: line_buffer[i] = z[i]; break; case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; case 5: line_buffer[i] = z[i]; break; case 6: line_buffer[i] = z[i]; break; } } switch (type) { case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; } } STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; if (force_filter >= 5) { force_filter = -1; } filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { int filter_type; if (force_filter > -1) { filter_type = force_filter; stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); } else { // Estimate the best filter by running through all of them: int best_filter = 0, best_filter_val = 0x7fffffff, est, i; for (filter_type = 0; filter_type < 5; filter_type++) { stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); // Estimate the entropy of the line using this filter; the less, the better. est = 0; for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); } if (est < best_filter_val) { best_filter_val = est; best_filter = filter_type; } } if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); filter_type = best_filter; } } // when we get here, filter_type contains the filter type, and line_buffer contains the data filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; // each tag requires 12 bytes of overhead out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); if (!out) return 0; *out_len = 8 + 12+13 + 12+zlen + 12; o=out; STBIW_MEMMOVE(o,sig,8); o+= 8; stbiw__wp32(o, 13); // header length stbiw__wptag(o, "IHDR"); stbiw__wp32(o, x); stbiw__wp32(o, y); *o++ = 8; *o++ = STBIW_UCHAR(ctype[n]); *o++ = 0; *o++ = 0; *o++ = 0; stbiw__wpcrc(&o,13); stbiw__wp32(o, zlen); stbiw__wptag(o, "IDAT"); STBIW_MEMMOVE(o, zlib, zlen); o += zlen; STBIW_FREE(zlib); stbiw__wpcrc(&o, zlen); stbiw__wp32(o,0); stbiw__wptag(o, "IEND"); stbiw__wpcrc(&o,0); STBIW_ASSERT(o == out + *out_len); return out; } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { FILE *f; int len; unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; f = stbiw__fopen(filename, "wb"); if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); STBIW_FREE(png); return 1; } #endif STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) { int len; unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; func(context, png, len); STBIW_FREE(png); return 1; } /* *************************************************************************** * * JPEG writer * * This is based on Jon Olick's jo_jpeg.cpp: * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html */ static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { int bitBuf = *bitBufP, bitCnt = *bitCntP; bitCnt += bs[1]; bitBuf |= bs[0] << (24 - bitCnt); while(bitCnt >= 8) { unsigned char c = (bitBuf >> 16) & 255; stbiw__putc(s, c); if(c == 255) { stbiw__putc(s, 0); } bitBuf <<= 8; bitCnt -= 8; } *bitBufP = bitBuf; *bitCntP = bitCnt; } static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; float z1, z2, z3, z4, z5, z11, z13; float tmp0 = d0 + d7; float tmp7 = d0 - d7; float tmp1 = d1 + d6; float tmp6 = d1 - d6; float tmp2 = d2 + d5; float tmp5 = d2 - d5; float tmp3 = d3 + d4; float tmp4 = d3 - d4; // Even part float tmp10 = tmp0 + tmp3; // phase 2 float tmp13 = tmp0 - tmp3; float tmp11 = tmp1 + tmp2; float tmp12 = tmp1 - tmp2; d0 = tmp10 + tmp11; // phase 3 d4 = tmp10 - tmp11; z1 = (tmp12 + tmp13) * 0.707106781f; // c4 d2 = tmp13 + z1; // phase 5 d6 = tmp13 - z1; // Odd part tmp10 = tmp4 + tmp5; // phase 2 tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; // The rotator is modified from fig 4-8 to avoid extra negations. z5 = (tmp10 - tmp12) * 0.382683433f; // c6 z2 = tmp10 * 0.541196100f + z5; // c2-c6 z4 = tmp12 * 1.306562965f + z5; // c2+c6 z3 = tmp11 * 0.707106781f; // c4 z11 = tmp7 + z3; // phase 5 z13 = tmp7 - z3; *d5p = z13 + z2; // phase 6 *d3p = z13 - z2; *d1p = z11 + z4; *d7p = z11 - z4; *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; } static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { int tmp1 = val < 0 ? -val : val; val = val < 0 ? val-1 : val; bits[1] = 1; while(tmp1 >>= 1) { ++bits[1]; } bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { } // end0pos = first element in reverse order !=0 if(end0pos == 0) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); return DU[0]; } for(i = 1; i <= end0pos; ++i) { int startpos = i; int nrzeroes; unsigned short bits[2]; for (; DU[i]==0 && i<=end0pos; ++i) { } nrzeroes = i-startpos; if ( nrzeroes >= 16 ) { int lng = nrzeroes>>4; int nrmarker; for (nrmarker=1; nrmarker <= lng; ++nrmarker) stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); nrzeroes &= 15; } stbiw__jpg_calcBits(DU[i], bits); stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); } if(end0pos != 63) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); } return DU[0]; } static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { // Constants that don't pollute global namespace static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; static const unsigned char std_ac_luminance_values[] = { 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; static const unsigned char std_ac_chrominance_values[] = { 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; // Huffman tables static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; static const unsigned short YAC_HT[256][2] = { {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const unsigned short UVAC_HT[256][2] = { {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; int row, col, i, k; float fdtbl_Y[64], fdtbl_UV[64]; unsigned char YTable[64], UVTable[64]; if(!data || !width || !height || comp > 4 || comp < 1) { return 0; } quality = quality ? quality : 90; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; for(i = 0; i < 64; ++i) { int uvti, yti = (YQT[i]*quality+50)/100; YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); uvti = (UVQT[i]*quality+50)/100; UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); } for(row = 0, k = 0; row < 8; ++row) { for(col = 0; col < 8; ++col, ++k) { fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); } } // Write Headers { static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; s->func(s->context, (void*)head0, sizeof(head0)); s->func(s->context, (void*)YTable, sizeof(YTable)); stbiw__putc(s, 1); s->func(s->context, UVTable, sizeof(UVTable)); s->func(s->context, (void*)head1, sizeof(head1)); s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); stbiw__putc(s, 0x10); // HTYACinfo s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); stbiw__putc(s, 1); // HTUDCinfo s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); stbiw__putc(s, 0x11); // HTUACinfo s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); s->func(s->context, (void*)head2, sizeof(head2)); } // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; const unsigned char *imageData = (const unsigned char *)data; int DCY=0, DCU=0, DCV=0; int bitBuf=0, bitCnt=0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; int x, y, pos; for(y = 0; y < height; y += 8) { for(x = 0; x < width; x += 8) { float YDU[64], UDU[64], VDU[64]; for(row = y, pos = 0; row < y+8; ++row) { // row >= height => use last input row int clamped_row = (row < height) ? row : height - 1; int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; for(col = x; col < x+8; ++col, ++pos) { float r, g, b; // if col >= width => use pixel from last input column int p = base_p + ((col < width) ? col : (width-1))*comp; r = imageData[p+0]; g = imageData[p+ofsG]; b = imageData[p+ofsB]; YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; } } DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } // Do the bit alignment of the EOI marker stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); } // EOI stbiw__putc(s, 0xFF); stbiw__putc(s, 0xD9); return 1; } STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); stbi__end_write_file(&s); return r; } else return 0; } #endif #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history 1.10 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 1.09 (2018-02-11) fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 1.08 (2018-01-29) add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1.07 (2017-07-24) doc fix 1.06 (2017-07-23) writing JPEG (using Jon Olick's code) 1.05 ??? 1.04 (2017-03-03) monochrome BMP expansion 1.03 ??? 1.02 (2016-04-02) avoid allocating large structures on the stack 1.01 (2016-01-16) STBIW_REALLOC_SIZED: support allocators with no realloc support avoid race-condition in crc initialization minor compile issues 1.00 (2015-09-14) installable file IO function 0.99 (2015-09-13) warning fixes; TGA rle support 0.98 (2015-04-08) added STBIW_MALLOC, STBIW_ASSERT etc 0.97 (2015-01-18) fixed HDR asserts, rewrote HDR rle logic 0.96 (2015-01-17) add HDR output fix monochrome BMP 0.95 (2014-08-17) add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) warning fixes 0.92 (2010-08-01) casts to unsigned char to fix warnings 0.91 (2010-07-17) first public release 0.90 first internal release */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ================================================ FILE: lib/third_party/imgui/imgui_test_engine/source/imgui_capture_tool.cpp ================================================ // dear imgui test engine // (screen/video capture tool) // This is usable as a standalone applet or controlled by the test engine. // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. // Two mode of operation: // - Interactive: call ImGuiCaptureToolUI::ShowCaptureToolWindow() // - Programmatic: generally via ImGuiTestContext::CaptureXXX functions // FIXME: This probably needs a rewrite, it's a bit too complicated. /* Index of this file: // [SECTION] Includes // [SECTION] ImGuiCaptureImageBuf // [SECTION] ImGuiCaptureContext // [SECTION] ImGuiCaptureToolUI */ //----------------------------------------------------------------------------- // [SECTION] Includes //----------------------------------------------------------------------------- #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui.h" #include "imgui_internal.h" #include "imgui_te_engine.h" #include "imgui_capture_tool.h" #include "imgui_te_utils.h" // ImPathFindFilename, ImPathFindExtension, ImPathFixSeparatorsForCurrentOS, ImFileCreateDirectoryChain, ImOsOpenInShell #include "thirdparty/Str/Str.h" //----------------------------------------------------------------------------- // [SECTION] Link stb_image_write.h //----------------------------------------------------------------------------- #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE // Compile time options: //#define IMGUI_STB_NAMESPACE ImStb //#define IMGUI_STB_IMAGE_WRITE_FILENAME "my_folder/stb_image_write.h" //#define IMGUI_DISABLE_STB_IMAGE_WRITE_IMPLEMENTATION // stb_image_write #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration #pragma warning (disable: 4457) // declaration of 'xx' hides function parameter #elif defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations"// warning: 'sprintf' has been explicitly marked deprecated here #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-conversion" #endif #ifdef IMGUI_STB_NAMESPACE namespace IMGUI_STB_NAMESPACE { #endif #ifndef STB_IMAGE_WRITE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) #ifndef IMGUI_DISABLE_STB_IMAGE_WRITE_IMPLEMENTATION // in case the user already have an implementation in another compilation unit #define STB_IMAGE_WRITE_IMPLEMENTATION #endif #ifdef IMGUI_STB_IMAGE_WRITE_FILENAME #include IMGUI_STB_IMAGE_WRITE_FILENAME #else #include "thirdparty/stb/imstb_image_write.h" #endif // #ifdef IMGUI_STB_IMAGE_WRITE_FILENAME #endif // #ifndef STB_IMAGE_WRITE_IMPLEMENTATION #ifdef IMGUI_STB_NAMESPACE } // namespace ImStb using namespace IMGUI_STB_NAMESPACE; #endif #ifdef _MSC_VER #pragma warning (pop) #elif defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif #endif // #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE //----------------------------------------------------------------------------- // [SECTION] ImGuiCaptureImageBuf // Helper class for simple bitmap manipulation (not particularly efficient!) //----------------------------------------------------------------------------- void ImGuiCaptureImageBuf::Clear() { if (Data) IM_FREE(Data); Data = nullptr; } void ImGuiCaptureImageBuf::CreateEmpty(int w, int h) { Clear(); Width = w; Height = h; Data = (unsigned int*)IM_ALLOC((size_t)(Width * Height * 4)); memset(Data, 0, (size_t)(Width * Height * 4)); } bool ImGuiCaptureImageBuf::SaveFile(const char* filename) { #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE IM_ASSERT(Data != nullptr); ImFileCreateDirectoryChain(filename, ImPathFindFilename(filename)); int ret = stbi_write_png(filename, Width, Height, 4, Data, Width * 4); return ret != 0; #else IM_UNUSED(filename); return false; #endif } void ImGuiCaptureImageBuf::RemoveAlpha() { unsigned int* p = Data; int n = Width * Height; while (n-- > 0) { *p |= IM_COL32_A_MASK; p++; } } //----------------------------------------------------------------------------- // [SECTION] ImGuiCaptureContext //----------------------------------------------------------------------------- #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE static void HideOtherWindows(const ImGuiCaptureArgs* args) { ImGuiContext& g = *GImGui; for (ImGuiWindow* window : g.Windows) { if (window->Flags & ImGuiWindowFlags_ChildWindow) continue; if (window->Flags & ImGuiWindowFlags_Tooltip) continue; if ((window->Flags & ImGuiWindowFlags_Popup) && (args->InFlags & ImGuiCaptureFlags_IncludePopups) != 0) continue; if (args->InCaptureWindows.contains(window)) continue; #ifdef IMGUI_HAS_DOCK bool should_hide_window = true; for (ImGuiWindow* capture_window : args->InCaptureWindows) { if (capture_window->DockNode != nullptr && capture_window->DockNode->HostWindow->RootWindow == window) { should_hide_window = false; break; } } if (!should_hide_window) continue; #endif // IMGUI_HAS_DOCK // Not overwriting HiddenFramesCanSkipItems or HiddenFramesCannotSkipItems since they have side-effects (e.g. preserving ContentsSize) if (window->WasActive || window->Active) window->HiddenFramesForRenderOnly = 2; } } #endif // IMGUI_TEST_ENGINE_ENABLE_CAPTURE static ImRect GetMainViewportRect() { ImGuiViewport* viewport = ImGui::GetMainViewport(); return ImRect(viewport->Pos, viewport->Pos + viewport->Size); } void ImGuiCaptureContext::PreNewFrame() { const ImGuiCaptureArgs* args = _CaptureArgs; if (args == nullptr) return; ImGuiContext& g = *GImGui; // Force mouse position. Hovered window is reset in ImGui::NewFrame() based on mouse real mouse position. if (_FrameNo > 2 && (args->InFlags & ImGuiCaptureFlags_StitchAll) != 0) { IM_ASSERT(args->InCaptureWindows.Size == 1); g.IO.MousePos = args->InCaptureWindows[0]->Pos + _MouseRelativeToWindowPos; g.HoveredWindow = _HoveredWindow; } } void ImGuiCaptureContext::PreRender() { ImGuiContext& g = *GImGui; _BackupMouseDrawCursor = g.IO.MouseDrawCursor; if (IsCapturing()) { const ImGuiCaptureArgs* args = _CaptureArgs; IM_ASSERT(args != NULL); g.IO.MouseDrawCursor = !(args->InFlags & ImGuiCaptureFlags_HideMouseCursor); } } void ImGuiCaptureContext::PostRender() { ImGuiContext& g = *GImGui; g.IO.MouseDrawCursor = _BackupMouseDrawCursor; } void ImGuiCaptureContext::RestoreBackedUpData() { // Restore window positions unconditionally. We may have moved them ourselves during capture. ImGuiContext& g = *GImGui; for (int n = 0; n < _WindowsData.Size; n++) { ImGuiWindow* window = _WindowsData[n].Window; if (window->Hidden) continue; ImGui::SetWindowPos(window, _WindowsData[n].BackupRect.Min, ImGuiCond_Always); ImGui::SetWindowSize(window, _WindowsData[n].BackupRect.GetSize(), ImGuiCond_Always); } g.Style.DisplayWindowPadding = _BackupDisplayWindowPadding; g.Style.DisplaySafeAreaPadding = _BackupDisplaySafeAreaPadding; } void ImGuiCaptureContext::ClearState() { _FrameNo = _ChunkNo = 0; _VideoLastFrameTime = 0; _MouseRelativeToWindowPos = ImVec2(-FLT_MAX, -FLT_MAX); _HoveredWindow = nullptr; _CaptureArgs = nullptr; } // Returns true when capture is in progress. ImGuiCaptureStatus ImGuiCaptureContext::CaptureUpdate(ImGuiCaptureArgs* args) { #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; // Sanity checks IM_ASSERT(args != nullptr); IM_ASSERT(ScreenCaptureFunc != nullptr); IM_ASSERT(args->InOutputImageBuf != nullptr || args->InOutputFile[0]); IM_ASSERT(args->InRecordFPSTarget != 0); if (_VideoRecording) { IM_ASSERT(args->InOutputFile[0] && "Output filename must be specified when recording videos."); IM_ASSERT(args->InOutputImageBuf == nullptr && "Output buffer cannot be specified when recording videos."); IM_ASSERT((args->InFlags & ImGuiCaptureFlags_StitchAll) == 0 && "Image stitching is not supported when recording videos."); if (!ImFileExist(VideoCaptureEncoderPath)) { fprintf(stderr, "Video encoder not found at \"%s\", video capturing failed.\n", VideoCaptureEncoderPath); return ImGuiCaptureStatus_Error; } } ImGuiCaptureImageBuf* output = args->InOutputImageBuf ? args->InOutputImageBuf : &_CaptureBuf; const ImRect viewport_rect = GetMainViewportRect(); // Hide other windows so they can't be seen visible behind captured window if ((args->InFlags & ImGuiCaptureFlags_IncludeOtherWindows) == 0 && !args->InCaptureWindows.empty()) HideOtherWindows(args); // Recording will be set to false when we are stopping video capture. const bool is_recording_video = IsCapturingVideo(); const double current_time_sec = ImGui::GetTime(); if (is_recording_video && _VideoLastFrameTime > 0) { double delta_sec = current_time_sec - _VideoLastFrameTime; if (delta_sec < 1.0 / args->InRecordFPSTarget) return ImGuiCaptureStatus_InProgress; } // Capture can be performed in single frame if we are capturing a rect. const bool instant_capture = (args->InFlags & ImGuiCaptureFlags_Instant) != 0; const bool is_capturing_explicit_rect = args->InCaptureRect.GetWidth() > 0 && args->InCaptureRect.GetHeight() > 0; if (instant_capture) { IM_ASSERT(args->InCaptureWindows.empty()); IM_ASSERT(is_capturing_explicit_rect); IM_ASSERT(is_recording_video == false); IM_ASSERT((args->InFlags & ImGuiCaptureFlags_StitchAll) == 0); } // Do not start a capture process while mouse button is pressed. In case mouse cursor is hovering a captured window, // pressed button may cause window to be repositioned unexpectedly. This is only important in stitched mode, because // this is the only time we move mouse cursor. if ((args->InFlags & ImGuiCaptureFlags_StitchAll) != 0) if (g.IO.MouseDown[0] && _FrameNo == 0) return ImGuiCaptureStatus_InProgress; //----------------------------------------------------------------- // Frame 0: Initialize capture state //----------------------------------------------------------------- if (_FrameNo == 0) { if (is_recording_video) { // Determinate size alignment const char* extension = (char*)ImPathFindExtension(args->InOutputFile); if (args->InSizeAlign == 0) { if (strcmp(extension, ".gif") == 0) args->InSizeAlign = 1; else args->InSizeAlign = 2; // mp4 wants >= 2 } IM_ASSERT(args->InSizeAlign > 0); } // When recording, same args should have been passed to BeginVideoCapture(). IM_ASSERT(!_VideoRecording || _CaptureArgs == args); _CaptureArgs = args; _ChunkNo = 0; _CaptureRect = _CapturedWindowRect = ImRect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); _WindowsData.clear(); _BackupDisplayWindowPadding = g.Style.DisplayWindowPadding; _BackupDisplaySafeAreaPadding = g.Style.DisplaySafeAreaPadding; g.Style.DisplayWindowPadding = ImVec2(0, 0); // Allow windows to be positioned fully outside of visible viewport. g.Style.DisplaySafeAreaPadding = ImVec2(0, 0); if (is_capturing_explicit_rect) { // Capture arbitrary rectangle. If any windows are specified in this mode only they will appear in captured region. _CaptureRect = args->InCaptureRect; if (args->InCaptureWindows.empty() && !instant_capture) { // Gather all top level windows. We will need to move them in order to capture regions larger than viewport. for (ImGuiWindow* window : g.Windows) { // Child windows will be included by their parents. if (window->ParentWindow != nullptr) continue; if ((window->Flags & ImGuiWindowFlags_Popup) && !(args->InFlags & ImGuiCaptureFlags_IncludePopups)) continue; args->InCaptureWindows.push_back(window); } } } // Save rectangle covering all windows and find top-left corner of combined rect which will be used to // translate this group of windows to top-left corner of the screen. for (ImGuiWindow* window : args->InCaptureWindows) { _CapturedWindowRect.Add(window->Rect()); ImGuiCaptureWindowData window_data; window_data.BackupRect = window->Rect(); window_data.Window = window; _WindowsData.push_back(window_data); } if (args->InFlags & ImGuiCaptureFlags_StitchAll) { IM_ASSERT(is_capturing_explicit_rect == false && "ImGuiCaptureContext: capture of full window contents is not possible when capturing specified rect."); IM_ASSERT(args->InCaptureWindows.Size == 1 && "ImGuiCaptureContext: capture of full window contents is not possible when capturing more than one window."); // Resize window to it's contents and capture it's entire width/height. However if window is bigger than it's contents - keep original size. ImGuiWindow* window = args->InCaptureWindows[0]; ImVec2 full_size = window->SizeFull; // Mouse cursor is relative to captured window even if it is not hovered, in which case cursor is kept off the window to prevent appearing in screenshot multiple times by accident. _MouseRelativeToWindowPos = io.MousePos - window->Pos + window->Scroll; // FIXME-CAPTURE: Window width change may affect vertical content size if window contains text that wraps. To accurately position mouse cursor for capture we avoid horizontal resize. // Instead window width should be set manually before capture, as it is simple to do and most of the time we already have a window of desired width. //full_size.x = ImMax(window->SizeFull.x, window->ContentSize.x + (window->WindowPadding.x + window->WindowBorderSize) * 2); full_size.y = ImMax(window->SizeFull.y, window->ContentSize.y + (window->WindowPadding.y + window->WindowBorderSize) * 2 + window->DecoOuterSizeY1); ImGui::SetWindowSize(window, full_size); _HoveredWindow = g.HoveredWindow; } else { _MouseRelativeToWindowPos = ImVec2(-FLT_MAX, -FLT_MAX); _HoveredWindow = nullptr; } } else { IM_ASSERT(args == _CaptureArgs); // Capture args can not change mid-capture. } //----------------------------------------------------------------- // Frame 1: Skipped to allow window size to update fully //----------------------------------------------------------------- //----------------------------------------------------------------- // Frame 2: Position windows, lock rectangle, create capture buffer //----------------------------------------------------------------- if (_FrameNo == 2 || instant_capture) { // Move group of windows so combined rectangle position is at the top-left corner + padding and create combined // capture rect of entire area that will be saved to screenshot. Doing this on the second frame because when // ImGuiCaptureFlags_StitchAll flag is used we need to allow window to reposition. // Repositioning of a window may take multiple frames, depending on whether window was already rendered or not. if (args->InFlags & ImGuiCaptureFlags_StitchAll) { ImVec2 move_offset = ImVec2(args->InPadding, args->InPadding) - _CapturedWindowRect.Min + viewport_rect.Min; IM_ASSERT(args->InCaptureWindows.Size == _WindowsData.Size); for (int n = 0; n < _WindowsData.Size; n++) { ImGuiWindow* window = _WindowsData[n].Window; _WindowsData[n].PosDuringCapture = window->Pos + move_offset; ImGui::SetWindowPos(window, _WindowsData[n].PosDuringCapture); } } // Determine capture rectangle if not provided by user if (!is_capturing_explicit_rect) { if (args->InCaptureWindows.Size > 0) { for (ImGuiWindow* window : args->InCaptureWindows) _CaptureRect.Add(window->Rect()); _CaptureRect.Expand(args->InPadding); } else { _CaptureRect = viewport_rect; } } if ((args->InFlags & ImGuiCaptureFlags_StitchAll) == 0) { // Can not capture area outside of screen. Clip capture rect, since we are capturing only visible rect anyway. _CaptureRect.ClipWith(viewport_rect); // Align size // FIXME: ffmpeg + codec can possibly handle that better on their side. ImVec2 capture_size_aligned = _CaptureRect.GetSize(); if (args->InSizeAlign > 1) { // Round up IM_ASSERT(ImIsPowerOfTwo(args->InSizeAlign)); capture_size_aligned.x = (float)IM_MEMALIGN((int)capture_size_aligned.x, args->InSizeAlign); capture_size_aligned.y = (float)IM_MEMALIGN((int)capture_size_aligned.y, args->InSizeAlign); // Unless will stray off viewport, then round down if (_CaptureRect.Min.x + capture_size_aligned.x >= viewport_rect.Max.x) capture_size_aligned.x -= args->InSizeAlign; if (_CaptureRect.Min.y + capture_size_aligned.y >= viewport_rect.Max.y) capture_size_aligned.y -= args->InSizeAlign; IM_ASSERT(capture_size_aligned.x > 0); IM_ASSERT(capture_size_aligned.y > 0); _CaptureRect.Max = _CaptureRect.Min + capture_size_aligned; } } // Initialize capture buffer. IM_ASSERT(!_CaptureRect.IsInverted()); args->OutImageSize = _CaptureRect.GetSize(); output->CreateEmpty((int)_CaptureRect.GetWidth(), (int)_CaptureRect.GetHeight()); } //----------------------------------------------------------------- // Frame 4+N*4: Capture a frame //----------------------------------------------------------------- const ImRect clip_rect = viewport_rect; ImRect capture_rect = _CaptureRect; capture_rect.ClipWith(clip_rect); const int capture_height = ImMin((int)io.DisplaySize.y, (int)_CaptureRect.GetHeight()); const int x1 = (int)(capture_rect.Min.x - clip_rect.Min.x); const int y1 = (int)(capture_rect.Min.y - clip_rect.Min.y); const int w = (int)capture_rect.GetWidth(); const int h = (int)ImMin(output->Height - _ChunkNo * capture_height, capture_height); // Position windows if ((_FrameNo > 2) && (args->InFlags & ImGuiCaptureFlags_StitchAll)) { // Unlike SetNextWindowPos(), SetWindowPos() will still perform viewport clamping, affecting support for io.ConfigWindowsMoveFromTitleBarOnly. IM_ASSERT(args->InCaptureWindows.Size == _WindowsData.Size); for (int n = 0; n < _WindowsData.Size; n++) ImGui::SetWindowPos(_WindowsData[n].Window, _WindowsData[n].PosDuringCapture - ImVec2(0, (float)capture_height * _ChunkNo)); } if (((_FrameNo > 2) && (_FrameNo % 4) == 0) || (is_recording_video && _FrameNo > 2) || instant_capture) { // FIXME: Implement capture of regions wider than viewport. // Capture a portion of image. Capturing of windows wider than viewport is not implemented yet. if (h > 0) { IM_ASSERT(w == output->Width); if (args->InFlags & ImGuiCaptureFlags_StitchAll) IM_ASSERT(h <= output->Height); // When stitching, image can be taller than captured viewport. else IM_ASSERT(h == output->Height); ImGuiID viewport_id = 0; #ifdef IMGUI_HAS_VIEWPORT if (args->InFlags & ImGuiCaptureFlags_StitchAll) viewport_id = _WindowsData[0].Window->ViewportId; else viewport_id = ImGui::GetMainViewport()->ID; #endif //printf("ScreenCaptureFunc x1: %d, y1: %d, w: %d, h: %d\n", x1, y1, w, h); if (!ScreenCaptureFunc(viewport_id, x1, y1, w, h, &output->Data[_ChunkNo * w * capture_height], ScreenCaptureUserData)) { fprintf(stderr, "Screen capture function failed.\n"); RestoreBackedUpData(); ClearState(); return ImGuiCaptureStatus_Error; } if (args->InFlags & ImGuiCaptureFlags_StitchAll) { // Window moves up in order to expose it's lower part. _ChunkNo++; _CaptureRect.TranslateY(-(float)h); } if (is_recording_video && (args->InFlags & ImGuiCaptureFlags_NoSave) == 0) { // _VideoEncoderPipe is nullptr when recording just started. Initialize recording state. if (_VideoEncoderPipe == nullptr) { // First video frame, initialize now that dimensions are known. const unsigned int width = (unsigned int)capture_rect.GetWidth(); const unsigned int height = (unsigned int)capture_rect.GetHeight(); IM_ASSERT(VideoCaptureEncoderPath != nullptr && VideoCaptureEncoderPath[0]); Str256f encoder_exe(VideoCaptureEncoderPath), cmd(""); ImPathFixSeparatorsForCurrentOS(encoder_exe.c_str()); ImFileCreateDirectoryChain(args->InOutputFile, ImPathFindFilename(args->InOutputFile)); #if _WIN32 cmd.append("\""); // On windows, entire command wrapped in quotes allows use of quotes for parameters. #endif const char* extension = (char*)ImPathFindExtension(args->InOutputFile); if (strcmp(extension, ".gif") == 0) { IM_ASSERT(GifCaptureEncoderParams != nullptr && GifCaptureEncoderParams[0]); cmd.appendf("\"%s\" %s", encoder_exe.c_str(), GifCaptureEncoderParams); } else { IM_ASSERT(VideoCaptureEncoderParams != nullptr && VideoCaptureEncoderParams[0]); cmd.appendf("\"%s\" %s", encoder_exe.c_str(), VideoCaptureEncoderParams); } #if _WIN32 cmd.append("\""); #endif ImStrReplace(&cmd, "$FPS", Str16f("%d", args->InRecordFPSTarget).c_str()); ImStrReplace(&cmd, "$WIDTH", Str16f("%d", width).c_str()); ImStrReplace(&cmd, "$HEIGHT", Str16f("%d", height).c_str()); ImStrReplace(&cmd, "$OUTPUT", args->InOutputFile); fprintf(stdout, "# %s\n", cmd.c_str()); _VideoEncoderPipe = ImOsPOpen(cmd.c_str(), "w"); IM_ASSERT(_VideoEncoderPipe != nullptr); } // Save new video frame fwrite(output->Data, 1, output->Width * output->Height * 4, _VideoEncoderPipe); } if (is_recording_video) _VideoLastFrameTime = current_time_sec; } // Image is finalized immediately when we are not stitching. Otherwise, image is finalized when we have captured and stitched all frames. if (!_VideoRecording && (!(args->InFlags & ImGuiCaptureFlags_StitchAll) || h <= 0)) { output->RemoveAlpha(); if (_VideoEncoderPipe != nullptr) { // At this point _Recording is false, but we know we were recording because _VideoEncoderPipe is not nullptr. Finalize video here. ImOsPClose(_VideoEncoderPipe); _VideoEncoderPipe = nullptr; } else if (args->InOutputImageBuf == nullptr) { // Save single frame. if ((args->InFlags & ImGuiCaptureFlags_NoSave) == 0) output->SaveFile(args->InOutputFile); output->Clear(); } RestoreBackedUpData(); ClearState(); return ImGuiCaptureStatus_Done; } } // Keep going _FrameNo++; return ImGuiCaptureStatus_InProgress; #else IM_UNUSED(args); return ImGuiCaptureStatus_Done; #endif } void ImGuiCaptureContext::BeginVideoCapture(ImGuiCaptureArgs* args) { IM_ASSERT(args != nullptr); IM_ASSERT(_VideoRecording == false); IM_ASSERT(_VideoEncoderPipe == nullptr); IM_ASSERT(args->InRecordFPSTarget >= 1 && args->InRecordFPSTarget <= 100); ImFileCreateDirectoryChain(args->InOutputFile, ImPathFindFilename(args->InOutputFile)); _VideoRecording = true; _CaptureArgs = args; } void ImGuiCaptureContext::EndVideoCapture() { IM_ASSERT(_CaptureArgs != nullptr); IM_ASSERT(_VideoRecording == true); _VideoRecording = false; _CaptureArgs = nullptr; } bool ImGuiCaptureContext::IsCapturingVideo() { return _VideoRecording; } bool ImGuiCaptureContext::IsCapturing() { return _CaptureArgs != nullptr; } //----------------------------------------------------------------------------- // ImGuiCaptureToolUI //----------------------------------------------------------------------------- ImGuiCaptureToolUI::ImGuiCaptureToolUI() { // Filename template for where screenshots will be saved. May contain directories or variation of %d format. ImStrncpy(_OutputFileTemplate, "output/captures/imgui_capture_%04d.png", IM_ARRAYSIZE(_OutputFileTemplate)); } // Interactively pick a single window void ImGuiCaptureToolUI::_CaptureWindowPicker(ImGuiCaptureArgs* args) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const ImVec2 button_sz = ImVec2(TEXT_BASE_WIDTH * 30, 0.0f); const ImGuiID picking_id = ImGui::GetID("##picking"); if (ImGui::Button("Capture Single Window..", button_sz)) _StateIsPickingWindow = true; if (_StateIsPickingWindow) { // Picking a window ImGuiWindow* capture_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : nullptr; ImDrawList* fg_draw_list = ImGui::GetForegroundDrawList(); ImGui::SetActiveID(picking_id, g.CurrentWindow); // Steal active ID so our click won't interact with something else. ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); ImGui::SetTooltip("Capture window: '%s'\nPress ESC to cancel.", capture_window ? capture_window->Name : ""); // FIXME: Would be nice to have a way to enforce front-most windows. Perhaps make this Render() feature more generic. //if (capture_window) // g.NavWindowingTarget = capture_window; // Draw rect that is about to be captured const ImRect viewport_rect = GetMainViewportRect(); const ImU32 col_dim_overlay = IM_COL32(0, 0, 0, 40); if (capture_window) { ImRect r = capture_window->Rect(); r.Expand(args->InPadding); r.ClipWith(ImRect(ImVec2(0, 0), io.DisplaySize)); r.Expand(1.0f); fg_draw_list->AddRect(r.Min, r.Max, IM_COL32_WHITE, 0.0f, 0, 2.0f); ImGui::RenderRectFilledWithHole(fg_draw_list, viewport_rect, r, col_dim_overlay, 0.0f); } else { fg_draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col_dim_overlay); } if (ImGui::IsMouseClicked(0) && capture_window && _InitializeOutputFile()) { ImGui::FocusWindow(capture_window); _SelectedWindows.resize(0); _StateIsPickingWindow = false; _StateIsCapturing = true; args->InCaptureWindows.clear(); args->InCaptureWindows.push_back(capture_window); } if (ImGui::IsKeyPressed(ImGuiKey_Escape)) _StateIsPickingWindow = _StateIsCapturing = false; } else { if (ImGui::GetActiveID() == picking_id) ImGui::ClearActiveID(); } } void ImGuiCaptureToolUI::_CaptureWindowsSelector(ImGuiCaptureContext* context, ImGuiCaptureArgs* args) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; // Gather selected windows ImRect capture_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (ImGuiWindow* window : g.Windows) { if (window->WasActive == false) continue; if (window->Flags & ImGuiWindowFlags_ChildWindow) continue; const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0; if ((args->InFlags & ImGuiCaptureFlags_IncludePopups) && is_popup) { capture_rect.Add(window->Rect()); args->InCaptureWindows.push_back(window); continue; } if (is_popup) continue; if (_SelectedWindows.contains(window->RootWindow->ID)) { capture_rect.Add(window->Rect()); args->InCaptureWindows.push_back(window); } } const bool allow_capture = !capture_rect.IsInverted() && args->InCaptureWindows.Size > 0 && _OutputFileTemplate[0]; const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const ImVec2 button_sz = ImVec2(TEXT_BASE_WIDTH * 30, 0.0f); // Capture Multiple Button { char label[64]; ImFormatString(label, 64, "Capture Multiple (%d)###CaptureMultiple", args->InCaptureWindows.Size); if (!allow_capture) ImGui::BeginDisabled(); bool do_capture = ImGui::Button(label, button_sz); do_capture |= io.KeyAlt && ImGui::IsKeyPressed(ImGuiKey_C); if (!allow_capture) ImGui::EndDisabled(); ImGui::SetItemTooltip("Alternatively press Alt+C to capture selection."); if (do_capture && _InitializeOutputFile()) _StateIsCapturing = true; } // Record video button // (Prefer 100/FPS to be an integer) { const bool is_capturing_video = context->IsCapturingVideo(); if (is_capturing_video) { if (ImGui::Button("Stop capturing video###CaptureVideo", button_sz)) context->EndVideoCapture(); } else { char label[64]; ImFormatString(label, 64, "Capture video (%d)###CaptureVideo", args->InCaptureWindows.Size); if (!allow_capture) ImGui::BeginDisabled(); if (ImGui::Button(label, button_sz) && _InitializeOutputFile()) { // File template will most likely end with .png, but we need a different extension for videos. IM_ASSERT(VideoCaptureExtension != nullptr && VideoCaptureExtension[0]); char* ext = (char*)ImPathFindExtension(args->InOutputFile); ImStrncpy(ext, VideoCaptureExtension, (size_t)(ext - args->InOutputFile)); _StateIsCapturing = true; context->BeginVideoCapture(args); } if (!allow_capture) ImGui::EndDisabled(); } } // Draw capture rectangle ImDrawList* draw_list = ImGui::GetForegroundDrawList(); if (allow_capture && !_StateIsPickingWindow && !_StateIsCapturing) { IM_ASSERT(capture_rect.GetWidth() > 0); IM_ASSERT(capture_rect.GetHeight() > 0); const ImRect viewport_rect = GetMainViewportRect(); capture_rect.Expand(args->InPadding); capture_rect.ClipWith(viewport_rect); draw_list->AddRect(capture_rect.Min - ImVec2(1.0f, 1.0f), capture_rect.Max + ImVec2(1.0f, 1.0f), IM_COL32_WHITE); } ImGui::Separator(); // Show window list and update rectangles ImGui::Text("Windows:"); if (ImGui::BeginTable("split", 2)) { ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch); for (ImGuiWindow* window : g.Windows) { if (!window->WasActive) continue; const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) || (window->Flags & ImGuiWindowFlags_Tooltip); if (is_popup) continue; if (window->Flags & ImGuiWindowFlags_ChildWindow) continue; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::PushID(window); // Ensure that text after the ## is actually displayed to the user (FIXME: won't be able to check/uncheck from that portion of the text) bool is_selected = _SelectedWindows.contains(window->RootWindow->ID); if (ImGui::Checkbox(window->Name, &is_selected)) { if (is_selected) _SelectedWindows.push_back(window->RootWindow->ID); else _SelectedWindows.find_erase_unsorted(window->RootWindow->ID); } if (const char* remaining_text = ImGui::FindRenderedTextEnd(window->Name)) if (remaining_text[0] != 0) { if (remaining_text > window->Name) ImGui::SameLine(0, 1); else ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::TextUnformatted(remaining_text); } ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 9.0f); ImGui::DragFloat2("Pos", &window->Pos.x, 0.05f, 0.0f, 0.0f, "%.0f"); ImGui::SameLine(); ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 9.0f); ImGui::DragFloat2("Size", &window->SizeFull.x, 0.05f, 0.0f, 0.0f, "%.0f"); ImGui::PopID(); } ImGui::EndTable(); } } void ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, bool* p_open) { // Update capturing if (_StateIsCapturing) { ImGuiCaptureArgs* args = &_CaptureArgs; if (context->IsCapturingVideo() || args->InCaptureWindows.Size > 1) args->InFlags &= ~ImGuiCaptureFlags_StitchAll; if (context->_VideoRecording && ImGui::IsKeyPressed(ImGuiKey_Escape)) context->EndVideoCapture(); ImGuiCaptureStatus status = context->CaptureUpdate(args); if (status != ImGuiCaptureStatus_InProgress) { if (status == ImGuiCaptureStatus_Done) ImStrncpy(OutputLastFilename, args->InOutputFile, IM_ARRAYSIZE(OutputLastFilename)); _StateIsCapturing = false; _FileCounter++; } } // Update UI if (!ImGui::Begin("Dear ImGui Capture Tool", p_open)) { ImGui::End(); return; } if (context->ScreenCaptureFunc == nullptr) { ImGui::TextColored(ImVec4(1, 0, 0, 1), "Backend is missing ScreenCaptureFunc!"); ImGui::End(); return; } ImGuiIO& io = ImGui::GetIO(); ImGuiStyle& style = ImGui::GetStyle(); // Options ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Options")) { // Open Last { const bool has_last_file_name = (OutputLastFilename[0] != 0); if (!has_last_file_name) ImGui::BeginDisabled(); if (ImGui::Button("Open Last")) ImOsOpenInShell(OutputLastFilename); if (!has_last_file_name) ImGui::EndDisabled(); if (has_last_file_name) ImGui::SetItemTooltip("Open %s", OutputLastFilename); ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); } // Open Directory { char output_dir[256]; strcpy(output_dir, _OutputFileTemplate); char* output_filename = (char*)ImPathFindFilename(output_dir); if (output_filename > output_dir) output_filename[-1] = 0; else strcpy(output_dir, "."); if (ImGui::Button("Open Directory")) { ImPathFixSeparatorsForCurrentOS(output_dir); ImOsOpenInShell(output_dir); } ImGui::SetItemTooltip("Open %s/", output_dir); } const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const float BUTTON_WIDTH = (float)(int)-(TEXT_BASE_WIDTH * 26); ImGui::PushItemWidth(BUTTON_WIDTH); ImGui::InputText("Output template", _OutputFileTemplate, IM_ARRAYSIZE(_OutputFileTemplate)); ImGui::SetItemTooltip( "Output template should contain one %%d (or variation of it) format variable. " "Multiple captures will be saved with an increasing number to avoid overwriting same file."); _ShowEncoderConfigFields(context); ImGui::DragFloat("Padding", &_CaptureArgs.InPadding, 0.1f, 0, 32, "%.0f"); ImGui::SetItemTooltip("Extra padding around captured area."); ImGui::DragInt("Video FPS", &_CaptureArgs.InRecordFPSTarget, 0.1f, 10, 100, "%d fps"); ImGui::SetItemTooltip("Target FPS for video captures."); if (ImGui::Button("Snap Windows To Grid", ImVec2(BUTTON_WIDTH, 0))) _SnapWindowsToGrid(SnapGridSize); ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); ImGui::SetNextItemWidth((float)(int)-(TEXT_BASE_WIDTH * 5)); ImGui::DragFloat("##SnapGridSize", &SnapGridSize, 1.0f, 1.0f, 128.0f, "%.0f"); ImGui::Checkbox("Software Mouse Cursor", &io.MouseDrawCursor); bool content_stitching_available = _CaptureArgs.InCaptureWindows.Size <= 1; #ifdef IMGUI_HAS_VIEWPORT content_stitching_available &= !(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable); #endif ImGui::BeginDisabled(!content_stitching_available); ImGui::CheckboxFlags("Stitch full contents height", &_CaptureArgs.InFlags, ImGuiCaptureFlags_StitchAll); ImGui::EndDisabled(); if (!content_stitching_available) ImGui::SetItemTooltip("Content stitching is not possible when using viewports."); ImGui::CheckboxFlags("Include other windows", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludeOtherWindows); ImGui::CheckboxFlags("Include popups", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludePopups); ImGui::SetItemTooltip("Capture area will be expanded to include visible tooltips."); ImGui::PopItemWidth(); ImGui::TreePop(); } ImGui::Separator(); if (!_StateIsCapturing) _CaptureArgs.InCaptureWindows.clear(); _CaptureWindowPicker(&_CaptureArgs); _CaptureWindowsSelector(context, &_CaptureArgs); ImGui::Separator(); ImGui::End(); } // Move/resize all windows so they are neatly aligned on a grid // This is an easy way of ensuring some form of alignment without specifying detailed constraints. void ImGuiCaptureToolUI::_SnapWindowsToGrid(float cell_size) { ImGuiContext& g = *GImGui; for (ImGuiWindow* window : g.Windows) { if (!window->WasActive) continue; if (window->Flags & ImGuiWindowFlags_ChildWindow) continue; if ((window->Flags & ImGuiWindowFlags_Popup) || (window->Flags & ImGuiWindowFlags_Tooltip)) continue; ImRect rect = window->Rect(); rect.Min.x = ImFloor(rect.Min.x / cell_size) * cell_size; rect.Min.y = ImFloor(rect.Min.y / cell_size) * cell_size; rect.Max.x = ImFloor(rect.Max.x / cell_size) * cell_size; rect.Max.y = ImFloor(rect.Max.y / cell_size) * cell_size; ImGui::SetWindowPos(window, rect.Min); ImGui::SetWindowSize(window, rect.GetSize()); } } bool ImGuiCaptureToolUI::_InitializeOutputFile() { // Create output folder and decide of output filename ImFormatString(_CaptureArgs.InOutputFile, IM_ARRAYSIZE(_CaptureArgs.InOutputFile), _OutputFileTemplate, _FileCounter + 1); ImPathFixSeparatorsForCurrentOS(_CaptureArgs.InOutputFile); if (!ImFileCreateDirectoryChain(_CaptureArgs.InOutputFile, ImPathFindFilename(_CaptureArgs.InOutputFile))) { fprintf(stderr, "ImGuiCaptureContext: unable to create directory for file '%s'.\n", _CaptureArgs.InOutputFile); return false; } return true; } bool ImGuiCaptureToolUI::_ShowEncoderConfigFields(ImGuiCaptureContext* context) { ImGuiContext& g = *GImGui; const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const float BUTTON_WIDTH = (float)(int)-(TEXT_BASE_WIDTH * 26); bool modified = false; if (context->VideoCaptureEncoderPathSize) { ImGui::PushItemWidth(BUTTON_WIDTH); modified |= ImGui::InputText("Video Encoder Path", context->VideoCaptureEncoderPath, context->VideoCaptureEncoderPathSize); const bool encoder_exe_missing = !ImFileExist(context->VideoCaptureEncoderPath); if (encoder_exe_missing) ImGui::ItemErrorFrame(IM_COL32(255, 0, 0, 255)); ImGui::SetItemTooltip("Absolute or relative path to video encoder executable (e.g. \"path/to/ffmpeg.exe\"). Required for video recording.%s", encoder_exe_missing ? "\nFile does not exist!" : ""); } struct CmdLineParamsInfo { const char* Title = nullptr; char* Params = nullptr; int ParamsSize = 0; const char* DefaultCmdLineParams = nullptr; const char* VideoFileExt = nullptr; CmdLineParamsInfo(const char* title, char* params, int params_size, const char* default_cmd, const char* ext) { Title = title; Params = params; ParamsSize = params_size; DefaultCmdLineParams = default_cmd; VideoFileExt = ext; } }; CmdLineParamsInfo params_info[] = { { "Video Encoder params", context->VideoCaptureEncoderParams, context->VideoCaptureEncoderParamsSize, IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG, ".mp4" }, { "Gif Encoder params", context->GifCaptureEncoderParams, context->GifCaptureEncoderParamsSize, IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG, ".gif" }, }; for (CmdLineParamsInfo& info : params_info) { if (info.ParamsSize == 0) continue; // Can not be edited. IM_ASSERT(info.Params != nullptr); ImGui::PushID(&info); float small_button_width = ImGui::CalcTextSize("..").x + ImGui::GetStyle().FramePadding.x * 2.0f; ImGui::PushItemWidth(BUTTON_WIDTH - small_button_width); modified |= ImGui::InputText("###Params", info.Params, info.ParamsSize); ImGui::SameLine(0.0f, 0.0f); ImRect input_rect = g.LastItemData.Rect; if (ImGui::Button("..")) ImGui::OpenPopup("CmdParamsPopup"); input_rect.Add(g.LastItemData.Rect); ImGui::SetNextWindowSize(ImVec2(input_rect.GetWidth(), 0.0f)); ImGui::SetNextWindowPos(input_rect.GetBL()); if (ImGui::BeginPopup("CmdParamsPopup")) { ImGui::Text("Reset to default params for FFMPEG and %s file format:", info.VideoFileExt); ImGui::Indent(); float wrap_width = ImGui::GetContentRegionAvail().x - g.Style.FramePadding.x * 2; ImVec2 text_size = ImGui::CalcTextSize(info.DefaultCmdLineParams, nullptr, false, wrap_width); if (ImGui::Selectable("###Reset", false, 0, text_size + g.Style.FramePadding * 2)) { ImStrncpy(info.Params, info.DefaultCmdLineParams, info.ParamsSize); ImGui::CloseCurrentPopup(); } ImDrawList* draw_list = ImGui::GetWindowDrawList(); draw_list->AddText(nullptr, 0, g.LastItemData.Rect.GetTL() + g.Style.FramePadding, ImGui::GetColorU32(ImGuiCol_Text), info.DefaultCmdLineParams, nullptr, wrap_width); ImGui::Unindent(); ImGui::Separator(); ImGui::TextUnformatted( "Command line parameters passed to video encoder executable.\n" "Following variables may be used:\n" "$FPS - target FPS\n" "$WIDTH - width of captured frame\n" "$HEIGHT - height of captured frame\n" "$OUTPUT - video output file"); ImGui::EndPopup(); } ImGui::SameLine(0, g.Style.ItemInnerSpacing.x); ImGui::TextUnformatted(info.Title); if (!info.Params[0]) ImGui::ItemErrorFrame(IM_COL32(255, 0, 0, 255)); ImGui::PopID(); } if (VideoCaptureExtensionSize) { IM_ASSERT(VideoCaptureExtension != nullptr); ImGui::PushItemWidth(BUTTON_WIDTH); if (ImGui::BeginCombo("Video format (default)", VideoCaptureExtension)) { const char* supported_exts[] = { ".gif", ".mp4" }; for (auto& ext: supported_exts) if (ImGui::Selectable(ext, strcmp(VideoCaptureExtension, ext) == 0)) { ImStrncpy(VideoCaptureExtension, ext, VideoCaptureExtensionSize); modified = true; } ImGui::EndCombo(); } ImGui::SetItemTooltip("File extension for captured video file."); } return modified; } //----------------------------------------------------------------------------- ================================================ FILE: lib/third_party/imgui/imgui_test_engine/source/imgui_te_context.cpp ================================================ // dear imgui // (context when a running test + end user automation API) // This is the main (if not only) interface that your Tests will be using. // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui_te_context.h" #include "imgui.h" #include "imgui_internal.h" #include "imgui_te_engine.h" #include "imgui_te_internal.h" #include "imgui_te_perftool.h" #include "imgui_te_utils.h" #include "thirdparty/Str/Str.h" //------------------------------------------------------------------------- // [SECTION] ImGuiTestRefDesc //------------------------------------------------------------------------- ImGuiTestRefDesc::ImGuiTestRefDesc(const ImGuiTestRef& ref) { if (ref.Path && ref.ID != 0) ImFormatString(Buf, IM_ARRAYSIZE(Buf), "'%s' (id 0x%08X)", ref.Path, ref.ID); else if (ref.Path) ImFormatString(Buf, IM_ARRAYSIZE(Buf), "'%s'", ref.Path); else ImFormatString(Buf, IM_ARRAYSIZE(Buf), "0x%08X", ref.ID); } ImGuiTestRefDesc::ImGuiTestRefDesc(const ImGuiTestRef& ref, const ImGuiTestItemInfo& item) { if (ref.Path && item.ID != 0) ImFormatString(Buf, IM_ARRAYSIZE(Buf), "'%s' (id 0x%08X)", ref.Path, item.ID); else if (ref.Path) ImFormatString(Buf, IM_ARRAYSIZE(Buf), "'%s'", ref.Path); else ImFormatString(Buf, IM_ARRAYSIZE(Buf), "0x%08X (label \"%s\")", ref.ID, item.DebugLabel); } //------------------------------------------------------------------------- // [SECTION] ImGuiTestContextDepthScope //------------------------------------------------------------------------- // Helper to increment/decrement the function depth (so our log entry can be padded accordingly) #define IM_TOKENCONCAT_INTERNAL(x, y) x ## y #define IM_TOKENCONCAT(x, y) IM_TOKENCONCAT_INTERNAL(x, y) #define IMGUI_TEST_CONTEXT_REGISTER_DEPTH(_THIS) ImGuiTestContextDepthScope IM_TOKENCONCAT(depth_register, __LINE__)(_THIS) struct ImGuiTestContextDepthScope { ImGuiTestContext* TestContext; ImGuiTestContextDepthScope(ImGuiTestContext* ctx) { TestContext = ctx; TestContext->ActionDepth++; } ~ImGuiTestContextDepthScope() { TestContext->ActionDepth--; } }; //------------------------------------------------------------------------- // [SECTION] Enum names helpers //------------------------------------------------------------------------- inline const char* GetActionName(ImGuiTestAction action) { switch (action) { case ImGuiTestAction_Unknown: return "Unknown"; case ImGuiTestAction_Hover: return "Hover"; case ImGuiTestAction_Click: return "Click"; case ImGuiTestAction_DoubleClick: return "DoubleClick"; case ImGuiTestAction_Check: return "Check"; case ImGuiTestAction_Uncheck: return "Uncheck"; case ImGuiTestAction_Open: return "Open"; case ImGuiTestAction_Close: return "Close"; case ImGuiTestAction_Input: return "Input"; case ImGuiTestAction_NavActivate: return "NavActivate"; case ImGuiTestAction_COUNT: default: return "N/A"; } } inline const char* GetActionVerb(ImGuiTestAction action) { switch (action) { case ImGuiTestAction_Unknown: return "Unknown"; case ImGuiTestAction_Hover: return "Hovered"; case ImGuiTestAction_Click: return "Clicked"; case ImGuiTestAction_DoubleClick: return "DoubleClicked"; case ImGuiTestAction_Check: return "Checked"; case ImGuiTestAction_Uncheck: return "Unchecked"; case ImGuiTestAction_Open: return "Opened"; case ImGuiTestAction_Close: return "Closed"; case ImGuiTestAction_Input: return "Input"; case ImGuiTestAction_NavActivate: return "NavActivated"; case ImGuiTestAction_COUNT: default: return "N/A"; } } //------------------------------------------------------------------------- // [SECTION] ImGuiTestContext // This is the interface that most tests will interact with. //------------------------------------------------------------------------- void ImGuiTestContext::LogEx(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, ...) { va_list args; va_start(args, fmt); LogExV(level, flags, fmt, args); va_end(args); } void ImGuiTestContext::LogExV(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, va_list args) { ImGuiTestContext* ctx = this; //ImGuiTest* test = ctx->Test; IM_ASSERT(level > ImGuiTestVerboseLevel_Silent && level < ImGuiTestVerboseLevel_COUNT); if (level == ImGuiTestVerboseLevel_Debug && ctx->ActionDepth > 1) level = ImGuiTestVerboseLevel_Trace; // Log all messages that we may want to print in future. if (EngineIO->ConfigVerboseLevelOnError < level) return; ImGuiTestLog* log = &ctx->TestOutput->Log; const int prev_size = log->Buffer.size(); //const char verbose_level_char = ImGuiTestEngine_GetVerboseLevelName(level)[0]; //if (flags & ImGuiTestLogFlags_NoHeader) // log->Buffer.appendf("[%c] ", verbose_level_char); //else // log->Buffer.appendf("[%c] [%04d] ", verbose_level_char, ctx->FrameCount); if ((flags & ImGuiTestLogFlags_NoHeader) == 0) log->Buffer.appendf("[%04d] ", ctx->FrameCount); if (level >= ImGuiTestVerboseLevel_Debug) log->Buffer.appendf("-- %*s", ImMax(0, (ctx->ActionDepth - 1) * 2), ""); log->Buffer.appendfv(fmt, args); log->Buffer.append("\n"); log->UpdateLineOffsets(EngineIO, level, log->Buffer.begin() + prev_size); LogToTTY(level, log->Buffer.c_str() + prev_size); LogToDebugger(level, log->Buffer.c_str() + prev_size); } void ImGuiTestContext::LogDebug(const char* fmt, ...) { va_list args; va_start(args, fmt); LogExV(ImGuiTestVerboseLevel_Debug, ImGuiTestLogFlags_None, fmt, args); va_end(args); } void ImGuiTestContext::LogInfo(const char* fmt, ...) { va_list args; va_start(args, fmt); LogExV(ImGuiTestVerboseLevel_Info, ImGuiTestLogFlags_None, fmt, args); va_end(args); } void ImGuiTestContext::LogWarning(const char* fmt, ...) { va_list args; va_start(args, fmt); LogExV(ImGuiTestVerboseLevel_Warning, ImGuiTestLogFlags_None, fmt, args); va_end(args); } void ImGuiTestContext::LogError(const char* fmt, ...) { va_list args; va_start(args, fmt); LogExV(ImGuiTestVerboseLevel_Error, ImGuiTestLogFlags_None, fmt, args); va_end(args); } void ImGuiTestContext::LogToTTY(ImGuiTestVerboseLevel level, const char* message, const char* message_end) { IM_ASSERT(level > ImGuiTestVerboseLevel_Silent && level < ImGuiTestVerboseLevel_COUNT); if (!EngineIO->ConfigLogToTTY) return; ImGuiTestContext* ctx = this; ImGuiTestOutput* test_output = ctx->TestOutput; ImGuiTestLog* log = &test_output->Log; if (test_output->Status == ImGuiTestStatus_Error) { // Current test failed. if (!CachedLinesPrintedToTTY) { // Print all previous logged messages first // FIXME: Can't use ExtractLinesAboveVerboseLevel() because we want to keep error level... CachedLinesPrintedToTTY = true; for (int i = 0; i < log->LineInfo.Size; i++) { ImGuiTestLogLineInfo& line_info = log->LineInfo[i]; if (line_info.Level > EngineIO->ConfigVerboseLevelOnError) continue; char* line_begin = log->Buffer.Buf.Data + line_info.LineOffset; char* line_end = strchr(line_begin, '\n'); LogToTTY(line_info.Level, line_begin, line_end + 1); } // We already printed current line as well, so return now. return; } // Otherwise print only current message. If we are executing here log level already is within range of // ConfigVerboseLevelOnError setting. } else if (EngineIO->ConfigVerboseLevel < level) { // Skip printing messages of lower level than configured. return; } switch (level) { case ImGuiTestVerboseLevel_Warning: ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_BrightYellow); break; case ImGuiTestVerboseLevel_Error: ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_BrightRed); break; default: ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_White); break; } if (message_end) fprintf(stdout, "%.*s", (int)(message_end - message), message); else fprintf(stdout, "%s", message); ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_White); fflush(stdout); } void ImGuiTestContext::LogToDebugger(ImGuiTestVerboseLevel level, const char* message) { IM_ASSERT(level > ImGuiTestVerboseLevel_Silent && level < ImGuiTestVerboseLevel_COUNT); if (!EngineIO->ConfigLogToDebugger) return; if (EngineIO->ConfigVerboseLevel < level) return; switch (level) { default: break; case ImGuiTestVerboseLevel_Error: ImOsOutputDebugString("[error] "); break; case ImGuiTestVerboseLevel_Warning: ImOsOutputDebugString("[warn.] "); break; case ImGuiTestVerboseLevel_Info: ImOsOutputDebugString("[info ] "); break; case ImGuiTestVerboseLevel_Debug: ImOsOutputDebugString("[debug] "); break; case ImGuiTestVerboseLevel_Trace: ImOsOutputDebugString("[trace] "); break; } ImOsOutputDebugString(message); } void ImGuiTestContext::LogBasicUiState() { ImGuiID item_hovered_id = UiContext->HoveredIdPreviousFrame; ImGuiID item_active_id = UiContext->ActiveId; ImGuiTestItemInfo* item_hovered_info = item_hovered_id ? ImGuiTestEngine_FindItemInfo(Engine, item_hovered_id, "") : nullptr; ImGuiTestItemInfo* item_active_info = item_active_id ? ImGuiTestEngine_FindItemInfo(Engine, item_active_id, "") : nullptr; LogDebug("Hovered: 0x%08X (\"%s\"), Active: 0x%08X(\"%s\")", item_hovered_id, item_hovered_info->ID != 0 ? item_hovered_info->DebugLabel : "", item_active_id, item_active_info->ID != 0 ? item_active_info->DebugLabel : ""); } void ImGuiTestContext::LogItemList(ImGuiTestItemList* items) { for (const ImGuiTestItemInfo& info : *items) LogDebug("- 0x%08X: depth %d: '%s' in window '%s'\n", info.ID, info.Depth, info.DebugLabel, info.Window->Name); } void ImGuiTestContext::Finish(ImGuiTestStatus status) { if (ActiveFunc == ImGuiTestActiveFunc_GuiFunc) { IM_ASSERT(status == ImGuiTestStatus_Success || status == ImGuiTestStatus_Unknown); if (RunFlags & ImGuiTestRunFlags_GuiFuncOnly) return; if (TestOutput->Status == ImGuiTestStatus_Running) TestOutput->Status = status; } else if (ActiveFunc == ImGuiTestActiveFunc_TestFunc) { IM_ASSERT(status == ImGuiTestStatus_Unknown); // To set Success from a TestFunc() you can 'return' from it. if (TestOutput->Status == ImGuiTestStatus_Running) TestOutput->Status = status; } } void ImGuiTestContext::Yield(int count) { IM_ASSERT(count > 0); while (count > 0) { ImGuiTestEngine_Yield(Engine); count--; } } // Supported values for ImGuiTestRunFlags: // - ImGuiTestRunFlags_NoError: if child test fails, return false and do not mark parent test as failed. // - ImGuiTestRunFlags_ShareVars: share generic vars and custom vars between child and parent tests. // - ImGuiTestRunFlags_ShareTestContext ImGuiTestStatus ImGuiTestContext::RunChildTest(const char* child_test_name, ImGuiTestRunFlags run_flags) { if (IsError()) return ImGuiTestStatus_Error; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("RunChildTest %s", child_test_name); ImGuiTest* child_test = ImGuiTestEngine_FindTestByName(Engine, nullptr, child_test_name); IM_CHECK_SILENT_RETV(child_test != nullptr, ImGuiTestStatus_Error); IM_CHECK_SILENT_RETV(child_test != Test, ImGuiTestStatus_Error); // Can't recursively run same test. ImGuiTestStatus parent_status = TestOutput->Status; TestOutput->Status = ImGuiTestStatus_Running; ImGuiTestEngine_RunTest(Engine, this, child_test, run_flags); ImGuiTestStatus child_status = TestOutput->Status; // Restore parent status TestOutput->Status = parent_status; if (child_status == ImGuiTestStatus_Error && (run_flags & ImGuiTestRunFlags_NoError) == 0) TestOutput->Status = ImGuiTestStatus_Error; // Return child status LogDebug("(returning to parent test)"); return child_status; } // Return true to request aborting TestFunc // Called via IM_SUSPEND_TESTFUNC() bool ImGuiTestContext::SuspendTestFunc(const char* file, int line) { if (IsError()) return false; if (file != nullptr) { file = ImPathFindFilename(file); LogError("SuspendTestFunc() at %s:%d", file, line); } else { LogError("SuspendTestFunc()"); } // Save relevant state. // FIXME-TESTS: Saving/restoring window z-order could be desirable. ImVec2 mouse_pos = Inputs->MousePosValue; ImGuiTestRunFlags run_flags = RunFlags; #if IMGUI_VERSION_NUM >= 18992 ImGui::TeleportMousePos(mouse_pos); #endif RunFlags |= ImGuiTestRunFlags_GuiFuncOnly; TestOutput->Status = ImGuiTestStatus_Suspended; while (TestOutput->Status == ImGuiTestStatus_Suspended && !Abort) Yield(); TestOutput->Status = ImGuiTestStatus_Running; // Restore relevant state. RunFlags = run_flags; Inputs->MousePosValue = mouse_pos; // Terminate TestFunc on abort, continue otherwise. return Abort; } // Sleep a given amount of time (unless running in Fast mode: there it will Yield once) void ImGuiTestContext::Sleep(float time) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast) { LogEx(ImGuiTestVerboseLevel_Trace, ImGuiTestLogFlags_None, "Sleep(%.2f) -> Yield() in fast mode", time); //ImGuiTestEngine_AddExtraTime(Engine, time); // We could add time, for now we have no use for it... ImGuiTestEngine_Yield(Engine); } else { LogEx(ImGuiTestVerboseLevel_Trace, ImGuiTestLogFlags_None, "Sleep(%.2f)", time); while (time > 0.0f && !Abort) { ImGuiTestEngine_Yield(Engine); time -= UiContext->IO.DeltaTime; } } } // This is useful when you need to wait a certain amount of time (even in Fast mode) // Sleep for a given clock time from the point of view of the Dear ImGui context, without affecting wall clock time of the running application. // FIXME: This makes sense for apps only relying on io.DeltaTime. void ImGuiTestContext::SleepNoSkip(float time, float framestep_in_second) { if (IsError()) return; LogDebug("SleepNoSkip %f seconds (in %f increments)", time, framestep_in_second); while (time > 0.0f && !Abort) { ImGuiTestEngine_SetDeltaTime(Engine, framestep_in_second); ImGuiTestEngine_Yield(Engine); time -= UiContext->IO.DeltaTime; } } void ImGuiTestContext::SleepShort() { if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast) Sleep(EngineIO->ActionDelayShort); } void ImGuiTestContext::SleepStandard() { if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast) Sleep(EngineIO->ActionDelayStandard); } void ImGuiTestContext::SetInputMode(ImGuiInputSource input_mode) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("SetInputMode %d", input_mode); IM_ASSERT(input_mode == ImGuiInputSource_Mouse || input_mode == ImGuiInputSource_Keyboard || input_mode == ImGuiInputSource_Gamepad); InputMode = input_mode; if (InputMode == ImGuiInputSource_Keyboard || InputMode == ImGuiInputSource_Gamepad) { #if IMGUI_VERSION_NUM >= 19136 ImGui::SetNavCursorVisible(true); UiContext->NavHighlightItemUnderNav = true; #else UiContext->NavDisableHighlight = false; UiContext->NavDisableMouseHover = true; #endif } else { #if IMGUI_VERSION_NUM >= 19136 ImGui::SetNavCursorVisible(false); UiContext->NavHighlightItemUnderNav = false; #else UiContext->NavDisableHighlight = true; UiContext->NavDisableMouseHover = false; #endif } } // Shortcut for when we have a window pointer, avoid mistakes with slashes in child names. void ImGuiTestContext::SetRef(ImGuiWindow* window) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); IM_CHECK_SILENT(window != nullptr); LogDebug("SetRef '%s' 0x%08X", window->Name, window->ID); // We grab the ID directly and avoid ImHashDecoratedPath so "/" in window names are not ignored. size_t len = strlen(window->Name); IM_ASSERT(len < IM_ARRAYSIZE(RefStr) - 1); strcpy(RefStr, window->Name); RefID = RefWindowID = window->ID; MouseSetViewport(window); // Automatically uncollapse by default if (!(OpFlags & ImGuiTestOpFlags_NoAutoUncollapse)) WindowCollapse(window->ID, false); } // It is exceptionally OK to call SetRef() in GuiFunc, as a facility to call seeded ctx->GetId() in GuiFunc. // FIXME-TESTS: May be good to focus window when docked? Otherwise locate request won't even see an item? void ImGuiTestContext::SetRef(ImGuiTestRef ref) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); if (ActiveFunc == ImGuiTestActiveFunc_TestFunc) LogDebug("SetRef '%s' 0x%08X", ref.Path ? ref.Path : "nullptr", ref.ID); if (ref.Path) { size_t len = strlen(ref.Path); IM_ASSERT(len < IM_ARRAYSIZE(RefStr) - 1); strcpy(RefStr, ref.Path); RefID = GetID(ref.Path, ImGuiTestRef()); } else { RefStr[0] = 0; RefID = ref.ID; } RefWindowID = 0; // Early out if (ref.IsEmpty()) return; // Try to infer window // (This is in order to set viewport, uncollapse window, and store its base id for leading "/" operator) // (0) Windows is fully specified in path? ImGuiWindow* window = GetWindowByRef(""); // (1) Try first element of ref path, it is most likely a window name and item lookup won't be necessary. if (window == nullptr && ref.Path != nullptr) { // "Window/SomeItem" -> search for "Window" const char* name_begin = ref.Path; while (*name_begin == '/') // Skip leading slashes name_begin++; const char* name_end = name_begin - 1; do { name_end = strchr(name_end + 1, '/'); } while (name_end != nullptr && name_end > name_begin && name_end[-1] == '\\'); window = GetWindowByRef(ImHashDecoratedPath(name_begin, name_end)); } if (ActiveFunc == ImGuiTestActiveFunc_GuiFunc) return; // (2) Ref was specified as an ID and points to an item therefore item lookup is unavoidable. // FIXME: Maybe display something in log when that happens? if (window == nullptr) { ImGuiTestItemInfo item_info = ItemInfo(RefID, ImGuiTestOpFlags_NoError); if (item_info.ID != 0) window = item_info.Window; } // Set viewport and base ID for single "/" operator. if (window) { RefWindowID = window->ID; MouseSetViewport(window); } // Automatically uncollapse by default if (window && !(OpFlags & ImGuiTestOpFlags_NoAutoUncollapse)) WindowCollapse(window->ID, false); } ImGuiTestRef ImGuiTestContext::GetRef() { return RefID; } ImGuiWindow* ImGuiTestContext::GetWindowByRef(ImGuiTestRef ref) { ImGuiID window_id = GetID(ref); ImGuiWindow* window = ImGui::FindWindowByID(window_id); return window; } ImGuiID ImGuiTestContext::GetID(ImGuiTestRef ref) { if (ref.ID) return ref.ID; return GetID(ref, RefID); } // Refer to Wiki to read details // https://github.com/ocornut/imgui_test_engine/wiki/Named-References // - Meaning of leading "//" ................. "//rootnode" : ignore SetRef // - Meaning of leading "//$FOCUSED" ......... "//$FOCUSED/node" : "node" in currently focused window // - Meaning of leading "/" .................. "/node" : move to root of window pointed by SetRef() when SetRef() uses a path // - Meaning of $$xxxx literal encoding ...... "list/$$1" : hash of "list" + hash if (int)1, equivalent of PushID("hello"); PushID(1); //// - Meaning of leading "../" .............. "../node" : move back 1 level from SetRef path() when SetRef() uses a path // Unimplemented // FIXME: "//$FOCUSED/.." is currently not usable. ImGuiID ImGuiTestContext::GetID(ImGuiTestRef ref, ImGuiTestRef seed_ref) { ImGuiContext& g = *UiContext; if (ref.ID) return ref.ID; // FIXME: What if seed_ref != 0 // Handle special $FOCUSED variable. // (Note that we don't and can't really support a "$HOVERED" equivalent for the hovered window. // Why? Because it is extremely fragile to use: with late translation of variable held in string, // it is extremely common that the "expected" hovered window at the time of passing the string has // changed in later uses of the same reference.) // You can however easily use: // SetRef(g.HoveredWindow->ID); const char* FOCUSED_PREFIX = "//$FOCUSED"; const size_t FOCUSED_PREFIX_LEN = 10; const char* path = ref.Path ? ref.Path : ""; if (strncmp(path, FOCUSED_PREFIX, FOCUSED_PREFIX_LEN) == 0) if (path[FOCUSED_PREFIX_LEN] == '/' || path[FOCUSED_PREFIX_LEN] == 0) { path += FOCUSED_PREFIX_LEN; if (path[0] == '/') path++; if (g.NavWindow) seed_ref = g.NavWindow->ID; else LogError("\"//$FOCUSED\" was used with no focused window!"); } if (path[0] == '/') { path++; if (path[0] == '/') { // "//" : Double-slash prefix resets ID seed to 0. seed_ref = ImGuiTestRef(); } else { // "/" : Single-slash prefix sets seed to the "current window", which a parent window containing an item with RefID id. if (ActiveFunc == ImGuiTestActiveFunc_GuiFunc) seed_ref = ImGuiTestRef(g.CurrentWindow->ID); else seed_ref = RefWindowID; } } return ImHashDecoratedPath(path, nullptr, seed_ref.Path ? GetID(seed_ref) : seed_ref.ID); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiID ImGuiTestContext::GetIDByInt(int n) { return ImHashData(&n, sizeof(n), GetID(RefID)); } ImGuiID ImGuiTestContext::GetIDByInt(int n, ImGuiTestRef seed_ref) { return ImHashData(&n, sizeof(n), GetID(seed_ref)); } ImGuiID ImGuiTestContext::GetIDByPtr(void* p) { return ImHashData(&p, sizeof(p), GetID(RefID)); } ImGuiID ImGuiTestContext::GetIDByPtr(void* p, ImGuiTestRef seed_ref) { return ImHashData(&p, sizeof(p), GetID(seed_ref)); } #endif ImVec2 ImGuiTestContext::GetMainMonitorWorkPos() { #ifdef IMGUI_HAS_VIEWPORT if (UiContext->IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ImGui::GetMainViewport()); return monitor->WorkPos; } #endif return ImGui::GetMainViewport()->WorkPos; } ImVec2 ImGuiTestContext::GetMainMonitorWorkSize() { #ifdef IMGUI_HAS_VIEWPORT if (UiContext->IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ImGui::GetMainViewport()); return monitor->WorkSize; } #endif return ImGui::GetMainViewport()->WorkSize; } #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE static bool ImGuiTestContext_CanCaptureScreenshot(ImGuiTestContext* ctx) { ImGuiTestEngineIO* io = ctx->EngineIO; return io->ConfigCaptureEnabled; } static bool ImGuiTestContext_CanCaptureVideo(ImGuiTestContext* ctx) { ImGuiTestEngineIO* io = ctx->EngineIO; return io->ConfigCaptureEnabled && ImFileExist(io->VideoCaptureEncoderPath); } #endif bool ImGuiTestContext::CaptureAddWindow(ImGuiTestRef ref) { ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT_RETV(window != nullptr, false); CaptureArgs->InCaptureWindows.push_back(window); return true; } static void CaptureInitAutoFilename(ImGuiTestContext* ctx, const char* ext) { IM_ASSERT(ext != nullptr && ext[0] == '.'); if (ctx->CaptureArgs->InOutputFile[0] == 0) ctx->CaptureSetExtension(ext); // Reset extension of specified filename or auto-generate a new filename. } bool ImGuiTestContext::CaptureScreenshot(int capture_flags) { if (IsError()) return false; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogInfo("CaptureScreenshot()"); ImGuiCaptureArgs* args = CaptureArgs; args->InFlags = capture_flags; // Auto filename CaptureInitAutoFilename(this, ".png"); #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE // Way capture tool is implemented doesn't prevent ClampWindowPos() from running, // so we disable that feature at the moment. (imgui_test_engine/#33) ImGuiIO& io = ImGui::GetIO(); bool backup_io_config_move_window_from_title_bar_only = io.ConfigWindowsMoveFromTitleBarOnly; if (capture_flags & ImGuiCaptureFlags_StitchAll) io.ConfigWindowsMoveFromTitleBarOnly = false; bool can_capture = ImGuiTestContext_CanCaptureScreenshot(this); if (!can_capture) args->InFlags |= ImGuiCaptureFlags_NoSave; bool ret = ImGuiTestEngine_CaptureScreenshot(Engine, args); if (can_capture) LogInfo("Saved '%s' (%d*%d pixels)", args->InOutputFile, (int)args->OutImageSize.x, (int)args->OutImageSize.y); else LogWarning("Skipped saving '%s' (%d*%d pixels) (enable in 'Misc->Options')", args->InOutputFile, (int)args->OutImageSize.x, (int)args->OutImageSize.y); if (capture_flags & ImGuiCaptureFlags_StitchAll) io.ConfigWindowsMoveFromTitleBarOnly = backup_io_config_move_window_from_title_bar_only; return ret; #else IM_UNUSED(args); LogWarning("Skipped screenshot capture: disabled by IMGUI_TEST_ENGINE_ENABLE_CAPTURE=0."); return false; #endif } void ImGuiTestContext::CaptureReset() { *CaptureArgs = ImGuiCaptureArgs(); } // FIXME-TESTS: Add ImGuiCaptureFlags_NoHideOtherWindows void ImGuiTestContext::CaptureScreenshotWindow(ImGuiTestRef ref, int capture_flags) { CaptureReset(); CaptureAddWindow(ref); CaptureScreenshot(capture_flags); } void ImGuiTestContext::CaptureSetExtension(const char* ext) { IM_ASSERT(ext && ext[0] == '.'); ImGuiCaptureArgs* args = CaptureArgs; if (args->InOutputFile[0] == 0) { ImFormatString(args->InOutputFile, IM_ARRAYSIZE(args->InOutputFile), "output/captures/%s_%04d%s", Test->Name, CaptureCounter, ext); CaptureCounter++; } else { char* filename_ext = (char*)ImPathFindExtension(args->InOutputFile); ImStrncpy(filename_ext, ext, (size_t)(filename_ext - args->InOutputFile)); } } bool ImGuiTestContext::CaptureBeginVideo() { if (IsError()) return false; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogInfo("CaptureBeginVideo()"); ImGuiCaptureArgs* args = CaptureArgs; // Auto filename CaptureInitAutoFilename(this, EngineIO->VideoCaptureExtension); #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE bool can_capture = ImGuiTestContext_CanCaptureVideo(this); if (!can_capture) args->InFlags |= ImGuiCaptureFlags_NoSave; return ImGuiTestEngine_CaptureBeginVideo(Engine, args); #else IM_UNUSED(args); LogWarning("Skipped recording GIF: disabled by IMGUI_TEST_ENGINE_ENABLE_CAPTURE=0."); return false; #endif } bool ImGuiTestContext::CaptureEndVideo() { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogInfo("CaptureEndVideo()"); ImGuiCaptureArgs* args = CaptureArgs; bool ret = Engine->CaptureContext.IsCapturingVideo() && ImGuiTestEngine_CaptureEndVideo(Engine, args); if (!ret) return false; #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE // In-progress capture was canceled by user. Delete incomplete file. if (IsError()) { //ImFileDelete(args->OutSavedFileName); return false; } bool can_capture = ImGuiTestContext_CanCaptureVideo(this); if (can_capture) { LogInfo("Saved '%s' (%d*%d pixels)", args->InOutputFile, (int)args->OutImageSize.x, (int)args->OutImageSize.y); } else { if (!EngineIO->ConfigCaptureEnabled) LogWarning("Skipped saving '%s' video because: io.ConfigCaptureEnabled == false (enable in Misc->Options)", args->InOutputFile); else LogWarning("Skipped saving '%s' video because: Video Encoder not found.", args->InOutputFile); } #endif return ret; } // Handle wildcard search on the TestFunc side. // Results will be resolved on the Gui side via the following call-chain: // IMGUI_TEST_ENGINE_ITEM_INFO() -> ImGuiTestEngineHook_ItemInfo() -> ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel() ImGuiID ImGuiTestContext::ItemInfoHandleWildcardSearch(const char* wildcard_prefix_start, const char* wildcard_prefix_end, const char* wildcard_suffix_start) { LogDebug("Wildcard matching.."); // Wildcard matching // Note that task->InPrefixId may be 0 as well (= we don't know the window) ImGuiTestFindByLabelTask* task = &Engine->FindByLabelTask; if (wildcard_prefix_start < wildcard_prefix_end) task->InPrefixId = ImHashDecoratedPath(wildcard_prefix_start, wildcard_prefix_end, RefID); else task->InPrefixId = RefID; task->OutItemId = 0; // Advance pointer to point it to the last label task->InSuffix = task->InSuffixLastItem = wildcard_suffix_start; for (const char* c = task->InSuffix; *c; c++) if (*c == '/') task->InSuffixLastItem = c + 1; task->InSuffixLastItemHash = ImHashStr(task->InSuffixLastItem, 0, 0); // Count number of labels task->InSuffixDepth = 1; for (const char* c = wildcard_suffix_start; *c; c++) if (*c == '/') task->InSuffixDepth++; int retries = 0; while (retries < 2 && task->OutItemId == 0) { ImGuiTestEngine_Yield(Engine); retries++; } // Wildcard matching requires item to be visible, because clipped items are unaware of their labels. Try panning through entire window, searching for target item. // (Scrollbar position restoration in theory may be desirable, however it interferes with typical use of found item) // FIXME-TESTS: This doesn't recurse properly into each child.. // FIXME: Down the line if we refactor ItemAdd() return value to distinguish render-clipping vs logic-clipping etc, we should instead temporarily enable a "no clip" // mode without the need for scrolling. if (task->OutItemId == 0) { ImGuiTestItemInfo base_item = ItemInfo(task->InPrefixId, ImGuiTestOpFlags_NoError); ImGuiWindow* window = (base_item.ID != 0) ? base_item.Window : GetWindowByRef(task->InPrefixId); if (window) { ImVec2 rect_size = window->InnerRect.GetSize(); for (float scroll_x = 0.0f; task->OutItemId == 0; scroll_x += rect_size.x) { for (float scroll_y = 0.0f; task->OutItemId == 0; scroll_y += rect_size.y) { window->Scroll.x = scroll_x; window->Scroll.y = scroll_y; retries = 0; while (retries < 2 && task->OutItemId == 0) { ImGuiTestEngine_Yield(Engine); retries++; } if (window->Scroll.y >= window->ScrollMax.y) break; } if (window->Scroll.x >= window->ScrollMax.x) break; } } } ImGuiID full_id = task->OutItemId; // FIXME: InFilterItemStatusFlags is intentionally not cleared here, because it is set in ItemAction() and reused in later calls to ItemInfo() to resolve ambiguities. task->InPrefixId = 0; task->InSuffix = task->InSuffixLastItem = nullptr; task->InSuffixLastItemHash = 0; task->InSuffixDepth = 0; task->OutItemId = 0; // -V1048 // Variable 'OutItemId' was assigned the same value. False-positive, because value of OutItemId could be modified from other thread during ImGuiTestEngine_Yield() call. return full_id; } static void ItemInfoErrorLog(ImGuiTestContext* ctx, ImGuiTestRef ref, ImGuiID full_id, ImGuiTestOpFlags flags) { if (flags & ImGuiTestOpFlags_NoError) return; if (ctx->Engine->UiContextHasHooks == false) IM_ERRORF_NOHDR("%s", "IMGUI DOES NOT SEEM COMPILED WITH '#define IMGUI_ENABLE_TEST_ENGINE'!\nMAKE SURE THAT BOTH 'imgui' AND 'imgui_test_engine' ARE USING THE SAME 'imconfig' FILE."); // Prefixing the string with / ignore the reference/current ID Str256 msg; if (ref.Path && ref.Path[0] == '/' && ctx->RefStr[0] != 0) msg.setf("Unable to locate item: '%s' (0x%08X)", ref.Path, full_id); else if (ref.Path && full_id != 0) msg.setf("Unable to locate item: '%s/%s' (0x%08X)", ctx->RefStr, ref.Path, full_id); else if (ref.Path) msg.setf("Unable to locate item: '%s/%s' (0x%08X)", ctx->RefStr, ref.Path, full_id); else msg.setf("Unable to locate item: 0x%08X", ref.ID); //if (flags & ImGuiTestOpFlags_NoError) // ctx->LogInfo("Ignored: %s", msg.c_str()); // FIXME //else IM_ERRORF_NOHDR("%s", msg.c_str()); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoError ImGuiTestItemInfo ImGuiTestContext::ItemInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags) { if (IsError()) return ItemInfoNull(); const ImGuiTestOpFlags SUPPORTED_FLAGS = ImGuiTestOpFlags_NoError; IM_ASSERT((flags & ~SUPPORTED_FLAGS) == 0); ImGuiID full_id = 0; if (const char* p = ref.Path ? strstr(ref.Path, "**/") : nullptr) { // Wildcard matching // FIXME-TESTS: Need to verify that this is not inhibited by a \, so \**/ should not pass, but \\**/ should :) // We could add a simple helpers that would iterate the strings, handling inhibitors, and let you check if a given characters is inhibited or not. const char* wildcard_prefix_start = ref.Path; const char* wildcard_prefix_end = p; const char* wildcard_suffix_start = wildcard_prefix_end + 3; full_id = ItemInfoHandleWildcardSearch(wildcard_prefix_start, wildcard_prefix_end, wildcard_suffix_start); } else { // Regular matching full_id = GetID(ref); } // If ui_ctx->TestEngineHooksEnabled is not already on (first ItemInfo() task in a while) we'll probably need an extra frame to warmup IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestItemInfo* item = nullptr; int retries = 0; int max_retries = 2; int extra_retries_for_appearing = 0; while (full_id && retries < max_retries) { item = ImGuiTestEngine_FindItemInfo(Engine, full_id, ref.Path); // While a window is appearing it is likely to be resizing and items moving. Wait an extra frame for things to settle. (FIXME: Could use another source e.g. Hidden? AutoFitFramesX?) if (item && item->Window && item->Window->Appearing && extra_retries_for_appearing == 0) { item = nullptr; max_retries++; extra_retries_for_appearing++; } if (item) return *item; ImGuiTestEngine_Yield(Engine); retries++; } ItemInfoErrorLog(this, ref, full_id, flags); return ItemInfoNull(); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoError ImGuiTestItemInfo ImGuiTestContext::ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags) { // First query bool can_open_full_path = (ref.Path != nullptr); ImGuiTestItemInfo item = ItemInfo(ref, (can_open_full_path ? ImGuiTestOpFlags_NoError : ImGuiTestOpFlags_None) | (flags & ImGuiTestOpFlags_NoError)); if (item.ID != 0) return item; if (!can_open_full_path) return ItemInfoNull(); // Tries to auto open intermediaries leading to final path. // Note that openables cannot be part of the **/ (else it means we would have to open everything). // - Openables can be before the wildcard "Node2/Node3/**/Button" // - Openables can be after the wildcard "**/Node2/Node3/Lv4/Button" int opened_parents = 0; for (const char* parent_end = strstr(ref.Path, "/"); parent_end != nullptr; parent_end = strstr(parent_end + 1, "/")) { // Skip "**/* sections if (strncmp(ref.Path, "**/", parent_end - ref.Path) == 0) continue; Str128 parent_id; parent_id.set(ref.Path, parent_end); ImGuiTestItemInfo parent_item = ItemInfo(parent_id.c_str(), ImGuiTestOpFlags_NoError); if (parent_item.ID != 0) { #ifdef IMGUI_HAS_DOCK ImGuiWindow* parent_window = parent_item.Window; #endif if ((parent_item.StatusFlags & ImGuiItemStatusFlags_Openable) != 0 && (parent_item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0) { // Open intermediary item if ((parent_item.ItemFlags & ImGuiItemFlags_Disabled) == 0) // FIXME: Report disabled state in log? { ItemAction(ImGuiTestAction_Open, parent_item.ID, ImGuiTestOpFlags_NoAutoOpenFullPath); opened_parents++; } } #ifdef IMGUI_HAS_DOCK else if (parent_window->ID == parent_item.ID && parent_window->DockIsActive && parent_window->DockTabIsVisible == false) { // Make tab visible ItemClick(parent_item.ID); opened_parents++; } #endif } } if (opened_parents > 0) item = ItemInfo(ref, (flags & ImGuiTestOpFlags_NoError)); if (item.ID == 0) ItemInfoErrorLog(this, ref, 0, flags); return item; } // Find a window given a path or an ID. // In the case of when a path is passed, this handle finding child windows as well. // e.g. // ctx->WindowInfo("//Test Window"); // OK // ctx->WindowInfo("//Test Window/Child/SubChild"); // OK // ctx->WindowInfo("//$FOCUSED/Child"); // OK // ctx->SetRef("Test Window); ctx->WindowInfo("Child"); // OK // ctx->WindowInfo(GetID("//Test Window")); // OK (find by raw ID without a path) // ctx->WindowInfo(GetID("//Test Window/Child/SubChild)); // *INCORRECT* GetID() doesn't unmangle child names. // ctx->WindowInfo("//Test Window/Button"); // *INCORRECT* Only finds windows, not items. // Return: // - Return pointer is always valid. // - Valid fields are: // - item->ID : window ID (may be == 0, if the window doesn't exist) // - item->Window : window pointer (may be == nullptr, if the window doesn't exist) // - Other fields correspond to the title-bar/tab item of a window, so likely not what you want (same as using IsItemXXX after Begin) // - If you want other fields simply get them via the window-> pointer. // - Likely you may want to feed the return value into SetRef(): e.g. 'ctx->SetRef(item->ID)' or 'ctx->SetRef(WindowInfo("//Window/Child")->ID);' // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoError // Todos: // - FIXME: Missing support for wildcards. ImGuiTestItemInfo ImGuiTestContext::WindowInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags) { if (IsError()) return ItemInfoNull(); IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); // Query by ID (not very useful but supported) if (ref.ID != 0) { LogDebug("WindowInfo: by id: 0x%08X", ref.ID); IM_ASSERT(ref.Path == nullptr); ImGuiWindow* window = GetWindowByRef(ref); if (window == nullptr) { if ((flags & ImGuiTestOpFlags_NoError) == 0) LogError("WindowInfo: cannot find window by ID!"); // FIXME: What if we want to query a not-yet-existing window by ID? return ItemInfoNull(); } return ItemInfo(window->ID, flags & ImGuiTestOpFlags_NoError); } // Query by Path: this is where the meat of our work is. LogDebug("WindowInfo: by path: '%s'", ref.Path ? ref.Path : "nullptr"); ImGuiWindow* window = nullptr; ImGuiID window_idstack_back = 0; const char* current = ref.Path; while (*current || window == nullptr) { // Handle SetRef(), if any (this will also handle "//$FOCUSED" syntax) Str128 part_name; if (window == nullptr && RefID != 0 && strncmp(ref.Path, "//", 2) != 0) { window = GetWindowByRef(""); window_idstack_back = window ? window->ID : 0; } else { // Find next part of the path + create a zero-terminated copy for convenience const char* part_start = current; const char* part_end = ImFindNextDecoratedPartInPath(current); if (part_end == nullptr) { current = part_end = part_start + strlen(part_start); } else if (part_end > part_start) { current = part_end; part_end--; IM_ASSERT(part_end[0] == '/'); } part_name.setf("%.*s", (int)(part_end - part_start), part_start); // Find root window or child window if (window == nullptr) { // Root: defer first element to GetID(), this will handle SetRef(), "//" and "//$FOCUSED" syntax. ImGuiID window_id = GetID(part_name.c_str()); window = GetWindowByRef(window_id); window_idstack_back = window ? window->ID : 0; } else { ImGuiID child_window_id = 0; ImGuiWindow* child_window = nullptr; { // Child: Attempt 1: Try to BeginChild(const char*) variant and mimic its logic. Str128 child_window_full_name; #if (IMGUI_VERSION_NUM >= 18996) && (IMGUI_VERSION_NUM < 18999) if (window_idstack_back == window->ID) { child_window_full_name.setf("%s/%s", window->Name, part_name.c_str()); } else #endif { ImGuiID child_item_id = GetID(part_name.c_str(), window_idstack_back); child_window_full_name.setf("%s/%s_%08X", window->Name, part_name.c_str(), child_item_id); } child_window_id = ImHashStr(child_window_full_name.c_str()); // We do NOT use ImHashDecoratedPath() child_window = GetWindowByRef(child_window_id); } if (child_window == nullptr) { // Child: Attempt 2: Try for BeginChild(ImGuiID id) variant and mimic its logic. // FIXME: This only really works when ID passed to BeginChild() was derived from a string. // We could support $$xxxx syntax to encode ID in parameter? ImGuiID child_item_id = GetID(part_name.c_str(), window_idstack_back); Str128f child_window_full_name("%s/%08X", window->Name, child_item_id); child_window_id = ImHashStr(child_window_full_name.c_str()); // We do NOT use ImHashDecoratedPath() child_window = GetWindowByRef(child_window_id); } if (child_window == nullptr) { // Assume that part is an arbitrary PushID(const char*) window_idstack_back = GetID(part_name.c_str(), window_idstack_back); } else { window = child_window; window_idstack_back = window ? window->ID : 0; } } } // Process result // FIXME: What if we want to query a not-yet-existing window by ID? if (window == nullptr) { if ((flags & ImGuiTestOpFlags_NoError) == 0) LogError("WindowInfo: element \"%s\" doesn't seem to exist.", part_name.c_str()); return ItemInfoNull(); } } IM_ASSERT(window != nullptr); IM_ASSERT(window_idstack_back != 0); // Stopped on "window/node/" if (window_idstack_back != 0 && window_idstack_back != window->ID) { if ((flags & ImGuiTestOpFlags_NoError) == 0) LogError("WindowInfo: element doesn't seem to exist or isn't a window."); return ItemInfoNull(); } return ItemInfo(window->ID, flags & ImGuiTestOpFlags_NoError); } void ImGuiTestContext::ScrollToTop(ImGuiTestRef ref) { if (IsError()) return; ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT(window != nullptr); if (window->Scroll.y == 0.0f) return; ScrollToY(ref, 0.0f); Yield(); } void ImGuiTestContext::ScrollToBottom(ImGuiTestRef ref) { if (IsError()) return; ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT(window != nullptr); if (window->Scroll.y == window->ScrollMax.y) return; ScrollToY(ref, window->ScrollMax.y); Yield(); } bool ImGuiTestContext::ScrollErrorCheck(ImGuiAxis axis, float expected, float actual, int* remaining_attempts) { if (IsError()) { (*remaining_attempts)--; return false; } float THRESHOLD = 1.0f; if (ImFabs(actual - expected) < THRESHOLD) return true; (*remaining_attempts)--; if (*remaining_attempts > 0) { LogInfo("Failed to set Scroll%c. Requested %.2f, got %.2f. Will try again.", 'X' + axis, expected, actual); return true; } else { IM_ERRORF("Failed to set Scroll%c. Requested %.2f, got %.2f. Aborting.", 'X' + axis, expected, actual); return false; } } // FIXME-TESTS: Mostly the same code as ScrollbarEx() static ImVec2 GetWindowScrollbarMousePositionForScroll(ImGuiWindow* window, ImGuiAxis axis, float scroll_v) { ImGuiContext& g = *GImGui; ImRect bb = ImGui::GetWindowScrollbarRect(window, axis); // From Scrollbar(): //float* scroll_v = &window->Scroll[axis]; const float size_avail_v = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; const float size_contents_v = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; // From ScrollbarEx() onward: // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) const float scrollbar_size_v = bb.Max[axis] - bb.Min[axis]; // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. const float win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), 1.0f); const float grab_h_pixels = ImClamp(scrollbar_size_v * (size_avail_v / win_size_v), g.Style.GrabMinSize, scrollbar_size_v); const float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v); const float scroll_ratio = ImSaturate(scroll_v / scroll_max); const float grab_v = scroll_ratio * (scrollbar_size_v - grab_h_pixels); // Grab position ImVec2 position; position[axis] = bb.Min[axis] + grab_v + grab_h_pixels * 0.5f; position[axis ^ 1] = bb.GetCenter()[axis ^ 1]; return position; } #if IMGUI_VERSION_NUM < 18993 #define ImTrunc ImFloor #endif static bool ScrollToWithScrollbar(ImGuiTestContext* ctx, ImGuiWindow* window, ImGuiAxis axis, float scroll_target) { ImGuiContext& g = *ctx->UiContext; ctx->Yield(); ctx->WindowFocus(window->ID); if (window->ScrollbarSizes[axis ^ 1] <= 0.0f) // Verify if still exists after yield return false; const ImRect scrollbar_rect = ImGui::GetWindowScrollbarRect(window, axis); const float scrollbar_size_v = scrollbar_rect.Max[axis] - scrollbar_rect.Min[axis]; const float window_resize_grip_size = ImTrunc(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); // In case of a very small window, directly use SetScrollX/Y function to prevent resizing it // FIXME-TESTS: GetWindowScrollbarMousePositionForScroll() doesn't return the exact value when scrollbar grip is too small if (scrollbar_size_v < window_resize_grip_size) return false; ctx->MouseSetViewport(window); const float scroll_src = window->Scroll[axis]; ImVec2 scrollbar_src_pos = GetWindowScrollbarMousePositionForScroll(window, axis, scroll_src); scrollbar_src_pos[axis] = ImMin(scrollbar_src_pos[axis], scrollbar_rect.Min[axis] + scrollbar_size_v - window_resize_grip_size); ctx->MouseMoveToPos(scrollbar_src_pos); ctx->MouseDown(0); ctx->SleepStandard(); ImVec2 scrollbar_dst_pos = GetWindowScrollbarMousePositionForScroll(window, axis, scroll_target); ctx->MouseMoveToPos(scrollbar_dst_pos); ctx->MouseUp(0); ctx->SleepStandard(); // Verify that things worked const float scroll_result = window->Scroll[axis]; if (ImFabs(scroll_result - scroll_target) < 1.0f) return true; // FIXME-TESTS: Investigate ctx->LogWarning("Failed to set Scroll%c. Requested %.2f, got %.2f.", 'X' + axis, scroll_target, scroll_result); return true; } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoFocusWindow void ImGuiTestContext::ScrollTo(ImGuiTestRef ref, ImGuiAxis axis, float scroll_target, ImGuiTestOpFlags flags) { ImGuiContext& g = *UiContext; if (IsError()) return; ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT(window != nullptr); // Early out const float scroll_target_clamp = ImClamp(scroll_target, 0.0f, window->ScrollMax[axis]); if (ImFabs(window->Scroll[axis] - scroll_target_clamp) < 1.0f) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); const char axis_c = (char)('X' + axis); LogDebug("ScrollTo %c %.1f/%.1f", axis_c, scroll_target, window->ScrollMax[axis]); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepStandard(); // Try to use Scrollbar if available const ImGuiTestItemInfo scrollbar_item = ItemInfo(ImGui::GetWindowScrollbarID(window, axis), ImGuiTestOpFlags_NoError); if (scrollbar_item.ID != 0 && /*EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast && */ !(flags & ImGuiTestOpFlags_NoFocusWindow)) { if (ScrollToWithScrollbar(this, window, axis, scroll_target_clamp)) { // Verify that things worked const float scroll_result = window->Scroll[axis]; if (ImFabs(scroll_result - scroll_target_clamp) < 1.0f) return; // FIXME-TESTS: Investigate LogWarning("Failed to set Scroll%c. Requested %.2f, got %.2f.", 'X' + axis, scroll_target_clamp, scroll_result); } } // Fallback: manual slow scroll // FIXME-TESTS: Consider using mouse wheel, since it can work without taking focus int remaining_failures = 3; while (!Abort) { if (ImFabs(window->Scroll[axis] - scroll_target_clamp) < 1.0f) break; const float scroll_speed = (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast) ? FLT_MAX : ImFloor(EngineIO->ScrollSpeed * g.IO.DeltaTime + 0.99f); const float scroll_next = ImLinearSweep(window->Scroll[axis], scroll_target, scroll_speed); if (axis == ImGuiAxis_X) ImGui::SetScrollX(window, scroll_next); else ImGui::SetScrollY(window, scroll_next); // Error handling to avoid getting stuck in this function. Yield(); if (!ScrollErrorCheck(axis, scroll_next, window->Scroll[axis], &remaining_failures)) break; } // Need another frame for the result->Rect to stabilize Yield(); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoFocusWindow void ImGuiTestContext::ScrollToPos(ImGuiTestRef window_ref, float pos_v, ImGuiAxis axis, ImGuiTestOpFlags flags) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ScrollToPos %c %.2f", 'X' + axis, pos_v); // Ensure window size and ScrollMax are up-to-date Yield(); ImGuiWindow* window = GetWindowByRef(window_ref); IM_CHECK_SILENT(window != NULL); float item_curr = pos_v; float item_target = ImFloor(window->InnerClipRect.GetCenter()[axis]); float scroll_delta = item_target - item_curr; float scroll_target = ImClamp(window->Scroll[axis] - scroll_delta, 0.0f, window->ScrollMax[axis]); ScrollTo(window->ID, axis, scroll_target, (flags & ImGuiTestOpFlags_NoFocusWindow)); } void ImGuiTestContext::ScrollToPosX(ImGuiTestRef window_ref, float pos_x) { ScrollToPos(window_ref, pos_x, ImGuiAxis_X); } void ImGuiTestContext::ScrollToPosY(ImGuiTestRef window_ref, float pos_y) { ScrollToPos(window_ref, pos_y, ImGuiAxis_Y); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoFocusWindow void ImGuiTestContext::ScrollToItem(ImGuiTestRef ref, ImGuiAxis axis, ImGuiTestOpFlags flags) { if (IsError()) return; // If the item is not currently visible, scroll to get it in the center of our window IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestItemInfo item = ItemInfo(ref); ImGuiTestRefDesc desc(ref, item); LogDebug("ScrollToItem %c %s", 'X' + axis, desc.c_str()); if (item.ID == 0) return; // Ensure window size and ScrollMax are up-to-date Yield(); // TabBar are a special case because they have no scrollbar and rely on ScrollButton "<" and ">" // FIXME-TESTS: Consider moving to its own function. ImGuiContext& g = *UiContext; if (axis == ImGuiAxis_X) if (ImGuiTabBar* tab_bar = g.TabBars.GetByKey(item.ParentID)) if (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) { ScrollToTabItem(tab_bar, item.ID); return; } ImGuiWindow* window = item.Window; float item_curr = ImFloor(item.RectFull.GetCenter()[axis]); float item_target = ImFloor(window->InnerClipRect.GetCenter()[axis]); float scroll_delta = item_target - item_curr; float scroll_target = ImClamp(window->Scroll[axis] - scroll_delta, 0.0f, window->ScrollMax[axis]); ScrollTo(window->ID, axis, scroll_target, (flags & ImGuiTestOpFlags_NoFocusWindow)); } void ImGuiTestContext::ScrollToItemX(ImGuiTestRef ref) { ScrollToItem(ref, ImGuiAxis_X); } void ImGuiTestContext::ScrollToItemY(ImGuiTestRef ref) { ScrollToItem(ref, ImGuiAxis_Y); } void ImGuiTestContext::ScrollToTabItem(ImGuiTabBar* tab_bar, ImGuiID tab_id) { if (IsError()) return; // Cancel if "##v", because it's outside the tab_bar rect, and will be considered as "not visible" even if it is! //if (GetID("##v") == item->ID) // return; IM_CHECK_SILENT(tab_bar != nullptr); const ImGuiTabItem* selected_tab_item = ImGui::TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId); const ImGuiTabItem* target_tab_item = ImGui::TabBarFindTabByID(tab_bar, tab_id); if (target_tab_item == nullptr) return; int selected_tab_index = tab_bar->Tabs.index_from_ptr(selected_tab_item); int target_tab_index = tab_bar->Tabs.index_from_ptr(target_tab_item); ImGuiTestRef backup_ref = GetRef(); SetRef(tab_bar->ID); if (selected_tab_index > target_tab_index) { MouseMove("##<"); for (int i = 0; i < selected_tab_index - target_tab_index; ++i) MouseClick(0); } else { MouseMove("##>"); for (int i = 0; i < target_tab_index - selected_tab_index; ++i) MouseClick(0); } // Skip the scroll animation if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast) { tab_bar->ScrollingAnim = tab_bar->ScrollingTarget; Yield(); } SetRef(backup_ref); } // Verify that ScrollMax is stable regardless of scrolling position // - This can break when the layout of clipped items doesn't match layout of unclipped items // - This can break with non-rounded calls to ItemSize(), namely when the starting position is negative (above visible area) // We should ideally be more tolerant of non-rounded sizes passed by the users. // - One of the net visible effect of an unstable ScrollMax is that the End key would put you at a spot that's not exactly the lowest spot, // and so a second press to End would you move again by a few pixels. // FIXME-TESTS: Make this an iterative, smooth scroll. void ImGuiTestContext::ScrollVerifyScrollMax(ImGuiTestRef ref) { ImGuiWindow* window = GetWindowByRef(ref); ImGui::SetScrollY(window, 0.0f); Yield(); float scroll_max_0 = window->ScrollMax.y; ImGui::SetScrollY(window, window->ScrollMax.y); Yield(); float scroll_max_1 = window->ScrollMax.y; IM_CHECK_EQ(scroll_max_0, scroll_max_1); } void ImGuiTestContext::NavMoveTo(ImGuiTestRef ref) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiContext& g = *UiContext; ImGuiTestItemInfo item = ItemInfo(ref); ImGuiTestRefDesc desc(ref, item); LogDebug("NavMove to %s", desc.c_str()); if (item.ID == 0) return; if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepStandard(); // Focus window before scrolling/moving so things are nicely visible WindowFocus(item.Window->ID); // Teleport // FIXME-NAV: We should have a nav request feature that does this, // except it'll have to queue the request to find rect, then set scrolling, which would incur a 2 frame delay :/ // FIXME-TESTS-NOT_SAME_AS_END_USER IM_ASSERT(g.NavMoveSubmitted == false); ImRect rect_rel = item.RectFull; rect_rel.Translate(ImVec2(-item.Window->Pos.x, -item.Window->Pos.y)); ImGui::SetNavID(item.ID, (ImGuiNavLayer)item.NavLayer, 0, rect_rel); #if IMGUI_VERSION_NUM >= 19136 ImGui::SetNavCursorVisible(true); g.NavHighlightItemUnderNav = true; #else g.NavDisableHighlight = false; g.NavDisableMouseHover = true; #endif g.NavMousePosDirty = true; ImGui::ScrollToBringRectIntoView(item.Window, item.RectFull); while (g.NavMoveSubmitted) Yield(); Yield(); if (!Abort) if (g.NavId != item.ID) IM_ERRORF_NOHDR("Unable to set NavId to %s", desc.c_str()); } void ImGuiTestContext::NavActivate() { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("NavActivate"); Yield(); // ? KeyPress(ImGuiKey_Space); } void ImGuiTestContext::NavInput() { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("NavInput"); KeyPress(ImGuiKey_Enter); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_MoveToEdgeL // - ImGuiTestOpFlags_MoveToEdgeR // - ImGuiTestOpFlags_MoveToEdgeU // - ImGuiTestOpFlags_MoveToEdgeD static ImVec2 GetMouseAimingPos(const ImGuiTestItemInfo& item, ImGuiTestOpFlags flags) { ImRect r = item.RectClipped; ImVec2 pos; if (flags & ImGuiTestOpFlags_MoveToEdgeL) pos.x = (r.Min.x + 1.0f); else if (flags & ImGuiTestOpFlags_MoveToEdgeR) pos.x = (r.Max.x - 1.0f); else pos.x = (r.Min.x + r.Max.x) * 0.5f; if (flags & ImGuiTestOpFlags_MoveToEdgeU) pos.y = (r.Min.y + 1.0f); else if (flags & ImGuiTestOpFlags_MoveToEdgeD) pos.y = (r.Max.y - 1.0f); else pos.y = (r.Min.y + r.Max.y) * 0.5f; return pos; } void ImGuiTestContext::_MakeAimingSpaceOverPos(ImGuiViewport* viewport, ImGuiWindow* over_window, const ImVec2& over_pos) { ImGuiContext& g = *UiContext; // if window == nullptr : make space to reach void // if window != nullptr : make space to reach window IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("_MakeAimingSpaceOverPos(over_window = '%s', over_pos = %.2f,%.2f)", over_window ? over_window->Name : "N/A", over_pos.x, over_pos.y); const int over_window_n = (over_window != nullptr) ? ImGui::FindWindowDisplayIndex(over_window) : -1; #if IMGUI_VERSION_NUM < 19183 const ImVec2 hover_padding = g.WindowsHoverPadding; #else const ImVec2 hover_padding = ImVec2(g.WindowsBorderHoverPadding, g.WindowsBorderHoverPadding); #endif const ImVec2 window_min_pos = over_pos + hover_padding + ImVec2(1.0f, 1.0f); for (int window_n = g.Windows.Size - 1; window_n > over_window_n; window_n--) { ImGuiWindow* window = g.Windows[window_n]; if (window->WasActive == false) continue; #ifdef IMGUI_HAS_DOCK if (window->Viewport != viewport) continue; if (window->RootWindowDockTree != window) continue; #else IM_UNUSED(viewport); if (window->RootWindow != window) continue; if (window->Flags & ImGuiWindowFlags_NoMove) continue; #endif if (window->Rect().Contains(window_min_pos)) { WindowMove(window->ID, window_min_pos); // Verify that we have managed to move the window.. if (ImLengthSqr(window->Pos - window_min_pos) >= 1.0f) { LogWarning("Failed to move window '%s'! While trying to make space to click at (%.2f,%.2f) over window '%s'.", window->Name, over_pos.x, over_pos.y, over_window ? over_window->Name : "N/A"); //IM_CHECK_EQ(window->Pos, window_min_pos); // Failed to move window to make space } } } } static void FocusOrMakeClickableAtPos(ImGuiTestContext* ctx, ImGuiWindow* window, const ImVec2& pos) { IM_ASSERT(window != nullptr); // Avoid unnecessary focus // While this is generally desirable and much more consistent with user behavior, // it make test-engine behavior a little less deterministic. // incorrectly written tests could possibly succeed or fail based on position of other windows. bool is_covered = ctx->FindHoveredWindowAtPos(pos) != window; #if IMGUI_VERSION_NUM >= 18944 bool is_inhibited = ImGui::IsWindowContentHoverable(window) == false; #else bool is_inhibited = false; #endif // FIXME-TESTS-NOT_SAME_AS_END_USER: This has too many side effect, could we do without? // - e.g. This can close a modal. if (is_covered || is_inhibited) { // Testing ImGuiWindowFlags_NoBringToFrontOnFocus is similar to what FocusWindow() does ImGuiWindow* focus_front_window = window ? window->RootWindow : nullptr; #ifdef IMGUI_HAS_DOCK ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : nullptr; #else ImGuiWindow* display_front_window = window ? window->RootWindow : nullptr; #endif if ((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) { // FIXME-TESTS: Aim to make it the common/default path, and WindowBringToFront() the exceptional path! ctx->_MakeAimingSpaceOverPos(window->Viewport, window, pos); } else { ctx->WindowBringToFront(window->ID); } } } // Conceptucally this could be called ItemHover() // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoFocusWindow // - ImGuiTestOpFlags_NoCheckHoveredId // - ImGuiTestOpFlags_IsSecondAttempt [used when recursively calling ourself) // - ImGuiTestOpFlags_MoveToEdgeXXX flags // FIXME-TESTS: This is too eagerly trying to scroll everything even if already visible. // FIXME: Maybe ImGuiTestOpFlags_NoCheckHoveredId could be automatic if we detect that another item is active as intended? void ImGuiTestContext::MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiContext& g = *UiContext; ImGuiTestItemInfo item; if (flags & ImGuiTestOpFlags_NoAutoOpenFullPath) item = ItemInfo(ref); else item = ItemInfoOpenFullPath(ref); ImGuiTestRefDesc desc(ref, item); LogDebug("MouseMove to %s", desc.c_str()); if (item.ID == 0) return; ImGuiWindow* window = item.Window; if (!window->WasActive) { LogError("Window '%s' is not active!", window->Name); return; } // FIXME-TESTS: If window was not brought to front (because of either ImGuiWindowFlags_NoBringToFrontOnFocus or ImGuiTestOpFlags_NoFocusWindow) // then we need to make space by moving other windows away. // An easy to reproduce this bug is to run "docking_dockspace_tab_amend" with Test Engine UI over top-left corner, covering the Tools menu. // Check visibility and scroll if necessary { #if IMGUI_VERSION_NUM < 19183 const ImVec2 hover_padding = g.WindowsHoverPadding; #else const ImVec2 hover_padding = ImVec2(g.WindowsBorderHoverPadding, g.WindowsBorderHoverPadding); #endif if (item.NavLayer == ImGuiNavLayer_Main) { float min_visible_size = 10.0f; float min_window_size_x = window->DecoInnerSizeX1 + window->DecoOuterSizeX1 + window->DecoOuterSizeX2 + min_visible_size + hover_padding.x * 2.0f; float min_window_size_y = window->DecoInnerSizeY1 + window->DecoOuterSizeY1 + window->DecoOuterSizeY2 + min_visible_size + hover_padding.y * 2.0f; if ((window->Size.x < min_window_size_x || window->Size.y < min_window_size_y) && (window->Flags & ImGuiWindowFlags_NoResize) == 0 && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) { LogDebug("MouseMove: Will attempt to resize window to make item in main scrolling layer visible."); if (window->Size.x < min_window_size_x) WindowResize(window->ID, ImVec2(min_window_size_x, window->Size.y)); if (window->Size.y < min_window_size_y) WindowResize(window->ID, ImVec2(window->Size.x, min_window_size_y)); item = ItemInfo(item.ID); } } ImRect window_r = window->InnerClipRect; window_r.Expand(ImVec2(-hover_padding.x, -hover_padding.y)); ImRect item_r_clipped; item_r_clipped.Min.x = ImClamp(item.RectFull.Min.x, window_r.Min.x, window_r.Max.x); item_r_clipped.Min.y = ImClamp(item.RectFull.Min.y, window_r.Min.y, window_r.Max.y); item_r_clipped.Max.x = ImClamp(item.RectFull.Max.x, window_r.Min.x, window_r.Max.x); item_r_clipped.Max.y = ImClamp(item.RectFull.Max.y, window_r.Min.y, window_r.Max.y); // In theory all we need is one visible point, but it is generally nicer if we scroll toward visibility. // Bias toward reducing amount of horizontal scroll. float visibility_ratio_x = (item_r_clipped.GetWidth() + 1.0f) / (item.RectFull.GetWidth() + 1.0f); float visibility_ratio_y = (item_r_clipped.GetHeight() + 1.0f) / (item.RectFull.GetHeight() + 1.0f); if (item.NavLayer == ImGuiNavLayer_Main) { if (visibility_ratio_x < 0.70f) ScrollToItem(ref, ImGuiAxis_X, ImGuiTestOpFlags_NoFocusWindow); if (visibility_ratio_y < 0.90f) ScrollToItem(ref, ImGuiAxis_Y, ImGuiTestOpFlags_NoFocusWindow); } // FIXME: Scroll parent window } // Menu layer is not scrollable: attempt to resize window. if (item.NavLayer == ImGuiNavLayer_Menu) { // FIXME-TESTS: We designed RectClipped as being within RectFull which is not what we want here. Approximate using window's Max.x ImRect window_r = window->Rect(); if (item.RectFull.Min.x > window_r.Max.x) { float extra_width_desired = item.RectFull.Max.x - window_r.Max.x; // item->RectClipped.Max.x; if (extra_width_desired > 0.0f && (flags & ImGuiTestOpFlags_IsSecondAttempt) == 0) { LogDebug("MouseMove: Will attempt to resize window to make item in menu layer visible."); WindowResize(window->ID, window->Size + ImVec2(extra_width_desired, 0.0f)); } } } // Update item item = ItemInfo(item.ID); // FIXME-TESTS-NOT_SAME_AS_END_USER ImVec2 pos = item.RectFull.GetCenter(); if (WindowTeleportToMakePosVisible(window->ID, pos)) item = ItemInfo(item.ID); // Handle the off-chance that e.g. item/window stops being submitted while scrolling (easy to repro by pressing Esc during a long scroll) if (item.ID == 0) { LogError("MouseMove: item doesn't exist anymore (after scrolling)"); return; } // Keep a copy of item info const ImGuiTestItemInfo item_initial_state = item; // Target point pos = GetMouseAimingPos(item, flags); // Focus window if (!(flags & ImGuiTestOpFlags_NoFocusWindow) && item.Window != nullptr) FocusOrMakeClickableAtPos(this, item.Window, pos); // Another is window active test (in the case focus change has a side effect but also as we have yield an extra frame) if (!item.Window->WasActive) { LogError("MouseMove: Window '%s' is not active (after aiming)", item.Window->Name); return; } MouseSetViewport(item.Window); MouseMoveToPos(pos); // Focus again in case something made us lost focus (which could happen on a simple hover) if (!(flags & ImGuiTestOpFlags_NoFocusWindow)) FocusOrMakeClickableAtPos(this, item.Window, pos); // Check hovering target: may be an item (common) or a window (rare) if (!Abort && !(flags & ImGuiTestOpFlags_NoCheckHoveredId)) { ImGuiID hovered_id; bool is_hovered_item; // Give a few extra frames to validate hovering. // In the vast majority of case this will be set on the first attempt, // but e.g. blocking popups may need to close based on external logic. for (int remaining_attempts = 3; remaining_attempts > 0; remaining_attempts--) { hovered_id = g.HoveredIdPreviousFrame; is_hovered_item = (hovered_id == item.ID); if (is_hovered_item) break; Yield(); } bool is_hovered_window = is_hovered_item ? true : false; if (!is_hovered_item) for (ImGuiWindow* hovered_window = g.HoveredWindow; hovered_window != nullptr && !is_hovered_window; hovered_window = hovered_window->ParentWindow) if (hovered_window->ID == item.ID && hovered_window == item.Window) is_hovered_window = true; if (!is_hovered_item && !is_hovered_window) { // Check if we are accidentally hovering resize grip (which uses ImGuiButtonFlags_FlattenChildren) if (!(window->Flags & ImGuiWindowFlags_NoResize) && !(flags & ImGuiTestOpFlags_IsSecondAttempt)) { bool is_hovering_resize_corner = false; for (int n = 0; n < 2; n++) is_hovering_resize_corner |= (hovered_id == ImGui::GetWindowResizeCornerID(window, n)); if (is_hovering_resize_corner) { LogDebug("MouseMove: Child obstructed by parent's ResizeGrip, trying to resize window and trying again.."); #if IMGUI_VERSION_NUM < 19172 float extra_size = window->CalcFontSize() * 3.0f; #else float extra_size = window->FontRefSize * 3.0f; #endif WindowResize(window->ID, window->Size + ImVec2(extra_size, extra_size)); MouseMove(ref, flags | ImGuiTestOpFlags_IsSecondAttempt); return; } } // Update item item = ItemInfo(item.ID); // Log message // FIXME: Consider a second attempt? ImVec2 pos_old = item_initial_state.RectFull.Min; ImVec2 pos_new = item.RectFull.Min; ImVec2 size_old = item_initial_state.RectFull.GetSize(); ImVec2 size_new = item.RectFull.GetSize(); Str256f error_message( "MouseMove: Unable to Hover %s:\n" "- Expected item 0x%08X in window '%s', targeted position: (%.1f,%.1f)'\n" "- Hovered id was 0x%08X in '%s'.\n" "- Before mouse move: Item Pos (%6.1f,%6.1f) Size (%6.1f,%6.1f)\n" "- After mouse move: Item Pos (%6.1f,%6.1f) Size (%6.1f,%6.1f)", desc.c_str(), item.ID, item.Window ? item.Window->Name : "", pos.x, pos.y, hovered_id, g.HoveredWindow ? g.HoveredWindow->Name : "", pos_old.x, pos_old.y, size_old.x, size_old.y, pos_new.x, pos_new.y, size_new.x, size_new.y); IM_ERRORF_NOHDR("%s", error_message.c_str()); } } } void ImGuiTestContext::MouseSetViewport(ImGuiWindow* window) { IM_CHECK_SILENT(window != nullptr); #ifdef IMGUI_HAS_VIEWPORT ImGuiViewportP* viewport = window ? window->Viewport : nullptr; ImGuiID viewport_id = viewport ? viewport->ID : 0; if (window->Viewport == nullptr) IM_CHECK(window->WasActive == false); // only time this is allowed is an inactive window (where the viewport was destroyed) if (Inputs->MouseHoveredViewport != viewport_id) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseSetViewport changing to 0x%08X (window '%s')", viewport_id, window->Name); Inputs->MouseHoveredViewport = viewport_id; Yield(2); } #else IM_UNUSED(window); #endif } // May be 0 to specify "automatic" (based on platform stack, rarely used) void ImGuiTestContext::MouseSetViewportID(ImGuiID viewport_id) { if (IsError()) return; if (Inputs->MouseHoveredViewport != viewport_id) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseSetViewportID changing to 0x%08X", viewport_id); Inputs->MouseHoveredViewport = viewport_id; ImGuiTestEngine_Yield(Engine); } } // Make the point at 'pos' (generally expected to be within window's boundaries) visible in the viewport, // so it can be later focused then clicked. bool ImGuiTestContext::WindowTeleportToMakePosVisible(ImGuiTestRef ref, ImVec2 pos) { ImGuiContext& g = *UiContext; if (IsError()) return false; ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT_RETV(window != nullptr, false); #ifdef IMGUI_HAS_DOCK // This is particularly useful for docked windows, as we have to move root dockspace window instead of docket window // itself. As a side effect this also adds support for child windows. window = window->RootWindowDockTree; #endif ImRect visible_r; visible_r.Min = GetMainMonitorWorkPos(); visible_r.Max = visible_r.Min + GetMainMonitorWorkSize(); if (!visible_r.Contains(pos)) { // Fallback move window directly to make our item reachable with the mouse. // FIXME-TESTS-NOT_SAME_AS_END_USER float pad = g.FontSize; ImVec2 delta; delta.x = (pos.x < visible_r.Min.x) ? (visible_r.Min.x - pos.x + pad) : (pos.x > visible_r.Max.x) ? (visible_r.Max.x - pos.x - pad) : 0.0f; delta.y = (pos.y < visible_r.Min.y) ? (visible_r.Min.y - pos.y + pad) : (pos.y > visible_r.Max.y) ? (visible_r.Max.y - pos.y - pad) : 0.0f; ImGui::SetWindowPos(window, window->Pos + delta, ImGuiCond_Always); LogDebug("WindowTeleportToMakePosVisible '%s' delta (%.1f,%.1f)", window->Name, delta.x, delta.y); Yield(); return true; } return false; } // ignore_list is a nullptr-terminated list of pointers // Windows that are below all of ignore_list windows are not hidden. // FIXME-TESTS-NOT_SAME_AS_END_USER: Aim to get rid of this. void ImGuiTestContext::_ForeignWindowsHideOverPos(const ImVec2& pos, ImGuiWindow** ignore_list) { ImGuiContext& g = *UiContext; if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ForeignWindowsHideOverPos (%.0f,%.0f)", pos.x, pos.y); IM_CHECK_SILENT(ignore_list != nullptr); // It makes little sense to call this function with an empty list. IM_CHECK_SILENT(ignore_list[0] != nullptr); //auto& ctx = this; IM_SUSPEND_TESTFUNC(); // Find lowest ignored window index. All windows rendering above this index will be hidden. All windows rendering // below this index do not prevent interactions with these windows already, and they can be ignored. int min_window_index = g.Windows.Size; for (int i = 0; ignore_list[i]; i++) min_window_index = ImMin(min_window_index, ImGui::FindWindowDisplayIndex(ignore_list[i])); #if IMGUI_VERSION_NUM < 19183 const ImVec2 hover_padding = g.WindowsHoverPadding; #else const ImVec2 hover_padding = ImVec2(g.WindowsBorderHoverPadding, g.WindowsBorderHoverPadding); #endif bool hidden_windows = false; for (int i = 0; i < g.Windows.Size; i++) { ImGuiWindow* other_window = g.Windows[i]; if (other_window->RootWindow == other_window && other_window->WasActive) { ImRect r = other_window->Rect(); r.Expand(hover_padding); if (r.Contains(pos)) { for (int j = 0; ignore_list[j]; j++) #ifdef IMGUI_HAS_DOCK if (ignore_list[j]->RootWindowDockTree == other_window->RootWindowDockTree) #else if (ignore_list[j] == other_window) #endif { other_window = nullptr; break; } if (other_window && ImGui::FindWindowDisplayIndex(other_window) < min_window_index) other_window = nullptr; if (other_window) { ForeignWindowsToHide.push_back(other_window); hidden_windows = true; } } } } if (hidden_windows) Yield(); } void ImGuiTestContext::_ForeignWindowsUnhideAll() { ForeignWindowsToHide.clear(); Yield(); } void ImGuiTestContext::MouseMoveToPos(ImVec2 target) { ImGuiContext& g = *UiContext; if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseMoveToPos from (%.0f,%.0f) to (%.0f,%.0f)", Inputs->MousePosValue.x, Inputs->MousePosValue.y, target.x, target.y); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepStandard(); // Enforce a mouse move if we are already at destination, to enforce g.NavHighlightItemUnderNav gets cleared. #if IMGUI_VERSION_NUM >= 19136 if (g.NavHighlightItemUnderNav && ImLengthSqr(Inputs->MousePosValue - target) < 1.0f) #else if (g.NavDisableMouseHover && ImLengthSqr(Inputs->MousePosValue - target) < 1.0f) #endif { Inputs->MousePosValue = target + ImVec2(1.0f, 0.0f); ImGuiTestEngine_Yield(Engine); } if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast) { Inputs->MousePosValue = target; ImGuiTestEngine_Yield(Engine); ImGuiTestEngine_Yield(Engine); return; } // Simulate slower movements. We use a slightly curved movement to make the movement look less robotic. // Calculate some basic parameters const ImVec2 start_pos = Inputs->MousePosValue; const ImVec2 delta = target - start_pos; const float length2 = ImLengthSqr(delta); const float length = (length2 > 0.0001f) ? ImSqrt(length2) : 1.0f; const float inv_length = 1.0f / length; // Short distance alter speed and wobble float base_speed = EngineIO->MouseSpeed; float base_wobble = EngineIO->MouseWobble; if (length < base_speed * 1.0f) { // Time = 1.0f -> wobble max, Time = 0.0f -> no wobble base_wobble *= length / base_speed; // Slow down for short movements(all movement in the 0.0f..1.0f range are remapped to a 0.5f..1.0f seconds) if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) { float approx_time = length / base_speed; approx_time = 0.5f + ImSaturate(approx_time * 0.5f); base_speed = length / approx_time; } } // Calculate a vector perpendicular to the motion delta const ImVec2 perp = ImVec2(delta.y, -delta.x) * inv_length; // Calculate how much wobble we want, clamped to max out when the delta is 100 pixels (shorter movements get less wobble) const float position_offset_magnitude = ImClamp(length, 1.0f, 100.0f) * base_wobble; // Wobble positions, using a sine wave based on position as a cheap way to get a deterministic offset ImVec2 intermediate_pos_a = start_pos + (delta * 0.3f); ImVec2 intermediate_pos_b = start_pos + (delta * 0.6f); intermediate_pos_a += perp * ImSin(intermediate_pos_a.y * 0.1f) * position_offset_magnitude; intermediate_pos_b += perp * ImCos(intermediate_pos_b.y * 0.1f) * position_offset_magnitude; // We manipulate Inputs->MousePosValue without reading back from g.IO.MousePos because the later is rounded. // To handle high framerate it is easier to bypass this rounding. float current_dist = 0.0f; // Our current distance along the line (in pixels) while (true) { float move_speed = base_speed * g.IO.DeltaTime; //if (g.IO.KeyShift) // move_speed *= 0.1f; current_dist += move_speed; // Move along the line // Calculate a parametric position on the direct line that we will use for the curve float t = current_dist * inv_length; t = ImClamp(t, 0.0f, 1.0f); t = 1.0f - ((ImCos(t * IM_PI) + 1.0f) * 0.5f); // Generate a smooth curve with acceleration/deceleration //ImGui::GetOverlayDrawList()->AddCircle(target, 10.0f, IM_COL32(255, 255, 0, 255)); if (t >= 1.0f) { Inputs->MousePosValue = target; ImGuiTestEngine_Yield(Engine); ImGuiTestEngine_Yield(Engine); return; } else { // Use a bezier curve through the wobble points Inputs->MousePosValue = ImBezierCubicCalc(start_pos, intermediate_pos_a, intermediate_pos_b, target, t); //ImGui::GetOverlayDrawList()->AddBezierCurve(start_pos, intermediate_pos_a, intermediate_pos_b, target, IM_COL32(255,0,0,255), 1.0f); ImGuiTestEngine_Yield(Engine); } } } // This always teleport the mouse regardless of fast/slow mode. Useful e.g. to set initial mouse position for a GIF recording. // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoYield void ImGuiTestContext::MouseTeleportToPos(ImVec2 target, ImGuiTestOpFlags flags) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseTeleportToPos from (%.0f,%.0f) to (%.0f,%.0f)", Inputs->MousePosValue.x, Inputs->MousePosValue.y, target.x, target.y); Inputs->MousePosValue = target; if ((flags & ImGuiTestOpFlags_NoYield) == 0) { ImGuiTestEngine_Yield(Engine); ImGuiTestEngine_Yield(Engine); } } void ImGuiTestContext::MouseDown(ImGuiMouseButton button) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseDown %d", button); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepStandard(); UiContext->IO.MouseClickedTime[button] = -FLT_MAX; // Prevent accidental double-click from happening ever Inputs->MouseButtonsValue |= (1 << button); Yield(); } void ImGuiTestContext::MouseUp(ImGuiMouseButton button) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseUp %d", button); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepShort(); Inputs->MouseButtonsValue &= ~(1 << button); Yield(); } // TODO: click time argument (seconds and/or frames) void ImGuiTestContext::MouseClick(ImGuiMouseButton button) { if (IsError()) return; MouseClickMulti(button, 1); } // TODO: click time argument (seconds and/or frames) void ImGuiTestContext::MouseClickMulti(ImGuiMouseButton button, int count) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); if (count > 1) LogDebug("MouseClickMulti %d x%d", button, count); else LogDebug("MouseClick %d", button); // Make sure mouse buttons are released IM_ASSERT(count >= 1); IM_ASSERT(Inputs->MouseButtonsValue == 0); Yield(); // Press UiContext->IO.MouseClickedTime[button] = -FLT_MAX; // Prevent accidental double-click from happening ever for (int n = 0; n < count; n++) { Inputs->MouseButtonsValue = (1 << button); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepShort(); else if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast) Yield(2); // Leave enough time for non-alive IDs to expire. (#5325) else Yield(); Inputs->MouseButtonsValue = 0; if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast) Yield(2); // Not strictly necessary but covers more variant. else Yield(); } // Now NewFrame() has seen the mouse release. // Let the imgui frame finish, now e.g. Button() function will return true. Start a new frame. Yield(); } // TODO: click time argument (seconds and/or frames) void ImGuiTestContext::MouseDoubleClick(ImGuiMouseButton button) { MouseClickMulti(button, 2); } void ImGuiTestContext::MouseLiftDragThreshold(ImGuiMouseButton button) { if (IsError()) return; ImGuiContext& g = *UiContext; g.IO.MouseDragMaxDistanceSqr[button] = (g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) + (g.IO.MouseDragThreshold * g.IO.MouseDragThreshold); } ImGuiWindow* ImGuiTestContext::FindHoveredWindowAtPos(const ImVec2& pos) { #if IMGUI_VERSION_NUM >= 19062 ImGuiWindow* hovered_window = nullptr; ImGui::FindHoveredWindowEx(pos, true, &hovered_window, nullptr); return hovered_window; #else // Modeled on FindHoveredWindow() in imgui.cpp. // Ideally that core function would be refactored to avoid this copy. // - Need to take account of MovingWindow specificities and early out. // - Need to be able to skip viewport compare. // So for now we use a custom function. ImGuiContext& g = *UiContext; ImVec2 padding_regular = g.Style.TouchExtraPadding; ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular; for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; if (!window->Active || window->Hidden) continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize; ImRect r = window->OuterRectClipped; r.Expand(hit_padding); if (!r.Contains(pos)) continue; // Support for one rectangular hole in any given window // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512) if (window->HitTestHoleSize.x != 0) { ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y); ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y); if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos)) continue; } return window; } return nullptr; #endif } static bool IsPosOnVoid(ImGuiContext& g, const ImVec2& pos) { #if IMGUI_VERSION_NUM < 19183 const ImVec2 hover_padding = g.WindowsHoverPadding; #else const ImVec2 hover_padding = ImVec2(g.WindowsBorderHoverPadding, g.WindowsBorderHoverPadding); #endif for (ImGuiWindow* window : g.Windows) #ifdef IMGUI_HAS_DOCK if (window->RootWindowDockTree == window && window->WasActive) #else if (window->RootWindow == window && window->WasActive) #endif { ImRect r = window->Rect(); r.Expand(hover_padding); if (r.Contains(pos)) return false; } return true; } // Sample viewport for an easy location with nothing on it. // FIXME-OPT: If ever any problematic: // - (1) could iterate g.WindowsFocusOrder[] now that we made the switch of it only containing root windows // - (2) increase steps iteratively // - (3) remember last answer and tries it first. // - (4) shortpath to failure negative if a window covers the whole viewport? bool ImGuiTestContext::FindExistingVoidPosOnViewport(ImGuiViewport* viewport, ImVec2* out) { ImGuiContext& g = *UiContext; if (IsError()) return false; for (int yn = 0; yn < 20; yn++) for (int xn = 0; xn < 20; xn++) { ImVec2 pos = viewport->Pos + viewport->Size * ImVec2(xn / 20.0f, yn / 20.0f); if (!IsPosOnVoid(g, pos)) continue; *out = pos; return true; } return false; } ImVec2 ImGuiTestContext::GetPosOnVoid(ImGuiViewport* viewport) { if (IsError()) return ImVec2(); ImVec2 void_pos; bool found_existing_void_pos = FindExistingVoidPosOnViewport(viewport, &void_pos); if (found_existing_void_pos) return void_pos; // Move windows away // FIXME: Should be optional and otherwise error. void_pos = viewport->Pos + ImVec2(1, 1); _MakeAimingSpaceOverPos(viewport, nullptr, void_pos); return void_pos; } ImVec2 ImGuiTestContext::GetWindowTitlebarPoint(ImGuiTestRef window_ref) { // FIXME-TESTS: Need to find a -visible- click point. drag_pos may end up being outside of main viewport. if (IsError()) return ImVec2(); ImGuiWindow* window = GetWindowByRef(window_ref); if (window == nullptr) { IM_ERRORF_NOHDR("Unable to locate ref window: '%s'", window_ref.Path); return ImVec2(); } ImVec2 drag_pos; for (int n = 0; n < 2; n++) { #ifdef IMGUI_HAS_DOCK if (window->DockNode != nullptr && window->DockNode->TabBar != nullptr) { ImGuiTabBar* tab_bar = window->DockNode->TabBar; ImGuiTabItem* tab = ImGui::TabBarFindTabByID(tab_bar, window->TabId); IM_ASSERT(tab != nullptr); drag_pos = tab_bar->BarRect.Min + ImVec2(tab->Offset + tab->Width * 0.5f, tab_bar->BarRect.GetHeight() * 0.5f); } else #endif { #if IMGUI_VERSION_NUM >= 19071 const float h = window->TitleBarHeight; #else const float h = window->TitleBarHeight(); #endif drag_pos = ImFloor(window->Pos + ImVec2(window->Size.x, h) * 0.5f); } // If we didn't have to teleport it means we can reach the position already if (!WindowTeleportToMakePosVisible(window->ID, drag_pos)) break; } return drag_pos; } // Click position which should have no windows. // Default to last mouse viewport if viewport not specified. void ImGuiTestContext::MouseMoveToVoid(ImGuiViewport* viewport) { ImGuiContext& g = *UiContext; if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseMoveToVoid"); #ifdef IMGUI_HAS_VIEWPORT if (viewport == nullptr && g.MouseViewport && (g.MouseViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows)) viewport = g.MouseViewport; #endif if (viewport == nullptr) viewport = ImGui::GetMainViewport(); ImVec2 pos = GetPosOnVoid(viewport); // This may call WindowMove and alter mouse viewport. #ifdef IMGUI_HAS_VIEWPORT MouseSetViewportID(viewport->ID); #endif MouseMoveToPos(pos); IM_CHECK(g.HoveredWindow == nullptr); } void ImGuiTestContext::MouseClickOnVoid(int mouse_button, ImGuiViewport* viewport) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseClickOnVoid %d", mouse_button); MouseMoveToVoid(viewport); MouseClick(mouse_button); } void ImGuiTestContext::MouseDragWithDelta(ImVec2 delta, ImGuiMouseButton button) { ImGuiContext& g = *UiContext; if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseDragWithDelta %d (%.1f, %.1f)", button, delta.x, delta.y); MouseDown(button); MouseMoveToPos(g.IO.MousePos + delta); MouseUp(button); } // Important: always call MouseWheelX()/MouseWheelY() with an understand that holding Shift will swap axises. // - On Windows/Linux, this swap is done in ImGui::NewFrame() // - On OSX, this swap is generally done by the backends // - In simulated test engine, always assume Windows/Linux behavior as we will swap in ImGuiTestEngine_ApplyInputToImGuiContext() void ImGuiTestContext::MouseWheel(ImVec2 delta) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("MouseWheel(%g, %g)", delta.x, delta.y); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepStandard(); float td = 0.0f; const float scroll_speed = 15.0f; // Units per second. while (delta.x != 0.0f || delta.y != 0.0f) { ImVec2 scroll; if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast) { scroll = delta; } else { td += UiContext->IO.DeltaTime; scroll = ImFloor(delta * ImVec2(td, td) * scroll_speed); } if (scroll.x != 0.0f || scroll.y != 0.0f) { scroll = ImClamp(scroll, ImVec2(ImMin(delta.x, 0.0f), ImMin(delta.y, 0.0f)), ImVec2(ImMax(delta.x, 0.0f), ImMax(delta.y, 0.0f))); Inputs->MouseWheel = scroll; delta -= scroll; td = 0; } Yield(); } } void ImGuiTestContext::KeyDown(ImGuiKeyChord key_chord) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); #if IMGUI_VERSION_NUM >= 19012 const char* chord_desc = ImGui::GetKeyChordName(key_chord); #else char chord_desc[32]; ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc)); #endif LogDebug("KeyDown(%s)", chord_desc); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepShort(); Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, true)); Yield(); Yield(); } void ImGuiTestContext::KeyUp(ImGuiKeyChord key_chord) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); #if IMGUI_VERSION_NUM >= 19012 const char* chord_desc = ImGui::GetKeyChordName(key_chord); #else char chord_desc[32]; ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc)); #endif LogDebug("KeyUp(%s)", chord_desc); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepShort(); Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, false)); Yield(); Yield(); } void ImGuiTestContext::KeyPress(ImGuiKeyChord key_chord, int count) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); #if IMGUI_VERSION_NUM >= 19012 const char* chord_desc = ImGui::GetKeyChordName(key_chord); #else char chord_desc[32]; ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc)); #endif LogDebug("KeyPress(%s, %d)", chord_desc, count); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepShort(); while (count > 0) { count--; Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, true)); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepShort(); else Yield(); Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, false)); Yield(); // Give a frame for items to react Yield(); } } void ImGuiTestContext::KeyHold(ImGuiKeyChord key_chord, float time) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); #if IMGUI_VERSION_NUM >= 19012 const char* chord_desc = ImGui::GetKeyChordName(key_chord); #else char chord_desc[32]; ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc)); #endif LogDebug("KeyHold(%s, %.2f sec)", chord_desc, time); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepStandard(); Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, true)); SleepNoSkip(time, 1 / 100.0f); Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, false)); Yield(); // Give a frame for items to react } // No extra yield void ImGuiTestContext::KeySetEx(ImGuiKeyChord key_chord, bool is_down, float time) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); #if IMGUI_VERSION_NUM >= 19012 const char* chord_desc = ImGui::GetKeyChordName(key_chord); #else char chord_desc[32]; ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc)); #endif LogDebug("KeySetEx(%s, is_down=%d, time=%.f)", chord_desc, is_down, time); Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, is_down)); if (time > 0.0f) SleepNoSkip(time, 1.0f / 100.0f); } void ImGuiTestContext::KeyChars(const char* chars) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("KeyChars('%s')", chars); if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic) SleepStandard(); while (*chars) { unsigned int c = 0; int bytes_count = ImTextCharFromUtf8(&c, chars, nullptr); chars += bytes_count; if (c > 0 && c <= 0xFFFF) Inputs->Queue.push_back(ImGuiTestInput::ForChar((ImWchar)c)); if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast) Sleep(1.0f / EngineIO->TypingSpeed); } Yield(); } void ImGuiTestContext::KeyCharsAppend(const char* chars) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("KeyCharsAppend('%s')", chars); KeyPress(ImGuiKey_End); KeyChars(chars); } void ImGuiTestContext::KeyCharsAppendEnter(const char* chars) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("KeyCharsAppendEnter('%s')", chars); KeyPress(ImGuiKey_End); KeyChars(chars); KeyPress(ImGuiKey_Enter); } void ImGuiTestContext::KeyCharsReplace(const char* chars) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("KeyCharsReplace('%s')", chars); #if IMGUI_VERSION_NUM < 19063 KeyPress(ImGuiKey_A | ImGuiMod_Shortcut); #else KeyPress(ImGuiKey_A | ImGuiMod_Ctrl); #endif if (chars[0]) KeyChars(chars); else KeyPress(ImGuiKey_Delete); } void ImGuiTestContext::KeyCharsReplaceEnter(const char* chars) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("KeyCharsReplaceEnter('%s')", chars); #if IMGUI_VERSION_NUM < 19063 KeyPress(ImGuiKey_A | ImGuiMod_Shortcut); #else KeyPress(ImGuiKey_A | ImGuiMod_Ctrl); #endif if (chars[0]) KeyChars(chars); else KeyPress(ImGuiKey_Delete); KeyPress(ImGuiKey_Enter); } // depth = 1 -> immediate child of 'parent' in ID Stack // FIXME: Configurable filter for InLayerMask. Perhaps we can expose a GatherItemEx() that takes a ImGuiTestGatherTask struct as input. void ImGuiTestContext::GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth) { IM_ASSERT(out_list != nullptr); IM_ASSERT(depth > 0 || depth == -1); if (IsError()) return; ImGuiTestGatherTask* task = &Engine->GatherTask; IM_ASSERT(task->InParentID == 0); IM_ASSERT(task->LastItemInfo == nullptr); // Register gather tasks if (depth == -1) depth = 99; if (parent.ID == 0) parent.ID = GetID(parent); task->InParentID = parent.ID; task->InMaxDepth = depth; task->InLayerMask = (1 << ImGuiNavLayer_Main); task->OutList = out_list; // Keep running while gathering // The corresponding hook is ItemAdd() -> ImGuiTestEngineHook_ItemAdd() -> ImGuiTestEngineHook_ItemAdd_GatherTask() const int begin_gather_size = out_list->GetSize(); while (true) { const int begin_gather_size_for_frame = out_list->GetSize(); Yield(); const int end_gather_size_for_frame = out_list->GetSize(); if (begin_gather_size_for_frame == end_gather_size_for_frame) break; } const int end_gather_size = out_list->GetSize(); // FIXME-TESTS: To support filter we'd need to process the list here, // Because ImGuiTestItemList is a pool (ImVector + map ID->index) we'll need to filter, rewrite, rebuild map ImGuiTestItemInfo parent_item = ItemInfo(parent, ImGuiTestOpFlags_NoError); LogDebug("GatherItems from %s, %d deep: found %d items.", ImGuiTestRefDesc(parent, parent_item).c_str(), depth, end_gather_size - begin_gather_size); task->Clear(); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoAutoOpenFullPath // - ImGuiTestOpFlags_NoError void ImGuiTestContext::ItemAction(ImGuiTestAction action, ImGuiTestRef ref, ImGuiTestOpFlags flags, void* action_arg) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); // [DEBUG] Breakpoint //if (ref.ID == 0x0d4af068) // printf(""); // FIXME-TESTS: Fix that stuff const bool is_wildcard = ref.Path != nullptr && strstr(ref.Path, "**/") != 0; if (is_wildcard) { // This is a fragile way to avoid some ambiguities, we're relying on expected action to further filter by status flags. // These flags are not cleared by ItemInfo() because ItemAction() may call ItemInfo() again to get same item and thus it // needs these flags to remain in place. if (action == ImGuiTestAction_Check || action == ImGuiTestAction_Uncheck) Engine->FindByLabelTask.InFilterItemStatusFlags = ImGuiItemStatusFlags_Checkable; else if (action == ImGuiTestAction_Open || action == ImGuiTestAction_Close) Engine->FindByLabelTask.InFilterItemStatusFlags = ImGuiItemStatusFlags_Openable; } // Find item ImGuiTestItemInfo item; if (flags & ImGuiTestOpFlags_NoAutoOpenFullPath) item = ItemInfo(ref, (flags & ImGuiTestOpFlags_NoError)); else item = ItemInfoOpenFullPath(ref, (flags & ImGuiTestOpFlags_NoError)); ImGuiTestRefDesc desc(ref, item); LogDebug("Item%s %s%s", GetActionName(action), desc.c_str(), (InputMode == ImGuiInputSource_Mouse) ? "" : " (w/ Nav)"); if (item.ID == 0) { if (flags & ImGuiTestOpFlags_NoError) LogDebug("Action skipped: Item doesn't exist + used ImGuiTestOpFlags_NoError."); return; } // Automatically uncollapse by default if (item.Window && !(OpFlags & ImGuiTestOpFlags_NoAutoUncollapse)) WindowCollapse(item.Window->ID, false); if (action == ImGuiTestAction_Hover) { MouseMove(ref, flags); } if (action == ImGuiTestAction_Click || action == ImGuiTestAction_DoubleClick) { if (InputMode == ImGuiInputSource_Mouse) { const int mouse_button = (int)(intptr_t)action_arg; IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); MouseMove(ref, flags); if (action == ImGuiTestAction_DoubleClick) MouseDoubleClick(mouse_button); else MouseClick(mouse_button); } else { action = ImGuiTestAction_NavActivate; } } if (action == ImGuiTestAction_NavActivate) { IM_ASSERT(action_arg == nullptr); // Unused NavMoveTo(ref); NavActivate(); if (action == ImGuiTestAction_DoubleClick) IM_ASSERT(0); } else if (action == ImGuiTestAction_Input) { IM_ASSERT(action_arg == nullptr); // Unused if (InputMode == ImGuiInputSource_Mouse) { MouseMove(ref, flags); KeyDown(ImGuiMod_Ctrl); MouseClick(0); KeyUp(ImGuiMod_Ctrl); } else { NavMoveTo(ref); NavInput(); } } else if (action == ImGuiTestAction_Open) { IM_ASSERT(action_arg == nullptr); // Unused if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0) { MouseMove(ref, flags); // Some item may open just by hovering, give them that chance item = ItemInfo(item.ID); if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0) { MouseClick(0); item = ItemInfo(item.ID); if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0) { MouseDoubleClick(0); // Attempt a double-click // FIXME-TESTS: let's not start doing those fuzzy things.. item = ItemInfo(item.ID); if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0) IM_ERRORF_NOHDR("Unable to Open item: '%s' in '%s'", desc.c_str(), item.Window ? item.Window->Name : "N/A"); } } //Yield(); } } else if (action == ImGuiTestAction_Close) { IM_ASSERT(action_arg == nullptr); // Unused if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) != 0) { ItemClick(ref, 0, flags); item = ItemInfo(item.ID); if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) != 0) { ItemDoubleClick(ref, flags); // Attempt a double-click // FIXME-TESTS: let's not start doing those fuzzy things.. widget should give direction of how to close/open... e.g. do you we close a TabItem? item = ItemInfo(item.ID); if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) != 0) IM_ERRORF_NOHDR("Unable to Close item: %s", ImGuiTestRefDesc(ref, item).c_str()); } Yield(); } } else if (action == ImGuiTestAction_Check) { IM_ASSERT(action_arg == nullptr); // Unused if ((item.StatusFlags & ImGuiItemStatusFlags_Checkable) && !(item.StatusFlags & ImGuiItemStatusFlags_Checked)) { ItemClick(ref, 0, flags); } ItemVerifyCheckedIfAlive(ref, true); // We can't just IM_ASSERT(ItemIsChecked()) because the item may disappear and never update its StatusFlags any more! } else if (action == ImGuiTestAction_Uncheck) { IM_ASSERT(action_arg == nullptr); // Unused if ((item.StatusFlags & ImGuiItemStatusFlags_Checkable) && (item.StatusFlags & ImGuiItemStatusFlags_Checked)) { ItemClick(ref, 0, flags); } ItemVerifyCheckedIfAlive(ref, false); // We can't just IM_ASSERT(ItemIsChecked()) because the item may disappear and never update its StatusFlags any more! } //if (is_wildcard) Engine->FindByLabelTask.InFilterItemStatusFlags = ImGuiItemStatusFlags_None; } void ImGuiTestContext::ItemActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent, const ImGuiTestActionFilter* filter) { int max_depth = filter ? filter->MaxDepth : -1; if (max_depth == -1) max_depth = 99; int max_passes = filter ? filter->MaxPasses : -1; if (max_passes == -1) max_passes = 99; IM_ASSERT(max_depth > 0 && max_passes > 0); IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ItemActionAll() %s", GetActionName(action)); if (!ref_parent.IsEmpty()) { // Open parent's parents ImGuiTestItemInfo parent_info = ItemInfoOpenFullPath(ref_parent); if (parent_info.ID != 0) { // Open parent if (action == ImGuiTestAction_Open) if ((parent_info.StatusFlags & ImGuiItemStatusFlags_Openable) && (parent_info.ItemFlags & ImGuiItemFlags_Disabled) == 0) ItemOpen(ref_parent, ImGuiTestOpFlags_NoError); } } // Find child items int actioned_total = 0; for (int pass = 0; pass < max_passes; pass++) { ImGuiTestItemList items; GatherItems(&items, ref_parent, max_depth); //LogItemList(&items); // Find deep most items int highest_depth = -1; if (action == ImGuiTestAction_Close) for (auto& item : items) if ((item.StatusFlags & ImGuiItemStatusFlags_Openable) && (item.StatusFlags & ImGuiItemStatusFlags_Opened)) // Not checking Disabled state here highest_depth = ImMax(highest_depth, item.Depth); const int actioned_total_at_beginning_of_pass = actioned_total; // Process top-to-bottom in most cases int scan_start = 0; int scan_end = items.GetSize(); int scan_dir = +1; if (action == ImGuiTestAction_Close) { // Close bottom-to-top because // 1) it is more likely to handle same-depth parent/child relationship better (e.g. CollapsingHeader) // 2) it gives a nicer sense of symmetry with the corresponding open operation. scan_start = items.GetSize() - 1; scan_end = -1; scan_dir = -1; } int processed_count_per_depth[8]; memset(processed_count_per_depth, 0, sizeof(processed_count_per_depth)); for (int n = scan_start; n != scan_end; n += scan_dir) { if (IsError()) break; const ImGuiTestItemInfo& item = *items[n]; if (filter && filter->RequireAllStatusFlags != 0) if ((item.StatusFlags & filter->RequireAllStatusFlags) != filter->RequireAllStatusFlags) continue; if (filter && filter->RequireAnyStatusFlags != 0) if ((item.StatusFlags & filter->RequireAnyStatusFlags) != 0) continue; if (filter && filter->MaxItemCountPerDepth != nullptr) { if (item.Depth < IM_ARRAYSIZE(processed_count_per_depth)) { if (processed_count_per_depth[item.Depth] >= filter->MaxItemCountPerDepth[item.Depth]) continue; processed_count_per_depth[item.Depth]++; } } switch (action) { case ImGuiTestAction_Hover: case ImGuiTestAction_Click: ItemAction(action, item.ID); actioned_total++; break; case ImGuiTestAction_Check: if ((item.StatusFlags & ImGuiItemStatusFlags_Checkable) && !(item.StatusFlags & ImGuiItemStatusFlags_Checked)) if ((item.ItemFlags & ImGuiItemFlags_Disabled) == 0) { ItemAction(action, item.ID); actioned_total++; } break; case ImGuiTestAction_Uncheck: if ((item.StatusFlags & ImGuiItemStatusFlags_Checkable) && (item.StatusFlags & ImGuiItemStatusFlags_Checked)) if ((item.ItemFlags & ImGuiItemFlags_Disabled) == 0) { ItemAction(action, item.ID); actioned_total++; } break; case ImGuiTestAction_Open: if ((item.StatusFlags & ImGuiItemStatusFlags_Openable) && !(item.StatusFlags & ImGuiItemStatusFlags_Opened)) if ((item.ItemFlags & ImGuiItemFlags_Disabled) == 0) { ItemAction(action, item.ID); actioned_total++; } break; case ImGuiTestAction_Close: if (item.Depth == highest_depth && (item.StatusFlags & ImGuiItemStatusFlags_Openable) && (item.StatusFlags & ImGuiItemStatusFlags_Opened)) if ((item.ItemFlags & ImGuiItemFlags_Disabled) == 0) { ItemClose(item.ID); actioned_total++; } break; default: IM_ASSERT(0); } } if (IsError()) break; if (action == ImGuiTestAction_Hover) break; if (actioned_total_at_beginning_of_pass == actioned_total) break; } LogDebug("%s %d items in total!", GetActionVerb(action), actioned_total); } void ImGuiTestContext::ItemOpenAll(ImGuiTestRef ref_parent, int max_depth, int max_passes) { ImGuiTestActionFilter filter; filter.MaxDepth = max_depth; filter.MaxPasses = max_passes; ItemActionAll(ImGuiTestAction_Open, ref_parent, &filter); } void ImGuiTestContext::ItemCloseAll(ImGuiTestRef ref_parent, int max_depth, int max_passes) { ImGuiTestActionFilter filter; filter.MaxDepth = max_depth; filter.MaxPasses = max_passes; ItemActionAll(ImGuiTestAction_Close, ref_parent, &filter); } void ImGuiTestContext::ItemInputValue(ImGuiTestRef ref, int value) { char buf[32]; ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", value); ItemInput(ref); KeyCharsReplaceEnter(buf); } void ImGuiTestContext::ItemInputValue(ImGuiTestRef ref, float value) { char buf[32]; ImFormatString(buf, IM_ARRAYSIZE(buf), "%f", value); ItemInput(ref); KeyCharsReplaceEnter(buf); } void ImGuiTestContext::ItemInputValue(ImGuiTestRef ref, const char* value) { ItemInput(ref); KeyCharsReplaceEnter(value); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoError bool ImGuiTestContext::ItemReadAsScalar(ImGuiTestRef ref, ImGuiDataType data_type, void* out_data, ImGuiTestOpFlags flags) { if (IsError()) return false; const ImGuiDataTypeInfo* data_type_info = ImGui::DataTypeGetInfo(data_type); const ImGuiTestOpFlags SUPPORTED_FLAGS = ImGuiTestOpFlags_NoError; IM_ASSERT((flags & ~SUPPORTED_FLAGS) == 0); IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ItemSelectReadValue '%s' 0x%08X as %s", ref.Path ? ref.Path : "nullptr", ref.ID, data_type_info->Name); IM_CHECK_SILENT_RETV(out_data != nullptr, false); Str256 backup_clipboard = ImGui::GetClipboardText(); ItemInput(ref, flags); #if IMGUI_VERSION_NUM < 19063 KeyPress(ImGuiKey_A | ImGuiMod_Shortcut); KeyPress(ImGuiKey_C | ImGuiMod_Shortcut); // Copy to clipboard #else KeyPress(ImGuiKey_A | ImGuiMod_Ctrl); KeyPress(ImGuiKey_C | ImGuiMod_Ctrl); // Copy to clipboard #endif KeyPress(ImGuiKey_Enter); const char* clipboard = ImGui::GetClipboardText(); bool ret = ImGui::DataTypeApplyFromText(clipboard, data_type, out_data, data_type_info->ScanFmt); if (ret == false) { if ((flags & ImGuiTestOpFlags_NoError) == 0) { LogError("Unable to parse buffer '%s' as %s", clipboard, data_type_info->Name); IM_CHECK_RETV(ret, false); } } ImGui::SetClipboardText(backup_clipboard.c_str()); return ret; } int ImGuiTestContext::ItemReadAsInt(ImGuiTestRef ref) { int v = 0; ItemReadAsScalar(ref, ImGuiDataType_S32, (void*)&v); return v; } float ImGuiTestContext::ItemReadAsFloat(ImGuiTestRef ref) { float v = 0.0f; ItemReadAsScalar(ref, ImGuiDataType_Float, (void*)&v); return v; } // Convenient wrapper for ItemSelectAndReadString using our own storage // Returned pointer is only valid until next call to same function. const char* ImGuiTestContext::ItemReadAsString(ImGuiTestRef ref) { if (IsError()) return ""; size_t required_1 = ItemReadAsString(ref, TempString.Data, TempString.capacity()); if ((int)required_1 > TempString.capacity()) { TempString.reserve((int)required_1); size_t required_2 = ItemReadAsString(ref, TempString.Data, TempString.capacity()); IM_CHECK_SILENT_RETV(required_1 == required_2, ""); } return TempString.Data; } // return required buffer size to store output value (#26, #66) // write up to out_buf_size to out_buf, always zero-terminated. // if (out_buf == nulltr) || (out_buf_size < return value), then you want to. // You'd probably want to wrap this in a helper for your preferred string type. size_t ImGuiTestContext::ItemReadAsString(ImGuiTestRef ref, char* out_buf, size_t out_buf_size) { if (IsError()) { if (out_buf_size > 0) out_buf[0] = 0; return 0; } IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ItemSelectAndReadString '%s' 0x%08X as string", ref.Path ? ref.Path : "nullptr", ref.ID); IM_CHECK_SILENT_RETV(out_buf != nullptr || out_buf_size == 0, false); Str256 backup_clipboard = ImGui::GetClipboardText(); ItemInput(ref); #if IMGUI_VERSION_NUM < 19063 KeyPress(ImGuiKey_A | ImGuiMod_Shortcut); KeyPress(ImGuiKey_C | ImGuiMod_Shortcut); // Copy to clipboard #else KeyPress(ImGuiKey_A | ImGuiMod_Ctrl); KeyPress(ImGuiKey_C | ImGuiMod_Ctrl); // Copy to clipboard #endif KeyPress(ImGuiKey_Enter); const char* value_str = ImGui::GetClipboardText(); size_t value_required_buf_size = strlen(value_str) + 1; if (out_buf_size > 0) ImFormatString(out_buf, out_buf_size, "%.*s", (int)ImMax(value_required_buf_size, out_buf_size), value_str); ImGui::SetClipboardText(backup_clipboard.c_str()); return value_required_buf_size; } void ImGuiTestContext::ItemHold(ImGuiTestRef ref, float time) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("ItemHold %s", desc.c_str()); MouseMove(ref); Yield(); Inputs->MouseButtonsValue = (1 << 0); Sleep(time); Inputs->MouseButtonsValue = 0; Yield(); } void ImGuiTestContext::ItemHoldForFrames(ImGuiTestRef ref, int frames) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("ItemHoldForFrames %s", desc.c_str()); MouseMove(ref); Yield(); Inputs->MouseButtonsValue = (1 << 0); Yield(frames); Inputs->MouseButtonsValue = 0; Yield(); } // Used to test opening containers (TreeNode, Tabs) while dragging a payload. // Hold for 1 second and then release mouse button. void ImGuiTestContext::ItemDragOverAndHold(ImGuiTestRef ref_src, ImGuiTestRef ref_dst) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestItemInfo item_src = ItemInfo(ref_src); ImGuiTestItemInfo item_dst = ItemInfo(ref_dst); ImGuiTestRefDesc desc_src(ref_src, item_src); ImGuiTestRefDesc desc_dst(ref_dst, item_dst); LogDebug("ItemDragOverAndHold %s to %s", desc_src.c_str(), desc_dst.c_str()); MouseMove(ref_src, ImGuiTestOpFlags_NoCheckHoveredId); SleepStandard(); MouseDown(0); // Enforce lifting drag threshold even if both item are exactly at the same location. // Don't lift the threshold in the same frame as calling MouseDown() as it can trigger two actions. Yield(); MouseLiftDragThreshold(); MouseMove(ref_dst, ImGuiTestOpFlags_NoCheckHoveredId); SleepNoSkip(1.0f, 1.0f / 10.0f); MouseUp(0); } void ImGuiTestContext::ItemDragAndDrop(ImGuiTestRef ref_src, ImGuiTestRef ref_dst, ImGuiMouseButton button) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestItemInfo item_src = ItemInfo(ref_src); ImGuiTestItemInfo item_dst = ItemInfo(ref_dst); ImGuiTestRefDesc desc_src(ref_src, item_src); ImGuiTestRefDesc desc_dst(ref_dst, item_dst); LogDebug("ItemDragAndDrop %s to %s", desc_src.c_str(), desc_dst.c_str()); // Try to keep destination window above other windows. MouseMove() operation will avoid focusing destination window // as that may steal ActiveID and break operation. // FIXME-TESTS: This does not handle a case where source and destination windows overlap. if (item_dst.Window != nullptr) WindowBringToFront(item_dst.Window->ID); // Use item_src/item_dst instead of ref_src/ref_dst so references with e.g. //$FOCUSED are latched once in the ItemInfo() call. MouseMove(item_src.ID, ImGuiTestOpFlags_NoCheckHoveredId); SleepStandard(); MouseDown(button); // Enforce lifting drag threshold even if both item are exactly at the same location. // Don't lift the threshold in the same frame as calling MouseDown() as it can trigger two actions. Yield(); MouseLiftDragThreshold(); MouseMove(item_dst.ID, ImGuiTestOpFlags_NoCheckHoveredId | ImGuiTestOpFlags_NoFocusWindow); SleepStandard(); MouseUp(button); } void ImGuiTestContext::ItemDragWithDelta(ImGuiTestRef ref_src, ImVec2 pos_delta) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestItemInfo item_src = ItemInfo(ref_src); ImGuiTestRefDesc desc_src(ref_src, item_src); LogDebug("ItemDragWithDelta %s to (%f, %f)", desc_src.c_str(), pos_delta.x, pos_delta.y); MouseMove(ref_src, ImGuiTestOpFlags_NoCheckHoveredId); SleepStandard(); MouseDown(0); MouseMoveToPos(UiContext->IO.MousePos + pos_delta); SleepStandard(); MouseUp(0); } bool ImGuiTestContext::ItemExists(ImGuiTestRef ref) { ImGuiTestItemInfo item = ItemInfo(ref, ImGuiTestOpFlags_NoError); return item.ID != 0; } // May want to add support for ImGuiTestOpFlags_NoError if item does not exist? bool ImGuiTestContext::ItemIsChecked(ImGuiTestRef ref) { ImGuiTestItemInfo item = ItemInfo(ref); return (item.StatusFlags & ImGuiItemStatusFlags_Checked) != 0; } // May want to add support for ImGuiTestOpFlags_NoError if item does not exist? bool ImGuiTestContext::ItemIsOpened(ImGuiTestRef ref) { ImGuiTestItemInfo item = ItemInfo(ref); return (item.StatusFlags & ImGuiItemStatusFlags_Opened) != 0; } void ImGuiTestContext::ItemVerifyCheckedIfAlive(ImGuiTestRef ref, bool checked) { // This is designed to deal with disappearing items which will not update their state, // e.g. a checkable menu item in a popup which closes when checked. // Otherwise ItemInfo() data is preserved for an additional frame. Yield(); ImGuiTestItemInfo item = ItemInfo(ref, ImGuiTestOpFlags_NoError); if (item.ID == 0) return; if (item.TimestampMain + 1 >= ImGuiTestEngine_GetFrameCount(Engine) && item.TimestampStatus == item.TimestampMain) IM_CHECK_SILENT(((item.StatusFlags & ImGuiItemStatusFlags_Checked) != 0) == checked); } // FIXME-TESTS: Could this be handled by ItemClose()? void ImGuiTestContext::TabClose(ImGuiTestRef ref) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("TabClose %s", desc.c_str()); // Move into first, then click close button as it appears MouseMove(ref); ImGuiTestRef backup_ref = GetRef(); SetRef(GetID(ref)); ItemClick("#CLOSE"); SetRef(backup_ref); } bool ImGuiTestContext::TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order) { if (IsError()) return false; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("TabBarCompareOrder"); IM_CHECK_SILENT_RETV(tab_bar != nullptr, false); // Display char buf[256]; char* buf_end = buf + IM_ARRAYSIZE(buf); char* p = buf; for (int i = 0; i < tab_bar->Tabs.Size; i++) p += ImFormatString(p, buf_end - p, "%s\"%s\"", i ? ", " : " ", ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[i])); LogDebug(" Current {%s }", buf); p = buf; for (int i = 0; tab_order[i] != nullptr; i++) p += ImFormatString(p, buf_end - p, "%s\"%s\"", i ? ", " : " ", tab_order[i]); LogDebug(" Expected {%s }", buf); // Compare for (int i = 0; tab_order[i] != nullptr; i++) { if (i >= tab_bar->Tabs.Size) return false; const char* current = ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[i]); const char* expected = tab_order[i]; if (strcmp(current, expected) != 0) return false; } return true; } // Automatically insert "##MenuBar" between window and menus. // Automatically open and navigate sub-menus // FIXME: Currently assume that any path after the window are sub-menus. void ImGuiTestContext::MenuAction(ImGuiTestAction action, ImGuiTestRef ref) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("MenuAction %s", desc.c_str()); IM_ASSERT(ref.Path != nullptr); // MenuAction() doesn't support **/ in most case it would be equivalent to opening all menus to "search". // [01] Works: // MenuClick("File/New"): // [02] Works: // MenuClick("File"); // MenuClick("File/New"); // [03] Works: // MenuClick("File"); // ItemClick("**/New"); // [04] Doesn't work: (may work in the future) // MenuClick("File"); // MenuClick("**/New"); // [05] Doesn't work: (unlikely to ever work) // MenuClick("**/New"); if (strncmp(ref.Path, "**/", 3) == 0) { LogError("\"**/\" is not yet supported by MenuAction()."); return; } int depth = 0; const char* path = ref.Path; const char* path_end = path + strlen(path); ImGuiWindow* ref_window = nullptr; if ((path[0] == '/' && path[1] == '/') || (RefID == 0)) { const char* end = strstr(path + 2, "/"); IM_CHECK_SILENT(end != nullptr); // Menu interaction without any menus specified in ref. Str64 window_name; window_name.append(path, end); ref_window = GetWindowByRef(GetID(window_name.c_str())); path = end + 1; if (ref_window == nullptr) LogError("MenuAction: missing ref window (invalid name \"//%s\" ?", window_name.c_str()); } else { ref_window = GetWindowByRef(RefID); if (ref_window == nullptr) LogError("MenuAction: missing ref window (invalid SetRef value?)"); } IM_CHECK_SILENT(ref_window != nullptr); // A ref window must always be set ImGuiWindow* current_window = ref_window; Str128 buf; while (path < path_end && !IsError()) { const char* p = ImStrchrRangeWithEscaping(path, path_end, '/'); if (p == nullptr) p = path_end; const bool is_target_item = (p == path_end); #if IMGUI_VERSION_NUM >= 19174 if (current_window->Flags & ImGuiWindowFlags_MenuBar) buf.setf("//%s/##MenuBar/%.*s", current_window->Name, (int)(p - path), path); // Click menu in menu bar #else if (current_window->Flags & ImGuiWindowFlags_MenuBar) buf.setf("//%s/##menubar/%.*s", current_window->Name, (int)(p - path), path); // Click menu in menu bar #endif else buf.setf("//%s/%.*s", current_window->Name, (int)(p - path), path); // Click sub menu in its own window #if IMGUI_VERSION_NUM < 18520 if (depth == 0 && (current_window->Flags & ImGuiWindowFlags_Popup)) depth++; #endif // Timestamps updated in hooks submitted in ui code. ImGuiTestItemInfo item = ItemInfo(buf.c_str()); IM_CHECK_SILENT(item.ID != 0); if (item.TimestampStatus < UiContext->FrameCount) { Yield(); item = ItemInfo(buf.c_str()); IM_CHECK_SILENT(item.ID != 0); } if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0) // Open menus can be ignored completely. { // We cannot move diagonally to a menu item because depending on the angle and other items we cross on our path we could close our target menu. // First move horizontally into the menu, then vertically! if (depth > 0) { MouseSetViewport(item.Window); if (Inputs->MousePosValue.x <= item.RectFull.Min.x || Inputs->MousePosValue.x >= item.RectFull.Max.x) MouseMoveToPos(ImVec2(item.RectFull.GetCenter().x, Inputs->MousePosValue.y)); if (Inputs->MousePosValue.y <= item.RectFull.Min.y || Inputs->MousePosValue.y >= item.RectFull.Max.y) MouseMoveToPos(ImVec2(Inputs->MousePosValue.x, item.RectFull.GetCenter().y)); } if (is_target_item) { // Final item ItemAction(action, buf.c_str()); break; } else { // Then aim at the menu item. Menus may be navigated by holding mouse button down by hovering a menu. ItemAction(Inputs->MouseButtonsValue ? ImGuiTestAction_Hover : ImGuiTestAction_Click, buf.c_str()); } } #if IMGUI_VERSION_NUM < 19187 current_window = GetWindowByRef(Str16f("//##Menu_%02d", depth).c_str()); #else current_window = GetWindowByRef(Str16f("//###Menu_%02d", depth).c_str()); #endif IM_CHECK_SILENT(current_window != nullptr); path = p + 1; depth++; } } void ImGuiTestContext::MenuActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent) { ImGuiTestItemList items; MenuAction(ImGuiTestAction_Open, ref_parent); GatherItems(&items, "//$FOCUSED", 1); //LogItemList(&items); for (auto item : items) { MenuAction(ImGuiTestAction_Open, ref_parent); // We assume that every interaction will close the menu again if (action == ImGuiTestAction_Check || action == ImGuiTestAction_Uncheck) { ImGuiTestItemInfo info2 = ItemInfo(item.ID); // refresh info if ((info2.ItemFlags & ImGuiItemFlags_Disabled) != 0) // FIXME: Report disabled state in log? Make that optional? continue; if ((info2.StatusFlags & ImGuiItemStatusFlags_Checkable) == 0) continue; } ItemAction(action, item.ID); } } static bool IsWindowACombo(ImGuiWindow* window) { if ((window->Flags & ImGuiWindowFlags_Popup) == 0) return false; if (strncmp(window->Name, "##Combo_", strlen("##Combo_")) != 0) return false; return true; } // Usage: ComboClick("ComboName/ItemName"); void ImGuiTestContext::ComboClick(ImGuiTestRef ref) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("ComboClick %s", desc.c_str()); IM_ASSERT(ref.Path != nullptr); // Should always pass an actual path, not an ID. const char* path = ref.Path; const char* path_end = path + strlen(path); const char* p = ImStrchrRangeWithEscaping(path, path_end, '/'); if (p == nullptr) { LogError("Error: path should contains a / separator, e.g. ComboClick(\"mycombo/myitem\")"); IM_CHECK(p != nullptr); } Str128f combo_popup_buf = Str128f("%.*s", (int)(p-path), path); ItemClick(combo_popup_buf.c_str()); ImGuiWindow* popup = GetWindowByRef("//$FOCUSED"); IM_CHECK_SILENT(popup && IsWindowACombo(popup)); Str128f combo_item_buf = Str128f("//%s/**/%s", popup->Name, p + 1); ItemClick(combo_item_buf.c_str()); // For if Combo Selectables uses ImGuiSelectableFlags_NoAutoClosePopups if (GetWindowByRef("//$FOCUSED") == popup) KeyPress(ImGuiKey_Enter); } void ImGuiTestContext::ComboClickAll(ImGuiTestRef ref_parent) { ItemClick(ref_parent); ImGuiWindow* popup = GetWindowByRef("//$FOCUSED"); IM_CHECK_SILENT(popup && IsWindowACombo(popup)); ImGuiTestItemList items; GatherItems(&items, "//$FOCUSED"); for (auto item : items) { // Reopen popup when closed if (GetWindowByRef("//$FOCUSED") != popup) ItemClick(ref_parent); ItemClick(item.ID); } // For if Combo Selectables uses ImGuiSelectableFlags_NoAutoClosePopups if (GetWindowByRef("//$FOCUSED") == popup) KeyPress(ImGuiKey_Enter); } static ImGuiTableColumn* HelperTableFindColumnByName(ImGuiTable* table, const char* name) { for (int i = 0; i < table->Columns.size(); i++) if (strcmp(ImGui::TableGetColumnName(table, i), name) == 0) return &table->Columns[i]; return nullptr; } void ImGuiTestContext::TableOpenContextMenu(ImGuiTestRef ref, int column_n) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("TableOpenContextMenu %s", desc.c_str()); ImGuiTable* table = ImGui::TableFindByID(GetID(ref)); IM_CHECK_SILENT(table != nullptr); if (column_n == -1) column_n = table->RightMostEnabledColumn; IM_CHECK(column_n >= 0 && column_n <= table->ColumnsCount); ImGuiTableColumn* column = &table->Columns[column_n]; IM_CHECK_SILENT(column->IsEnabled); ImGuiID header_id = TableGetHeaderID(table, column_n); // Make visible if (!ItemExists(header_id)) ScrollToPosX(table->InnerWindow->ID, (column->MinX + column->MaxX) * 0.5f); ItemClick(header_id, ImGuiMouseButton_Right); Yield(); } ImGuiSortDirection ImGuiTestContext::TableClickHeader(ImGuiTestRef ref, const char* label, ImGuiKeyChord key_mods) { IM_ASSERT((key_mods & ~ImGuiMod_Mask_) == 0); // Cannot pass keys only mods ImGuiTable* table = ImGui::TableFindByID(GetID(ref)); IM_CHECK_SILENT_RETV(table != nullptr, ImGuiSortDirection_None); ImGuiTableColumn* column = HelperTableFindColumnByName(table, label); IM_CHECK_SILENT_RETV(column != nullptr, ImGuiSortDirection_None); if (key_mods != ImGuiMod_None) KeyDown(key_mods); ImGuiID header_id = TableGetHeaderID(table, label); // Make visible if (!ItemExists(header_id)) ScrollToPosX(table->InnerWindow->ID, (column->MinX + column->MaxX) * 0.5f); ItemClick(header_id, ImGuiMouseButton_Left); if (key_mods != ImGuiMod_None) KeyUp(key_mods); return (ImGuiSortDirection)column->SortDirection; } void ImGuiTestContext::TableSetColumnEnabled(ImGuiTestRef ref, const char* label, bool enabled) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("TableSetColumnEnabled %s label '%s' enabled = %d", desc.c_str(), label, enabled); ImGuiTable* table = ImGui::TableFindByID(GetID(ref)); IM_CHECK_SILENT(table != NULL); ImGuiTableColumn* column = HelperTableFindColumnByName(table, label); int column_n = column->IsEnabled ? table->Columns.index_from_ptr(column) : -1; TableOpenContextMenu(ref, column_n); ImGuiTestRef backup_ref = GetRef(); SetRef("//$FOCUSED"); if (enabled) ItemCheck(label); else ItemUncheck(label); PopupCloseOne(); SetRef(backup_ref); } void ImGuiTestContext::TableResizeColumn(ImGuiTestRef ref, int column_n, float width) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("TableResizeColumn %s column %d width %.2f", desc.c_str(), column_n, width); ImGuiTable* table = ImGui::TableFindByID(GetID(ref)); IM_CHECK_SILENT(table != nullptr); ImGuiID resize_id = ImGui::TableGetColumnResizeID(table, column_n); float old_width = table->Columns[column_n].WidthGiven; ItemDragWithDelta(resize_id, ImVec2(width - old_width, 0)); IM_CHECK_EQ(table->Columns[column_n].WidthRequest, width); } const ImGuiTableSortSpecs* ImGuiTestContext::TableGetSortSpecs(ImGuiTestRef ref) { ImGuiTable* table = ImGui::TableFindByID(GetID(ref)); IM_CHECK_SILENT_RETV(table != nullptr, nullptr); ImGuiContext& g = *UiContext; ImSwap(table, g.CurrentTable); const ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs(); ImSwap(table, g.CurrentTable); return sort_specs; } void ImGuiTestContext::WindowClose(ImGuiTestRef ref) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("WindowClose"); ImGuiTestRef backup_ref = GetRef(); SetRef(GetID(ref)); #ifdef IMGUI_HAS_DOCK // When docked: first move to Tab to make Close Button appear. if (ImGuiWindow* window = GetWindowByRef("")) if (window->DockIsActive) MouseMove(window->TabId); #endif ItemClick("#CLOSE"); SetRef(backup_ref); } void ImGuiTestContext::WindowCollapse(ImGuiTestRef window_ref, bool collapsed) { if (IsError()) return; ImGuiWindow* window = GetWindowByRef(window_ref); if (window == nullptr) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); if (window->Collapsed != collapsed) { LogDebug("WindowCollapse %d", collapsed); ImGuiTestOpFlags backup_op_flags = OpFlags; OpFlags |= ImGuiTestOpFlags_NoAutoUncollapse; ImGuiTestRef backup_ref = GetRef(); SetRef(window->ID); ItemClick("#COLLAPSE"); SetRef(backup_ref); OpFlags = backup_op_flags; Yield(); IM_CHECK(window->Collapsed == collapsed); } } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoError void ImGuiTestContext::WindowFocus(ImGuiTestRef ref, ImGuiTestOpFlags flags) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiTestRefDesc desc(ref); LogDebug("WindowFocus('%s')", desc.c_str()); ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT(window != nullptr); if (window) { ImGui::FocusWindow(window); // FIXME-TESTS-NOT_SAME_AS_END_USER: In theory should be replaced by click on title-bar or tab? Yield(); } // We cannot guarantee this will work 100% // - Some modal inhibition may kick-in. // - Because merely hovering an item may e.g. open a window or change focus. // In particular this can be the case with MenuItem. So trying to Open a MenuItem may lead to its child opening while hovering, // causing this function to seemingly fail (even if the end goal was reached). ImGuiContext& g = *UiContext; if ((window != g.NavWindow) && !(flags & ImGuiTestOpFlags_NoError)) LogDebug("-- Expected focused window '%s', but '%s' got focus back.", window->Name, g.NavWindow ? g.NavWindow->Name : ""); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoError // - ImGuiTestOpFlags_NoFocusWindow // FIXME: In principle most calls to this could be replaced by WindowFocus()? void ImGuiTestContext::WindowBringToFront(ImGuiTestRef ref, ImGuiTestOpFlags flags) { ImGuiContext& g = *UiContext; if (IsError()) return; ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT(window != nullptr); IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); if (window != g.NavWindow && !(flags & ImGuiTestOpFlags_NoFocusWindow)) { LogDebug("WindowBringToFront()->FocusWindow('%s')", window->Name); ImGui::FocusWindow(window); // FIXME-TESTS-NOT_SAME_AS_END_USER: In theory should be replaced by click on title-bar or tab? Yield(2); } else if (window->RootWindow != g.Windows.back()->RootWindow) { LogDebug("BringWindowToDisplayFront('%s') (window.back=%s)", window->Name, g.Windows.back()->Name); ImGui::BringWindowToDisplayFront(window); // FIXME-TESTS-NOT_SAME_AS_END_USER: This is not an actually possible action for end-user. Yield(2); } // Same as WindowFocus() if ((window != g.NavWindow) && !(flags & ImGuiTestOpFlags_NoError)) LogDebug("-- Expected focused window '%s', but '%s' got focus back.", window->Name, g.NavWindow ? g.NavWindow->Name : ""); } // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoFocusWindow void ImGuiTestContext::WindowMove(ImGuiTestRef ref, ImVec2 input_pos, ImVec2 pivot, ImGuiTestOpFlags flags) { if (IsError()) return; ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT(window != nullptr); IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("WindowMove '%s' (%.1f,%.1f) ", window->Name, input_pos.x, input_pos.y); ImVec2 target_pos = ImFloor(input_pos - pivot * window->Size); if (ImLengthSqr(target_pos - window->Pos) < 0.001f) { //MouseMoveToPos(window->Pos); //?? return; } if ((flags & ImGuiTestOpFlags_NoFocusWindow) == 0) WindowFocus(window->ID); WindowCollapse(window->ID, false); MouseSetViewport(window); MouseMoveToPos(GetWindowTitlebarPoint(ref)); //IM_CHECK_SILENT(UiContext->HoveredWindow == window); MouseDown(0); // Disable docking #ifdef IMGUI_HAS_DOCK if (UiContext->IO.ConfigDockingWithShift) KeyUp(ImGuiMod_Shift); else KeyDown(ImGuiMod_Shift); #endif ImVec2 delta = target_pos - window->Pos; MouseMoveToPos(Inputs->MousePosValue + delta); Yield(); MouseUp(); #ifdef IMGUI_HAS_DOCK KeyUp(ImGuiMod_Shift); #endif MouseSetViewport(window); // Update in case window has changed viewport } void ImGuiTestContext::WindowResize(ImGuiTestRef ref, ImVec2 size) { if (IsError()) return; ImGuiWindow* window = GetWindowByRef(ref); IM_CHECK_SILENT(window != nullptr); size = ImFloor(size); IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("WindowResize '%s' (%.1f,%.1f)", window->Name, size.x, size.y); if (ImLengthSqr(size - window->Size) < 0.001f) return; WindowFocus(window->ID); WindowCollapse(window->ID, false); // Extra yield as newly created window that have AutoFitFramesX/AutoFitFramesY set are temporarily not submitting their resize widgets. Give them a bit of slack. Yield(); // Aim at resize border or resize corner ImGuiID border_x2 = ImGui::GetWindowResizeBorderID(window, ImGuiDir_Right); ImGuiID border_y2 = ImGui::GetWindowResizeBorderID(window, ImGuiDir_Down); ImGuiID resize_br = ImGui::GetWindowResizeCornerID(window, 0); ImGuiID id; if (ImAbs(size.x - window->Size.x) < 0.0001f && ItemExists(border_y2)) id = border_y2; else if (ImAbs(size.y - window->Size.y) < 0.0001f && ItemExists(border_x2)) id = border_x2; else id = resize_br; MouseMove(id, ImGuiTestOpFlags_IsSecondAttempt); if (size.x <= 0.0f || size.y <= 0.0f) { IM_ASSERT(size.x <= 0.0f && size.y <= 0.0f); MouseDoubleClick(0); Yield(); } else { MouseDown(0); ImVec2 delta = size - window->Size; MouseMoveToPos(Inputs->MousePosValue + delta); Yield(); // At this point we don't guarantee the final size! MouseUp(); } MouseSetViewport(window); // Update in case window has changed viewport } void ImGuiTestContext::PopupCloseOne() { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("PopupCloseOne"); ImGuiContext& g = *UiContext; if (g.OpenPopupStack.Size > 0) ImGui::ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); // FIXME-TESTS-NOT_SAME_AS_END_USER Yield(); } void ImGuiTestContext::PopupCloseAll() { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("PopupCloseAll"); ImGuiContext& g = *UiContext; if (g.OpenPopupStack.Size > 0) ImGui::ClosePopupToLevel(0, true); // FIXME-TESTS-NOT_SAME_AS_END_USER Yield(); } // Match code in BeginPopupEx() ImGuiID ImGuiTestContext::PopupGetWindowID(ImGuiTestRef ref) { Str30f popup_name("//##Popup_%08x", GetID(ref)); return GetID(popup_name.c_str()); } #ifdef IMGUI_HAS_VIEWPORT void ImGuiTestContext::ViewportPlatform_SetWindowPos(ImGuiViewport* viewport, const ImVec2& pos) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ViewportPlatform_SetWindowPos(0x%08X, {%.2f,%.2f)", viewport->ID, pos.x, pos.y); Inputs->Queue.push_back(ImGuiTestInput::ForViewportSetPos(viewport->ID, pos)); // Queued since this will poke into backend, best to do in main thread. Yield(); // Submit to Platform Yield(); // Let Dear ImGui next frame see it } void ImGuiTestContext::ViewportPlatform_SetWindowSize(ImGuiViewport* viewport, const ImVec2& size) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ViewportPlatform_SetWindowSize(0x%08X, {%.2f,%.2f)", viewport->ID, size.x, size.y); Inputs->Queue.push_back(ImGuiTestInput::ForViewportSetSize(viewport->ID, size)); // Queued since this will poke into backend, best to do in main thread. Yield(); // Submit to Platform Yield(); // Let Dear ImGui next frame see it } // Simulate a platform focus WITHOUT a click perceived by dear imgui. Similar to clicking on Platform title bar. void ImGuiTestContext::ViewportPlatform_SetWindowFocus(ImGuiViewport* viewport) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ViewportPlatform_SetWindowFocus(0x%08X)", viewport->ID); Inputs->Queue.push_back(ImGuiTestInput::ForViewportFocus(viewport->ID)); // Queued since this will poke into backend, best to do in main thread. Yield(); // Submit to Platform Yield(); // Let Dear ImGui next frame see it } // Simulate a platform window closure. void ImGuiTestContext::ViewportPlatform_CloseWindow(ImGuiViewport* viewport) { if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("ViewportPlatform_CloseWindow(0x%08X)", viewport->ID); Inputs->Queue.push_back(ImGuiTestInput::ForViewportClose(viewport->ID)); // Queued since this will poke into backend, best to do in main thread. Yield(); // Submit to Platform Yield(3); // Let Dear ImGui next frame see it } #endif #ifdef IMGUI_HAS_DOCK // Note: unlike DockBuilder functions, for _nodes_ this require the node to be visible. // Supported values for ImGuiTestOpFlags: // - ImGuiTestOpFlags_NoFocusWindow // FIXME-TESTS: USING ImGuiTestOpFlags_NoFocusWindow leads to increase of ForeignWindowsHideOverPos(), best to avoid void ImGuiTestContext::DockInto(ImGuiTestRef src_id, ImGuiTestRef dst_id, ImGuiDir split_dir, bool split_outer, ImGuiTestOpFlags flags) { ImGuiContext& g = *UiContext; if (IsError()) return; IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); ImGuiWindow* window_src = GetWindowByRef(src_id); ImGuiWindow* window_dst = GetWindowByRef(dst_id); ImGuiDockNode* node_src = ImGui::DockBuilderGetNode(GetID(src_id)); ImGuiDockNode* node_dst = ImGui::DockBuilderGetNode(GetID(dst_id)); IM_CHECK_SILENT((window_src != nullptr) != (node_src != nullptr)); // Src must be either a window either a node IM_CHECK_SILENT((window_dst != nullptr) != (node_dst != nullptr)); // Dst must be either a window either a node // Infer node from window. Not the opposite as docking a node would imply docking all of it. if (node_src) window_src = node_src->HostWindow; if (node_dst) window_dst = node_dst->HostWindow; Str128f log("DockInto() Src: %s '%s' (0x%08X), Dst: %s '%s' (0x%08X), SplitDir = %d", node_src ? "node" : "window", node_src ? "" : window_src->Name, node_src ? node_src->ID : window_src->ID, node_dst ? "node" : "window", node_dst ? "" : window_dst->Name, node_dst ? node_dst->ID : window_dst->ID, split_dir); LogDebug("%s", log.c_str()); IM_CHECK_SILENT(window_src != nullptr); IM_CHECK_SILENT(window_dst != nullptr); IM_CHECK_SILENT(window_src->WasActive); IM_CHECK_SILENT(window_dst->WasActive); // Avoid focusing if we don't need it (this facilitate avoiding focus flashing when recording animated gifs) if (!(flags & ImGuiTestOpFlags_NoFocusWindow)) { if (g.Windows[g.Windows.Size - 2] != window_dst) WindowFocus(window_dst->ID); if (g.Windows[g.Windows.Size - 1] != window_src) WindowFocus(window_src->ID); } // Aim at title bar or tab or node grab ImGuiTestRef ref_src; if (node_src) ref_src = ImGui::DockNodeGetWindowMenuButtonId(node_src); // Whole node grab else ref_src = (window_src->DockIsActive ? window_src->TabId : window_src->MoveId); // FIXME-TESTS FIXME-DOCKING: Identify tab MouseMove(ref_src, ImGuiTestOpFlags_NoCheckHoveredId); SleepStandard(); // Start dragging source, so it gets undocked already, because we calculate target position // (Consider the possibility that dragging this out will move target position) MouseDown(0); if (g.IO.ConfigDockingWithShift) KeyDown(ImGuiMod_Shift); Yield(); MouseLiftDragThreshold(); if (window_src->DockIsActive) MouseMoveToPos(g.IO.MousePos + ImVec2(0, ImGui::GetFrameHeight() * 2.0f)); else Yield(); // A yield is necessary to start the moving, otherwise if by the time we call MouseSetViewport() no frame has elapsed and the viewports differs, dragging will fail. // (Button still held) // Locate target ImVec2 drop_pos; bool drop_is_valid = ImGui::DockContextCalcDropPosForDocking(window_dst, node_dst, window_src, node_src, split_dir, split_outer, &drop_pos); IM_CHECK_SILENT(drop_is_valid); if (!drop_is_valid) { if (g.IO.ConfigDockingWithShift) KeyUp(ImGuiMod_Shift); return; } // Ensure we can reach target WindowTeleportToMakePosVisible(window_dst->ID, drop_pos); ImGuiWindow* friend_windows[] = { window_src, window_dst, nullptr }; _ForeignWindowsHideOverPos(drop_pos, friend_windows); // Drag drop_is_valid = ImGui::DockContextCalcDropPosForDocking(window_dst, node_dst, window_src, node_src, split_dir, split_outer, &drop_pos); IM_CHECK_SILENT(drop_is_valid); MouseSetViewport(window_dst); MouseMoveToPos(drop_pos); if (node_src) window_src = node_src->HostWindow; // Dragging a menu button may detach a node and create a new window. IM_CHECK_SILENT(g.MovingWindow == window_src); Yield(2); // Docking to dockspace over viewport (needs extra frame) or moving a dock node to another node (needs two extra frames) fails in fast mode without this. IM_CHECK_SILENT(g.HoveredWindowUnderMovingWindow && g.HoveredWindowUnderMovingWindow->RootWindowDockTree == window_dst->RootWindowDockTree); // Docking will happen on the mouse-up const ImGuiID prev_dock_id = window_src->DockId; const ImGuiID prev_dock_parent_id = (window_src->DockNode && window_src->DockNode->ParentNode) ? window_src->DockNode->ParentNode->ID : 0; const ImGuiID prev_dock_node_as_host_id = window_src->DockNodeAsHost ? window_src->DockNodeAsHost->ID : 0; MouseUp(0); // Cool down if (g.IO.ConfigDockingWithShift) KeyUp(ImGuiMod_Shift); _ForeignWindowsUnhideAll(); Yield(); Yield(); // Verify docking has succeeded! It's not easy to write a full fledged test, let's go for a simple one. if (!(flags & ImGuiTestOpFlags_NoError)) { const ImGuiID curr_dock_id = window_src->DockId; const ImGuiID curr_dock_parent_id = (window_src->DockNode && window_src->DockNode->ParentNode) ? window_src->DockNode->ParentNode->ID : 0; const ImGuiID curr_dock_node_as_host_id = window_src->DockNodeAsHost ? window_src->DockNodeAsHost->ID : 0; IM_CHECK_SILENT((prev_dock_id != curr_dock_id) || (prev_dock_parent_id != curr_dock_parent_id) || (prev_dock_node_as_host_id != curr_dock_node_as_host_id)); } } void ImGuiTestContext::DockClear(const char* window_name, ...) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("DockClear"); va_list args; va_start(args, window_name); while (window_name != nullptr) { ImGui::DockBuilderDockWindow(window_name, 0); window_name = va_arg(args, const char*); } va_end(args); if (ActiveFunc == ImGuiTestActiveFunc_TestFunc) Yield(2); // Give time to rebuild dock in case io.ConfigDockingAlwaysTabBar is set } bool ImGuiTestContext::WindowIsUndockedOrStandalone(ImGuiWindow* window) { if (window->DockNode == nullptr) return true; return DockIdIsUndockedOrStandalone(window->DockId); } bool ImGuiTestContext::DockIdIsUndockedOrStandalone(ImGuiID dock_id) { if (dock_id == 0) return true; if (ImGuiDockNode* node = ImGui::DockBuilderGetNode(dock_id)) if (node->IsFloatingNode() && node->IsLeafNode() && node->Windows.Size == 1) return true; return false; } void ImGuiTestContext::DockNodeHideTabBar(ImGuiDockNode* node, bool hidden) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("DockNodeHideTabBar %d", hidden); ImGuiTestRef backup_ref = GetRef(); if (hidden) { SetRef(node->HostWindow); ItemClick(ImGui::DockNodeGetWindowMenuButtonId(node)); ImGuiID popup_id = PopupGetWindowID(GetID("#WindowMenu", node->ID)); SetRef(popup_id); #if IMGUI_VERSION_NUM >= 18910 ItemClick("###HideTabBar"); #else ItemClick("Hide tab bar"); #endif IM_CHECK_SILENT(node->IsHiddenTabBar()); } else { IM_CHECK_SILENT(node->VisibleWindow != nullptr); SetRef(node->VisibleWindow); ItemClick("#UNHIDE", 0, ImGuiTestOpFlags_MoveToEdgeD | ImGuiTestOpFlags_MoveToEdgeR); IM_CHECK_SILENT(!node->IsHiddenTabBar()); } SetRef(backup_ref); } void ImGuiTestContext::UndockNode(ImGuiID dock_id) { IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("UndockNode 0x%08X", dock_id); ImGuiDockNode* node = ImGui::DockBuilderGetNode(dock_id); if (node == nullptr) return; if (node->IsFloatingNode()) return; if (node->Windows.empty()) return; #if IMGUI_VERSION_NUM >= 19071 const float h = node->Windows[0]->TitleBarHeight; #else const float h = node->Windows[0]->TitleBarHeight(); #endif if (!UiContext->IO.ConfigDockingWithShift) KeyDown(ImGuiMod_Shift); // Disable docking ItemDragWithDelta(ImGui::DockNodeGetWindowMenuButtonId(node), ImVec2(h, h) * -2); if (!UiContext->IO.ConfigDockingWithShift) KeyUp(ImGuiMod_Shift); MouseUp(); } void ImGuiTestContext::UndockWindow(const char* window_name) { IM_ASSERT(window_name != nullptr); IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this); LogDebug("UndockWindow \"%s\"", window_name); ImGuiWindow* window = GetWindowByRef(window_name); if (!window->DockIsActive) return; #if IMGUI_VERSION_NUM >= 19071 const float h = window->TitleBarHeight; #else const float h = window->TitleBarHeight(); #endif if (!UiContext->IO.ConfigDockingWithShift) KeyDown(ImGuiMod_Shift); ItemDragWithDelta(window->TabId, ImVec2(h, h) * -2); if (!UiContext->IO.ConfigDockingWithShift) KeyUp(ImGuiMod_Shift); Yield(); } #endif // #ifdef IMGUI_HAS_DOCK //------------------------------------------------------------------------- // ImGuiTestContext - Performance Tools //------------------------------------------------------------------------- // Calculate the reference DeltaTime, averaged over PerfIterations/500 frames, with GuiFunc disabled. void ImGuiTestContext::PerfCalcRef() { LogDebug("Measuring ref dt..."); RunFlags |= ImGuiTestRunFlags_GuiFuncDisable; ImMovingAverage delta_times; delta_times.Init(PerfIterations); for (int n = 0; n < PerfIterations && !Abort; n++) { Yield(); delta_times.AddSample(UiContext->IO.DeltaTime); } PerfRefDt = delta_times.GetAverage(); RunFlags &= ~ImGuiTestRunFlags_GuiFuncDisable; } void ImGuiTestContext::PerfCapture(const char* category, const char* test_name, const char* csv_file) { if (IsError()) return; // Calculate reference average DeltaTime if it wasn't explicitly called by TestFunc if (PerfRefDt < 0.0) PerfCalcRef(); IM_ASSERT(PerfRefDt >= 0.0); // Yield for the average to stabilize LogDebug("Measuring GUI dt..."); ImMovingAverage delta_times; delta_times.Init(PerfIterations); for (int n = 0; n < PerfIterations && !Abort; n++) { Yield(); delta_times.AddSample(UiContext->IO.DeltaTime); } if (Abort) return; double dt_curr = delta_times.GetAverage(); double dt_ref_ms = PerfRefDt * 1000; double dt_delta_ms = (dt_curr - PerfRefDt) * 1000; const ImBuildInfo* build_info = ImBuildGetCompilationInfo(); // Display results // FIXME-TESTS: Would be nice if we could submit a custom marker (e.g. branch/feature name) LogInfo("[PERF] Conditions: Stress x%d, %s, %s, %s, %s, %s", PerfStressAmount, build_info->Type, build_info->Cpu, build_info->OS, build_info->Compiler, build_info->Date); LogInfo("[PERF] Result: %+6.3f ms (from ref %+6.3f)", dt_delta_ms, dt_ref_ms); ImGuiPerfToolEntry entry; entry.Timestamp = Engine->BatchStartTime; entry.Category = category ? category : Test->Category; entry.TestName = test_name ? test_name : Test->Name; entry.DtDeltaMs = dt_delta_ms; entry.PerfStressAmount = PerfStressAmount; entry.GitBranchName = EngineIO->GitBranchName; entry.BuildType = build_info->Type; entry.Cpu = build_info->Cpu; entry.OS = build_info->OS; entry.Compiler = build_info->Compiler; entry.Date = build_info->Date; ImGuiTestEngine_PerfToolAppendToCSV(Engine->PerfTool, &entry, csv_file); // Disable the "Success" message RunFlags |= ImGuiTestRunFlags_NoSuccessMsg; } //------------------------------------------------------------------------- ================================================ FILE: lib/third_party/imgui/imgui_test_engine/source/imgui_te_coroutine.cpp ================================================ // dear imgui test engine // (coroutine interface + optional implementation) // Read https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #include "imgui_te_coroutine.h" #include "imgui.h" #ifdef _MSC_VER #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #endif //------------------------------------------------------------------------ // Coroutine implementation using std::thread // This implements a coroutine using std::thread, with a helper thread for each coroutine (with serialised execution, so threads never actually run concurrently) //------------------------------------------------------------------------ #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL #include "imgui_te_utils.h" #include "thirdparty/Str/Str.h" #include #include #include struct Coroutine_ImplStdThreadData { std::thread* Thread; // The thread this coroutine is using std::condition_variable StateChange; // Condition variable notified when the coroutine state changes std::mutex StateMutex; // Mutex to protect coroutine state bool CoroutineRunning; // Is the coroutine currently running? Lock StateMutex before access and notify StateChange on change bool CoroutineTerminated; // Has the coroutine terminated? Lock StateMutex before access and notify StateChange on change Str64 Name; // The name of this coroutine }; // The coroutine executing on the current thread (if it is a coroutine thread) static thread_local Coroutine_ImplStdThreadData* GThreadCoroutine = nullptr; // The main function for a coroutine thread static void CoroutineThreadMain(Coroutine_ImplStdThreadData* data, ImGuiTestCoroutineMainFunc func, void* ctx) { // Set our thread name ImThreadSetCurrentThreadDescription(data->Name.c_str()); // Set the thread coroutine GThreadCoroutine = data; // Wait for initial Run() while (1) { std::unique_lock lock(data->StateMutex); if (data->CoroutineRunning) break; data->StateChange.wait(lock); } // Run user code, which will then call Yield() when it wants to yield control func(ctx); // Mark as terminated { std::lock_guard lock(data->StateMutex); data->CoroutineTerminated = true; data->CoroutineRunning = false; data->StateChange.notify_all(); } } static ImGuiTestCoroutineHandle Coroutine_ImplStdThread_Create(ImGuiTestCoroutineMainFunc* func, const char* name, void* ctx) { Coroutine_ImplStdThreadData* data = new Coroutine_ImplStdThreadData(); data->Name = name; data->CoroutineRunning = false; data->CoroutineTerminated = false; data->Thread = new std::thread(CoroutineThreadMain, data, func, ctx); return (ImGuiTestCoroutineHandle)data; } static void Coroutine_ImplStdThread_Destroy(ImGuiTestCoroutineHandle handle) { Coroutine_ImplStdThreadData* data = (Coroutine_ImplStdThreadData*)handle; IM_ASSERT(data->CoroutineTerminated); // The coroutine needs to run to termination otherwise it may leak all sorts of things and this will deadlock if (data->Thread) { data->Thread->join(); delete data->Thread; data->Thread = nullptr; } delete data; data = nullptr; } // Run the coroutine until the next call to Yield(). Returns TRUE if the coroutine yielded, FALSE if it terminated (or had previously terminated) static bool Coroutine_ImplStdThread_Run(ImGuiTestCoroutineHandle handle) { Coroutine_ImplStdThreadData* data = (Coroutine_ImplStdThreadData*)handle; // Wake up coroutine thread { std::lock_guard lock(data->StateMutex); if (data->CoroutineTerminated) return false; // Coroutine has already finished data->CoroutineRunning = true; data->StateChange.notify_all(); } // Wait for coroutine to stop while (1) { std::unique_lock lock(data->StateMutex); if (!data->CoroutineRunning) { // Breakpoint here to catch the point where we return from the coroutine if (data->CoroutineTerminated) return false; // Coroutine finished break; } data->StateChange.wait(lock); } return true; } // Yield the current coroutine (can only be called from a coroutine) static void Coroutine_ImplStdThread_Yield() { IM_ASSERT(GThreadCoroutine); // This can only be called from a coroutine thread Coroutine_ImplStdThreadData* data = GThreadCoroutine; // Flag that we are not running any more { std::lock_guard lock(data->StateMutex); data->CoroutineRunning = false; data->StateChange.notify_all(); } // At this point the thread that called RunCoroutine() will leave the "Wait for coroutine to stop" loop // Wait until we get started up again while (1) { std::unique_lock lock(data->StateMutex); if (data->CoroutineRunning) break; // Breakpoint here if you want to catch the point where execution of this coroutine resumes data->StateChange.wait(lock); } } ImGuiTestCoroutineInterface* Coroutine_ImplStdThread_GetInterface() { static ImGuiTestCoroutineInterface intf; intf.CreateFunc = Coroutine_ImplStdThread_Create; intf.DestroyFunc = Coroutine_ImplStdThread_Destroy; intf.RunFunc = Coroutine_ImplStdThread_Run; intf.YieldFunc = Coroutine_ImplStdThread_Yield; return &intf; } #endif // #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL ================================================ FILE: lib/third_party/imgui/imgui_test_engine/source/imgui_te_engine.cpp ================================================ // dear imgui test engine // (core) // This is the interface that your initial setup (app init, main loop) will mostly be using. // Actual tests will mostly use the interface of imgui_te_context.h // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui_te_engine.h" #include "imgui.h" #include "imgui_internal.h" #include "imgui_te_utils.h" #include "imgui_te_context.h" #include "imgui_te_internal.h" #include "imgui_te_perftool.h" #include "imgui_te_exporters.h" #include "thirdparty/Str/Str.h" #if _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include // SetUnhandledExceptionFilter() #undef Yield // Undo some of the damage done by #else #if !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE #include // signal() #endif #include // sleep() #endif // Warnings #ifdef _MSC_VER #pragma warning (disable: 4127) // conditional expression is constant #endif /* Index of this file: // [SECTION] TODO // [SECTION] FORWARD DECLARATIONS // [SECTION] DATA STRUCTURES // [SECTION] TEST ENGINE FUNCTIONS // [SECTION] CRASH HANDLING // [SECTION] HOOKS FOR CORE LIBRARY // [SECTION] CHECK/ERROR FUNCTIONS FOR TESTS // [SECTION] SETTINGS // [SECTION] ImGuiTestLog // [SECTION] ImGuiTest */ //------------------------------------------------------------------------- // [SECTION] DATA //------------------------------------------------------------------------- static ImGuiTestEngine* GImGuiTestEngine = nullptr; //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- // Private functions static void ImGuiTestEngine_CoroutineStopAndJoin(ImGuiTestEngine* engine); static void ImGuiTestEngine_ClearInput(ImGuiTestEngine* engine); static void ImGuiTestEngine_ApplyInputToImGuiContext(ImGuiTestEngine* engine); static void ImGuiTestEngine_ProcessTestQueue(ImGuiTestEngine* engine); static void ImGuiTestEngine_ClearTests(ImGuiTestEngine* engine); static void ImGuiTestEngine_PreNewFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); static void ImGuiTestEngine_PostNewFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); static void ImGuiTestEngine_PreEndFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); static void ImGuiTestEngine_PreRender(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); static void ImGuiTestEngine_PostRender(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); static void ImGuiTestEngine_UpdateHooks(ImGuiTestEngine* engine); static void ImGuiTestEngine_RunGuiFunc(ImGuiTestEngine* engine); static void ImGuiTestEngine_RunTestFunc(ImGuiTestEngine* engine); static void ImGuiTestEngine_ErrorRecoverySetup(ImGuiTestEngine* engine); static void ImGuiTestEngine_ErrorRecoveryRun(ImGuiTestEngine* engine); static void ImGuiTestEngine_TestQueueCoroutineMain(void* engine_opaque); // Settings static void* ImGuiTestEngine_SettingsReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void ImGuiTestEngine_SettingsReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); static void ImGuiTestEngine_SettingsWriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); //------------------------------------------------------------------------- // [SECTION] TEST ENGINE FUNCTIONS //------------------------------------------------------------------------- // Public // - ImGuiTestEngine_CreateContext() // - ImGuiTestEngine_DestroyContext() // - ImGuiTestEngine_BindImGuiContext() // - ImGuiTestEngine_UnbindImGuiContext() // - ImGuiTestEngine_GetIO() // - ImGuiTestEngine_Abort() // - ImGuiTestEngine_QueueAllTests() //------------------------------------------------------------------------- // - ImGuiTestEngine_FindItemInfo() // - ImGuiTestEngine_ClearTests() // - ImGuiTestEngine_ApplyInputToImGuiContext() // - ImGuiTestEngine_PreNewFrame() // - ImGuiTestEngine_PostNewFrame() // - ImGuiTestEngine_Yield() // - ImGuiTestEngine_ProcessTestQueue() // - ImGuiTestEngine_QueueTest() // - ImGuiTestEngine_RunTest() //------------------------------------------------------------------------- ImGuiTestEngine::ImGuiTestEngine() { PerfRefDeltaTime = 0.0f; PerfDeltaTime100.Init(100); PerfDeltaTime500.Init(500); PerfTool = IM_NEW(ImGuiPerfTool); UiFilterTests = IM_NEW(Str256); // We bite the bullet of adding an extra alloc/indirection in order to avoid including Str.h in our header UiFilterPerfs = IM_NEW(Str256); // Initialize std::thread based coroutine implementation if requested #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL IM_ASSERT(IO.CoroutineFuncs == nullptr && "IO.CoroutineFuncs already setup elsewhere!"); IO.CoroutineFuncs = Coroutine_ImplStdThread_GetInterface(); #endif } ImGuiTestEngine::~ImGuiTestEngine() { IM_ASSERT(TestQueueCoroutine == nullptr); IM_DELETE(PerfTool); IM_DELETE(UiFilterTests); IM_DELETE(UiFilterPerfs); } // Using named functions here instead of lambda gives nicer call-stacks (mostly because we frequently step in PostNewFrame) static void ImGuiTestEngine_ShutdownHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook) { ImGuiTestEngine_UnbindImGuiContext((ImGuiTestEngine*)hook->UserData, ui_ctx); } static void ImGuiTestEngine_PreNewFrameHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook) { ImGuiTestEngine_PreNewFrame((ImGuiTestEngine*)hook->UserData, ui_ctx); } static void ImGuiTestEngine_PostNewFrameHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook) { ImGuiTestEngine_PostNewFrame((ImGuiTestEngine*)hook->UserData, ui_ctx); } static void ImGuiTestEngine_PreEndFrameHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook) { ImGuiTestEngine_PreEndFrame((ImGuiTestEngine*)hook->UserData, ui_ctx); } static void ImGuiTestEngine_PreRenderHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook) { ImGuiTestEngine_PreRender((ImGuiTestEngine*)hook->UserData, ui_ctx); } static void ImGuiTestEngine_PostRenderHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook) { ImGuiTestEngine_PostRender((ImGuiTestEngine*)hook->UserData, ui_ctx); } void ImGuiTestEngine_BindImGuiContext(ImGuiTestEngine* engine, ImGuiContext* ui_ctx) { IM_ASSERT(engine->UiContextTarget == ui_ctx); // Add .ini handle for ImGuiWindow type if (engine->IO.ConfigSavedSettings) { ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "TestEngine"; ini_handler.TypeHash = ImHashStr("TestEngine"); ini_handler.ReadOpenFn = ImGuiTestEngine_SettingsReadOpen; ini_handler.ReadLineFn = ImGuiTestEngine_SettingsReadLine; ini_handler.WriteAllFn = ImGuiTestEngine_SettingsWriteAll; ui_ctx->SettingsHandlers.push_back(ini_handler); engine->PerfTool->_AddSettingsHandler(); } // Install generic context hooks facility ImGuiContextHook hook; hook.Type = ImGuiContextHookType_Shutdown; hook.Callback = ImGuiTestEngine_ShutdownHook; hook.UserData = (void*)engine; ImGui::AddContextHook(ui_ctx, &hook); hook.Type = ImGuiContextHookType_NewFramePre; hook.Callback = ImGuiTestEngine_PreNewFrameHook; hook.UserData = (void*)engine; ImGui::AddContextHook(ui_ctx, &hook); hook.Type = ImGuiContextHookType_NewFramePost; hook.Callback = ImGuiTestEngine_PostNewFrameHook; hook.UserData = (void*)engine; ImGui::AddContextHook(ui_ctx, &hook); hook.Type = ImGuiContextHookType_EndFramePre; hook.Callback = ImGuiTestEngine_PreEndFrameHook; hook.UserData = (void*)engine; ImGui::AddContextHook(ui_ctx, &hook); hook.Type = ImGuiContextHookType_RenderPre; hook.Callback = ImGuiTestEngine_PreRenderHook; hook.UserData = (void*)engine; ImGui::AddContextHook(ui_ctx, &hook); hook.Type = ImGuiContextHookType_RenderPost; hook.Callback = ImGuiTestEngine_PostRenderHook; hook.UserData = (void*)engine; ImGui::AddContextHook(ui_ctx, &hook); // Install custom test engine hook data if (GImGuiTestEngine == nullptr) GImGuiTestEngine = engine; IM_ASSERT(ui_ctx->TestEngine == nullptr); ui_ctx->TestEngine = engine; engine->UiContextHasHooks = false; } void ImGuiTestEngine_UnbindImGuiContext(ImGuiTestEngine* engine, ImGuiContext* ui_ctx) { IM_ASSERT(engine->UiContextTarget == ui_ctx); // FIXME: Could use ImGui::RemoveContextHook() if we stored our hook ids for (int hook_n = 0; hook_n < ui_ctx->Hooks.Size; hook_n++) if (ui_ctx->Hooks[hook_n].UserData == engine) ImGui::RemoveContextHook(ui_ctx, ui_ctx->Hooks[hook_n].HookId); ImGuiTestEngine_CoroutineStopAndJoin(engine); IM_ASSERT(ui_ctx->TestEngine == engine); ui_ctx->TestEngine = nullptr; // Remove .ini handler IM_ASSERT(GImGui == ui_ctx); if (engine->IO.ConfigSavedSettings) { ImGui::RemoveSettingsHandler("TestEngine"); ImGui::RemoveSettingsHandler("TestEnginePerfTool"); } // Remove hook if (GImGuiTestEngine == engine) GImGuiTestEngine = nullptr; engine->UiContextTarget = engine->UiContextActive = nullptr; } // Create test context (not bound to any dear imgui context yet) ImGuiTestEngine* ImGuiTestEngine_CreateContext() { IMGUI_CHECKVERSION(); // <--- If you get a crash here: mismatching config, check that both imgui and imgui_test_engine are using same defines (e.g. using the same imconfig file) ImGuiTestEngine* engine = IM_NEW(ImGuiTestEngine)(); return engine; } void ImGuiTestEngine_DestroyContext(ImGuiTestEngine* engine) { // We require user to call DestroyContext() before ImGuiTestEngine_DestroyContext() in order to preserve ini data... // In case of e.g. dynamically creating a TestEngine as runtime and not caring about its settings, you may set io.ConfigSavedSettings to false // in order to allow earlier destruction of the context. if (engine->IO.ConfigSavedSettings) IM_ASSERT(engine->UiContextTarget == nullptr && "You need to call ImGui::DestroyContext() BEFORE ImGuiTestEngine_DestroyContext()"); // Shutdown coroutine ImGuiTestEngine_CoroutineStopAndJoin(engine); if (engine->UiContextTarget != nullptr) ImGuiTestEngine_UnbindImGuiContext(engine, engine->UiContextTarget); ImGuiTestEngine_ClearTests(engine); for (int n = 0; n < engine->InfoTasks.Size; n++) IM_DELETE(engine->InfoTasks[n]); engine->InfoTasks.clear(); IM_DELETE(engine); // Release hook if (GImGuiTestEngine == engine) GImGuiTestEngine = nullptr; } void ImGuiTestEngine_Start(ImGuiTestEngine* engine, ImGuiContext* ui_ctx) { IM_ASSERT(engine->Started == false); IM_ASSERT(engine->UiContextTarget == nullptr); engine->UiContextTarget = ui_ctx; ImGuiTestEngine_BindImGuiContext(engine, engine->UiContextTarget); // Create our coroutine // (we include the word "Main" in the name to facilitate filtering for both this thread and the "Main Thread" in debuggers) if (!engine->TestQueueCoroutine) { IM_ASSERT(engine->IO.CoroutineFuncs && "Missing CoroutineFuncs! Use '#define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 1' or define your own implementation!"); engine->TestQueueCoroutine = engine->IO.CoroutineFuncs->CreateFunc(ImGuiTestEngine_TestQueueCoroutineMain, "Main Dear ImGui Test Thread", engine); } engine->TestQueueCoroutineShouldExit = false; engine->Started = true; } void ImGuiTestEngine_Stop(ImGuiTestEngine* engine) { IM_ASSERT(engine->Started); IM_ASSERT(engine->UiContextTarget != NULL); engine->Abort = true; ImGuiTestEngine_CoroutineStopAndJoin(engine); //ImGuiTestEngine_UnbindImGuiContext(engine, engine->UiContextTarget); ImGuiTestEngine_Export(engine); engine->Started = false; } static void ImGuiTestEngine_CoroutineStopRequest(ImGuiTestEngine* engine) { if (engine->TestQueueCoroutine != nullptr) engine->TestQueueCoroutineShouldExit = true; } static void ImGuiTestEngine_CoroutineStopAndJoin(ImGuiTestEngine* engine) { if (engine->TestQueueCoroutine != nullptr) { // Run until the coroutine exits engine->TestQueueCoroutineShouldExit = true; while (true) { if (!engine->IO.CoroutineFuncs->RunFunc(engine->TestQueueCoroutine)) break; } engine->IO.CoroutineFuncs->DestroyFunc(engine->TestQueueCoroutine); engine->TestQueueCoroutine = nullptr; } } // [EXPERIMENTAL] Destroy and recreate ImGui context // This potentially allow us to test issues related to handling new windows, restoring settings etc. // This also gets us once inch closer to more dynamic management of context (e.g. jail tests in their own context) // FIXME: This is currently called by ImGuiTestEngine_PreNewFrame() in hook but may end up needing to be called // by main application loop in order to facilitate letting app know of the new pointers. For now none of our backends // preserve the pointer so may be fine. void ImGuiTestEngine_RebootUiContext(ImGuiTestEngine* engine) { IM_ASSERT(engine->Started); ImGuiContext* ctx = engine->UiContextTarget; ImGuiTestEngine_Stop(engine); ImGuiTestEngine_UnbindImGuiContext(engine, ctx); // Backup #ifdef IMGUI_HAS_TEXTURES ImGuiContext* backup_atlas_owner = ctx->IO.Fonts->OwnerContext; #else bool backup_atlas_owned_by_context = ctx->FontAtlasOwnedByContext; #endif ImFontAtlas* backup_atlas = ctx->IO.Fonts; ImGuiIO backup_io = ctx->IO; #ifdef IMGUI_HAS_VIEWPORT // FIXME: Break with multi-viewports as we don't preserve user windowing data properly. // Backend tend to store e.g. HWND data in viewport 0. if (ctx->IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) IM_ASSERT(0); //ImGuiViewport backup_viewport0 = *(ImGuiViewport*)ctx->Viewports[0]; //ImGuiPlatformIO backup_platform_io = ctx->PlatformIO; //ImGui::DestroyPlatformWindows(); #endif // Recreate #ifdef IMGUI_HAS_TEXTURES ctx->IO.Fonts->OwnerContext = backup_atlas_owner; #else ctx->FontAtlasOwnedByContext = false; #endif #if 1 ImGui::DestroyContext(); ImGui::CreateContext(backup_atlas); #else // Preserve same context pointer, which is probably misleading and not even necessary. ImGui::Shutdown(ctx); ctx->~ImGuiContext(); IM_PLACEMENT_NEW(ctx) ImGuiContext(backup_atlas); ImGui::Initialize(ctx); #endif // Restore #ifdef IMGUI_HAS_TEXTURES ctx->IO.Fonts->OwnerContext = ctx; #else ctx->FontAtlasOwnedByContext = backup_atlas_owned_by_context; #endif ctx->IO = backup_io; #ifdef IMGUI_HAS_VIEWPORT //backup_platform_io.Viewports.swap(ctx->PlatformIO.Viewports); //ctx->PlatformIO = backup_platform_io; //ctx->Viewports[0]->RendererUserData = backup_viewport0.RendererUserData; //ctx->Viewports[0]->PlatformUserData = backup_viewport0.PlatformUserData; //ctx->Viewports[0]->PlatformHandle = backup_viewport0.PlatformHandle; //ctx->Viewports[0]->PlatformHandleRaw = backup_viewport0.PlatformHandleRaw; //memset(&backup_viewport0, 0, sizeof(backup_viewport0)); #endif ImGuiTestEngine_Start(engine, ctx); } void ImGuiTestEngine_PostSwap(ImGuiTestEngine* engine) { engine->PostSwapCalled = true; if (engine->IO.ConfigFixedDeltaTime != 0.0f) ImGuiTestEngine_SetDeltaTime(engine, engine->IO.ConfigFixedDeltaTime); // Sync capture tool configurations from engine IO. engine->CaptureContext.ScreenCaptureFunc = engine->IO.ScreenCaptureFunc; engine->CaptureContext.ScreenCaptureUserData = engine->IO.ScreenCaptureUserData; engine->CaptureContext.VideoCaptureEncoderPath = engine->IO.VideoCaptureEncoderPath; engine->CaptureContext.VideoCaptureEncoderPathSize = IM_ARRAYSIZE(engine->IO.VideoCaptureEncoderPath); engine->CaptureContext.VideoCaptureEncoderParams = engine->IO.VideoCaptureEncoderParams; engine->CaptureContext.VideoCaptureEncoderParamsSize = IM_ARRAYSIZE(engine->IO.VideoCaptureEncoderParams); engine->CaptureContext.GifCaptureEncoderParams = engine->IO.GifCaptureEncoderParams; engine->CaptureContext.GifCaptureEncoderParamsSize = IM_ARRAYSIZE(engine->IO.GifCaptureEncoderParams); engine->CaptureTool.VideoCaptureExtension = engine->IO.VideoCaptureExtension; engine->CaptureTool.VideoCaptureExtensionSize = IM_ARRAYSIZE(engine->IO.VideoCaptureExtension); // Capture a screenshot from main thread while coroutine waits if (engine->CaptureCurrentArgs != nullptr) { ImGuiCaptureStatus status = engine->CaptureContext.CaptureUpdate(engine->CaptureCurrentArgs); if (status != ImGuiCaptureStatus_InProgress) { if (status == ImGuiCaptureStatus_Done) ImStrncpy(engine->CaptureTool.OutputLastFilename, engine->CaptureCurrentArgs->InOutputFile, IM_ARRAYSIZE(engine->CaptureTool.OutputLastFilename)); engine->CaptureCurrentArgs = nullptr; } } } ImGuiTestEngineIO& ImGuiTestEngine_GetIO(ImGuiTestEngine* engine) { return engine->IO; } void ImGuiTestEngine_AbortCurrentTest(ImGuiTestEngine* engine) { engine->Abort = true; if (ImGuiTestContext* test_context = engine->TestContext) test_context->Abort = true; } bool ImGuiTestEngine_TryAbortEngine(ImGuiTestEngine* engine) { ImGuiTestEngine_AbortCurrentTest(engine); ImGuiTestEngine_CoroutineStopRequest(engine); if (ImGuiTestEngine_IsTestQueueEmpty(engine)) return true; return false; // Still running coroutine } // FIXME-OPT ImGuiTest* ImGuiTestEngine_FindTestByName(ImGuiTestEngine* engine, const char* category, const char* name) { IM_ASSERT(category != nullptr || name != nullptr); for (int n = 0; n < engine->TestsAll.Size; n++) { ImGuiTest* test = engine->TestsAll[n]; if (name != nullptr && strcmp(test->Name, name) != 0) continue; if (category != nullptr && strcmp(test->Category, category) != 0) continue; return test; } return nullptr; } // FIXME-OPT static ImGuiTestInfoTask* ImGuiTestEngine_FindInfoTask(ImGuiTestEngine* engine, ImGuiID id) { for (int task_n = 0; task_n < engine->InfoTasks.Size; task_n++) { ImGuiTestInfoTask* task = engine->InfoTasks[task_n]; if (task->ID == id) return task; } return nullptr; } // Request information about one item. // Will push a request for the test engine to process. // Will return nullptr when results are not ready (or not available). ImGuiTestItemInfo* ImGuiTestEngine_FindItemInfo(ImGuiTestEngine* engine, ImGuiID id, const char* debug_id) { IM_ASSERT(id != 0); if (ImGuiTestInfoTask* task = ImGuiTestEngine_FindInfoTask(engine, id)) { if (task->Result.TimestampMain + 2 >= engine->FrameCount) { task->FrameCount = engine->FrameCount; // Renew task return &task->Result; } return nullptr; } // Create task ImGuiTestInfoTask* task = IM_NEW(ImGuiTestInfoTask)(); task->ID = id; task->FrameCount = engine->FrameCount; if (debug_id) { size_t debug_id_sz = strlen(debug_id); if (debug_id_sz < IM_ARRAYSIZE(task->DebugName) - 1) { memcpy(task->DebugName, debug_id, debug_id_sz + 1); } else { size_t header_sz = (size_t)(IM_ARRAYSIZE(task->DebugName) * 0.30f); size_t footer_sz = IM_ARRAYSIZE(task->DebugName) - 2 - header_sz; IM_ASSERT(header_sz > 0 && footer_sz > 0); ImFormatString(task->DebugName, IM_ARRAYSIZE(task->DebugName), "%.*s..%.*s", (int)header_sz, debug_id, (int)footer_sz, debug_id + debug_id_sz - footer_sz); } } engine->InfoTasks.push_back(task); return nullptr; } static void ImGuiTestEngine_ClearTests(ImGuiTestEngine* engine) { for (int n = 0; n < engine->TestsAll.Size; n++) IM_DELETE(engine->TestsAll[n]); engine->TestsAll.clear(); engine->TestsQueue.clear(); } // Called at the beginning of a test to ensure no previous inputs leak into the new test // FIXME-TESTS: Would make sense to reset mouse position as well? void ImGuiTestEngine_ClearInput(ImGuiTestEngine* engine) { IM_ASSERT(engine->UiContextTarget != nullptr); ImGuiContext& g = *engine->UiContextTarget; engine->Inputs.MouseButtonsValue = 0; engine->Inputs.Queue.clear(); engine->Inputs.MouseWheel = ImVec2(0, 0); // FIXME: Necessary? #if IMGUI_VERSION_NUM >= 18972 g.IO.ClearEventsQueue(); #else g.InputEventsQueue.resize(0); g.IO.ClearInputCharacters(); #endif g.IO.ClearInputKeys(); ImGuiTestEngine_ApplyInputToImGuiContext(engine); } bool ImGuiTestEngine_IsUsingSimulatedInputs(ImGuiTestEngine* engine) { if (engine->UiContextActive) if (!ImGuiTestEngine_IsTestQueueEmpty(engine)) if (!(engine->TestContext->RunFlags & ImGuiTestRunFlags_GuiFuncOnly)) return true; return false; } // Setup inputs in the tested Dear ImGui context. Essentially we override the work of the backend here. void ImGuiTestEngine_ApplyInputToImGuiContext(ImGuiTestEngine* engine) { IM_ASSERT(engine->UiContextTarget != nullptr); ImGuiContext& g = *engine->UiContextTarget; ImGuiIO& io = g.IO; const bool use_simulated_inputs = ImGuiTestEngine_IsUsingSimulatedInputs(engine); if (!use_simulated_inputs) return; // Erase events submitted by backend for (int n = 0; n < g.InputEventsQueue.Size; n++) if (g.InputEventsQueue[n].AddedByTestEngine == false) g.InputEventsQueue.erase(&g.InputEventsQueue[n--]); // To support using ImGuiKey_NavXXXX shortcuts pointing to gamepad actions // FIXME-TEST-ENGINE: Should restore g.IO.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; g.IO.BackendFlags |= ImGuiBackendFlags_HasGamepad; // Special flags to stop submitting events if (engine->TestContext->RunFlags & ImGuiTestRunFlags_EnableRawInputs) return; const int input_event_count_prev = g.InputEventsQueue.Size; // Apply mouse viewport #ifdef IMGUI_HAS_VIEWPORT ImGuiPlatformIO& platform_io = g.PlatformIO; ImGuiViewport* mouse_hovered_viewport; if (engine->Inputs.MouseHoveredViewport != 0) mouse_hovered_viewport = ImGui::FindViewportByID(engine->Inputs.MouseHoveredViewport); // Common case else mouse_hovered_viewport = ImGui::FindHoveredViewportFromPlatformWindowStack(engine->Inputs.MousePosValue); // Rarely used, some tests rely on this (e.g. "docking_dockspace_passthru_hover") may make it a opt-in feature instead? if (mouse_hovered_viewport && (mouse_hovered_viewport->Flags & ImGuiViewportFlags_NoInputs)) mouse_hovered_viewport = nullptr; //if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) io.AddMouseViewportEvent(mouse_hovered_viewport ? mouse_hovered_viewport->ID : 0); bool mouse_hovered_viewport_focused = mouse_hovered_viewport && (mouse_hovered_viewport->Flags & ImGuiViewportFlags_IsFocused) != 0; #endif // Apply mouse io.AddMousePosEvent(engine->Inputs.MousePosValue.x, engine->Inputs.MousePosValue.y); for (int n = 0; n < ImGuiMouseButton_COUNT; n++) { bool down = (engine->Inputs.MouseButtonsValue & (1 << n)) != 0; io.AddMouseButtonEvent(n, down); // A click simulate platform focus on the viewport. #ifdef IMGUI_HAS_VIEWPORT if (down && mouse_hovered_viewport && !mouse_hovered_viewport_focused) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { mouse_hovered_viewport_focused = true; engine->Inputs.Queue.push_back(ImGuiTestInput::ForViewportFocus(mouse_hovered_viewport->ID)); } #endif } // Apply mouse wheel // [OSX] Simulate OSX behavior of automatically swapping mouse wheel axis when SHIFT is held. // This is working in conjonction with the fact that ImGuiTestContext::MouseWheel() assume Windows-style behavior. ImVec2 wheel = engine->Inputs.MouseWheel; if (io.ConfigMacOSXBehaviors && (io.KeyMods & ImGuiMod_Shift)) // FIXME!! ImSwap(wheel.x, wheel.y); if (wheel.x != 0.0f || wheel.y != 0.0f) io.AddMouseWheelEvent(wheel.x, wheel.y); engine->Inputs.MouseWheel = ImVec2(0, 0); // Process input requests/queues if (engine->Inputs.Queue.Size > 0) { for (int n = 0; n < engine->Inputs.Queue.Size; n++) { const ImGuiTestInput& input = engine->Inputs.Queue[n]; switch (input.Type) { case ImGuiTestInputType_Key: { ImGuiKeyChord key_chord = input.KeyChord; #if IMGUI_VERSION_NUM >= 19016 && IMGUI_VERSION_NUM < 19063 key_chord = ImGui::FixupKeyChord(&g, key_chord); // This will add ImGuiMod_Alt when pressing ImGuiKey_LeftAlt or ImGuiKey_LeftRight #endif #if IMGUI_VERSION_NUM >= 19063 key_chord = ImGui::FixupKeyChord(key_chord); // This will add ImGuiMod_Alt when pressing ImGuiKey_LeftAlt or ImGuiKey_LeftRight #endif ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); ImGuiKeyChord mods = (key_chord & ImGuiMod_Mask_); if (mods != 0x00) { // OSX conversion #if IMGUI_VERSION_NUM >= 18912 && IMGUI_VERSION_NUM < 19063 if (mods & ImGuiMod_Shortcut) mods = (mods & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); #endif #if IMGUI_VERSION_NUM >= 19063 // MacOS: swap Cmd(Super) and Ctrl WILL BE SWAPPED BACK BY io.AddKeyEvent() if (g.IO.ConfigMacOSXBehaviors) { if ((mods & (ImGuiMod_Ctrl | ImGuiMod_Super)) == ImGuiMod_Super) mods = (mods & ~ImGuiMod_Super) | ImGuiMod_Ctrl; else if ((mods & (ImGuiMod_Ctrl | ImGuiMod_Super)) == ImGuiMod_Ctrl) mods = (mods & ~ImGuiMod_Ctrl) | ImGuiMod_Super; if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; } else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_RightCtrl; } else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; } else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_RightSuper; } } #endif // Submitting a ImGuiMod_XXX without associated key needs to add at least one of the key. if (mods & ImGuiMod_Ctrl) { io.AddKeyEvent(ImGuiMod_Ctrl, input.Down); if (key != ImGuiKey_LeftCtrl && key != ImGuiKey_RightCtrl) io.AddKeyEvent(ImGuiKey_LeftCtrl, input.Down); } if (mods & ImGuiMod_Shift) { io.AddKeyEvent(ImGuiMod_Shift, input.Down); if (key != ImGuiKey_LeftShift && key != ImGuiKey_RightShift) io.AddKeyEvent(ImGuiKey_LeftShift, input.Down); } if (mods & ImGuiMod_Alt) { io.AddKeyEvent(ImGuiMod_Alt, input.Down); if (key != ImGuiKey_LeftAlt && key != ImGuiKey_RightAlt) io.AddKeyEvent(ImGuiKey_LeftAlt, input.Down); } if (mods & ImGuiMod_Super) { io.AddKeyEvent(ImGuiMod_Super, input.Down); if (key != ImGuiKey_LeftSuper && key != ImGuiKey_RightSuper) io.AddKeyEvent(ImGuiKey_LeftSuper, input.Down); } } if (key != ImGuiKey_None) io.AddKeyEvent(key, input.Down); break; } case ImGuiTestInputType_Char: { IM_ASSERT(input.Char != 0); io.AddInputCharacter(input.Char); break; } #ifdef IMGUI_HAS_VIEWPORT case ImGuiTestInputType_ViewportFocus: { if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) break; IM_ASSERT(engine->TestContext != nullptr); ImGuiViewport* viewport = ImGui::FindViewportByID(input.ViewportId); if (viewport == nullptr) engine->TestContext->LogError("ViewportPlatform_SetWindowFocus(%08X): cannot find viewport anymore!", input.ViewportId); else if (platform_io.Platform_SetWindowFocus == nullptr) engine->TestContext->LogError("ViewportPlatform_SetWindowFocus(%08X): backend's Platform_SetWindowFocus() is not set", input.ViewportId); else platform_io.Platform_SetWindowFocus(viewport); break; } case ImGuiTestInputType_ViewportSetPos: { if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) break; IM_ASSERT(engine->TestContext != nullptr); ImGuiViewport* viewport = ImGui::FindViewportByID(input.ViewportId); if (viewport == nullptr) engine->TestContext->LogError("ViewportPlatform_SetWindowPos(%08X): cannot find viewport anymore!", input.ViewportId); else if (platform_io.Platform_SetWindowPos == nullptr) engine->TestContext->LogError("ViewportPlatform_SetWindowPos(%08X): backend's Platform_SetWindowPos() is not set", input.ViewportId); else platform_io.Platform_SetWindowPos(viewport, input.ViewportPosSize); break; } case ImGuiTestInputType_ViewportSetSize: { if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) break; IM_ASSERT(engine->TestContext != nullptr); ImGuiViewport* viewport = ImGui::FindViewportByID(input.ViewportId); if (viewport == nullptr) engine->TestContext->LogError("ViewportPlatform_SetWindowSize(%08X): cannot find viewport anymore!", input.ViewportId); else if (platform_io.Platform_SetWindowPos == nullptr) engine->TestContext->LogError("ViewportPlatform_SetWindowSize(%08X): backend's Platform_SetWindowSize() is not set", input.ViewportId); else platform_io.Platform_SetWindowSize(viewport, input.ViewportPosSize); break; } case ImGuiTestInputType_ViewportClose: { if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) break; IM_ASSERT(engine->TestContext != nullptr); ImGuiViewport* viewport = ImGui::FindViewportByID(input.ViewportId); if (viewport == nullptr) engine->TestContext->LogError("ViewportPlatform_CloseWindow(%08X): cannot find viewport anymore!", input.ViewportId); else viewport->PlatformRequestClose = true; // FIXME: doesn't apply to actual backend break; } #else case ImGuiTestInputType_ViewportFocus: case ImGuiTestInputType_ViewportSetPos: case ImGuiTestInputType_ViewportSetSize: case ImGuiTestInputType_ViewportClose: break; #endif case ImGuiTestInputType_None: default: break; } } engine->Inputs.Queue.resize(0); } const int input_event_count_curr = g.InputEventsQueue.Size; for (int n = input_event_count_prev; n < input_event_count_curr; n++) g.InputEventsQueue[n].AddedByTestEngine = true; } // FIXME: Trying to abort a running GUI test won't kill the app immediately. static void ImGuiTestEngine_UpdateWatchdog(ImGuiTestEngine* engine, ImGuiContext* ui_ctx, double t0, double t1) { IM_UNUSED(ui_ctx); ImGuiTestContext* test_ctx = engine->TestContext; if (engine->IO.ConfigRunSpeed != ImGuiTestRunSpeed_Fast || ImOsIsDebuggerPresent()) return; if (test_ctx->RunFlags & ImGuiTestRunFlags_RunFromGui) return; const float timer_warn = engine->IO.ConfigWatchdogWarning; const float timer_kill_test = engine->IO.ConfigWatchdogKillTest; const float timer_kill_app = engine->IO.ConfigWatchdogKillApp; // Emit a warning and then fail the test after a given time. if (t0 < timer_warn && t1 >= timer_warn) { test_ctx->LogWarning("[Watchdog] Running time for '%s' is >%.f seconds, may be excessive.", test_ctx->Test->Name, timer_warn); } if (t0 < timer_kill_test && t1 >= timer_kill_test) { test_ctx->LogError("[Watchdog] Running time for '%s' is >%.f seconds, aborting.", test_ctx->Test->Name, timer_kill_test); IM_CHECK(false); } // Final safety watchdog in case the TestFunc is calling Yield() but never returning. // Note that we are not catching infinite loop cases where the TestFunc may be running but not yielding.. if (t0 < timer_kill_app + 5.0f && t1 >= timer_kill_app + 5.0f) { test_ctx->LogError("[Watchdog] Emergency process exit as the test didn't return."); exit(1); } } static void ImGuiTestEngine_PreNewFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx) { if (engine->UiContextTarget != ui_ctx) return; IM_ASSERT(ui_ctx == GImGui); ImGuiContext& g = *ui_ctx; engine->CaptureContext.PreNewFrame(); if (engine->ToolDebugRebootUiContext) { ImGuiTestEngine_RebootUiContext(engine); ui_ctx = engine->UiContextTarget; engine->ToolDebugRebootUiContext = false; } // Inject extra time into the Dear ImGui context if (engine->OverrideDeltaTime >= 0.0f) { ui_ctx->IO.DeltaTime = engine->OverrideDeltaTime; engine->OverrideDeltaTime = -1.0f; } // NewFrame() will increase this so we are +1 ahead at the time of calling this engine->FrameCount = g.FrameCount + 1; if (ImGuiTestContext* test_ctx = engine->TestContext) { double t0 = test_ctx->RunningTime; double t1 = t0 + ui_ctx->IO.DeltaTime; test_ctx->FrameCount++; test_ctx->RunningTime = t1; ImGuiTestEngine_UpdateWatchdog(engine, ui_ctx, t0, t1); } engine->PerfDeltaTime100.AddSample(g.IO.DeltaTime); engine->PerfDeltaTime500.AddSample(g.IO.DeltaTime); if (!ImGuiTestEngine_IsTestQueueEmpty(engine) && !engine->Abort) { // Abort testing by holding ESC // When running GuiFunc only main_io == simulated_io we test for a long hold. ImGuiIO& main_io = g.IO; for (auto& e : g.InputEventsQueue) if (e.Type == ImGuiInputEventType_Key && e.Key.Key == ImGuiKey_Escape) engine->Inputs.HostEscDown = e.Key.Down; engine->Inputs.HostEscDownDuration = engine->Inputs.HostEscDown ? (ImMax(engine->Inputs.HostEscDownDuration, 0.0f) + main_io.DeltaTime) : -1.0f; const bool abort = engine->Inputs.HostEscDownDuration >= 0.20f; if (abort) { if (engine->TestContext) engine->TestContext->LogWarning("User aborted (pressed ESC)"); ImGuiTestEngine_AbortCurrentTest(engine); } } else { engine->Inputs.HostEscDown = false; engine->Inputs.HostEscDownDuration = -1.0f; } ImGuiTestEngine_ApplyInputToImGuiContext(engine); ImGuiTestEngine_UpdateHooks(engine); } static void ImGuiTestEngine_PostNewFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx) { if (engine->UiContextTarget != ui_ctx) return; IM_ASSERT(ui_ctx == GImGui); // Set initial mouse position to a decent value on startup if (engine->FrameCount == 1) engine->Inputs.MousePosValue = ImGui::GetMainViewport()->Pos; engine->IO.IsCapturing = engine->CaptureContext.IsCapturing(); // Garbage collect unused tasks const int LOCATION_TASK_ELAPSE_FRAMES = 20; for (int task_n = 0; task_n < engine->InfoTasks.Size; task_n++) { ImGuiTestInfoTask* task = engine->InfoTasks[task_n]; if (task->FrameCount < engine->FrameCount - LOCATION_TASK_ELAPSE_FRAMES) { IM_DELETE(task); engine->InfoTasks.erase(engine->InfoTasks.Data + task_n); task_n--; } } // Slow down whole app if (engine->ToolSlowDown) ImThreadSleepInMilliseconds(engine->ToolSlowDownMs); // Call user GUI function ImGuiTestEngine_RunGuiFunc(engine); } static void ImGuiTestEngine_PreEndFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx) { IM_UNUSED(ui_ctx); // Call user Test Function // (process on-going queues in a coroutine) ImGuiTestEngine_RunTestFunc(engine); // Update hooks and output flags ImGuiTestEngine_UpdateHooks(engine); // Disable vsync engine->IO.IsRequestingMaxAppSpeed = engine->IO.ConfigNoThrottle; if (engine->IO.ConfigRunSpeed == ImGuiTestRunSpeed_Fast && engine->IO.IsRunningTests) if (engine->TestContext && (engine->TestContext->RunFlags & ImGuiTestRunFlags_GuiFuncOnly) == 0) engine->IO.IsRequestingMaxAppSpeed = true; } static void ImGuiTestEngine_PreRender(ImGuiTestEngine* engine, ImGuiContext* ui_ctx) { if (engine->UiContextTarget != ui_ctx) return; IM_ASSERT(ui_ctx == GImGui); engine->CaptureContext.PreRender(); } static void ImGuiTestEngine_PostRender(ImGuiTestEngine* engine, ImGuiContext* ui_ctx) { if (engine->UiContextTarget != ui_ctx) return; IM_ASSERT(ui_ctx == GImGui); // When test are running make sure real backend doesn't pick mouse cursor shape from tests. // (If were to instead set io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange in ImGuiTestEngine_RunTest() that would get us 99% of the way, // but unfortunately backend wouldn't restore normal shape after modified by OS decoration such as resize, so not enough..) ImGuiContext& g = *ui_ctx; if (!engine->IO.ConfigMouseDrawCursor && !g.IO.MouseDrawCursor && ImGuiTestEngine_IsUsingSimulatedInputs(engine)) g.MouseCursor = ImGuiMouseCursor_Arrow; // Check ImDrawData integrity // This is currently a very cheap operation but may later become slower we if e.g. check idx boundaries. #ifdef IMGUI_HAS_DOCK if (engine->IO.CheckDrawDataIntegrity) for (ImGuiViewport* viewport : ImGui::GetPlatformIO().Viewports) DrawDataVerifyMatchingBufferCount(viewport->DrawData); #else if (engine->IO.CheckDrawDataIntegrity) DrawDataVerifyMatchingBufferCount(ImGui::GetDrawData()); #endif engine->CaptureContext.PostRender(); } static void ImGuiTestEngine_RunGuiFunc(ImGuiTestEngine* engine) { ImGuiTestContext* ctx = engine->TestContext; if (ctx && ctx->Test->GuiFunc) { if (!(ctx->RunFlags & ImGuiTestRunFlags_GuiFuncDisable)) { ImGuiTestActiveFunc backup_active_func = ctx->ActiveFunc; ctx->ActiveFunc = ImGuiTestActiveFunc_GuiFunc; engine->TestContext->Test->GuiFunc(engine->TestContext); ctx->ActiveFunc = backup_active_func; } ImGuiTestEngine_ErrorRecoveryRun(engine); } if (ctx) ctx->FirstGuiFrame = false; } static void ImGuiTestEngine_RunTestFunc(ImGuiTestEngine* engine) { ImGuiContext* ui_ctx = engine->UiContextTarget; // Process on-going queues in a coroutine // Run the test coroutine. This will resume the test queue from either the last point the test called YieldFromCoroutine(), // or the loop in ImGuiTestEngine_TestQueueCoroutineMain that does so if no test is running. // If you want to breakpoint the point execution continues in the test code, breakpoint the exit condition in YieldFromCoroutine() const int input_queue_size_before = ui_ctx->InputEventsQueue.Size; engine->IO.CoroutineFuncs->RunFunc(engine->TestQueueCoroutine); // Events added by TestFunc() marked automaticaly to not be deleted if (engine->TestContext && (engine->TestContext->RunFlags & ImGuiTestRunFlags_EnableRawInputs)) for (int n = input_queue_size_before; n < ui_ctx->InputEventsQueue.Size; n++) ui_ctx->InputEventsQueue[n].AddedByTestEngine = true; } // Main function for the test coroutine static void ImGuiTestEngine_TestQueueCoroutineMain(void* engine_opaque) { ImGuiTestEngine* engine = (ImGuiTestEngine*)engine_opaque; while (!engine->TestQueueCoroutineShouldExit) { ImGuiTestEngine_ProcessTestQueue(engine); engine->IO.CoroutineFuncs->YieldFunc(); } } static void ImGuiTestEngine_DisableWindowInputs(ImGuiWindow* window) { window->DisableInputsFrames = 1; for (ImGuiWindow* child_window : window->DC.ChildWindows) ImGuiTestEngine_DisableWindowInputs(child_window); } // Yield control back from the TestFunc to the main update + GuiFunc, for one frame. void ImGuiTestEngine_Yield(ImGuiTestEngine* engine) { ImGuiTestContext* ctx = engine->TestContext; // Can only yield in the test func! if (ctx) { IM_ASSERT(ctx->ActiveFunc == ImGuiTestActiveFunc_TestFunc && "Can only yield inside TestFunc()!"); for (ImGuiWindow* window : ctx->ForeignWindowsToHide) { window->HiddenFramesForRenderOnly = 2; // Hide root window ImGuiTestEngine_DisableWindowInputs(window); // Disable inputs for root window and all it's children recursively } } engine->IO.CoroutineFuncs->YieldFunc(); } void ImGuiTestEngine_SetDeltaTime(ImGuiTestEngine* engine, float delta_time) { IM_ASSERT(delta_time >= 0.0f); engine->OverrideDeltaTime = delta_time; } int ImGuiTestEngine_GetFrameCount(ImGuiTestEngine* engine) { return engine->FrameCount; } const char* ImGuiTestEngine_GetStatusName(ImGuiTestStatus v) { static const char* names[ImGuiTestStatus_COUNT] = { "Unknown", "Success", "Queued", "Running", "Error", "Suspended" }; IM_STATIC_ASSERT(IM_ARRAYSIZE(names) == ImGuiTestStatus_COUNT); if (v >= 0 && v < IM_ARRAYSIZE(names)) return names[v]; return "N/A"; } const char* ImGuiTestEngine_GetRunSpeedName(ImGuiTestRunSpeed v) { static const char* names[ImGuiTestRunSpeed_COUNT] = { "Fast", "Normal", "Cinematic" }; IM_STATIC_ASSERT(IM_ARRAYSIZE(names) == ImGuiTestRunSpeed_COUNT); if (v >= 0 && v < IM_ARRAYSIZE(names)) return names[v]; return "N/A"; } const char* ImGuiTestEngine_GetVerboseLevelName(ImGuiTestVerboseLevel v) { static const char* names[ImGuiTestVerboseLevel_COUNT] = { "Silent", "Error", "Warning", "Info", "Debug", "Trace" }; IM_STATIC_ASSERT(IM_ARRAYSIZE(names) == ImGuiTestVerboseLevel_COUNT); if (v >= 0 && v < IM_ARRAYSIZE(names)) return names[v]; return "N/A"; } bool ImGuiTestEngine_CaptureScreenshot(ImGuiTestEngine* engine, ImGuiCaptureArgs* args) { if (engine->IO.ScreenCaptureFunc == nullptr) { IM_ASSERT(0); return false; } IM_ASSERT(engine->CaptureCurrentArgs == nullptr && "Nested captures are not supported."); // Graphics API must render a window so it can be captured // FIXME: This should work without this, as long as Present vs Vsync are separated (we need a Present, we don't need Vsync) const ImGuiTestRunSpeed backup_run_speed = engine->IO.ConfigRunSpeed; engine->IO.ConfigRunSpeed = ImGuiTestRunSpeed_Fast; const int frame_count = engine->FrameCount; // Because we rely on window->ContentSize for stitching, let 1 extra frame elapse to make sure any // windows which contents have changed in the last frame get a correct window->ContentSize value. // FIXME: Can remove this yield if not stitching if ((args->InFlags & ImGuiCaptureFlags_Instant) == 0) ImGuiTestEngine_Yield(engine); // This will yield until ImGuiTestEngine_PostSwap() -> ImGuiCaptureContext::CaptureUpdate() return false. // - CaptureUpdate() will call user provided test_io.ScreenCaptureFunc() function // - Capturing is likely to take multiple frames depending on settings. int frames_yielded = 0; engine->CaptureCurrentArgs = args; engine->PostSwapCalled = false; while (engine->CaptureCurrentArgs != nullptr) { ImGuiTestEngine_Yield(engine); frames_yielded++; if (frames_yielded > 4) IM_ASSERT(engine->PostSwapCalled && "ImGuiTestEngine_PostSwap() is not being called by application! Must be called in order."); } // Verify that the ImGuiCaptureFlags_Instant flag got honored if (args->InFlags & ImGuiCaptureFlags_Instant) IM_ASSERT(frame_count + 1 == engine->FrameCount); engine->IO.ConfigRunSpeed = backup_run_speed; return true; } bool ImGuiTestEngine_CaptureBeginVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args) { if (engine->IO.ScreenCaptureFunc == nullptr) { IM_ASSERT(0); return false; } IM_ASSERT(engine->CaptureCurrentArgs == nullptr && "Nested captures are not supported."); // RunSpeed set to Fast -> Switch to Cinematic, no throttle // RunSpeed set to Normal -> No change // RunSpeed set to Cinematic -> No change engine->BackupConfigRunSpeed = engine->IO.ConfigRunSpeed; engine->BackupConfigNoThrottle = engine->IO.ConfigNoThrottle; if (engine->IO.ConfigRunSpeed == ImGuiTestRunSpeed_Fast) { engine->IO.ConfigRunSpeed = ImGuiTestRunSpeed_Cinematic; engine->IO.ConfigNoThrottle = true; engine->IO.ConfigFixedDeltaTime = 1.0f / 60.0f; } engine->CaptureCurrentArgs = args; engine->CaptureContext.BeginVideoCapture(args); return true; } bool ImGuiTestEngine_CaptureEndVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args) { IM_UNUSED(args); IM_ASSERT(engine->CaptureContext.IsCapturingVideo() && "No video capture is in progress."); engine->CaptureContext.EndVideoCapture(); while (engine->CaptureCurrentArgs != nullptr) // Wait until last frame is captured and gif is saved. ImGuiTestEngine_Yield(engine); engine->IO.ConfigRunSpeed = engine->BackupConfigRunSpeed; engine->IO.ConfigNoThrottle = engine->BackupConfigNoThrottle; engine->IO.ConfigFixedDeltaTime = 0; engine->CaptureCurrentArgs = nullptr; return true; } static void ImGuiTestEngine_ProcessTestQueue(ImGuiTestEngine* engine) { // Avoid tracking scrolling in UI when running a single test const bool track_scrolling = (engine->TestsQueue.Size > 1) || (engine->TestsQueue.Size == 1 && (engine->TestsQueue[0].RunFlags & ImGuiTestRunFlags_RunFromCommandLine)); // Backup some state ImGuiIO& io = ImGui::GetIO(); const char* backup_ini_filename = io.IniFilename; ImGuiWindow* backup_nav_window = engine->UiContextTarget->NavWindow; io.IniFilename = nullptr; int ran_tests = 0; engine->BatchStartTime = ImTimeGetInMicroseconds(); engine->IO.IsRunningTests = true; for (int n = 0; n < engine->TestsQueue.Size; n++) { ImGuiTestRunTask* run_task = &engine->TestsQueue[n]; IM_ASSERT(run_task->Test->Output.Status == ImGuiTestStatus_Queued); // FIXME-TESTS: Blind mode not supported IM_ASSERT(engine->UiContextTarget != nullptr); IM_ASSERT(engine->UiContextActive == nullptr); engine->UiContextActive = engine->UiContextTarget; engine->UiSelectedTest = run_task->Test; if (track_scrolling) engine->UiSelectAndScrollToTest = run_task->Test; // Run test ImGuiTestEngine_RunTest(engine, nullptr, run_task->Test, run_task->RunFlags); // Cleanup IM_ASSERT(engine->TestContext == nullptr); IM_ASSERT(engine->UiContextActive == engine->UiContextTarget); engine->UiContextActive = nullptr; // Auto select the first error test //if (test->Status == ImGuiTestStatus_Error) // if (engine->UiSelectedTest == nullptr || engine->UiSelectedTest->Status != ImGuiTestStatus_Error) // engine->UiSelectedTest = test; ran_tests++; } engine->IO.IsRunningTests = false; engine->BatchEndTime = ImTimeGetInMicroseconds(); engine->Abort = false; engine->TestsQueue.clear(); // Restore UI state (done after all ImGuiTestEngine_RunTest() are done) if (ran_tests) { if (engine->IO.ConfigRestoreFocusAfterTests) ImGui::FocusWindow(backup_nav_window); } io.IniFilename = backup_ini_filename; } bool ImGuiTestEngine_IsTestQueueEmpty(ImGuiTestEngine* engine) { return engine->TestsQueue.Size == 0; } static bool ImGuiTestEngine_IsRunningTest(ImGuiTestEngine* engine, ImGuiTest* test) { for (ImGuiTestRunTask& t : engine->TestsQueue) if (t.Test == test) return true; return false; } void ImGuiTestEngine_QueueTest(ImGuiTestEngine* engine, ImGuiTest* test, ImGuiTestRunFlags run_flags) { if (ImGuiTestEngine_IsRunningTest(engine, test)) return; // Detect lack of signal from imgui context, most likely not compiled with IMGUI_ENABLE_TEST_ENGINE=1 // FIXME: Why is in this function? if (engine->UiContextTarget && engine->FrameCount < engine->UiContextTarget->FrameCount - 2) { ImGuiTestEngine_AbortCurrentTest(engine); IM_ASSERT(0 && "Not receiving signal from core library. Did you call ImGuiTestEngine_CreateContext() with the correct context? Did you compile imgui/ with IMGUI_ENABLE_TEST_ENGINE=1?"); test->Output.Status = ImGuiTestStatus_Error; return; } test->Output.Status = ImGuiTestStatus_Queued; ImGuiTestRunTask run_task; run_task.Test = test; run_task.RunFlags = run_flags; engine->TestsQueue.push_back(run_task); } // Called by IM_REGISTER_TEST(). Prefer calling IM_REGISTER_TEST() in your code so src_file/src_line are automatically passed. ImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* category, const char* name, const char* src_file, int src_line) { ImGuiTestGroup group = ImGuiTestGroup_Tests; if (strcmp(category, "perf") == 0) group = ImGuiTestGroup_Perfs; ImGuiTest* t = IM_NEW(ImGuiTest)(); t->Group = group; t->Category = category; t->Name = name; t->SourceFile = src_file; t->SourceLine = t->SourceLineEnd = src_line; engine->TestsAll.push_back(t); engine->TestsSourceLinesDirty = true; return t; } void ImGuiTestEngine_UnregisterTest(ImGuiTestEngine* engine, ImGuiTest* test) { // Cannot unregister a running test. Please contact us if you need this. if (engine->TestContext != nullptr) IM_ASSERT(engine->TestContext->Test != test); // Remove from lists bool found = engine->TestsAll.find_erase(test); IM_ASSERT(found); // Calling ImGuiTestEngine_UnregisterTest() on an unknown test. for (int n = 0; n < engine->TestsQueue.Size; n++) { ImGuiTestRunTask& task = engine->TestsQueue[n]; if (task.Test == test) { engine->TestsQueue.erase(&task); n--; } } if (engine->UiSelectAndScrollToTest == test) engine->UiSelectAndScrollToTest = nullptr; if (engine->UiSelectedTest == test) engine->UiSelectedTest = nullptr; engine->TestsSourceLinesDirty = true; IM_DELETE(test); } void ImGuiTestEngine_UnregisterAllTests(ImGuiTestEngine* engine) { // Cannot unregister a running test. Please contact us if you need this. IM_ASSERT(engine->TestContext == nullptr); engine->TestsAll.clear_delete(); engine->TestsQueue.clear(); engine->UiSelectAndScrollToTest = nullptr; engine->UiSelectedTest = nullptr; engine->TestsSourceLinesDirty = true; } ImGuiPerfTool* ImGuiTestEngine_GetPerfTool(ImGuiTestEngine* engine) { return engine->PerfTool; } // Filter tests by a specified query. Query is composed of one or more comma-separated filter terms optionally prefixed/suffixed with modifiers. // Available modifiers: // - '-' prefix excludes tests matched by the term. // - '^' prefix anchors term matching to the start of the string. // - '$' suffix anchors term matching to the end of the string. // Special keywords: // - "all" : all tests, no matter what group they are in. // - "tests" : tests in ImGuiTestGroup_Tests group. // - "perfs" : tests in ImGuiTestGroup_Perfs group. // Example queries: // - "" : empty query matches no tests. // - "^nav_" : all tests with name starting with "nav_". // - "_nav$" : all tests with name ending with "_nav". // - "-xxx" : all tests and perfs that do not contain "xxx". // - "tests,-scroll,-^nav_" : all tests (but no perfs) that do not contain "scroll" in their name and does not start with "nav_". // Note: while we borrowed ^ and $ from regex conventions, we do not support actual regex syntax except for behavior of these two modifiers. bool ImGuiTestEngine_PassFilter(ImGuiTest* test, const char* filter_specs) { IM_ASSERT(filter_specs != nullptr); auto str_iequal = [](const char* s1, const char* s2, const char* s2_end) { size_t s2_len = (size_t)(s2_end - s2); if (strlen(s1) != s2_len) return false; return ImStrnicmp(s1, s2, s2_len) == 0; }; auto str_iendswith = [&str_iequal](const char* s1, const char* s2, const char* s2_end) { size_t s1_len = strlen(s1); size_t s2_len = (size_t)(s2_end - s2); if (s1_len < s2_len) return false; s1 = s1 + s1_len - s2_len; return str_iequal(s1, s2, s2_end); }; bool include = false; const char* prefixes = "^-"; // When filter starts with exclude condition, we assume we have included all tests from the start. This enables // writing "-window" instead of "all,-window". for (int i = 0; filter_specs[i]; i++) if (filter_specs[i] == '-') include = true; // First filter is exclusion else if (strchr(prefixes, filter_specs[i]) == nullptr) break; // End of prefixes for (const char* filter_start = filter_specs; filter_start[0];) { // Filter modifiers bool is_exclude = false; bool is_anchor_to_start = false; bool is_anchor_to_end = false; for (;;) { if (filter_start[0] == '-') is_exclude = true; else if (filter_start[0] == '^') is_anchor_to_start = true; else break; filter_start++; } const char* filter_end = strstr(filter_start, ","); filter_end = filter_end ? filter_end : filter_start + strlen(filter_start); is_anchor_to_end = filter_end[-1] == '$'; if (is_anchor_to_end) filter_end--; if (str_iequal("all", filter_start, filter_end)) include = !is_exclude; else if (str_iequal("tests", filter_start, filter_end)) include = (test->Group == ImGuiTestGroup_Tests) ? !is_exclude : include; else if (str_iequal("perfs", filter_start, filter_end)) include = (test->Group == ImGuiTestGroup_Perfs) ? !is_exclude : include; else { // General filtering for (int n = 0; n < 2; n++) { const char* name = (n == 0) ? test->Name : test->Category; bool match = true; // "foo" - match a substring. if (!is_anchor_to_start && !is_anchor_to_end) match = ImStristr(name, nullptr, filter_start, filter_end) != nullptr; // "^foo" - match start of the string. // "foo$" - match end of the string. // FIXME: (minor) '^aaa$' will incorrectly match 'aaabbbaaa'. if (is_anchor_to_start) match &= ImStrnicmp(name, filter_start, filter_end - filter_start) == 0; if (is_anchor_to_end) match &= str_iendswith(name, filter_start, filter_end); if (match) { include = is_exclude ? false : true; break; } } } while (filter_end[0] == ',' || filter_end[0] == '$') filter_end++; filter_start = filter_end; } return include; } void ImGuiTestEngine_QueueTests(ImGuiTestEngine* engine, ImGuiTestGroup group, const char* filter_str, ImGuiTestRunFlags run_flags) { IM_ASSERT(group >= ImGuiTestGroup_Unknown && group < ImGuiTestGroup_COUNT); for (int n = 0; n < engine->TestsAll.Size; n++) { ImGuiTest* test = engine->TestsAll[n]; if (group != ImGuiTestGroup_Unknown && test->Group != group) continue; if (filter_str != nullptr) if (!ImGuiTestEngine_PassFilter(test, filter_str)) continue; ImGuiTestEngine_QueueTest(engine, test, run_flags); } } void ImGuiTestEngine_UpdateTestsSourceLines(ImGuiTestEngine* engine) { engine->TestsSourceLinesDirty = false; if (engine->TestsAll.empty()) return; struct TestAndSourceLine { ImGuiTest* Test; int SourceLine; }; ImPool> db; for (ImGuiTest* test : engine->TestsAll) { if (test->SourceFile == nullptr) continue; ImGuiID srcfile_hash = ImHashStr(test->SourceFile); ImVector* srcfile_tests = db.GetOrAddByKey(srcfile_hash); srcfile_tests->push_back({ test, test->SourceLine }); } int pool_size = db.GetMapSize(); for (int map_n = 0; map_n < pool_size; map_n++) if (ImVector* srcfile_tests = db.TryGetMapData(map_n)) { ImQsort(srcfile_tests->Data, (size_t)srcfile_tests->Size, sizeof(TestAndSourceLine), [](const void* lhs, const void* rhs) { return ((const TestAndSourceLine*)lhs)->SourceLine - ((const TestAndSourceLine*)rhs)->SourceLine; }); for (int test_n = 0; test_n < srcfile_tests->Size - 1; test_n++) { TestAndSourceLine& tasl = (*srcfile_tests)[test_n]; IM_ASSERT(tasl.Test->SourceLine == tasl.SourceLine); tasl.Test->SourceLineEnd = (*srcfile_tests)[test_n + 1].SourceLine - 1; } } } // count_remaining could be >0 if e.g. called during a crash handler or aborting a run. void ImGuiTestEngine_GetResultSummary(ImGuiTestEngine* engine, ImGuiTestEngineResultSummary* out_results) { int count_tested = 0; int count_success = 0; int count_remaining = 0; for (int n = 0; n < engine->TestsAll.Size; n++) { ImGuiTest* test = engine->TestsAll[n]; if (test->Output.Status == ImGuiTestStatus_Unknown) continue; if (test->Output.Status == ImGuiTestStatus_Queued) { count_remaining++; continue; } IM_ASSERT(test->Output.Status != ImGuiTestStatus_Running); count_tested++; if (test->Output.Status == ImGuiTestStatus_Success) count_success++; } out_results->CountTested = count_tested; out_results->CountSuccess = count_success; out_results->CountInQueue = count_remaining; } // Get a copy of the test list void ImGuiTestEngine_GetTestList(ImGuiTestEngine* engine, ImVector* out_tests) { *out_tests = engine->TestsAll; } // Get a copy of the test queue void ImGuiTestEngine_GetTestQueue(ImGuiTestEngine* engine, ImVector* out_tests) { *out_tests = engine->TestsQueue; } static void ImGuiTestEngine_UpdateHooks(ImGuiTestEngine* engine) { ImGuiContext* ui_ctx = engine->UiContextTarget; IM_ASSERT(ui_ctx->TestEngine == engine); bool want_hooking = false; //if (engine->TestContext != nullptr) // want_hooking = true; if (engine->InfoTasks.Size > 0) want_hooking = true; if (engine->FindByLabelTask.InSuffix != nullptr) want_hooking = true; if (engine->GatherTask.InParentID != 0) want_hooking = true; // Update test engine specific hooks ui_ctx->TestEngineHookItems = want_hooking; } struct ImGuiTestContextUiContextBackup { ImGuiIO IO; #if IMGUI_VERSION_NUM >= 19103 ImGuiPlatformIO PlatformIO; #endif ImGuiStyle Style; ImGuiDebugLogFlags DebugLogFlags; ImGuiKeyChord ConfigNavWindowingKeyNext; ImGuiKeyChord ConfigNavWindowingKeyPrev; #if IMGUI_VERSION_NUM >= 19123 ImGuiErrorCallback ErrorCallback; void* ErrorCallbackUserData; #endif void Backup(ImGuiContext& g) { IO = g.IO; #if IMGUI_VERSION_NUM >= 19103 PlatformIO = g.PlatformIO; #endif Style = g.Style; DebugLogFlags = g.DebugLogFlags; #if IMGUI_VERSION_NUM >= 18837 ConfigNavWindowingKeyNext = g.ConfigNavWindowingKeyNext; ConfigNavWindowingKeyPrev = g.ConfigNavWindowingKeyPrev; #endif #if IMGUI_VERSION_NUM >= 19123 ErrorCallback = g.ErrorCallback; ErrorCallbackUserData = g.ErrorCallbackUserData; #endif memset(IO.MouseDown, 0, sizeof(IO.MouseDown)); for (int n = 0; n < IM_ARRAYSIZE(IO.KeysData); n++) IO.KeysData[n].Down = false; } void Restore(ImGuiContext& g) { #if IMGUI_VERSION_NUM < 18993 IO.MetricsActiveAllocations = g.IO.MetricsActiveAllocations; #endif g.IO = IO; #if IMGUI_VERSION_NUM >= 19103 // FIXME: This will invalidate pointers platform_io.Monitors[]. // User is not expected to point to monitor ever, but some may do that.... //g.PlatformIO = PlatformIO; RestoreClipboardFuncs(g); // We only need to restore this for now. We'll find if we need more. #endif g.Style = Style; g.DebugLogFlags = DebugLogFlags; #if IMGUI_VERSION_NUM >= 18837 g.ConfigNavWindowingKeyNext = ConfigNavWindowingKeyNext; g.ConfigNavWindowingKeyPrev = ConfigNavWindowingKeyPrev; #endif #if IMGUI_VERSION_NUM >= 19123 g.ErrorCallback = ErrorCallback; g.ErrorCallbackUserData = ErrorCallbackUserData; #endif } void RestoreClipboardFuncs(ImGuiContext& g) { #if IMGUI_VERSION_NUM >= 19103 g.PlatformIO.Platform_GetClipboardTextFn = PlatformIO.Platform_GetClipboardTextFn; g.PlatformIO.Platform_SetClipboardTextFn = PlatformIO.Platform_SetClipboardTextFn; g.PlatformIO.Platform_ClipboardUserData = PlatformIO.Platform_ClipboardUserData; #else g.IO.GetClipboardTextFn = IO.GetClipboardTextFn; g.IO.SetClipboardTextFn = IO.SetClipboardTextFn; g.IO.ClipboardUserData = IO.ClipboardUserData; #endif } }; // FIXME: Work toward simplifying this function? void ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* parent_ctx, ImGuiTest* test, ImGuiTestRunFlags run_flags) { ImGuiTestContext stack_ctx; ImGuiCaptureArgs stack_capture_args; ImGuiTestContext* ctx; if (run_flags & ImGuiTestRunFlags_ShareTestContext) { // Reuse existing test context IM_ASSERT(parent_ctx != nullptr); ctx = parent_ctx; } else { // Create a test context ctx = &stack_ctx; ctx->Engine = engine; ctx->EngineIO = &engine->IO; ctx->Inputs = &engine->Inputs; ctx->CaptureArgs = &stack_capture_args; ctx->UserVars = nullptr; ctx->PerfStressAmount = engine->IO.PerfStressAmount; #ifdef IMGUI_HAS_DOCK ctx->HasDock = true; #else ctx->HasDock = false; #endif } ImGuiTestOutput* test_output; if (parent_ctx == nullptr) { ctx->Test = test; test_output = ctx->TestOutput = &test->Output; test_output->StartTime = ImTimeGetInMicroseconds(); } else { ctx->Test = parent_ctx->Test; test_output = ctx->TestOutput = parent_ctx->TestOutput; } if (engine->Abort) { test_output->Status = ImGuiTestStatus_Unknown; if (parent_ctx == nullptr) test_output->EndTime = test_output->StartTime; ctx->Test = nullptr; ctx->TestOutput = nullptr; ctx->CaptureArgs = nullptr; return; } test_output->Status = ImGuiTestStatus_Running; ctx->RunFlags = run_flags; ctx->UiContext = engine->UiContextActive; engine->TestContext = ctx; ImGuiTestEngine_UpdateHooks(engine); void* backup_user_vars = nullptr; ImGuiTestGenericVars backup_generic_vars; if (run_flags & ImGuiTestRunFlags_ShareVars) { // Share user vars and generic vars IM_CHECK_SILENT(parent_ctx != nullptr); IM_CHECK_SILENT(test->VarsSize == parent_ctx->Test->VarsSize); IM_CHECK_SILENT(test->VarsConstructor == parent_ctx->Test->VarsConstructor); IM_CHECK_SILENT(test->VarsPostConstructor == parent_ctx->Test->VarsPostConstructor); IM_CHECK_SILENT(test->VarsPostConstructorUserFn == parent_ctx->Test->VarsPostConstructorUserFn); IM_CHECK_SILENT(test->VarsDestructor == parent_ctx->Test->VarsDestructor); if ((run_flags & ImGuiTestRunFlags_ShareTestContext) == 0) { ctx->GenericVars = parent_ctx->GenericVars; ctx->UserVars = parent_ctx->UserVars; } } else { // Create user vars if (run_flags & ImGuiTestRunFlags_ShareTestContext) { backup_user_vars = parent_ctx->UserVars; backup_generic_vars = parent_ctx->GenericVars; } ctx->GenericVars.Clear(); if (test->VarsConstructor != nullptr) { ctx->UserVars = IM_ALLOC(test->VarsSize); memset(ctx->UserVars, 0, test->VarsSize); test->VarsConstructor(ctx->UserVars); if (test->VarsPostConstructor != nullptr && test->VarsPostConstructorUserFn != nullptr) test->VarsPostConstructor(ctx, ctx->UserVars, test->VarsPostConstructorUserFn); } } // Log header if (parent_ctx == nullptr) { ctx->LogEx(ImGuiTestVerboseLevel_Info, ImGuiTestLogFlags_NoHeader, "----------------------------------------------------------------------"); // Intentionally TTY only (just before clear: make it a flag?) test_output->Log.Clear(); ctx->LogWarning("Test: '%s' '%s'..", test->Category, test->Name); } else { ctx->LogWarning("Child Test: '%s' '%s'..", test->Category, test->Name); ctx->LogDebug("(ShareVars=%d ShareTestContext=%d)", (run_flags & ImGuiTestRunFlags_ShareVars) ? 1 : 0, (run_flags & ImGuiTestRunFlags_ShareTestContext) ? 1 : 0); } // Clear ImGui inputs to avoid key/mouse leaks from one test to another ImGuiTestEngine_ClearInput(engine); // Backup entire IO and style. Allows tests modifying them and not caring about restoring state. ImGuiTestContextUiContextBackup backup_ui_context; backup_ui_context.Backup(*ctx->UiContext); // Setup IO: software mouse cursor, viewport support ImGuiIO& io = ctx->UiContext->IO; if (engine->IO.ConfigMouseDrawCursor) io.MouseDrawCursor = true; #ifdef IMGUI_HAS_VIEWPORT // We always fill io.MouseHoveredViewport manually (maintained in ImGuiTestInputs::SimulatedIO) // so ensure we don't leave a chance to Dear ImGui to interpret things differently. // FIXME: As written, this would prevent tests from toggling ImGuiConfigFlags_ViewportsEnable and have correct value for ImGuiBackendFlags_HasMouseHoveredViewport if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; else io.BackendFlags &= ~ImGuiBackendFlags_HasMouseHoveredViewport; #endif // Setup IO: override clipboard if ((ctx->RunFlags & ImGuiTestRunFlags_GuiFuncOnly) == 0) { #if IMGUI_VERSION_NUM >= 19103 ImGuiPlatformIO& platform_io = ctx->UiContext->PlatformIO; platform_io.Platform_GetClipboardTextFn = [](ImGuiContext* ui_ctx) -> const char* { ImGuiTestContext* ctx = (ImGuiTestContext*)ui_ctx->PlatformIO.Platform_ClipboardUserData; return ctx->Clipboard.empty() ? "" : ctx->Clipboard.Data; }; platform_io.Platform_SetClipboardTextFn = [](ImGuiContext* ui_ctx, const char* text) { ImGuiTestContext* ctx = (ImGuiTestContext*)ui_ctx->PlatformIO.Platform_ClipboardUserData; ctx->Clipboard.resize((int)strlen(text) + 1); strcpy(ctx->Clipboard.Data, text); }; platform_io.Platform_ClipboardUserData = ctx; #else io.GetClipboardTextFn = [](void* user_data) -> const char* { ImGuiTestContext* ctx = (ImGuiTestContext*)user_data; return ctx->Clipboard.empty() ? "" : ctx->Clipboard.Data; }; io.SetClipboardTextFn = [](void* user_data, const char* text) { ImGuiTestContext* ctx = (ImGuiTestContext*)user_data; ctx->Clipboard.resize((int)strlen(text) + 1); strcpy(ctx->Clipboard.Data, text); }; io.ClipboardUserData = ctx; #endif } // Setup IO: error handling ImGuiTestEngine_ErrorRecoverySetup(engine); // Mark as currently running the TestFunc (this is the only time when we are allowed to yield) IM_ASSERT(ctx->ActiveFunc == ImGuiTestActiveFunc_None || ctx->ActiveFunc == ImGuiTestActiveFunc_TestFunc); ImGuiTestActiveFunc backup_active_func = ctx->ActiveFunc; ctx->ActiveFunc = ImGuiTestActiveFunc_TestFunc; ctx->FirstGuiFrame = (test->GuiFunc != nullptr) ? true : false; ctx->FrameCount = parent_ctx ? parent_ctx->FrameCount : 0; ctx->ErrorCounter = 0; ctx->SetRef(""); ctx->SetInputMode(ImGuiInputSource_Mouse); ctx->UiContext->NavInputSource = ImGuiInputSource_Keyboard; ctx->Clipboard.clear(); // Warm up GUI // - We need one mandatory frame running GuiFunc before running TestFunc // - We add a second frame, to avoid running tests while e.g. windows are typically appearing for the first time, hidden, // measuring their initial size. Most tests are going to be more meaningful with this stabilized base. if (!(test->Flags & ImGuiTestFlags_NoGuiWarmUp)) { ctx->FrameCount -= 2; ctx->Yield(); if (test_output->Status == ImGuiTestStatus_Running) // To allow GuiFunc calling Finish() in first frame ctx->Yield(); } ctx->FirstTestFrameCount = ctx->FrameCount; // Call user test function (optional) if (ctx->RunFlags & ImGuiTestRunFlags_GuiFuncOnly) { // No test function while (!engine->Abort && test_output->Status == ImGuiTestStatus_Running) ctx->Yield(); } else { if (test->TestFunc) { // Test function test->TestFunc(ctx); // In case test failed without finishing gif capture - finish it here. This may trigger due to user error or // due to IM_SUSPEND_TESTFUNC() terminating TestFunc() early. if (engine->CaptureContext.IsCapturingVideo()) { ImGuiCaptureArgs* args = engine->CaptureCurrentArgs; ImGuiTestEngine_CaptureEndVideo(engine, args); //ImFileDelete(args->OutSavedFileName); ctx->LogWarning("Recovered from missing CaptureEndVideo()"); } } else { // No test function if (test->Flags & ImGuiTestFlags_NoAutoFinish) while (!engine->Abort && test_output->Status == ImGuiTestStatus_Running) ctx->Yield(); } // Capture failure screenshot. if (ctx->IsError() && engine->IO.ConfigCaptureOnError) { // FIXME-VIEWPORT: Tested windows may be in their own viewport. This only captures everything in main viewport. Capture tool may be extended to capture viewport windows as well. This would leave out OS windows which may be a cause of failure. ImGuiCaptureArgs args; args.InFlags = ImGuiCaptureFlags_Instant; args.InCaptureRect.Min = ImGui::GetMainViewport()->Pos; args.InCaptureRect.Max = args.InCaptureRect.Min + ImGui::GetMainViewport()->Size; ImFormatString(args.InOutputFile, IM_ARRAYSIZE(args.InOutputFile), "output/failures/%s_%04d.png", ctx->Test->Name, ctx->ErrorCounter); if (ImGuiTestEngine_CaptureScreenshot(engine, &args)) ctx->LogDebug("Saved '%s' (%d*%d pixels)", args.InOutputFile, (int)args.OutImageSize.x, (int)args.OutImageSize.y); } // Recover missing End*/Pop* calls. ImGuiTestEngine_ErrorRecoveryRun(engine); if (engine->IO.ConfigRunSpeed != ImGuiTestRunSpeed_Fast) ctx->SleepStandard(); // Stop in GuiFunc mode if (engine->IO.ConfigKeepGuiFunc && ctx->IsError()) { // Position mouse cursor ctx->UiContext->IO.WantSetMousePos = true; ctx->UiContext->IO.MousePos = engine->Inputs.MousePosValue; // Restore backend clipboard functions backup_ui_context.RestoreClipboardFuncs(*ctx->UiContext); // Unhide foreign windows (may be useful sometimes to inspect GuiFunc state... sometimes not) //ctx->ForeignWindowsUnhideAll(); } // Keep GuiFunc spinning // FIXME-TESTS: after an error, this is not visible in the UI because status is not _Running anymore... if (engine->IO.ConfigKeepGuiFunc) { if (engine->TestsQueue.Size == 1 || test_output->Status == ImGuiTestStatus_Error) { #if IMGUI_VERSION_NUM >= 18992 ImGui::TeleportMousePos(engine->Inputs.MousePosValue); #endif while (engine->IO.ConfigKeepGuiFunc && !engine->Abort) { ctx->RunFlags |= ImGuiTestRunFlags_GuiFuncOnly; ctx->Yield(); } } } } IM_ASSERT(engine->CaptureCurrentArgs == nullptr && "Active capture was not terminated in the test code."); // Process and display result/status test_output->EndTime = ImTimeGetInMicroseconds(); if (test_output->Status == ImGuiTestStatus_Running) test_output->Status = ImGuiTestStatus_Success; if (engine->Abort && test_output->Status != ImGuiTestStatus_Error) test_output->Status = ImGuiTestStatus_Unknown; // Log result if (test_output->Status == ImGuiTestStatus_Success) { if ((ctx->RunFlags & ImGuiTestRunFlags_NoSuccessMsg) == 0) ctx->LogInfo("Success."); } else if (engine->Abort) ctx->LogWarning("Aborted."); else if (test_output->Status == ImGuiTestStatus_Error) ctx->LogError("%s test failed.", test->Name); else ctx->LogWarning("Unknown status."); // Additional yields to avoid consecutive tests who may share identifiers from missing their window/item activation. ctx->RunFlags |= ImGuiTestRunFlags_GuiFuncDisable; ctx->Yield(3); // Restore active func ctx->ActiveFunc = backup_active_func; if (parent_ctx) parent_ctx->FrameCount = ctx->FrameCount; // Restore backed up IO and style backup_ui_context.Restore(*ctx->UiContext); if (run_flags & ImGuiTestRunFlags_ShareVars) { // Share generic vars? if ((run_flags & ImGuiTestRunFlags_ShareTestContext) == 0) parent_ctx->GenericVars = ctx->GenericVars; } else { // Destruct user vars if (test->VarsConstructor != nullptr) { test->VarsDestructor(ctx->UserVars); if (ctx->UserVars) IM_FREE(ctx->UserVars); ctx->UserVars = nullptr; } if (run_flags & ImGuiTestRunFlags_ShareTestContext) { parent_ctx->UserVars = backup_user_vars; parent_ctx->GenericVars = backup_generic_vars; } } // 'ctx' at this point is either a local variable or shared with parent. //ctx->Test = nullptr; //ctx->TestOutput = nullptr; //ctx->CaptureArgs = nullptr; IM_ASSERT(engine->TestContext == ctx); engine->TestContext = parent_ctx; } #if IMGUI_VERSION_NUM < 19123 static void LogAsWarningFunc(void* user_data, const char* fmt, ...) { ImGuiTestContext* ctx = (ImGuiTestContext*)user_data; va_list args; va_start(args, fmt); ctx->LogExV(ImGuiTestVerboseLevel_Warning, ImGuiTestLogFlags_None, fmt, args); va_end(args); } static void LogAsDebugFunc(void* user_data, const char* fmt, ...) { ImGuiTestContext* ctx = (ImGuiTestContext*)user_data; va_list args; va_start(args, fmt); ctx->LogExV(ImGuiTestVerboseLevel_Debug, ImGuiTestLogFlags_None, fmt, args); va_end(args); } #else static void LogAsWarningFunc(ImGuiContext*, void* user_data, const char* msg) { ImGuiContext& g = *GImGui; ImGuiTestContext* ctx = (ImGuiTestContext*)user_data; ImGuiWindow* window = g.CurrentWindow; ctx->LogEx(ImGuiTestVerboseLevel_Warning, ImGuiTestLogFlags_None, "In '%s': %s", window ? window->Name : "nullptr", msg); } static void LogAsDebugFunc(ImGuiContext*, void* user_data, const char* msg) { ImGuiContext& g = *GImGui; ImGuiTestContext* ctx = (ImGuiTestContext*)user_data; ImGuiWindow* window = g.CurrentWindow; ctx->LogEx(ImGuiTestVerboseLevel_Debug, ImGuiTestLogFlags_None, "In '%s': %s", window ? window->Name : "nullptr", msg); } #endif void ImGuiTestEngine_ErrorRecoverySetup(ImGuiTestEngine* engine) { ImGuiTestContext* ctx = engine->TestContext; IM_ASSERT(ctx != nullptr); IM_ASSERT(ctx->Test != nullptr); #if IMGUI_VERSION_NUM >= 19123 if ((ctx->Test->Flags & ImGuiTestFlags_NoRecoveryWarnings) == 0) { ctx->UiContext->ErrorCallback = LogAsWarningFunc; ctx->UiContext->ErrorCallbackUserData = ctx; } else { ctx->UiContext->ErrorCallback = LogAsDebugFunc; ctx->UiContext->ErrorCallbackUserData = ctx; } ctx->UiContext->IO.ConfigErrorRecoveryEnableAssert = ((ctx->Test->Flags & ImGuiTestFlags_NoRecoveryWarnings) == 0 && ctx->TestOutput->Status != ImGuiTestStatus_Error); #else IM_UNUSED(ctx); #endif } void ImGuiTestEngine_ErrorRecoveryRun(ImGuiTestEngine* engine) { ImGuiTestContext* ctx = engine->TestContext; IM_ASSERT(ctx != nullptr); IM_ASSERT(ctx->Test != nullptr); ImGuiTestEngine_ErrorRecoverySetup(engine); #if IMGUI_VERSION_NUM < 19123 // If we are _already_ in a test error state, recovering is normal so we'll hide the log. const bool verbose = (ctx->TestOutput->Status != ImGuiTestStatus_Error) || (engine->IO.ConfigVerboseLevel >= ImGuiTestVerboseLevel_Debug); if (verbose && (ctx->Test->Flags & ImGuiTestFlags_NoRecoveryWarnings) == 0) ImGui::ErrorCheckEndFrameRecover(LogAsWarningFunc, ctx); else ImGui::ErrorCheckEndFrameRecover(LogAsDebugFunc, ctx); #else // This would automatically be done in EndFrame() but doing it here means we get a report earlier and in the right co-routine. // And the state we entered in happens to be the NewFrame() state (hence using g.StackSizesInNewFrame) ImGuiContext& g = *GImGui; ImGui::ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame); #endif } //------------------------------------------------------------------------- // [SECTION] CRASH HANDLING //------------------------------------------------------------------------- // - ImGuiTestEngine_CrashHandler() // - ImGuiTestEngine_InstallDefaultCrashHandler() //------------------------------------------------------------------------- void ImGuiTestEngine_CrashHandler() { ImGuiContext& g = *GImGui; ImGuiTestEngine* engine = (ImGuiTestEngine*)g.TestEngine; ImGuiTest* crashed_test = (engine->TestContext && engine->TestContext->Test) ? engine->TestContext->Test : nullptr; ImOsConsoleSetTextColor(ImOsConsoleStream_StandardError, ImOsConsoleTextColor_BrightRed); if (crashed_test != nullptr) fprintf(stderr, "**ImGuiTestEngine_CrashHandler()** Crashed while running \"%s\" :(\n", crashed_test->Name); else fprintf(stderr, "**ImGuiTestEngine_CrashHandler()** Crashed :(\n"); static bool handled = false; if (handled) return; handled = true; // Write stop times, because thread executing tests will no longer run. engine->BatchEndTime = ImTimeGetInMicroseconds(); if (crashed_test && crashed_test->Output.Status == ImGuiTestStatus_Running) { crashed_test->Output.Status = ImGuiTestStatus_Error; crashed_test->Output.EndTime = engine->BatchEndTime; } // Export test run results. ImGuiTestEngine_Export(engine); ImGuiTestEngine_PrintResultSummary(engine); } #ifdef _WIN32 static LONG WINAPI ImGuiTestEngine_CrashHandlerWin32(LPEXCEPTION_POINTERS) { ImGuiTestEngine_CrashHandler(); return EXCEPTION_EXECUTE_HANDLER; } #else static void ImGuiTestEngine_CrashHandlerUnix(int signal) { IM_UNUSED(signal); ImGuiTestEngine_CrashHandler(); abort(); } #endif void ImGuiTestEngine_InstallDefaultCrashHandler() { #ifdef _WIN32 SetUnhandledExceptionFilter(&ImGuiTestEngine_CrashHandlerWin32); #elif !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE // Install a crash handler to relevant signals. struct sigaction action = {}; action.sa_handler = ImGuiTestEngine_CrashHandlerUnix; action.sa_flags = SA_SIGINFO; sigaction(SIGILL, &action, nullptr); sigaction(SIGABRT, &action, nullptr); sigaction(SIGFPE, &action, nullptr); sigaction(SIGSEGV, &action, nullptr); sigaction(SIGPIPE, &action, nullptr); sigaction(SIGBUS, &action, nullptr); #endif } //------------------------------------------------------------------------- // [SECTION] HOOKS FOR CORE LIBRARY //------------------------------------------------------------------------- // - ImGuiTestEngineHook_ItemAdd() // - ImGuiTestEngineHook_ItemAdd_GatherTask() // - ImGuiTestEngineHook_ItemInfo() // - ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel() // - ImGuiTestEngineHook_Log() // - ImGuiTestEngineHook_AssertFunc() //------------------------------------------------------------------------- // This is rather slow at it runs on all items but only during a GatherItems() operations. static void ImGuiTestEngineHook_ItemAdd_GatherTask(ImGuiContext* ui_ctx, ImGuiTestEngine* engine, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data) { ImGuiContext& g = *ui_ctx; ImGuiWindow* window = g.CurrentWindow; ImGuiTestGatherTask* task = &engine->GatherTask; if ((task->InLayerMask & (1 << window->DC.NavLayerCurrent)) == 0) return; const ImGuiID parent_id = window->IDStack.Size ? window->IDStack.back() : 0; const ImGuiID gather_parent_id = task->InParentID; int result_depth = -1; if (gather_parent_id == parent_id) { result_depth = 0; } else { const int max_depth = task->InMaxDepth; // When using a 'PushID(label); Widget(""); PopID();` pattern flatten as 1 deep instead of 2 for simplicity. // We do this by offsetting our depth level. int curr_depth = (id == parent_id) ? -1 : 0; ImGuiWindow* curr_window = window; while (result_depth == -1 && curr_window != nullptr) { const int id_stack_size = curr_window->IDStack.Size; for (ImGuiID* p_id_stack = curr_window->IDStack.Data + id_stack_size - 1; p_id_stack >= curr_window->IDStack.Data; p_id_stack--, curr_depth++) { if (curr_depth >= max_depth) break; if (*p_id_stack == gather_parent_id) { result_depth = curr_depth; break; } } // Recurse in child (could be policy/option in GatherTask) if (curr_window->Flags & ImGuiWindowFlags_ChildWindow) curr_window = curr_window->ParentWindow; else curr_window = nullptr; } } if (result_depth != -1) { ImGuiTestItemInfo* item = task->OutList->Pool.GetOrAddByKey(id); // Add item->TimestampMain = engine->FrameCount; item->ID = id; item->ParentID = parent_id; item->Window = window; item->RectFull = item->RectClipped = bb; item->RectClipped.ClipWithFull(window->ClipRect); // This two step clipping is important, we want RectClipped to stays within RectFull item->RectClipped.ClipWithFull(item->RectFull); item->NavLayer = window->DC.NavLayerCurrent; item->Depth = result_depth; #if IMGUI_VERSION_NUM >= 19135 item->ItemFlags = item_data ? item_data->ItemFlags : ImGuiItemFlags_None; #else item->ItemFlags = item_data ? item_data->InFlags : ImGuiItemFlags_None; #endif item->StatusFlags = item_data ? item_data->StatusFlags : ImGuiItemStatusFlags_None; task->LastItemInfo = item; } } void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data) { ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine; engine->UiContextHasHooks = true; IM_ASSERT(id != 0); ImGuiContext& g = *ui_ctx; ImGuiWindow* window = g.CurrentWindow; // FIXME-OPT: Early out if there are no active Info/Gather tasks. // Info Tasks if (ImGuiTestInfoTask* task = ImGuiTestEngine_FindInfoTask(engine, id)) { ImGuiTestItemInfo* item = &task->Result; item->TimestampMain = engine->FrameCount; item->ID = id; item->ParentID = window->IDStack.Size ? window->IDStack.back() : 0; item->Window = window; item->RectFull = item->RectClipped = bb; item->RectClipped.ClipWithFull(window->ClipRect); // This two step clipping is important, we want RectClipped to stays within RectFull item->RectClipped.ClipWithFull(item->RectFull); item->NavLayer = window->DC.NavLayerCurrent; item->Depth = 0; #if IMGUI_VERSION_NUM >= 19135 item->ItemFlags = item_data ? item_data->ItemFlags : ImGuiItemFlags_None; #else item->ItemFlags = item_data ? item_data->InFlags : ImGuiItemFlags_None; #endif item->StatusFlags = item_data ? item_data->StatusFlags : ImGuiItemStatusFlags_None; } // Gather Task (only 1 can be active) if (engine->GatherTask.InParentID != 0) ImGuiTestEngineHook_ItemAdd_GatherTask(ui_ctx, engine, id, bb, item_data); } #if IMGUI_VERSION_NUM < 18934 void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, const ImRect& bb, ImGuiID id) { ImGuiTestEngineHook_ItemAdd(ui_ctx, id, bb, nullptr); } #endif // Task is submitted in TestFunc by ItemInfo() -> ItemInfoHandleWildcardSearch() #ifdef IMGUI_HAS_IMSTR static void ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel(ImGuiContext* ui_ctx, ImGuiID id, const ImStrv label, ImGuiItemStatusFlags flags) #else static void ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel(ImGuiContext* ui_ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags) #endif { // At this point "label" is a match for the right-most name in user wildcard (e.g. the "bar" of "**/foo/bar" ImGuiContext& g = *ui_ctx; ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine; IM_UNUSED(label); // Match ABI of caller function (faster call) // Test for matching status flags ImGuiTestFindByLabelTask* label_task = &engine->FindByLabelTask; if (ImGuiItemStatusFlags filter_flags = label_task->InFilterItemStatusFlags) if (!(filter_flags & flags)) return; // Test for matching PREFIX (the "window" of "window/**/foo/bar" or the "" of "/**/foo/bar") // FIXME-TESTS: Stack depth limit? // FIXME-TESTS: Recurse back into parent window limit? bool match_prefix = false; if (label_task->InPrefixId == 0) { match_prefix = true; } else { // Recurse back into parent, so from "WindowA" with SetRef("WindowA") it is possible to use "**/Button" to reach "WindowA/ChildXXXX/Button" for (ImGuiWindow* window = g.CurrentWindow; window != nullptr && !match_prefix; window = window->ParentWindow) { const int id_stack_size = window->IDStack.Size; for (ImGuiID* p_id_stack = window->IDStack.Data + id_stack_size - 1; p_id_stack >= window->IDStack.Data; p_id_stack--) if (*p_id_stack == label_task->InPrefixId) { match_prefix = true; break; } } } if (!match_prefix) return; // Test for full matching SUFFIX (the "foo/bar" or "window/**/foo/bar") // Because at this point we have only compared the prefix and the right-most label (the "window" and "bar" or "window/**/foo/bar") // FIXME-TESTS: The entire suffix must be inside the final window: // - In theory, someone could craft a suffix that contains sub-window, e.g. "SomeWindow/**/SomeChild_XXXX/SomeItem" and this will fail. // - Once we make child path easier to access we can fix that. if (label_task->InSuffixDepth > 1) // This is merely an early out: for Depth==1 the compare has already been done in ImGuiTestEngineHook_ItemInfo() { ImGuiWindow* window = g.CurrentWindow; const int id_stack_size = window->IDStack.Size; int id_stack_pos = id_stack_size - label_task->InSuffixDepth; // At this point, IN MOST CASES (BUT NOT ALL) this should be the case: // ImHashStr(label, 0, g.CurrentWindow->IDStack.back()) == id // It's not always the case as we have situations where we call IMGUI_TEST_ENGINE_ITEM_INFO() outside of the right stack location: // e.g. Begin(), or items using the PushID(label); SubItem(""); PopID(); idiom. // If you are curious or need to understand this more in depth, uncomment this assert to detect them: // ImGuiID tmp_id = ImHashStr(label, 0, g.CurrentWindow->IDStack.back()); // IM_ASSERT(tmp_id == id); // The "Try with parent" case is designed to handle that. May need further tuning. ImGuiID base_id = id_stack_pos >= 0 ? window->IDStack.Data[id_stack_pos] : 0; // base_id correspond to the "**" ImGuiID find_id = ImHashDecoratedPath(label_task->InSuffix, nullptr, base_id); // hash the whole suffix e.g. "foo/bar" over our base if (id != find_id) { // Try with parent base_id = id_stack_pos > 0 ? window->IDStack.Data[id_stack_pos - 1] : 0; find_id = ImHashDecoratedPath(label_task->InSuffix, nullptr, base_id); if (id != find_id) return; } } // Success label_task->OutItemId = id; } // label is optional #ifdef IMGUI_HAS_IMSTR void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, ImStrv label, ImGuiItemStatusFlags flags) #else void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags) #endif { ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine; IM_ASSERT(id != 0); ImGuiContext& g = *ui_ctx; //ImGuiWindow* window = g.CurrentWindow; //IM_ASSERT(window->DC.LastItemId == id || window->DC.LastItemId == 0); // Need _ItemAdd() to be submitted before _ItemInfo() // Update Info Task status flags if (ImGuiTestInfoTask* task = ImGuiTestEngine_FindInfoTask(engine, id)) { ImGuiTestItemInfo* item = &task->Result; item->TimestampStatus = g.FrameCount; item->StatusFlags = flags; if (label) ImStrncpy(item->DebugLabel, label, IM_ARRAYSIZE(item->DebugLabel)); } // Update Gather Task status flags if (engine->GatherTask.LastItemInfo && engine->GatherTask.LastItemInfo->ID == id) { ImGuiTestItemInfo* item = engine->GatherTask.LastItemInfo; item->TimestampStatus = g.FrameCount; item->StatusFlags = flags; if (label) ImStrncpy(item->DebugLabel, label, IM_ARRAYSIZE(item->DebugLabel)); } // Update Find by Label Task // FIXME-TESTS FIXME-OPT: Compare by hashes instead of strcmp to support "###" operator. // Perhaps we could use strcmp() if we detect that ### is not used, that would be faster. ImGuiTestFindByLabelTask* label_task = &engine->FindByLabelTask; if (label && label_task->InSuffixLastItem && label_task->OutItemId == 0) #ifdef IMGUI_HAS_IMSTR if (label_task->InSuffixLastItemHash == ImHashStr(label)) #else if (label_task->InSuffixLastItemHash == ImHashStr(label, 0)) #endif ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel(ui_ctx, id, label, flags); } // Forward core/user-land text to test log // This is called via the user-land IMGUI_TEST_ENGINE_LOG() macro. void ImGuiTestEngineHook_Log(ImGuiContext* ui_ctx, const char* fmt, ...) { ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine; if (engine == nullptr || engine->TestContext == nullptr) return; va_list args; va_start(args, fmt); engine->TestContext->LogExV(ImGuiTestVerboseLevel_Debug, ImGuiTestLogFlags_None, fmt, args); va_end(args); } // Helper to output extra information (e.g. current test) during an assert. // Your custom assert code may optionally want to call this. void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* function, int line) { if (ImGuiTestEngine* engine = GImGuiTestEngine) if (ImGuiTestContext* ctx = engine->TestContext) { ctx->LogError("Assert: '%s'", expr); ctx->LogWarning("In %s:%d, function %s()", file, line, function); if (ImGuiTest* test = ctx->Test) ctx->LogWarning("While running test: %s %s", test->Category, test->Name); } } // Used by IM_CHECK_OP() macros ImGuiTextBuffer* ImGuiTestEngine_GetTempStringBuilder() { static ImGuiTextBuffer builder; builder.Buf.resize(1); builder.Buf[0] = 0; return &builder; } // Out of convenience for main library we allow this to be called before TestEngine is initialized. const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ui_ctx, ImGuiID id) { if (ui_ctx->TestEngine == nullptr || id == 0) return nullptr; if (ImGuiTestItemInfo* id_info = ImGuiTestEngine_FindItemInfo((ImGuiTestEngine*)ui_ctx->TestEngine, id, "")) return id_info->DebugLabel; return nullptr; } //------------------------------------------------------------------------- // [SECTION] CHECK/ERROR FUNCTIONS FOR TESTS //------------------------------------------------------------------------- // - ImGuiTestEngine_Check() // - ImGuiTestEngine_Error() //------------------------------------------------------------------------- // Return true to request a debugger break bool ImGuiTestEngine_Check(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, bool result, const char* expr) { ImGuiTestEngine* engine = GImGuiTestEngine; (void)func; // Removed absolute path from output so we have deterministic output (otherwise __FILE__ gives us machine dending output) const char* file_without_path = file ? ImPathFindFilename(file) : ""; if (ImGuiTestContext* ctx = engine->TestContext) { ImGuiTest* test = ctx->Test; //ctx->LogDebug("IM_CHECK(%s)", expr); if (!result) { if (!(ctx->RunFlags & ImGuiTestRunFlags_GuiFuncOnly)) test->Output.Status = ImGuiTestStatus_Error; if (file) ctx->LogError("Error %s:%d '%s'", file_without_path, line, expr); else ctx->LogError("Error '%s'", expr); ctx->ErrorCounter++; } else if (!(flags & ImGuiTestCheckFlags_SilentSuccess)) { if (file) ctx->LogInfo("OK %s:%d '%s'", file_without_path, line, expr); else ctx->LogInfo("OK '%s'", expr); } } else { IM_ASSERT(0 && "No active tests!"); } if (result == false && engine->IO.ConfigStopOnError && !engine->Abort) engine->Abort = true; //ImGuiTestEngine_Abort(engine); if (result == false && engine->IO.ConfigBreakOnError && !engine->Abort) return true; return false; } bool ImGuiTestEngine_CheckStrOp(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* op, const char* lhs_var, const char* lhs_value, const char* rhs_var, const char* rhs_value, bool* out_res) { int res_strcmp = strcmp(lhs_value, rhs_value); bool res = 0; if (strcmp(op, "==") == 0) res = (res_strcmp == 0); else if (strcmp(op, "!=") == 0) res = (res_strcmp != 0); else IM_ASSERT(0); *out_res = res; ImGuiTextBuffer buf; // FIXME-OPT: Now we can probably remove that allocation bool lhs_is_literal = lhs_var[0] == '\"'; bool rhs_is_literal = rhs_var[0] == '\"'; if (strchr(lhs_value, '\n') != nullptr || strchr(rhs_value, '\n') != nullptr) { // Multi line strings size_t lhs_value_len = strlen(lhs_value); size_t rhs_value_len = strlen(rhs_value); if (lhs_value_len > 0 && lhs_value[lhs_value_len - 1] == '\n') // Strip trailing carriage return as we are adding one ourselves lhs_value_len--; if (rhs_value_len > 0 && rhs_value[rhs_value_len - 1] == '\n') rhs_value_len--; buf.appendf( "\n" "---------------------------------------- // lhs: %s\n" "%.*s\n" "---------------------------------------- // rhs: %s, compare op: %s\n" "%.*s\n" "----------------------------------------\n", lhs_is_literal ? "literal" : lhs_var, (int)lhs_value_len, lhs_value, rhs_is_literal ? "literal" : rhs_var, op, (int)rhs_value_len, rhs_value); } else { // Single line strings buf.appendf( "%s [\"%s\"] %s %s [\"%s\"]", lhs_is_literal ? "" : lhs_var, lhs_value, op, rhs_is_literal ? "" : rhs_var, rhs_value); } return ImGuiTestEngine_Check(file, func, line, flags, res, buf.c_str()); } bool ImGuiTestEngine_Error(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* fmt, ...) { va_list args; va_start(args, fmt); Str256 buf; buf.setfv(fmt, args); bool ret = ImGuiTestEngine_Check(file, func, line, flags, false, buf.c_str()); va_end(args); ImGuiTestEngine* engine = GImGuiTestEngine; if (engine && engine->Abort) return false; return ret; } //------------------------------------------------------------------------- // [SECTION] SETTINGS //------------------------------------------------------------------------- // FIXME: In our wildest dreams we could provide a imgui_club/ serialization helper that would be // easy to use in both the ReadLine and WriteAll functions. //------------------------------------------------------------------------- static void* ImGuiTestEngine_SettingsReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { if (strcmp(name, "Data") != 0) return nullptr; return (void*)1; } static bool SettingsTryReadString(const char* line, const char* prefix, char* out_buf, size_t out_buf_size) { // Could also use scanf() with "%[^\n]" but it won't bound check. size_t prefix_len = strlen(prefix); if (strncmp(line, prefix, prefix_len) != 0) return false; line += prefix_len; IM_ASSERT(out_buf_size >= strlen(line) + 1); ImFormatString(out_buf, out_buf_size, "%s", line); return true; } static bool SettingsTryReadString(const char* line, const char* prefix, Str* out_str) { // Could also use scanf() with "%[^\n]" but it won't bound check. size_t prefix_len = strlen(prefix); if (strncmp(line, prefix, prefix_len) != 0) return false; line += prefix_len; out_str->set(line); return true; } static void ImGuiTestEngine_SettingsReadLine(ImGuiContext* ui_ctx, ImGuiSettingsHandler*, void* entry, const char* line) { ImGuiTestEngine* e = (ImGuiTestEngine*)ui_ctx->TestEngine; IM_ASSERT(e != nullptr); IM_ASSERT(e->UiContextTarget == ui_ctx); IM_UNUSED(entry); int n = 0; /**/ if (SettingsTryReadString(line, "FilterTests=", e->UiFilterTests)) { } else if (SettingsTryReadString(line, "FilterPerfs=", e->UiFilterPerfs)) { } else if (sscanf(line, "LogHeight=%f", &e->UiLogHeight) == 1) { } else if (sscanf(line, "CaptureTool=%d", &n) == 1) { e->UiCaptureToolOpen = (n != 0); } else if (sscanf(line, "PerfTool=%d", &n) == 1) { e->UiPerfToolOpen = (n != 0); } else if (sscanf(line, "StackTool=%d", &n) == 1) { e->UiStackToolOpen = (n != 0); } else if (sscanf(line, "CaptureEnabled=%d", &n) == 1) { e->IO.ConfigCaptureEnabled = (n != 0); } else if (sscanf(line, "CaptureOnError=%d", &n) == 1) { e->IO.ConfigCaptureOnError = (n != 0); } else if (SettingsTryReadString(line, "VideoCapturePathToEncoder=", e->IO.VideoCaptureEncoderPath, IM_ARRAYSIZE(e->IO.VideoCaptureEncoderPath))) { } else if (SettingsTryReadString(line, "VideoCaptureParamsToEncoder=", e->IO.VideoCaptureEncoderParams, IM_ARRAYSIZE(e->IO.VideoCaptureEncoderParams))) { } else if (SettingsTryReadString(line, "GifCaptureParamsToEncoder=", e->IO.GifCaptureEncoderParams, IM_ARRAYSIZE(e->IO.GifCaptureEncoderParams))) { } else if (SettingsTryReadString(line, "VideoCaptureExtension=", e->IO.VideoCaptureExtension, IM_ARRAYSIZE(e->IO.VideoCaptureExtension))) { } } static void ImGuiTestEngine_SettingsWriteAll(ImGuiContext* ui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine; IM_ASSERT(engine != nullptr); IM_ASSERT(engine->UiContextTarget == ui_ctx); buf->appendf("[%s][Data]\n", handler->TypeName); buf->appendf("FilterTests=%s\n", engine->UiFilterTests->c_str()); buf->appendf("FilterPerfs=%s\n", engine->UiFilterPerfs->c_str()); buf->appendf("LogHeight=%.0f\n", engine->UiLogHeight); buf->appendf("CaptureTool=%d\n", engine->UiCaptureToolOpen); buf->appendf("PerfTool=%d\n", engine->UiPerfToolOpen); buf->appendf("StackTool=%d\n", engine->UiStackToolOpen); buf->appendf("CaptureEnabled=%d\n", engine->IO.ConfigCaptureEnabled); buf->appendf("CaptureOnError=%d\n", engine->IO.ConfigCaptureOnError); buf->appendf("VideoCapturePathToEncoder=%s\n", engine->IO.VideoCaptureEncoderPath); buf->appendf("VideoCaptureParamsToEncoder=%s\n", engine->IO.VideoCaptureEncoderParams); buf->appendf("GifCaptureParamsToEncoder=%s\n", engine->IO.GifCaptureEncoderParams); buf->appendf("VideoCaptureExtension=%s\n", engine->IO.VideoCaptureExtension); buf->appendf("\n"); } //------------------------------------------------------------------------- // [SECTION] ImGuiTestLog //------------------------------------------------------------------------- void ImGuiTestLog::Clear() { Buffer.clear(); LineInfo.clear(); memset(&CountPerLevel, 0, sizeof(CountPerLevel)); } // Output: // - If 'buffer != nullptr': all extracted lines are appended to 'buffer'. Use 'buffer->c_str()' on your side to obtain the text. // - Return value: number of lines extracted (should be equivalent to number of '\n' inside buffer->c_str()). // - You may call the function with buffer == nullptr to only obtain a count without getting the data. // Verbose levels are inclusive: // - To get ONLY Error: Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Error // - To get ONLY Error and Warnings: Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Warning // - To get All Errors, Warnings, Debug... Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Trace int ImGuiTestLog::ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel level_min, ImGuiTestVerboseLevel level_max, ImGuiTextBuffer* out_buffer) { IM_ASSERT(level_min <= level_max); // Return count int count = 0; if (out_buffer == nullptr) { for (int n = level_min; n <= level_max; n++) count += CountPerLevel[n]; return count; } // Extract lines and return count for (auto& line_info : LineInfo) if (line_info.Level >= level_min && line_info.Level <= level_max) { const char* line_begin = Buffer.c_str() + line_info.LineOffset; const char* line_end = strchr(line_begin, '\n'); out_buffer->append(line_begin, line_end[0] == '\n' ? line_end + 1 : line_end); count++; } return count; } void ImGuiTestLog::UpdateLineOffsets(ImGuiTestEngineIO* engine_io, ImGuiTestVerboseLevel level, const char* start) { IM_UNUSED(engine_io); IM_ASSERT(Buffer.begin() <= start && start < Buffer.end()); const char* p_begin = start; const char* p_end = Buffer.end(); const char* p = p_begin; while (p < p_end) { const char* p_bol = p; const char* p_eol = strchr(p, '\n'); bool last_empty_line = (p_bol + 1 == p_end); if (!last_empty_line) { int offset = (int)(p_bol - Buffer.c_str()); LineInfo.push_back({level, offset}); CountPerLevel[level] += 1; } p = p_eol ? p_eol + 1 : nullptr; } } //------------------------------------------------------------------------- // [SECTION] ImGuiTest //------------------------------------------------------------------------- ImGuiTest::~ImGuiTest() { if (NameOwned) ImGui::MemFree((char*)Name); } void ImGuiTest::SetOwnedName(const char* name) { IM_ASSERT(!NameOwned); NameOwned = true; Name = ImStrdup(name); } //------------------------------------------------------------------------- ================================================ FILE: lib/third_party/imgui/imgui_test_engine/source/imgui_te_exporters.cpp ================================================ // dear imgui test engine // (result exporters) // Read https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #include "imgui_te_exporters.h" #include "imgui_te_engine.h" #include "imgui_te_internal.h" #include "thirdparty/Str/Str.h" //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- static void ImGuiTestEngine_ExportJUnitXml(ImGuiTestEngine* engine, const char* output_file); //------------------------------------------------------------------------- // [SECTION] TEST ENGINE EXPORTER FUNCTIONS //------------------------------------------------------------------------- // - ImGuiTestEngine_PrintResultSummary() // - ImGuiTestEngine_Export() // - ImGuiTestEngine_ExportEx() // - ImGuiTestEngine_ExportJUnitXml() //------------------------------------------------------------------------- void ImGuiTestEngine_PrintResultSummary(ImGuiTestEngine* engine) { ImGuiTestEngineResultSummary summary; ImGuiTestEngine_GetResultSummary(engine, &summary); if (summary.CountSuccess < summary.CountTested) { printf("\nFailing tests:\n"); for (ImGuiTest* test : engine->TestsAll) if (test->Output.Status == ImGuiTestStatus_Error) printf("- %s\n", test->Name); } bool success = (summary.CountSuccess == summary.CountTested); ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, success ? ImOsConsoleTextColor_BrightGreen : ImOsConsoleTextColor_BrightRed); printf("\nTests Result: %s\n", success ? "OK" : "Errors"); printf("(%d/%d tests passed)\n", summary.CountSuccess, summary.CountTested); if (summary.CountInQueue > 0) printf("(%d queued tests remaining)\n", summary.CountInQueue); ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_White); } // This is mostly a copy of ImGuiTestEngine_PrintResultSummary with few additions. static void ImGuiTestEngine_ExportResultSummary(ImGuiTestEngine* engine, FILE* fp, int indent_count, ImGuiTestGroup group) { int count_tested = 0; int count_success = 0; for (ImGuiTest* test : engine->TestsAll) { if (test->Group != group) continue; if (test->Output.Status != ImGuiTestStatus_Unknown) count_tested++; if (test->Output.Status == ImGuiTestStatus_Success) count_success++; } Str64 indent_str; indent_str.reserve(indent_count + 1); memset(indent_str.c_str(), ' ', indent_count); indent_str[indent_count] = 0; const char* indent = indent_str.c_str(); if (count_success < count_tested) { fprintf(fp, "\n%sFailing tests:\n", indent); for (ImGuiTest* test : engine->TestsAll) { if (test->Group != group) continue; if (test->Output.Status == ImGuiTestStatus_Error) fprintf(fp, "%s- %s\n", indent, test->Name); } fprintf(fp, "\n"); } fprintf(fp, "%sTests Result: %s\n", indent, (count_success == count_tested) ? "OK" : "Errors"); fprintf(fp, "%s(%d/%d tests passed)\n", indent, count_success, count_tested); } static bool ImGuiTestEngine_HasAnyLogLines(ImGuiTestLog* test_log, ImGuiTestVerboseLevel level) { for (auto& line_info : test_log->LineInfo) if (line_info.Level <= level) return true; return false; } static void ImGuiTestEngine_PrintLogLines(FILE* fp, ImGuiTestLog* test_log, int indent, ImGuiTestVerboseLevel level) { Str128 log_line; for (auto& line_info : test_log->LineInfo) { if (line_info.Level > level) continue; const char* line_start = test_log->Buffer.c_str() + line_info.LineOffset; const char* line_end = strstr(line_start, "\n"); // FIXME: Incorrect. log_line.set(line_start, line_end); ImStrXmlEscape(&log_line); // FIXME: Should not be here considering the function name. // Some users may want to disable indenting? fprintf(fp, "%*s%s\n", indent, "", log_line.c_str()); } } // Export using settings stored in ImGuiTestEngineIO // This is called by ImGuiTestEngine_CrashHandler(). void ImGuiTestEngine_Export(ImGuiTestEngine* engine) { ImGuiTestEngineIO& io = engine->IO; ImGuiTestEngine_ExportEx(engine, io.ExportResultsFormat, io.ExportResultsFilename); } // Export using custom settings. void ImGuiTestEngine_ExportEx(ImGuiTestEngine* engine, ImGuiTestEngineExportFormat format, const char* filename) { if (format == ImGuiTestEngineExportFormat_None) return; IM_ASSERT(filename != nullptr); if (format == ImGuiTestEngineExportFormat_JUnitXml) ImGuiTestEngine_ExportJUnitXml(engine, filename); else IM_ASSERT(0); } void ImGuiTestEngine_ExportJUnitXml(ImGuiTestEngine* engine, const char* output_file) { IM_ASSERT(engine != nullptr); IM_ASSERT(output_file != nullptr); FILE* fp = fopen(output_file, "w+b"); if (fp == nullptr) { fprintf(stderr, "Writing '%s' failed.\n", output_file); return; } // Per-testsuite test statistics. struct { const char* Name = nullptr; int Tests = 0; int Failures = 0; int Disabled = 0; } testsuites[ImGuiTestGroup_COUNT]; testsuites[ImGuiTestGroup_Tests].Name = "tests"; testsuites[ImGuiTestGroup_Perfs].Name = "perfs"; for (int n = 0; n < engine->TestsAll.Size; n++) { ImGuiTest* test = engine->TestsAll[n]; auto* stats = &testsuites[test->Group]; stats->Tests += 1; if (test->Output.Status == ImGuiTestStatus_Error) stats->Failures += 1; else if (test->Output.Status == ImGuiTestStatus_Unknown) stats->Disabled += 1; } // Attributes for tag. const char* testsuites_name = "Dear ImGui"; int testsuites_failures = 0; int testsuites_tests = 0; int testsuites_disabled = 0; float testsuites_time = (float)((double)(engine->BatchEndTime - engine->BatchStartTime) / 1000000.0); for (int testsuite_id = ImGuiTestGroup_Tests; testsuite_id < ImGuiTestGroup_COUNT; testsuite_id++) { testsuites_tests += testsuites[testsuite_id].Tests; testsuites_failures += testsuites[testsuite_id].Failures; testsuites_disabled += testsuites[testsuite_id].Disabled; } // FIXME: "errors" attribute and tag in may be supported if we have means to catch unexpected errors like assertions. fprintf(fp, "\n" "\n", testsuites_disabled, testsuites_failures, testsuites_name, testsuites_tests, testsuites_time); for (int testsuite_id = ImGuiTestGroup_Tests; testsuite_id < ImGuiTestGroup_COUNT; testsuite_id++) { // Attributes for tag. auto* testsuite = &testsuites[testsuite_id]; float testsuite_time = testsuites_time; // FIXME: We do not differentiate between tests and perfs, they are executed in one big batch. Str30 testsuite_timestamp = ""; ImTimestampToISO8601(engine->BatchStartTime, &testsuite_timestamp); fprintf(fp, " \n", testsuite->Name, testsuite->Tests, testsuite->Disabled, testsuite->Failures, testsuite_id, testsuite_time, testsuite_timestamp.c_str()); for (int n = 0; n < engine->TestsAll.Size; n++) { ImGuiTest* test = engine->TestsAll[n]; if (test->Group != testsuite_id) continue; ImGuiTestOutput* test_output = &test->Output; ImGuiTestLog* test_log = &test_output->Log; // Attributes for tag. const char* testcase_name = test->Name; const char* testcase_classname = test->Category; const char* testcase_status = ImGuiTestEngine_GetStatusName(test_output->Status); const float testcase_time = (float)((double)(test_output->EndTime - test_output->StartTime) / 1000000.0); fprintf(fp, " \n", testcase_name, testcase_classname, testcase_status, testcase_time); if (test_output->Status == ImGuiTestStatus_Error) { // Skip last error message because it is generic information that test failed. Str128 log_line; for (int i = test_log->LineInfo.Size - 2; i >= 0; i--) { ImGuiTestLogLineInfo* line_info = &test_log->LineInfo[i]; if (line_info->Level > engine->IO.ConfigVerboseLevelOnError) continue; if (line_info->Level == ImGuiTestVerboseLevel_Error) { const char* line_start = test_log->Buffer.c_str() + line_info->LineOffset; const char* line_end = strstr(line_start, "\n"); log_line.set(line_start, line_end); ImStrXmlEscape(&log_line); break; } } // Failing tests save their "on error" log output in text element of tag. fprintf(fp, " \n", log_line.c_str()); ImGuiTestEngine_PrintLogLines(fp, test_log, 8, engine->IO.ConfigVerboseLevelOnError); fprintf(fp, " \n"); } if (test_output->Status == ImGuiTestStatus_Unknown) { fprintf(fp, " \n"); } else { // Succeeding tests save their default log output output as "stdout". if (ImGuiTestEngine_HasAnyLogLines(test_log, engine->IO.ConfigVerboseLevel)) { fprintf(fp, " \n"); ImGuiTestEngine_PrintLogLines(fp, test_log, 8, engine->IO.ConfigVerboseLevel); fprintf(fp, " \n"); } // Save error messages as "stderr". if (ImGuiTestEngine_HasAnyLogLines(test_log, ImGuiTestVerboseLevel_Error)) { fprintf(fp, " \n"); ImGuiTestEngine_PrintLogLines(fp, test_log, 8, ImGuiTestVerboseLevel_Error); fprintf(fp, " \n"); } } fprintf(fp, " \n"); } if (testsuites[testsuite_id].Disabled < testsuites[testsuite_id].Tests) // Any tests executed { // Log all log messages as "stdout". fprintf(fp, " \n"); for (int n = 0; n < engine->TestsAll.Size; n++) { ImGuiTest* test = engine->TestsAll[n]; ImGuiTestOutput* test_output = &test->Output; if (test->Group != testsuite_id) continue; if (test_output->Status == ImGuiTestStatus_Unknown) continue; fprintf(fp, " [0000] Test: '%s' '%s'..\n", test->Category, test->Name); ImGuiTestVerboseLevel level = test_output->Status == ImGuiTestStatus_Error ? engine->IO.ConfigVerboseLevelOnError : engine->IO.ConfigVerboseLevel; ImGuiTestEngine_PrintLogLines(fp, &test_output->Log, 6, level); } ImGuiTestEngine_ExportResultSummary(engine, fp, 6, (ImGuiTestGroup)testsuite_id); fprintf(fp, " \n"); // Log all warning and error messages as "stderr". fprintf(fp, " \n"); for (int n = 0; n < engine->TestsAll.Size; n++) { ImGuiTest* test = engine->TestsAll[n]; ImGuiTestOutput* test_output = &test->Output; if (test->Group != testsuite_id) continue; if (test_output->Status == ImGuiTestStatus_Unknown) continue; fprintf(fp, " [0000] Test: '%s' '%s'..\n", test->Category, test->Name); ImGuiTestEngine_PrintLogLines(fp, &test_output->Log, 6, ImGuiTestVerboseLevel_Warning); } ImGuiTestEngine_ExportResultSummary(engine, fp, 6, (ImGuiTestGroup)testsuite_id); fprintf(fp, " \n"); } fprintf(fp, " \n"); } fprintf(fp, "\n"); fclose(fp); fprintf(stdout, "Saved test results to '%s' successfully.\n", output_file); } ================================================ FILE: lib/third_party/imgui/imgui_test_engine/source/imgui_te_perftool.cpp ================================================ // dear imgui test engine // (performance tool) // Browse and visualize samples recorded by ctx->PerfCapture() calls. // User access via 'Test Engine UI -> Tools -> Perf Tool' // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. /* Index of this file: // [SECTION] Header mess // [SECTION] ImGuiPerflogEntry // [SECTION] Types & everything else // [SECTION] USER INTERFACE // [SECTION] SETTINGS // [SECTION] TESTS */ // Terminology: // * Entry: information about execution of a single perf test. This corresponds to one line in CSV file. // * Batch: a group of entries that were created together during a single execution. A new batch is created each time // one or more perf tests are executed. All entries in a single batch will have a matching ImGuiPerflogEntry::Timestamp. // * Build: A group of batches that have matching BuildType, OS, Cpu, Compiler, GitBranchName. // * Baseline: A batch that we are comparing against. Baselines are identified by batch timestamp and build id. //------------------------------------------------------------------------- // [SECTION] Header mess //------------------------------------------------------------------------- #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui_te_perftool.h" #include "imgui.h" #include "imgui_internal.h" #include "imgui_te_utils.h" #include "thirdparty/Str/Str.h" #include // time(), localtime() #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT #include "implot.h" #include "implot_internal.h" #endif // For tests #include "imgui_te_engine.h" #include "imgui_te_context.h" #include "imgui_te_internal.h" // ImGuiTestEngine_GetPerfTool() #include "imgui_capture_tool.h" //------------------------------------------------------------------------- // [SECTION] ImGuiPerflogEntry //------------------------------------------------------------------------- void ImGuiPerfToolEntry::Set(const ImGuiPerfToolEntry& other) { Timestamp = other.Timestamp; Category = other.Category; TestName = other.TestName; DtDeltaMs = other.DtDeltaMs; DtDeltaMsMin = other.DtDeltaMsMin; DtDeltaMsMax = other.DtDeltaMsMax; NumSamples = other.NumSamples; PerfStressAmount = other.PerfStressAmount; GitBranchName = other.GitBranchName; BuildType = other.BuildType; Cpu = other.Cpu; OS = other.OS; Compiler = other.Compiler; Date = other.Date; //DateMax = ... VsBaseline = other.VsBaseline; LabelIndex = other.LabelIndex; } //------------------------------------------------------------------------- // [SECTION] Types & everything else //------------------------------------------------------------------------- typedef ImGuiID(*HashEntryFn)(ImGuiPerfToolEntry* entry); typedef void(*FormatEntryLabelFn)(ImGuiPerfTool* perftool, Str* result, ImGuiPerfToolEntry* entry); struct ImGuiPerfToolColumnInfo { const char* Title; int Offset; ImGuiDataType Type; bool ShowAlways; ImGuiTableFlags Flags; template T GetValue(const ImGuiPerfToolEntry* entry) const { return *(T*)((const char*)entry + Offset); } }; // Update _ShowEntriesTable() and SaveHtmlReport() when adding new entries. static const ImGuiPerfToolColumnInfo PerfToolColumnInfo[] = { { /* 00 */ "Date", offsetof(ImGuiPerfToolEntry, Timestamp), ImGuiDataType_U64, true, ImGuiTableColumnFlags_DefaultHide }, { /* 01 */ "Test Name", offsetof(ImGuiPerfToolEntry, TestName), ImGuiDataType_COUNT, true, 0 }, { /* 02 */ "Branch", offsetof(ImGuiPerfToolEntry, GitBranchName), ImGuiDataType_COUNT, true, 0 }, { /* 03 */ "Compiler", offsetof(ImGuiPerfToolEntry, Compiler), ImGuiDataType_COUNT, true, 0 }, { /* 04 */ "OS", offsetof(ImGuiPerfToolEntry, OS), ImGuiDataType_COUNT, true, 0 }, { /* 05 */ "CPU", offsetof(ImGuiPerfToolEntry, Cpu), ImGuiDataType_COUNT, true, 0 }, { /* 06 */ "Build", offsetof(ImGuiPerfToolEntry, BuildType), ImGuiDataType_COUNT, true, 0 }, { /* 07 */ "Stress", offsetof(ImGuiPerfToolEntry, PerfStressAmount), ImGuiDataType_S32, true, 0 }, { /* 08 */ "Avg ms", offsetof(ImGuiPerfToolEntry, DtDeltaMs), ImGuiDataType_Double, true, 0 }, { /* 09 */ "Min ms", offsetof(ImGuiPerfToolEntry, DtDeltaMsMin), ImGuiDataType_Double, false, 0 }, { /* 00 */ "Max ms", offsetof(ImGuiPerfToolEntry, DtDeltaMsMax), ImGuiDataType_Double, false, 0 }, { /* 11 */ "Samples", offsetof(ImGuiPerfToolEntry, NumSamples), ImGuiDataType_S32, false, 0 }, { /* 12 */ "VS Baseline", offsetof(ImGuiPerfToolEntry, VsBaseline), ImGuiDataType_Float, true, 0 }, }; static const char* PerfToolReportDefaultOutputPath = "./output/capture_perf_report.html"; // This is declared as a standalone function in order to run without a PerfTool instance void ImGuiTestEngine_PerfToolAppendToCSV(ImGuiPerfTool* perf_log, ImGuiPerfToolEntry* entry, const char* filename) { if (filename == nullptr) filename = IMGUI_PERFLOG_DEFAULT_FILENAME; if (!ImFileCreateDirectoryChain(filename, ImPathFindFilename(filename))) { fprintf(stderr, "Unable to create missing directory '%*s', perftool entry was not saved.\n", (int)(ImPathFindFilename(filename) - filename), filename); return; } // Appends to .csv FILE* f = fopen(filename, "a+b"); if (f == nullptr) { fprintf(stderr, "Unable to open '%s', perftool entry was not saved.\n", filename); return; } fprintf(f, "%llu,%s,%s,%.3f,x%d,%s,%s,%s,%s,%s,%s\n", entry->Timestamp, entry->Category, entry->TestName, entry->DtDeltaMs, entry->PerfStressAmount, entry->GitBranchName, entry->BuildType, entry->Cpu, entry->OS, entry->Compiler, entry->Date); fflush(f); fclose(f); // Register to runtime perf tool if any if (perf_log != nullptr) perf_log->AddEntry(entry); } // Tri-state button. Copied and modified ButtonEx(). static bool Button3(const char* label, int* value) { ImGuiWindow* window = ImGui::GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true); float dot_radius2 = g.FontSize; ImVec2 btn_size(dot_radius2 * 2, dot_radius2); ImVec2 pos = window->DC.CursorPos; ImVec2 size = ImGui::CalcItemSize(ImVec2(), btn_size.x + label_size.x + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x, label_size.y + style.FramePadding.y * 2.0f); const ImRect bb(pos, pos + size); ImGui::ItemSize(size, style.FramePadding.y); if (!ImGui::ItemAdd(bb, id)) return false; bool hovered, held; bool pressed = ImGui::ButtonBehavior(ImRect(pos, pos + style.FramePadding + btn_size), id, &hovered, &held, 0); // Render const ImU32 col = ImGui::GetColorU32(ImGuiCol_FrameBg); #if IMGUI_VERSION_NUM >= 19136 ImGui::RenderNavCursor(bb, id); #else ImGui::RenderNavHighlight(bb, id); #endif ImGui::RenderFrame(bb.Min + style.FramePadding, bb.Min + style.FramePadding + btn_size, col, true, /*style.FrameRounding*/ 5.0f); ImColor btn_col; if (held) btn_col = style.Colors[ImGuiCol_SliderGrabActive]; else if (hovered) btn_col = style.Colors[ImGuiCol_ButtonHovered]; else btn_col = style.Colors[ImGuiCol_SliderGrab]; ImVec2 center = bb.Min + ImVec2(dot_radius2 + (dot_radius2 * (float)*value), dot_radius2) * 0.5f + style.FramePadding; window->DrawList->AddCircleFilled(center, dot_radius2 * 0.5f, btn_col); ImRect text_bb; text_bb.Min = bb.Min + style.FramePadding + ImVec2(btn_size.x + style.ItemInnerSpacing.x, 0); text_bb.Max = text_bb.Min + label_size; ImGui::RenderTextClipped(text_bb.Min, text_bb.Max, label, nullptr, &label_size, style.ButtonTextAlign, &bb); *value = (*value + pressed) % 3; return pressed; } static ImGuiID GetBuildID(const ImGuiPerfToolEntry* entry) { IM_ASSERT(entry != nullptr); ImGuiID build_id = ImHashStr(entry->BuildType); build_id = ImHashStr(entry->OS, 0, build_id); build_id = ImHashStr(entry->Cpu, 0, build_id); build_id = ImHashStr(entry->Compiler, 0, build_id); build_id = ImHashStr(entry->GitBranchName, 0, build_id); return build_id; } static ImGuiID GetBuildID(const ImGuiPerfToolBatch* batch) { IM_ASSERT(batch != nullptr); IM_ASSERT(!batch->Entries.empty()); return GetBuildID(&batch->Entries.Data[0]); } // Batch ID depends on display type. It is either a build ID (when combinding by build type) or batch timestamp otherwise. static ImGuiID GetBatchID(const ImGuiPerfTool* perftool, const ImGuiPerfToolEntry* entry) { IM_ASSERT(perftool != nullptr); IM_ASSERT(entry != nullptr); if (perftool->_DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo) return GetBuildID(entry); else return (ImU32)entry->Timestamp; } static int PerfToolComparerStr(const void* a, const void* b) { return strcmp(*(const char**)b, *(const char**)a); } static int IMGUI_CDECL PerfToolComparerByEntryInfo(const void* lhs, const void* rhs) { const ImGuiPerfToolEntry* a = (const ImGuiPerfToolEntry*)lhs; const ImGuiPerfToolEntry* b = (const ImGuiPerfToolEntry*)rhs; // While build ID does include git branch it wont ensure branches are grouped together, therefore we do branch // sorting manually. int result = strcmp(a->GitBranchName, b->GitBranchName); // Now that we have groups of branches - sort individual builds within those groups. if (result == 0) result = ImClamp((int)((ImS64)GetBuildID(a) - (ImS64)GetBuildID(b)), -1, +1); // Group individual runs together within build groups. if (result == 0) result = (int)ImClamp((ImS64)b->Timestamp - (ImS64)a->Timestamp, -1, +1); // And finally sort individual runs by perf name so we can have a predictable order (used to optimize in _Rebuild()). if (result == 0) result = (int)strcmp(a->TestName, b->TestName); return result; } static ImGuiPerfTool* PerfToolInstance = nullptr; static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs) { IM_ASSERT(PerfToolInstance != nullptr); ImGuiPerfTool* tool = PerfToolInstance; const ImGuiTableSortSpecs* sort_specs = PerfToolInstance->_InfoTableSortSpecs; int batch_index_a, entry_index_a, mono_index_a, batch_index_b, entry_index_b, mono_index_b; tool->_UnpackSortedKey(*(ImU64*)lhs, &batch_index_a, &entry_index_a, &mono_index_a); tool->_UnpackSortedKey(*(ImU64*)rhs, &batch_index_b, &entry_index_b, &mono_index_b); for (int i = 0; i < sort_specs->SpecsCount; i++) { const ImGuiTableColumnSortSpecs* specs = &sort_specs->Specs[i]; const ImGuiPerfToolColumnInfo& col_info = PerfToolColumnInfo[specs->ColumnIndex]; const ImGuiPerfToolBatch* batch_a = &tool->_Batches[batch_index_a]; const ImGuiPerfToolBatch* batch_b = &tool->_Batches[batch_index_b]; ImGuiPerfToolEntry* a = &batch_a->Entries.Data[entry_index_a]; ImGuiPerfToolEntry* b = &batch_b->Entries.Data[entry_index_b]; if (specs->SortDirection == ImGuiSortDirection_Ascending) ImSwap(a, b); int result = 0; switch (col_info.Type) { case ImGuiDataType_S32: result = col_info.GetValue(a) - col_info.GetValue(b); break; case ImGuiDataType_U64: result = (int)(col_info.GetValue(a) - col_info.GetValue(b)); break; case ImGuiDataType_Float: result = (int)((col_info.GetValue(a) - col_info.GetValue(b)) * 1000.0f); break; case ImGuiDataType_Double: result = (int)((col_info.GetValue(a) - col_info.GetValue(b)) * 1000.0); break; case ImGuiDataType_COUNT: result = strcmp(col_info.GetValue(a), col_info.GetValue(b)); break; default: IM_ASSERT(false); } if (result != 0) return result; } return mono_index_a - mono_index_b; } // Dates are in format "YYYY-MM-DD" static bool IsDateValid(const char* date) { if (date[4] != '-' || date[7] != '-') return false; for (int i = 0; i < 10; i++) { if (i == 4 || i == 7) continue; if (date[i] < '0' || date[i] > '9') return false; } return true; } static float FormatVsBaseline(ImGuiPerfToolEntry* entry, ImGuiPerfToolEntry* baseline_entry, Str& out_label) { if (baseline_entry == nullptr) { out_label.appendf("--"); return FLT_MAX; } if (entry == baseline_entry) { out_label.append("baseline"); return FLT_MAX; } double percent_vs_first = 100.0 / baseline_entry->DtDeltaMs * entry->DtDeltaMs; double dt_change = -(100.0 - percent_vs_first); if (dt_change == INFINITY) out_label.appendf("--"); else if (ImAbs(dt_change) > 0.001f) out_label.appendf("%+.2lf%% (%s)", dt_change, dt_change < 0.0f ? "faster" : "slower"); else out_label.appendf("=="); return (float)dt_change; } #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT static void PerfToolFormatBuildInfo(ImGuiPerfTool* perftool, Str* result, ImGuiPerfToolBatch* batch) { IM_ASSERT(perftool != nullptr); IM_ASSERT(result != nullptr); IM_ASSERT(batch != nullptr); IM_ASSERT(batch->Entries.Size > 0); ImGuiPerfToolEntry* entry = &batch->Entries.Data[0]; Str64f legend_format("x%%-%dd %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%s%%s%%s%%s(%%-%dd sample%%s)%%s", perftool->_AlignStress, perftool->_AlignType, perftool->_AlignCpu, perftool->_AlignOs, perftool->_AlignCompiler, perftool->_AlignBranch, perftool->_AlignSamples); result->appendf(legend_format.c_str(), entry->PerfStressAmount, entry->BuildType, entry->Cpu, entry->OS, entry->Compiler, entry->GitBranchName, entry->Date, #if 0 // Show min-max dates. perftool->_CombineByBuildInfo ? " - " : "", entry->DateMax ? entry->DateMax : "", #else "", "", #endif *entry->Date ? " " : "", batch->NumSamples, batch->NumSamples > 1 ? "s" : "", // Singular/plural form of "sample(s)" batch->NumSamples > 1 || perftool->_AlignSamples == 1 ? "" : " " // Space after legend entry to separate * marking baseline ); } #endif static int PerfToolCountBuilds(ImGuiPerfTool* perftool, bool only_visible) { int num_builds = 0; ImU64 build_id = 0; for (ImGuiPerfToolEntry& entry : perftool->_SrcData) { if (build_id != GetBuildID(&entry)) { if (!only_visible || perftool->_IsVisibleBuild(&entry)) num_builds++; build_id = GetBuildID(&entry); } } return num_builds; } static bool InputDate(const char* label, char* date, int date_len, bool valid) { ImGui::SetNextItemWidth(ImGui::CalcTextSize("YYYY-MM-DD").x + ImGui::GetStyle().FramePadding.x * 2.0f); const bool date_valid = date[0] == 0 || (IsDateValid(date) && valid); if (!date_valid) { ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(255, 0, 0, 255)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); } bool date_changed = ImGui::InputTextWithHint(label, "YYYY-MM-DD", date, date_len); if (!date_valid) { ImGui::PopStyleVar(); ImGui::PopStyleColor(); } return date_changed; } static void FormatDate(ImU64 microseconds, char* buf, size_t buf_size) { time_t timestamp = (time_t)(microseconds / 1000000); tm* time = localtime(×tamp); ImFormatString(buf, buf_size, "%04d-%02d-%02d", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday); } static void FormatDateAndTime(ImU64 microseconds, char* buf, size_t buf_size) { time_t timestamp = (time_t)(microseconds / 1000000); tm* time = localtime(×tamp); ImFormatString(buf, buf_size, "%04d-%02d-%02d %02d:%02d:%02d", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); } static void RenderFilterInput(ImGuiPerfTool* perf, const char* hint, float width = -FLT_MIN) { if (ImGui::IsWindowAppearing()) strcpy(perf->_Filter, ""); ImGui::SetNextItemWidth(width); ImGui::InputTextWithHint("##filter", hint, perf->_Filter, IM_ARRAYSIZE(perf->_Filter)); if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere(); } static bool RenderMultiSelectFilter(ImGuiPerfTool* perf, const char* filter_hint, ImVector* labels) { ImGuiContext& g = *ImGui::GetCurrentContext(); ImGuiIO& io = ImGui::GetIO(); ImGuiStorage& visibility = perf->_Visibility; bool modified = false; RenderFilterInput(perf, filter_hint, -(ImGui::CalcTextSize("(?)").x + g.Style.ItemSpacing.x)); ImGui::SameLine(); ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Hold CTRL to invert other items.\nHold SHIFT to close popup instantly."); // Keep popup open for multiple actions if SHIFT is pressed. #if IMGUI_VERSION_NUM >= 19094 if (!io.KeyShift) ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false); #else if (!io.KeyShift) ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true); #endif if (ImGui::MenuItem("Show All")) { for (const char* label : *labels) if (strstr(label, perf->_Filter) != nullptr) visibility.SetBool(ImHashStr(label), true); modified = true; } if (ImGui::MenuItem("Hide All")) { for (const char* label : *labels) if (strstr(label, perf->_Filter) != nullptr) visibility.SetBool(ImHashStr(label), false); modified = true; } // Render perf labels in reversed order. Labels are sorted, but stored in reversed order to render them on the plot // from top down (ImPlot renders stuff from bottom up). int filtered_entries = 0; for (int i = labels->Size - 1; i >= 0; i--) { const char* label = (*labels)[i]; if (strstr(label, perf->_Filter) == nullptr) // Filter out entries not matching a filter query continue; if (filtered_entries == 0) ImGui::Separator(); ImGuiID build_id = ImHashStr(label); bool visible = visibility.GetBool(build_id, true); if (ImGui::MenuItem(label, nullptr, &visible)) { modified = true; if (io.KeyCtrl) { for (const char* label2 : *labels) { ImGuiID build_id2 = ImHashStr(label2); visibility.SetBool(build_id2, !visibility.GetBool(build_id2, true)); } } else { visibility.SetBool(build_id, !visibility.GetBool(build_id, true)); } } filtered_entries++; } if (!io.KeyShift) ImGui::PopItemFlag(); return modified; } // Based on ImPlot::SetupFinish(). #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT static ImRect ImPlotGetYTickRect(int t, int y = 0) { ImPlotContext& gp = *GImPlot; ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& ax = plot.YAxis(y); const ImPlotTicker& tkc = ax.Ticker; const bool opp = ax.IsOpposite(); ImRect result(1.0f, 1.0f, -1.0f, -1.0f); if (ax.HasTickLabels()) { const ImPlotTick& tk = tkc.Ticks[t]; const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x)); if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) { ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y); result.Min = start; result.Max = start + tk.LabelSize; } } return result; } #endif // #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT ImGuiPerfTool::ImGuiPerfTool() { _CsvParser = IM_NEW(ImGuiCsvParser)(); Clear(); } ImGuiPerfTool::~ImGuiPerfTool() { _SrcData.clear_destruct(); _Batches.clear_destruct(); IM_DELETE(_CsvParser); } void ImGuiPerfTool::AddEntry(ImGuiPerfToolEntry* entry) { if (strcmp(_FilterDateFrom, entry->Date) > 0) ImStrncpy(_FilterDateFrom, entry->Date, IM_ARRAYSIZE(_FilterDateFrom)); if (strcmp(_FilterDateTo, entry->Date) < 0) ImStrncpy(_FilterDateTo, entry->Date, IM_ARRAYSIZE(_FilterDateTo)); _SrcData.push_back(*entry); _Batches.clear_destruct(); } void ImGuiPerfTool::_Rebuild() { if (_SrcData.empty()) return; ImGuiStorage& temp_set = _TempSet; _Labels.resize(0); _LabelsVisible.resize(0); _InfoTableSort.resize(0); _Batches.clear_destruct(); _InfoTableSortDirty = true; // Gather all visible labels. Legend batches will store data in this order. temp_set.Data.resize(0); // name_id:IsLabelSeen for (ImGuiPerfToolEntry& entry : _SrcData) { ImGuiID name_id = ImHashStr(entry.TestName); if (!temp_set.GetBool(name_id)) { temp_set.SetBool(name_id, true); _Labels.push_back(entry.TestName); if (_IsVisibleTest(entry.TestName)) _LabelsVisible.push_front(entry.TestName); } } int num_visible_labels = _LabelsVisible.Size; // Labels are sorted in reverse order so they appear to be oredered from top down. ImQsort(_Labels.Data, _Labels.Size, sizeof(const char*), &PerfToolComparerStr); ImQsort(_LabelsVisible.Data, num_visible_labels, sizeof(const char*), &PerfToolComparerStr); // _SrcData vector stores sorted raw entries of imgui_perflog.csv. Sorting is very important, // algorithm depends on data being correctly sorted. Sorting _SrcData is OK, because it is only // ever appended to and never written out to disk. Entries are sorted by multiple criteria, // in specified order: // 1. By branch name // 2. By build ID // 3. By run timestamp // 4. By test name // This results in a neatly partitioned dataset where similar data is grouped together and where perf test order // is consistent in all batches. Sorting by build ID _before_ timestamp is also important as we will be aggregating // entries by build ID instead of timestamp, when appropriate display mode is enabled. ImQsort(_SrcData.Data, _SrcData.Size, sizeof(ImGuiPerfToolEntry), &PerfToolComparerByEntryInfo); // Sort groups of entries into batches. const bool combine_by_build_info = _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo; _LabelBarCounts.Data.resize(0); // Process all batches. `entry` is always a first batch element (guaranteed by _SrcData being sorted by timestamp). // At the end of this loop we fast-forward until next batch (first entry having different batch id (which is a // timestamp or build info)). for (ImGuiPerfToolEntry* entry = _SrcData.begin(); entry < _SrcData.end();) { // Filtered out entries can be safely ignored. Note that entry++ does not follow logic of fast-forwarding to the // next batch, as found at the end of this loop. This is OK, because all entries belonging to a same batch will // also have same date. if ((_FilterDateFrom[0] && strcmp(entry->Date, _FilterDateFrom) < 0) || (_FilterDateTo[0] && strcmp(entry->Date, _FilterDateTo) > 0)) { entry++; continue; } _Batches.push_back(ImGuiPerfToolBatch()); ImGuiPerfToolBatch& batch = _Batches.back(); batch.BatchID = GetBatchID(this, entry); batch.Entries.resize(num_visible_labels); // Fill in defaults. Done once before data aggregation loop, because same entry may be touched multiple times in // the following loop when entries are being combined by build info. for (int i = 0; i < num_visible_labels; i++) { ImGuiPerfToolEntry* e = &batch.Entries.Data[i]; *e = *entry; e->DtDeltaMs = 0; e->NumSamples = 0; e->LabelIndex = i; e->TestName = _LabelsVisible.Data[i]; } // Find perf test runs for this particular batch and accumulate them. for (int i = 0; i < num_visible_labels; i++) { // This inner loop walks all entries that belong to current batch. Due to sorting we are sure that batch // always starts with `entry`, and all entries that belong to a batch (whether we combine by build info or not) // will be grouped in _SrcData. ImGuiPerfToolEntry* aggregate = &batch.Entries.Data[i]; for (ImGuiPerfToolEntry* e = entry; e < _SrcData.end() && GetBatchID(this, e) == batch.BatchID; e++) { if (strcmp(e->TestName, aggregate->TestName) != 0) continue; aggregate->DtDeltaMs += e->DtDeltaMs; aggregate->NumSamples++; aggregate->DtDeltaMsMin = ImMin(aggregate->DtDeltaMsMin, e->DtDeltaMs); aggregate->DtDeltaMsMax = ImMax(aggregate->DtDeltaMsMax, e->DtDeltaMs); } } // In case data is combined by build info, DtDeltaMs will be a sum of all combined entries. Average it out. if (combine_by_build_info) for (int i = 0; i < num_visible_labels; i++) { ImGuiPerfToolEntry* aggregate = &batch.Entries.Data[i]; if (aggregate->NumSamples > 0) aggregate->DtDeltaMs /= aggregate->NumSamples; } // Advance to the next batch. batch.NumSamples = 1; if (combine_by_build_info) { ImU64 last_timestamp = entry->Timestamp; for (ImGuiID build_id = GetBuildID(entry); entry < _SrcData.end() && build_id == GetBuildID(entry);) { // Also count how many unique batches participate in this aggregated batch. if (entry->Timestamp != last_timestamp) { batch.NumSamples++; last_timestamp = entry->Timestamp; } entry++; } } else { for (ImU64 timestamp = entry->Timestamp; entry < _SrcData.end() && timestamp == entry->Timestamp;) entry++; } } // Create man entries for every batch. // Pushed after sorting so they are always at the start of the chart. const char* mean_labels[] = { "harmonic mean", "arithmetic mean", "geometric mean" }; int num_visible_mean_labels = 0; for (const char* label : mean_labels) { _Labels.push_back(label); if (_IsVisibleTest(label)) { _LabelsVisible.push_back(label); num_visible_mean_labels++; } } for (ImGuiPerfToolBatch& batch : _Batches) { double delta_sum = 0.0; double delta_prd = 1.0; double delta_rec = 0.0; for (int i = 0; i < batch.Entries.Size; i++) { ImGuiPerfToolEntry* entry = &batch.Entries.Data[i]; delta_sum += entry->DtDeltaMs; delta_prd *= entry->DtDeltaMs; delta_rec += 1 / entry->DtDeltaMs; } int visible_label_i = 0; for (int i = 0; i < IM_ARRAYSIZE(mean_labels); i++) { if (!_IsVisibleTest(mean_labels[i])) continue; batch.Entries.push_back(ImGuiPerfToolEntry()); ImGuiPerfToolEntry* mean_entry = &batch.Entries.back(); *mean_entry = batch.Entries.Data[0]; mean_entry->LabelIndex = _LabelsVisible.Size - num_visible_mean_labels + visible_label_i; mean_entry->TestName = _LabelsVisible.Data[mean_entry->LabelIndex]; mean_entry->GitBranchName = ""; mean_entry->BuildType = ""; mean_entry->Compiler = ""; mean_entry->OS = ""; mean_entry->Cpu = ""; mean_entry->Date = ""; visible_label_i++; if (i == 0) mean_entry->DtDeltaMs = num_visible_labels / delta_rec; else if (i == 1) mean_entry->DtDeltaMs = delta_sum / num_visible_labels; else if (i == 2) mean_entry->DtDeltaMs = pow(delta_prd, 1.0 / num_visible_labels); else IM_ASSERT(0); } IM_ASSERT(batch.Entries.Size == _LabelsVisible.Size); } // Find number of bars (batches) each label will render. for (ImGuiPerfToolBatch& batch : _Batches) { if (!_IsVisibleBuild(&batch)) continue; for (ImGuiPerfToolEntry& entry : batch.Entries) { ImGuiID label_id = ImHashStr(entry.TestName); int num_bars = _LabelBarCounts.GetInt(label_id) + 1; _LabelBarCounts.SetInt(label_id, num_bars); } } // Index branches, used for per-branch colors. temp_set.Data.resize(0); // ImHashStr(branch_name):linear_index int branch_index_last = 0; _BaselineBatchIndex = -1; for (ImGuiPerfToolBatch& batch : _Batches) { if (batch.Entries.empty()) continue; ImGuiPerfToolEntry* entry = &batch.Entries.Data[0]; ImGuiID branch_hash = ImHashStr(entry->GitBranchName); batch.BranchIndex = temp_set.GetInt(branch_hash, -1); if (batch.BranchIndex < 0) { batch.BranchIndex = branch_index_last++; temp_set.SetInt(branch_hash, batch.BranchIndex); } if (_BaselineBatchIndex < 0) if ((combine_by_build_info && GetBuildID(entry) == _BaselineBuildId) || _BaselineTimestamp == entry->Timestamp) _BaselineBatchIndex = _Batches.index_from_ptr(&batch); } // When per-branch colors are enabled we aggregate sample counts and set them to all batches with identical build info. temp_set.Data.resize(0); // build_id:TotalSamples if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors) { // Aggregate totals to temp_set. for (ImGuiPerfToolBatch& batch : _Batches) { ImGuiID build_id = GetBuildID(&batch); temp_set.SetInt(build_id, temp_set.GetInt(build_id, 0) + batch.NumSamples); } // Fill in batch sample counts. for (ImGuiPerfToolBatch& batch : _Batches) { ImGuiID build_id = GetBuildID(&batch); batch.NumSamples = temp_set.GetInt(build_id, 1); } } _NumVisibleBuilds = PerfToolCountBuilds(this, true); _NumUniqueBuilds = PerfToolCountBuilds(this, false); _CalculateLegendAlignment(); temp_set.Data.resize(0); } void ImGuiPerfTool::Clear() { _Labels.clear(); _LabelsVisible.clear(); _Batches.clear_destruct(); _Visibility.Clear(); _SrcData.clear_destruct(); _CsvParser->Clear(); ImStrncpy(_FilterDateFrom, "9999-99-99", IM_ARRAYSIZE(_FilterDateFrom)); ImStrncpy(_FilterDateTo, "0000-00-00", IM_ARRAYSIZE(_FilterDateFrom)); } bool ImGuiPerfTool::LoadCSV(const char* filename) { if (filename == nullptr) filename = IMGUI_PERFLOG_DEFAULT_FILENAME; Clear(); ImGuiCsvParser* parser = _CsvParser; parser->Columns = 11; if (!parser->Load(filename)) return false; // Read perf test entries from CSV for (int row = 0; row < parser->Rows; row++) { ImGuiPerfToolEntry entry; int col = 0; sscanf(parser->GetCell(row, col++), "%llu", &entry.Timestamp); entry.Category = parser->GetCell(row, col++); entry.TestName = parser->GetCell(row, col++); sscanf(parser->GetCell(row, col++), "%lf", &entry.DtDeltaMs); sscanf(parser->GetCell(row, col++), "x%d", &entry.PerfStressAmount); entry.GitBranchName = parser->GetCell(row, col++); entry.BuildType = parser->GetCell(row, col++); entry.Cpu = parser->GetCell(row, col++); entry.OS = parser->GetCell(row, col++); entry.Compiler = parser->GetCell(row, col++); entry.Date = parser->GetCell(row, col++); AddEntry(&entry); } return true; } void ImGuiPerfTool::ViewOnly(const char** perf_names) { // Data would not be built if we tried to view perftool of a particular test without first opening perftool via button. We need data to be built to hide perf tests. if (_Batches.empty()) _Rebuild(); // Hide other perf tests. for (const char* label : _Labels) { bool visible = false; for (const char** p_name = perf_names; !visible && *p_name; p_name++) visible |= strcmp(label, *p_name) == 0; _Visibility.SetBool(ImHashStr(label), visible); } } void ImGuiPerfTool::ViewOnly(const char* perf_name) { const char* names[] = { perf_name, nullptr }; ViewOnly(names); } ImGuiPerfToolEntry* ImGuiPerfTool::GetEntryByBatchIdx(int idx, const char* perf_name) { if (idx < 0) return nullptr; IM_ASSERT(idx < _Batches.Size); ImGuiPerfToolBatch& batch = _Batches.Data[idx]; for (int i = 0; i < batch.Entries.Size; i++) if (ImGuiPerfToolEntry* entry = &batch.Entries.Data[i]) if (strcmp(entry->TestName, perf_name) == 0) return entry; return nullptr; } bool ImGuiPerfTool::_IsVisibleBuild(ImGuiPerfToolBatch* batch) { IM_ASSERT(batch != nullptr); if (batch->Entries.empty()) return false; // All entries are hidden. return _IsVisibleBuild(&batch->Entries.Data[0]); } bool ImGuiPerfTool::_IsVisibleBuild(ImGuiPerfToolEntry* entry) { return _Visibility.GetBool(ImHashStr(entry->GitBranchName), true) && _Visibility.GetBool(ImHashStr(entry->Compiler), true) && _Visibility.GetBool(ImHashStr(entry->Cpu), true) && _Visibility.GetBool(ImHashStr(entry->OS), true) && _Visibility.GetBool(ImHashStr(entry->BuildType), true); } bool ImGuiPerfTool::_IsVisibleTest(const char* test_name) { return _Visibility.GetBool(ImHashStr(test_name), true); } void ImGuiPerfTool::_CalculateLegendAlignment() { // Estimate paddings for legend format so it looks nice and aligned // FIXME: Rely on font being monospace. May need to recalculate every frame on a per-need basis based on font? _AlignStress = _AlignType = _AlignCpu = _AlignOs = _AlignCompiler = _AlignBranch = _AlignSamples = 0; for (ImGuiPerfToolBatch& batch : _Batches) { if (batch.Entries.empty()) continue; ImGuiPerfToolEntry* entry = &batch.Entries.Data[0]; if (!_IsVisibleBuild(entry)) continue; _AlignStress = ImMax(_AlignStress, (int)ceil(log10(entry->PerfStressAmount))); _AlignType = ImMax(_AlignType, (int)strlen(entry->BuildType)); _AlignCpu = ImMax(_AlignCpu, (int)strlen(entry->Cpu)); _AlignOs = ImMax(_AlignOs, (int)strlen(entry->OS)); _AlignCompiler = ImMax(_AlignCompiler, (int)strlen(entry->Compiler)); _AlignBranch = ImMax(_AlignBranch, (int)strlen(entry->GitBranchName)); _AlignSamples = ImMax(_AlignSamples, (int)Str16f("%d", entry->NumSamples).length()); } } bool ImGuiPerfTool::SaveHtmlReport(const char* file_name, const char* image_file) { if (!ImFileCreateDirectoryChain(file_name, ImPathFindFilename(file_name))) return false; FILE* fp = fopen(file_name, "w+"); if (fp == nullptr) return false; fprintf(fp, "\n" "\n" "\n" " \n" " Dear ImGui perf report\n" "\n" "\n" "
\n");

    // Embed performance chart.
    fprintf(fp, "## Dear ImGui perf report\n\n");

    if (image_file != nullptr)
    {
        FILE* fp_img = fopen(image_file, "rb");
        if (fp_img != nullptr)
        {
            ImVector image_buffer;
            ImVector base64_buffer;
            fseek(fp_img, 0, SEEK_END);
            image_buffer.resize((int)ftell(fp_img));
            base64_buffer.resize(((image_buffer.Size / 3) + 1) * 4 + 1);
            rewind(fp_img);
            fread(image_buffer.Data, 1, image_buffer.Size, fp_img);
            fclose(fp_img);
            int len = ImStrBase64Encode((unsigned char*)image_buffer.Data, base64_buffer.Data, image_buffer.Size);
            base64_buffer.Data[len] = 0;
            fprintf(fp, "![](data:image/png;base64,%s)\n\n", base64_buffer.Data);
        }
    }

    // Print info table.
    const bool combine_by_build_info = _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo;
    for (const auto& column_info : PerfToolColumnInfo)
        if (column_info.ShowAlways || combine_by_build_info)
            fprintf(fp, "| %s ", column_info.Title);
    fprintf(fp, "|\n");
    for (const auto& column_info : PerfToolColumnInfo)
        if (column_info.ShowAlways || combine_by_build_info)
            fprintf(fp, "| -- ");
    fprintf(fp, "|\n");

    for (int row_index = _InfoTableSort.Size - 1; row_index >= 0; row_index--)
    {
        int batch_index_sorted, entry_index_sorted;
        _UnpackSortedKey(_InfoTableSort[row_index], &batch_index_sorted, &entry_index_sorted);
        ImGuiPerfToolBatch* batch = &_Batches[batch_index_sorted];
        ImGuiPerfToolEntry* entry = &batch->Entries[entry_index_sorted];
        const char* test_name = entry->TestName;
        if (!_IsVisibleBuild(entry) || entry->NumSamples == 0)
            continue;

        ImGuiPerfToolEntry* baseline_entry = GetEntryByBatchIdx(_BaselineBatchIndex, test_name);
        for (int i = 0; i < IM_ARRAYSIZE(PerfToolColumnInfo); i++)
        {
            Str30f label("");
            const ImGuiPerfToolColumnInfo& column_info = PerfToolColumnInfo[i];
            if (column_info.ShowAlways || combine_by_build_info)
            {
                switch (i)
                {
                case 0:
                {
                    char date[64];
                    FormatDateAndTime(entry->Timestamp, date, IM_ARRAYSIZE(date));
                    fprintf(fp, "| %s ", date);
                    break;
                }
                case 1:  fprintf(fp, "| %s ", entry->TestName);             break;
                case 2:  fprintf(fp, "| %s ", entry->GitBranchName);        break;
                case 3:  fprintf(fp, "| %s ", entry->Compiler);             break;
                case 4:  fprintf(fp, "| %s ", entry->OS);                   break;
                case 5:  fprintf(fp, "| %s ", entry->Cpu);                  break;
                case 6:  fprintf(fp, "| %s ", entry->BuildType);            break;
                case 7:  fprintf(fp, "| x%d ", entry->PerfStressAmount);    break;
                case 8:  fprintf(fp, "| %.2f ", entry->DtDeltaMs);          break;
                case 9:  fprintf(fp, "| %.2f ", entry->DtDeltaMsMin);       break;
                case 10: fprintf(fp, "| %.2f ", entry->DtDeltaMsMax);       break;
                case 11: fprintf(fp, "| %d ", entry->NumSamples);           break;
                case 12: FormatVsBaseline(entry, baseline_entry, label); fprintf(fp, "| %s ", label.c_str()); break;
                default: IM_ASSERT(0); break;
                }
            }
        }
        fprintf(fp, "|\n");
    }

    fprintf(fp, "
\n" " \n" " \n" "\n" "\n"); fclose(fp); return true; } void ImGuiPerfTool::_SetBaseline(int batch_index) { IM_ASSERT(batch_index < _Batches.Size); _BaselineBatchIndex = batch_index; if (batch_index >= 0) { _BaselineTimestamp = _Batches.Data[batch_index].Entries.Data[0].Timestamp; _BaselineBuildId = GetBuildID(&_Batches.Data[batch_index]); } } //------------------------------------------------------------------------- // [SECTION] USER INTERFACE //------------------------------------------------------------------------- void ImGuiPerfTool::ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open) { if (!ImGui::Begin("Dear ImGui Perf Tool", p_open)) { ImGui::End(); return; } if (ImGui::IsWindowAppearing() && Empty()) LoadCSV(); // ----------------------------------------------------------------------------------------------------------------- // Render utility buttons // ----------------------------------------------------------------------------------------------------------------- // Date filter ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Date Range:"); ImGui::SameLine(); bool dirty = _Batches.empty(); bool date_changed = InputDate("##date-from", _FilterDateFrom, IM_ARRAYSIZE(_FilterDateFrom), (strcmp(_FilterDateFrom, _FilterDateTo) <= 0 || !*_FilterDateTo)); if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) ImGui::OpenPopup("InputDate From Menu"); ImGui::SameLine(0, 0.0f); ImGui::TextUnformatted(".."); ImGui::SameLine(0, 0.0f); date_changed |= InputDate("##date-to", _FilterDateTo, IM_ARRAYSIZE(_FilterDateTo), (strcmp(_FilterDateFrom, _FilterDateTo) <= 0 || !*_FilterDateFrom)); if (date_changed) { dirty = (!_FilterDateFrom[0] || IsDateValid(_FilterDateFrom)) && (!_FilterDateTo[0] || IsDateValid(_FilterDateTo)); if (_FilterDateFrom[0] && _FilterDateTo[0]) dirty &= strcmp(_FilterDateFrom, _FilterDateTo) <= 0; } if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) ImGui::OpenPopup("InputDate To Menu"); ImGui::SameLine(); for (int i = 0; i < 2; i++) { if (ImGui::BeginPopup(i == 0 ? "InputDate From Menu" : "InputDate To Menu")) { char* date = i == 0 ? _FilterDateFrom : _FilterDateTo; int date_size = i == 0 ? IM_ARRAYSIZE(_FilterDateFrom) : IM_ARRAYSIZE(_FilterDateTo); if (i == 0 && ImGui::MenuItem("Set Min")) { for (ImGuiPerfToolEntry& entry : _SrcData) if (strcmp(date, entry.Date) > 0) { ImStrncpy(date, entry.Date, date_size); dirty = true; } } if (ImGui::MenuItem("Set Max")) { for (ImGuiPerfToolEntry& entry : _SrcData) if (strcmp(date, entry.Date) < 0) { ImStrncpy(date, entry.Date, date_size); dirty = true; } } if (ImGui::MenuItem("Set Today")) { time_t now = time(nullptr); FormatDate((ImU64)now * 1000000, date, date_size); dirty = true; } ImGui::EndPopup(); } } if (ImGui::Button(Str64f("Filter builds (%d/%d)###Filter builds", _NumVisibleBuilds, _NumUniqueBuilds).c_str())) ImGui::OpenPopup("Filter builds"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Hide or show individual builds."); ImGui::SameLine(); if (ImGui::Button(Str64f("Filter tests (%d/%d)###Filter tests", _LabelsVisible.Size, _Labels.Size).c_str())) ImGui::OpenPopup("Filter perfs"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Hide or show individual tests."); ImGui::SameLine(); dirty |= Button3("Combine", (int*)&_DisplayType); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::RadioButton("Display each run separately", _DisplayType == ImGuiPerfToolDisplayType_Simple); ImGui::RadioButton("Use one color per branch. Disables baseline comparisons!", _DisplayType == ImGuiPerfToolDisplayType_PerBranchColors); ImGui::RadioButton("Combine multiple runs with same build info into one averaged build entry.", _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo); ImGui::EndTooltip(); } ImGui::SameLine(); if (_ReportGenerating && ImGuiTestEngine_IsTestQueueEmpty(engine)) { _ReportGenerating = false; ImOsOpenInShell(PerfToolReportDefaultOutputPath); } if (_Batches.empty()) ImGui::BeginDisabled(); if (ImGui::Button("Html Export")) { // In order to capture a screenshot Report is saved by executing a "capture_perf_report" test. _ReportGenerating = true; ImGuiTestEngine_QueueTests(engine, ImGuiTestGroup_Tests, "capture_perf_report"); } if (_Batches.empty()) ImGui::EndDisabled(); ImGui::SameLine(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Generate a report and open it in the browser."); // Align help button to the right. ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImMax(0.0f, ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize("(?)").x)); ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::BulletText("To change baseline build, double-click desired build in the legend."); ImGui::BulletText("Extra information is displayed when hovering bars of a particular perf test and holding SHIFT."); ImGui::BulletText("Double-click plot to fit plot into available area."); ImGui::EndTooltip(); } if (ImGui::BeginPopup("Filter builds")) { ImGuiStorage& temp_set = _TempSet; temp_set.Data.resize(0); // ImHashStr(BuildProperty):seen static const char* columns[] = { "Branch", "Build", "CPU", "OS", "Compiler" }; bool show_all = ImGui::Button("Show All"); ImGui::SameLine(); bool hide_all = ImGui::Button("Hide All"); if (ImGui::BeginTable("Builds", IM_ARRAYSIZE(columns), ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { for (int i = 0; i < IM_ARRAYSIZE(columns); i++) ImGui::TableSetupColumn(columns[i]); ImGui::TableHeadersRow(); // Find columns with nothing checked. bool checked_any[] = { false, false, false, false, false }; for (ImGuiPerfToolEntry& entry : _SrcData) { const char* properties[] = { entry.GitBranchName, entry.BuildType, entry.Cpu, entry.OS, entry.Compiler }; for (int i = 0; i < IM_ARRAYSIZE(properties); i++) { ImGuiID hash = ImHashStr(properties[i]); checked_any[i] |= _Visibility.GetBool(hash, true); } } int property_offsets[] = { offsetof(ImGuiPerfToolEntry, GitBranchName), offsetof(ImGuiPerfToolEntry, BuildType), offsetof(ImGuiPerfToolEntry, Cpu), offsetof(ImGuiPerfToolEntry, OS), offsetof(ImGuiPerfToolEntry, Compiler), }; ImGui::TableNextRow(); for (int i = 0; i < IM_ARRAYSIZE(property_offsets); i++) { ImGui::TableSetColumnIndex(i); for (ImGuiPerfToolEntry& entry : _SrcData) { const char* property = *(const char**)((const char*)&entry + property_offsets[i]); ImGuiID hash = ImHashStr(property); if (temp_set.GetBool(hash)) continue; temp_set.SetBool(hash, true); bool visible = _Visibility.GetBool(hash, true) || show_all; if (hide_all) visible = false; bool modified = ImGui::Checkbox(property, &visible) || show_all || hide_all; _Visibility.SetBool(hash, visible); if (modified) { _CalculateLegendAlignment(); _NumVisibleBuilds = PerfToolCountBuilds(this, true); dirty = true; } if (!checked_any[i]) { ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImColor(1.0f, 0.0f, 0.0f, 0.2f)); if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) ImGui::SetTooltip("Check at least one item in each column to see any data."); } } } ImGui::EndTable(); } ImGui::EndPopup(); } if (ImGui::BeginPopup("Filter perfs")) { dirty |= RenderMultiSelectFilter(this, "Filter by perf test", &_Labels); if (ImGui::IsKeyPressed(ImGuiKey_Escape)) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } if (dirty) _Rebuild(); // Rendering a plot of empty dataset is not possible. if (_Batches.empty() || _LabelsVisible.Size == 0 || _NumVisibleBuilds == 0) { ImGui::TextUnformatted("No data is available. Run some perf tests or adjust filter settings."); } else { #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT // Splitter between two following child windows is rendered first. ImGuiStyle& style = ImGui::GetStyle(); float plot_height = 0.0f; float& table_height = _InfoTableHeight; ImGui::Splitter("splitter", &plot_height, &table_height, ImGuiAxis_Y, +1); // Double-click to move splitter to bottom if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { table_height = 0; plot_height = ImGui::GetContentRegionAvail().y - style.ItemSpacing.y; ImGui::ClearActiveID(); } // Render entries plot if (ImGui::BeginChild(ImGui::GetID("plot"), ImVec2(0, plot_height))) _ShowEntriesPlot(); ImGui::EndChild(); // Render entries tables if (table_height > 0.0f) { if (ImGui::BeginChild(ImGui::GetID("info-table"), ImVec2(0, table_height))) _ShowEntriesTable(); ImGui::EndChild(); } #else _ShowEntriesTable(); #endif } ImGui::End(); } #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT static double GetLabelVerticalOffset(double occupy_h, int max_visible_builds, int now_visible_builds) { const double h = occupy_h / (float)max_visible_builds; double offset = -h * ((max_visible_builds - 1) * 0.5); return (double)now_visible_builds * h + offset; } #endif void ImGuiPerfTool::_ShowEntriesPlot() { #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT ImGuiIO& io = ImGui::GetIO(); ImGuiStyle& style = ImGui::GetStyle(); Str256 label; Str256 display_label; ImPlot::PushStyleColor(ImPlotCol_AxisBgHovered, IM_COL32(0, 0, 0, 0)); ImPlot::PushStyleColor(ImPlotCol_AxisBgActive, IM_COL32(0, 0, 0, 0)); if (!ImPlot::BeginPlot("PerfTool", ImVec2(-1, -1), ImPlotFlags_NoTitle)) return; ImPlot::SetupAxis(ImAxis_X1, nullptr, ImPlotAxisFlags_NoTickLabels); if (_LabelsVisible.Size > 1) { ImPlot::SetupAxisTicks(ImAxis_Y1, 0, _LabelsVisible.Size, _LabelsVisible.Size, _LabelsVisible.Data); } else if (_LabelsVisible.Size == 1) { const char* labels[] = { _LabelsVisible[0], "" }; ImPlot::SetupAxisTicks(ImAxis_Y1, 0, _LabelsVisible.Size, 2, labels); } ImPlot::SetupLegend(ImPlotLocation_NorthEast); // Amount of vertical space bars of one label will occupy. 1.0 would leave no space between bars of adjacent labels. const float occupy_h = 0.8f; // Plot bars bool legend_hovered = false; ImGuiStorage& temp_set = _TempSet; temp_set.Data.resize(0); // ImHashStr(TestName):now_visible_builds_i int current_baseline_batch_index = _BaselineBatchIndex; // Cache this value before loop, so toggling it does not create flicker. for (int batch_index = 0; batch_index < _Batches.Size; batch_index++) { ImGuiPerfToolBatch& batch = _Batches[batch_index]; if (!_IsVisibleBuild(&batch.Entries.Data[0])) continue; // Plot bars. label.clear(); display_label.clear(); PerfToolFormatBuildInfo(this, &label, &batch); display_label.append(label.c_str()); ImGuiID batch_label_id; bool baseline_match = false; if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors) { // No "vs baseline" comparison for per-branch colors, because runs are combined in the legend, but not in the info table. batch_label_id = GetBuildID(&batch); } else { batch_label_id = ImHashData(&batch.BatchID, sizeof(batch.BatchID)); baseline_match = current_baseline_batch_index == batch_index; } display_label.appendf("%s###%08X", baseline_match ? " *" : "", batch_label_id); // Plot all bars one by one, so batches with varying number of bars would not contain empty holes. for (ImGuiPerfToolEntry& entry : batch.Entries) { if (entry.NumSamples == 0) continue; // Dummy entry, perf did not run for this test in this batch. ImGuiID label_id = ImHashStr(entry.TestName); const int max_visible_builds = _LabelBarCounts.GetInt(label_id); const int now_visible_builds = temp_set.GetInt(label_id); temp_set.SetInt(label_id, now_visible_builds + 1); double y_pos = (double)entry.LabelIndex + GetLabelVerticalOffset(occupy_h, max_visible_builds, now_visible_builds); ImPlot::SetNextFillStyle(ImPlot::GetColormapColor(_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors ? batch.BranchIndex : batch_index)); ImPlot::PlotBars(display_label.c_str(), &entry.DtDeltaMs, &y_pos, 1, occupy_h / (double)max_visible_builds, ImPlotBarsFlags_Horizontal); } legend_hovered |= ImPlot::IsLegendEntryHovered(display_label.c_str()); // Set baseline. if (ImPlot::IsLegendEntryHovered(display_label.c_str())) { if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) _SetBaseline(batch_index); } } // Plot highlights. ImPlotContext& gp = *GImPlot; ImPlotPlot& plot = *gp.CurrentPlot; _PlotHoverTest = -1; _PlotHoverBatch = -1; _PlotHoverTestLabel = false; bool can_highlight = !legend_hovered && (ImPlot::IsPlotHovered() || ImPlot::IsAxisHovered(ImAxis_Y1)); ImDrawList* plot_draw_list = ImPlot::GetPlotDrawList(); // Highlight bars when hovering a label. int hovered_label_index = -1; for (int i = 0; i < _LabelsVisible.Size && can_highlight; i++) { ImRect label_rect_loose = ImPlotGetYTickRect(i); // Rect around test label ImRect label_rect_tight; // Rect around test label, covering bar height and label area width label_rect_tight.Min.y = ImPlot::PlotToPixels(0, (float)i + 0.5f).y; label_rect_tight.Max.y = ImPlot::PlotToPixels(0, (float)i - 0.5f).y; label_rect_tight.Min.x = plot.CanvasRect.Min.x; label_rect_tight.Max.x = plot.PlotRect.Min.x; ImRect rect_bars; // Rect around bars only rect_bars.Min.x = plot.PlotRect.Min.x; rect_bars.Max.x = plot.PlotRect.Max.x; rect_bars.Min.y = ImPlot::PlotToPixels(0, (float)i + 0.5f).y; rect_bars.Max.y = ImPlot::PlotToPixels(0, (float)i - 0.5f).y; // Render underline signaling it is clickable. Clicks are handled when rendering info table. if (label_rect_loose.Contains(io.MousePos)) { ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); plot_draw_list->AddLine(ImFloor(label_rect_loose.GetBL()), ImFloor(label_rect_loose.GetBR()), ImColor(style.Colors[ImGuiCol_Text])); } // Highlight bars belonging to hovered label. if (label_rect_tight.Contains(io.MousePos)) { plot_draw_list->AddRectFilled(rect_bars.Min, rect_bars.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg])); _PlotHoverTestLabel = true; _PlotHoverTest = i; } if (rect_bars.Contains(io.MousePos)) hovered_label_index = i; } // Highlight individual bars when hovering them on the plot or info table. temp_set.Data.resize(0); // ImHashStr(hovered_label):now_visible_builds_i if (hovered_label_index < 0) hovered_label_index = _TableHoveredTest; if (hovered_label_index >= 0) { const char* hovered_label = _LabelsVisible.Data[hovered_label_index]; ImGuiID label_id = ImHashStr(hovered_label); for (ImGuiPerfToolBatch& batch : _Batches) { int batch_index = _Batches.index_from_ptr(&batch); if (!_IsVisibleBuild(&batch)) continue; ImGuiPerfToolEntry* entry = &batch.Entries.Data[hovered_label_index]; if (entry->NumSamples == 0) continue; // Dummy entry, perf did not run for this test in this batch. int max_visible_builds = _LabelBarCounts.GetInt(label_id); const int now_visible_builds = temp_set.GetInt(label_id); temp_set.SetInt(label_id, now_visible_builds + 1); float h = occupy_h / (float)max_visible_builds; float y_pos = (float)entry->LabelIndex; y_pos += (float)GetLabelVerticalOffset(occupy_h, max_visible_builds, now_visible_builds); ImRect rect_bar; // Rect around hovered bar only rect_bar.Min.x = plot.PlotRect.Min.x; rect_bar.Max.x = plot.PlotRect.Max.x; rect_bar.Min.y = ImPlot::PlotToPixels(0, y_pos - h * 0.5f + h).y; // ImPlot y_pos is for bar center, therefore we adjust positions by half-height to get a bounding box. rect_bar.Max.y = ImPlot::PlotToPixels(0, y_pos - h * 0.5f).y; // Mouse is hovering label or bars of a perf test - highlight them in info table. if (_PlotHoverTest < 0 && rect_bar.Min.y <= io.MousePos.y && io.MousePos.y < rect_bar.Max.y && io.MousePos.x > plot.PlotRect.Min.x) { // _LabelsVisible is inverted to make perf test order match info table order. Revert it back. _PlotHoverTest = hovered_label_index; _PlotHoverBatch = batch_index; plot_draw_list->AddRectFilled(rect_bar.Min, rect_bar.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg])); } // Mouse is hovering a row in info table - highlight relevant bars on the plot. if (_TableHoveredBatch == batch_index && _TableHoveredTest == hovered_label_index) plot_draw_list->AddRectFilled(rect_bar.Min, rect_bar.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg])); } } if (io.KeyShift && _PlotHoverTest >= 0) { // Info tooltip with delta times of each batch for a hovered test. const char* test_name = _LabelsVisible.Data[_PlotHoverTest]; ImGui::BeginTooltip(); float w = ImGui::CalcTextSize(test_name).x; float total_w = ImGui::GetContentRegionAvail().x; if (total_w > w) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (total_w - w) * 0.5f); ImGui::TextUnformatted(test_name); for (int i = 0; i < _Batches.Size; i++) { if (ImGuiPerfToolEntry* hovered_entry = GetEntryByBatchIdx(i, test_name)) ImGui::Text("%s %.3fms", label.c_str(), hovered_entry->DtDeltaMs); else ImGui::Text("%s --", label.c_str()); } ImGui::EndTooltip(); } ImPlot::EndPlot(); ImPlot::PopStyleColor(2); #else ImGui::TextUnformatted("Not enabled because ImPlot is not available (IMGUI_TEST_ENGINE_ENABLE_IMPLOT=0)."); #endif } void ImGuiPerfTool::_ShowEntriesTable() { ImGuiTableFlags table_flags = ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SortTristate | ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY; if (!ImGui::BeginTable("PerfInfo", IM_ARRAYSIZE(PerfToolColumnInfo), table_flags)) return; ImGuiStyle& style = ImGui::GetStyle(); int num_visible_labels = _LabelsVisible.Size; // Test name column is not sorted because we do sorting only within perf runs of a particular tests, // so as far as sorting function is concerned all items in first column are identical. for (int i = 0; i < IM_ARRAYSIZE(PerfToolColumnInfo); i++) { const ImGuiPerfToolColumnInfo& info = PerfToolColumnInfo[i]; ImGuiTableColumnFlags column_flags = info.Flags; if (i == 0 && _DisplayType != ImGuiPerfToolDisplayType_Simple) column_flags |= ImGuiTableColumnFlags_Disabled; // Date only visible in non-combining mode. if (!info.ShowAlways && _DisplayType != ImGuiPerfToolDisplayType_CombineByBuildInfo) column_flags |= ImGuiTableColumnFlags_Disabled; ImGui::TableSetupColumn(info.Title, column_flags); } ImGui::TableSetupScrollFreeze(0, 1); if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) if (sorts_specs->SpecsDirty || _InfoTableSortDirty) { // Fill sort table with unsorted indices. sorts_specs->SpecsDirty = _InfoTableSortDirty = false; // Reinitialize sorting table to unsorted state. _InfoTableSort.resize(num_visible_labels * _Batches.Size); for (int entry_index = 0, i = 0; entry_index < num_visible_labels; entry_index++) for (int batch_index = 0; batch_index < _Batches.Size; batch_index++, i++) _InfoTableSort.Data[i] = (((ImU64)batch_index * num_visible_labels + entry_index) << 24) | i; // Sort batches of each label. if (sorts_specs->SpecsCount > 0) { _InfoTableSortSpecs = sorts_specs; PerfToolInstance = this; ImQsort(_InfoTableSort.Data, (size_t)_InfoTableSort.Size, sizeof(_InfoTableSort.Data[0]), CompareWithSortSpecs); _InfoTableSortSpecs = nullptr; PerfToolInstance = nullptr; } } ImGui::TableHeadersRow(); // ImPlot renders bars from bottom to the top. We want bars to render from top to the bottom, therefore we loop // labels and batches in reverse order. _TableHoveredTest = -1; _TableHoveredBatch = -1; const bool scroll_into_view = _PlotHoverTestLabel && ImGui::IsMouseClicked(ImGuiMouseButton_Left); const float header_row_height = ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetHeight(); ImRect scroll_into_view_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (int row_index = _InfoTableSort.Size - 1; row_index >= 0; row_index--) { int batch_index_sorted, entry_index_sorted; _UnpackSortedKey(_InfoTableSort[row_index], &batch_index_sorted, &entry_index_sorted); ImGuiPerfToolBatch* batch = &_Batches[batch_index_sorted]; ImGuiPerfToolEntry* entry = &batch->Entries[entry_index_sorted]; const char* test_name = entry->TestName; if (!_IsVisibleBuild(entry) || !_IsVisibleTest(entry->TestName) || entry->NumSamples == 0) continue; ImGui::PushID(entry); ImGui::TableNextRow(); if (row_index & 1) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_TableRowBgAlt, 0.5f)); else ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_TableRowBg, 0.5f)); if (_PlotHoverTest == entry_index_sorted) { // Highlight a row that corresponds to hovered bar, or all rows that correspond to hovered perf test label. if (_PlotHoverBatch == batch_index_sorted || _PlotHoverTestLabel) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImColor(style.Colors[ImGuiCol_TextSelectedBg])); } ImGuiPerfToolEntry* baseline_entry = GetEntryByBatchIdx(_BaselineBatchIndex, test_name); // Date if (ImGui::TableNextColumn()) { char date[64]; FormatDateAndTime(entry->Timestamp, date, IM_ARRAYSIZE(date)); ImGui::TextUnformatted(date); } // Build info if (ImGui::TableNextColumn()) { // ImGuiSelectableFlags_Disabled + changing ImGuiCol_TextDisabled color prevents selectable from overriding table highlight behavior. ImGui::PushStyleColor(ImGuiCol_Header, style.Colors[ImGuiCol_Text]); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, style.Colors[ImGuiCol_TextSelectedBg]); ImGui::PushStyleColor(ImGuiCol_HeaderActive, style.Colors[ImGuiCol_TextSelectedBg]); ImGui::Selectable(entry->TestName, false, ImGuiSelectableFlags_SpanAllColumns); ImGui::PopStyleColor(3); if (ImGui::IsItemHovered()) { _TableHoveredTest = entry_index_sorted; _TableHoveredBatch = batch_index_sorted; } if (ImGui::BeginPopupContextItem()) { if (entry == baseline_entry) ImGui::BeginDisabled(); if (ImGui::MenuItem("Set as baseline")) _SetBaseline(batch_index_sorted); if (entry == baseline_entry) ImGui::EndDisabled(); ImGui::EndPopup(); } } if (ImGui::TableNextColumn()) ImGui::TextUnformatted(entry->GitBranchName); if (ImGui::TableNextColumn()) ImGui::TextUnformatted(entry->Compiler); if (ImGui::TableNextColumn()) ImGui::TextUnformatted(entry->OS); if (ImGui::TableNextColumn()) ImGui::TextUnformatted(entry->Cpu); if (ImGui::TableNextColumn()) ImGui::TextUnformatted(entry->BuildType); if (ImGui::TableNextColumn()) ImGui::Text("x%d", entry->PerfStressAmount); // Avg ms if (ImGui::TableNextColumn()) ImGui::Text("%.3lf", entry->DtDeltaMs); // Min ms if (ImGui::TableNextColumn()) ImGui::Text("%.3lf", entry->DtDeltaMsMin); // Max ms if (ImGui::TableNextColumn()) ImGui::Text("%.3lf", entry->DtDeltaMsMax); // Num samples if (ImGui::TableNextColumn()) ImGui::Text("%d", entry->NumSamples); // VS Baseline if (ImGui::TableNextColumn()) { float dt_change = (float)entry->VsBaseline; if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors) { ImGui::TextUnformatted("--"); } else { Str30 label; dt_change = FormatVsBaseline(entry, baseline_entry, label); ImGui::TextUnformatted(label.c_str()); if (dt_change != entry->VsBaseline) { entry->VsBaseline = dt_change; _InfoTableSortDirty = true; // Force re-sorting. } } } if (_PlotHoverTest == entry_index_sorted && scroll_into_view) { ImGuiTable* table = ImGui::GetCurrentTable(); scroll_into_view_rect.Add(ImGui::TableGetCellBgRect(table, 0)); } ImGui::PopID(); } if (scroll_into_view) { scroll_into_view_rect.Min.y -= header_row_height; // FIXME-TABLE: Compensate for frozen header row covering a first content row scrolled into view. ImGui::ScrollToRect(ImGui::GetCurrentWindow(), scroll_into_view_rect, ImGuiScrollFlags_NoScrollParent); } ImGui::EndTable(); } //------------------------------------------------------------------------- // [SECTION] SETTINGS //------------------------------------------------------------------------- static void PerflogSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler) { ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData; perftool->_Visibility.Clear(); } static void* PerflogSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char*) { return (void*)1; } static void PerflogSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler* ini_handler, void*, const char* line) { ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData; char buf[128]; int visible = -1, display_type = -1; /**/ if (sscanf(line, "DateFrom=%10s", perftool->_FilterDateFrom)) {} else if (sscanf(line, "DateTo=%10s", perftool->_FilterDateTo)) {} else if (sscanf(line, "DisplayType=%d", &display_type)) { perftool->_DisplayType = (ImGuiPerfToolDisplayType)display_type; } else if (sscanf(line, "BaselineBuildId=%llu", &perftool->_BaselineBuildId)) {} else if (sscanf(line, "BaselineTimestamp=%llu", &perftool->_BaselineTimestamp)) {} else if (sscanf(line, "TestVisibility=%[^,],%d", buf, &visible) == 2) { perftool->_Visibility.SetBool(ImHashStr(buf), !!visible); } else if (sscanf(line, "BuildVisibility=%[^,],%d", buf, &visible) == 2) { perftool->_Visibility.SetBool(ImHashStr(buf), !!visible); } } static void PerflogSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler) { ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData; perftool->_Batches.clear_destruct(); perftool->_SetBaseline(-1); } static void PerflogSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler, ImGuiTextBuffer* buf) { ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData; if (perftool->_Batches.empty()) return; buf->appendf("[%s][Data]\n", ini_handler->TypeName); buf->appendf("DateFrom=%s\n", perftool->_FilterDateFrom); buf->appendf("DateTo=%s\n", perftool->_FilterDateTo); buf->appendf("DisplayType=%d\n", perftool->_DisplayType); buf->appendf("BaselineBuildId=%llu\n", perftool->_BaselineBuildId); buf->appendf("BaselineTimestamp=%llu\n", perftool->_BaselineTimestamp); for (const char* label : perftool->_Labels) buf->appendf("TestVisibility=%s,%d\n", label, perftool->_Visibility.GetBool(ImHashStr(label), true)); ImGuiStorage& temp_set = perftool->_TempSet; temp_set.Data.clear(); for (ImGuiPerfToolEntry& entry : perftool->_SrcData) { const char* properties[] = { entry.GitBranchName, entry.BuildType, entry.Cpu, entry.OS, entry.Compiler }; for (int i = 0; i < IM_ARRAYSIZE(properties); i++) { ImGuiID hash = ImHashStr(properties[i]); if (!temp_set.GetBool(hash)) { temp_set.SetBool(hash, true); buf->appendf("BuildVisibility=%s,%d\n", properties[i], perftool->_Visibility.GetBool(hash, true)); } } } buf->append("\n"); } void ImGuiPerfTool::_AddSettingsHandler() { ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "TestEnginePerfTool"; ini_handler.TypeHash = ImHashStr("TestEnginePerfTool"); ini_handler.ClearAllFn = PerflogSettingsHandler_ClearAll; ini_handler.ReadOpenFn = PerflogSettingsHandler_ReadOpen; ini_handler.ReadLineFn = PerflogSettingsHandler_ReadLine; ini_handler.ApplyAllFn = PerflogSettingsHandler_ApplyAll; ini_handler.WriteAllFn = PerflogSettingsHandler_WriteAll; ini_handler.UserData = this; ImGui::AddSettingsHandler(&ini_handler); } void ImGuiPerfTool::_UnpackSortedKey(ImU64 key, int* batch_index, int* entry_index, int* monotonic_index) { IM_ASSERT(batch_index != nullptr); IM_ASSERT(entry_index != nullptr); const int num_visible_labels = _LabelsVisible.Size; *batch_index = (int)((key >> 24) / num_visible_labels); *entry_index = (int)((key >> 24) % num_visible_labels); if (monotonic_index) *monotonic_index = (int)(key & 0xFFFFFF); } //------------------------------------------------------------------------- // [SECTION] TESTS //------------------------------------------------------------------------- static bool SetPerfToolWindowOpen(ImGuiTestContext* ctx, bool is_open) { ctx->MenuClick("//Dear ImGui Test Engine/Tools"); bool was_open = ctx->ItemIsChecked("//$FOCUSED/Perf Tool"); ctx->MenuAction(is_open ? ImGuiTestAction_Check : ImGuiTestAction_Uncheck, "//Dear ImGui Test Engine/Tools/Perf Tool"); return was_open; } void RegisterTests_TestEnginePerfTool(ImGuiTestEngine* e) { ImGuiTest* t = nullptr; // ## Flex perf tool code. t = IM_REGISTER_TEST(e, "testengine", "testengine_cov_perftool"); t->GuiFunc = [](ImGuiTestContext* ctx) { IM_UNUSED(ctx); ImGui::Begin("Test Func", nullptr, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize); int loop_count = 1000; bool v1 = false, v2 = true; for (int n = 0; n < loop_count / 2; n++) { ImGui::PushID(n); ImGui::Checkbox("Hello, world", &v1); ImGui::Checkbox("Hello, world", &v2); ImGui::PopID(); } ImGui::End(); }; t->TestFunc = [](ImGuiTestContext* ctx) { ImGuiPerfTool* perftool = ImGuiTestEngine_GetPerfTool(ctx->Engine); const char* temp_perf_csv = "output/misc_cov_perf_tool.csv"; Str16f min_date_bkp = perftool->_FilterDateFrom; Str16f max_date_bkp = perftool->_FilterDateTo; // Execute few perf tests, serialize them to temporary csv file. ctx->PerfIterations = 50; // Make faster ctx->PerfCapture("perf", "misc_cov_perf_tool_1", temp_perf_csv); ctx->PerfCapture("perf", "misc_cov_perf_tool_2", temp_perf_csv); // Load perf data from csv file and open perf tool. perftool->Clear(); perftool->LoadCSV(temp_perf_csv); bool perf_was_open = SetPerfToolWindowOpen(ctx, true); ctx->Yield(); ImGuiWindow* window = ctx->GetWindowByRef("Dear ImGui Perf Tool"); IM_CHECK(window != nullptr); ImVec2 pos_bkp = window->Pos; ImVec2 size_bkp = window->Size; ctx->SetRef(window); ctx->WindowMove("", ImVec2(50, 50)); ctx->WindowResize("", ImVec2(1400, 900)); #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT ImGuiWindow* plot_child = ctx->WindowInfo("plot").Window; // "plot/PerfTool" prior to implot 2023/08/21 IM_CHECK(plot_child != nullptr); // Move legend to right side. ctx->MouseMoveToPos(plot_child->Rect().GetCenter()); ctx->MouseDoubleClick(ImGuiMouseButton_Left); // Auto-size plots while at it ctx->MouseClick(ImGuiMouseButton_Right); ctx->MenuClick("//$FOCUSED/Legend/NE"); // Click some stuff for more coverage. ctx->MouseMoveToPos(plot_child->Rect().GetCenter()); ctx->KeyPress(ImGuiMod_Shift); #endif ctx->ItemClick("##date-from", ImGuiMouseButton_Right); ctx->ItemClick(ctx->GetID("//$FOCUSED/Set Min")); ctx->ItemClick("##date-to", ImGuiMouseButton_Right); ctx->ItemClick(ctx->GetID("//$FOCUSED/Set Max")); ctx->ItemClick("###Filter builds"); ctx->ItemClick("###Filter tests"); ctx->ItemClick("Combine", 0, ImGuiTestOpFlags_MoveToEdgeL); // Toggle thrice to leave state unchanged ctx->ItemClick("Combine", 0, ImGuiTestOpFlags_MoveToEdgeL); ctx->ItemClick("Combine", 0, ImGuiTestOpFlags_MoveToEdgeL); // Restore original state. perftool->Clear(); // Clear test data and load original data ImFileDelete(temp_perf_csv); perftool->LoadCSV(); ctx->Yield(); #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT ctx->MouseMoveToPos(plot_child->Rect().GetCenter()); ctx->MouseDoubleClick(ImGuiMouseButton_Left); // Fit plot to original data #endif ImStrncpy(perftool->_FilterDateFrom, min_date_bkp.c_str(), IM_ARRAYSIZE(perftool->_FilterDateFrom)); ImStrncpy(perftool->_FilterDateTo, max_date_bkp.c_str(), IM_ARRAYSIZE(perftool->_FilterDateTo)); ImGui::SetWindowPos(window, pos_bkp); ImGui::SetWindowSize(window, size_bkp); SetPerfToolWindowOpen(ctx, perf_was_open); // Restore window visibility }; // ## Capture perf tool graph. t = IM_REGISTER_TEST(e, "capture", "capture_perf_report"); t->TestFunc = [](ImGuiTestContext* ctx) { ImGuiPerfTool* perftool = ImGuiTestEngine_GetPerfTool(ctx->Engine); const char* perf_report_image = nullptr; if (!ImFileExist(IMGUI_PERFLOG_DEFAULT_FILENAME)) { ctx->LogWarning("Perf tool has no data. Perf report generation was aborted."); return; } char min_date_bkp[sizeof(perftool->_FilterDateFrom)], max_date_bkp[sizeof(perftool->_FilterDateTo)]; ImStrncpy(min_date_bkp, perftool->_FilterDateFrom, IM_ARRAYSIZE(min_date_bkp)); ImStrncpy(max_date_bkp, perftool->_FilterDateTo, IM_ARRAYSIZE(max_date_bkp)); bool perf_was_open = SetPerfToolWindowOpen(ctx, true); ctx->Yield(); ImGuiWindow* window = ctx->GetWindowByRef("Dear ImGui Perf Tool"); IM_CHECK_SILENT(window != nullptr); ImVec2 pos_bkp = window->Pos; ImVec2 size_bkp = window->Size; ctx->SetRef(window); ctx->WindowMove("", ImVec2(50, 50)); ctx->WindowResize("", ImVec2(1400, 900)); #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT ctx->ItemDoubleClick("splitter"); // Hide info table ImGuiWindow* plot_child = ctx->WindowInfo("plot").Window; // "plot/PerfTool" prior to implot 2023/08/21 IM_CHECK(plot_child != nullptr); // Move legend to right side. ctx->MouseMoveToPos(plot_child->Rect().GetCenter()); ctx->MouseDoubleClick(ImGuiMouseButton_Left); // Auto-size plots while at it ctx->MouseClick(ImGuiMouseButton_Right); ctx->MenuClick("//$FOCUSED/Legend/NE"); #endif // Click some stuff for more coverage. ctx->ItemClick("##date-from", ImGuiMouseButton_Right); ctx->ItemClick(ctx->GetID("//$FOCUSED/Set Min")); ctx->ItemClick("##date-to", ImGuiMouseButton_Right); ctx->ItemClick(ctx->GetID("//$FOCUSED/Set Max")); #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT // Take a screenshot. ImGuiCaptureArgs* args = ctx->CaptureArgs; args->InCaptureRect = plot_child->Rect(); ctx->CaptureAddWindow(window->ID); ctx->CaptureScreenshot(ImGuiCaptureFlags_HideMouseCursor); ctx->ItemDragWithDelta("splitter", ImVec2(0, -180)); // Show info table perf_report_image = args->InOutputFile; #endif ImStrncpy(perftool->_FilterDateFrom, min_date_bkp, IM_ARRAYSIZE(min_date_bkp)); ImStrncpy(perftool->_FilterDateTo, max_date_bkp, IM_ARRAYSIZE(max_date_bkp)); ImGui::SetWindowPos(window, pos_bkp); ImGui::SetWindowSize(window, size_bkp); SetPerfToolWindowOpen(ctx, perf_was_open); // Restore window visibility #if !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE const char* perf_report_output = getenv("CAPTURE_PERF_REPORT_OUTPUT"); #else const char* perf_report_output = nullptr; #endif if (perf_report_output == nullptr) perf_report_output = PerfToolReportDefaultOutputPath; perftool->SaveHtmlReport(perf_report_output, perf_report_image); }; } //------------------------------------------------------------------------- ================================================ FILE: lib/third_party/imgui/imgui_test_engine/source/imgui_te_ui.cpp ================================================ // dear imgui test engine // (ui) // If you run tests in an interactive or visible application, you may want to call ImGuiTestEngine_ShowTestEngineWindows() // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui_te_ui.h" #include "imgui.h" #include "imgui_internal.h" #include "imgui_te_engine.h" #include "imgui_te_context.h" #include "imgui_te_internal.h" #include "imgui_te_perftool.h" #include "thirdparty/Str/Str.h" //------------------------------------------------------------------------- // TEST ENGINE: USER INTERFACE //------------------------------------------------------------------------- // - DrawTestLog() [internal] // - GetVerboseLevelName() [internal] // - ShowTestGroup() [internal] // - ImGuiTestEngine_ShowTestEngineWindows() //------------------------------------------------------------------------- // Look for " filename:number " in the string and add menu option to open source. static bool ParseLineAndDrawFileOpenItemForSourceFile(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end) { const char* separator = ImStrchrRange(line_start, line_end, ':'); if (separator == nullptr) return false; const char* path_end = separator; const char* path_begin = separator - 1; while (path_begin > line_start&& path_begin[-1] != ' ') path_begin--; if (path_begin == path_end) return false; int line_no = -1; sscanf(separator + 1, "%d ", &line_no); if (line_no == -1) return false; Str256f buf("Open '%.*s' at line %d", (int)(path_end - path_begin), path_begin, line_no); if (ImGui::MenuItem(buf.c_str())) { // FIXME-TESTS: Assume folder is same as folder of test->SourceFile! const char* src_path = test->SourceFile; const char* src_name = ImPathFindFilename(src_path); buf.setf("%.*s%.*s", (int)(src_name - src_path), src_path, (int)(path_end - path_begin), path_begin); ImGuiTestEngine_OpenSourceFile(e, buf.c_str(), line_no); } return true; } // Look for "[ ,"]filename.png" in the string and add menu option to open image. static bool ParseLineAndDrawFileOpenItemForImageFile(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end, const char* file_ext) { IM_UNUSED(e); IM_UNUSED(test); const char* extension = ImStristr(line_start, line_end, file_ext, nullptr); if (extension == nullptr) return false; const char* path_end = extension + strlen(file_ext); const char* path_begin = extension - 1; while (path_begin > line_start && path_begin[-1] != ' ' && path_begin[-1] != '\'' && path_begin[-1] != '\"') path_begin--; if (path_begin == path_end) return false; Str256 buf; // Open file buf.setf("Open file: %.*s", (int)(path_end - path_begin), path_begin); if (ImGui::MenuItem(buf.c_str())) { buf.setf("%.*s", (int)(path_end - path_begin), path_begin); ImPathFixSeparatorsForCurrentOS(buf.c_str()); ImOsOpenInShell(buf.c_str()); } // Open folder const char* folder_begin = path_begin; const char* folder_end = ImPathFindFilename(path_begin, path_end); buf.setf("Open folder: %.*s", (int)(folder_end - folder_begin), path_begin); if (ImGui::MenuItem(buf.c_str())) { buf.setf("%.*s", (int)(folder_end - folder_begin), folder_begin); ImPathFixSeparatorsForCurrentOS(buf.c_str()); ImOsOpenInShell(buf.c_str()); } return true; } static bool ParseLineAndDrawFileOpenItem(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end) { if (ParseLineAndDrawFileOpenItemForSourceFile(e, test, line_start, line_end)) return true; if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".png")) return true; if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".gif")) return true; if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, ".mp4")) return true; return false; } static float GetDpiScale() { #ifdef IMGUI_HAS_VIEWPORT return ImGui::GetWindowViewport()->DpiScale; #else return 1.0f; #endif } static void DrawTestLog(ImGuiTestEngine* e, ImGuiTest* test) { const ImU32 error_col = IM_COL32(255, 150, 150, 255); const ImU32 warning_col = IM_COL32(240, 240, 150, 255); const ImU32 unimportant_col = IM_COL32(190, 190, 190, 255); const float dpi_scale = GetDpiScale(); ImGuiTestOutput* test_output = &test->Output; ImGuiTestLog* log = &test_output->Log; const char* text = log->Buffer.begin(); const char* text_end = log->Buffer.end(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 2.0f) * dpi_scale); ImGuiListClipper clipper; ImGuiTestVerboseLevel max_log_level = test_output->Status == ImGuiTestStatus_Error ? e->IO.ConfigVerboseLevelOnError : e->IO.ConfigVerboseLevel; int line_count = log->ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel_Silent, max_log_level, nullptr); int current_index_clipped = -1; int current_index_abs = 0; clipper.Begin(line_count); while (clipper.Step()) { for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) { // Advance index_by_log_level to find log entry indicated by line_no. ImGuiTestLogLineInfo* line_info = nullptr; while (current_index_clipped < line_no) { line_info = &log->LineInfo[current_index_abs]; if (line_info->Level <= max_log_level) current_index_clipped++; current_index_abs++; } const char* line_start = text + line_info->LineOffset; const char* line_end = strchr(line_start, '\n'); if (line_end == nullptr) line_end = text_end; switch (line_info->Level) { case ImGuiTestVerboseLevel_Error: ImGui::PushStyleColor(ImGuiCol_Text, error_col); break; case ImGuiTestVerboseLevel_Warning: ImGui::PushStyleColor(ImGuiCol_Text, warning_col); break; case ImGuiTestVerboseLevel_Debug: case ImGuiTestVerboseLevel_Trace: ImGui::PushStyleColor(ImGuiCol_Text, unimportant_col); break; default: ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE); break; } #if IMGUI_VERSION_NUM >= 19072 ImGui::DebugTextUnformattedWithLocateItem(line_start, line_end); #else ImGui::TextUnformatted(line_start, line_end); #endif ImGui::PopStyleColor(); ImGui::PushID(line_no); if (ImGui::BeginPopupContextItem("Context", 1)) { if (!ParseLineAndDrawFileOpenItem(e, test, line_start, line_end)) ImGui::MenuItem("No options", nullptr, false, false); ImGui::EndPopup(); } ImGui::PopID(); } } ImGui::PopStyleVar(); } #if IMGUI_VERSION_NUM <= 18963 namespace ImGui { void SetItemTooltip(const char* fmt, ...) { if (ImGui::IsItemHovered()) { va_list args; va_start(args, fmt); ImGui::SetTooltipV(fmt, args); va_end(args); } } } // namespace ImGui #endif static bool ShowTestGroupFilterTest(ImGuiTestEngine* e, ImGuiTestGroup group, const char* filter, ImGuiTest* test) { if (test->Group != group) return false; if (!ImGuiTestEngine_PassFilter(test, *filter ? filter : "all")) return false; if ((e->UiFilterByStatusMask & (1 << test->Output.Status)) == 0) return false; return true; } static void GetFailingTestsAsString(ImGuiTestEngine* e, ImGuiTestGroup group, char separator, Str* out_string) { IM_ASSERT(out_string != nullptr); bool first = true; for (int i = 0; i < e->TestsAll.Size; i++) { ImGuiTest* failing_test = e->TestsAll[i]; Str* filter = (group == ImGuiTestGroup_Tests) ? e->UiFilterTests : e->UiFilterPerfs; if (failing_test->Group != group) continue; if (failing_test->Output.Status != ImGuiTestStatus_Error) continue; if (!ImGuiTestEngine_PassFilter(failing_test, filter->empty() ? "all" : filter->c_str())) continue; if (!first) out_string->append(separator); out_string->append(failing_test->Name); first = false; } } static void TestStatusButton(const char* id, const ImVec4& color, bool running, int display_counter) { ImGuiContext& g = *GImGui; ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true); ImGui::ColorButton(id, color, ImGuiColorEditFlags_NoTooltip); ImGui::PopItemFlag(); if (running) { //ImRect r = g.LastItemData.Rect; ImVec2 center = g.LastItemData.Rect.GetCenter(); float radius = ImFloor(ImMin(g.LastItemData.Rect.GetWidth(), g.LastItemData.Rect.GetHeight()) * 0.40f); float t = (float)(ImGui::GetTime() * 20.0f); ImVec2 off(ImCos(t) * radius, ImSin(t) * radius); ImGui::GetWindowDrawList()->AddLine(center - off, center + off, ImGui::GetColorU32(ImGuiCol_Text), 1.5f); //ImGui::RenderText(r.Min + style.FramePadding + ImVec2(0, 0), &"|\0/\0-\0\\"[(((ImGui::GetFrameCount() / 5) & 3) << 1)], nullptr); } else if (display_counter >= 0) { ImVec2 center = g.LastItemData.Rect.GetCenter(); Str30f buf("%d", display_counter); ImGui::GetWindowDrawList()->AddText(center - ImGui::CalcTextSize(buf.c_str()) * 0.5f, ImGui::GetColorU32(ImGuiCol_Text), buf.c_str()); } } static void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter, bool run) { ImGuiStyle& style = ImGui::GetStyle(); ImGuiIO& io = ImGui::GetIO(); const float dpi_scale = GetDpiScale(); // Colored Status button: will be displayed later below // - Save position of test run status button and make space for it. const ImVec2 status_button_pos = ImGui::GetCursorPos(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetFrameHeight() + style.ItemInnerSpacing.x); //ImGui::Text("TESTS (%d)", engine->TestsAll.Size); #if IMGUI_VERSION_NUM >= 19066 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip | ImGuiInputFlags_RouteFromRootWindow); run |= ImGui::Button("Run"); #elif IMGUI_VERSION_NUM >= 18837 run |= ImGui::Button("Run") || ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_R); #if IMGUI_VERSION_NUM > 18963 ImGui::SetItemTooltip("Ctrl+R"); #endif #else run |= ImGui::Button("Run"); #endif if (run) { for (int n = 0; n < e->TestsAll.Size; n++) { ImGuiTest* test = e->TestsAll[n]; if (!ShowTestGroupFilterTest(e, group, filter->c_str(), test)) continue; ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_None); } } ImGui::SameLine(); { ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6.0f); const char* filter_by_status_desc = ""; if (e->UiFilterByStatusMask == ~0u) filter_by_status_desc = "All"; else if (e->UiFilterByStatusMask == ~(1u << ImGuiTestStatus_Success)) filter_by_status_desc = "Not OK"; else if (e->UiFilterByStatusMask == (1u << ImGuiTestStatus_Error)) filter_by_status_desc = "Errors"; if (ImGui::BeginCombo("##filterbystatus", filter_by_status_desc)) { if (ImGui::Selectable("All", e->UiFilterByStatusMask == ~0u)) e->UiFilterByStatusMask = (ImU32)~0u; if (ImGui::Selectable("Not OK", e->UiFilterByStatusMask == ~(1u << ImGuiTestStatus_Success))) e->UiFilterByStatusMask = (ImU32)~(1u << ImGuiTestStatus_Success); if (ImGui::Selectable("Errors", e->UiFilterByStatusMask == (1u << ImGuiTestStatus_Error))) e->UiFilterByStatusMask = (ImU32)(1u << ImGuiTestStatus_Error); ImGui::EndCombo(); } } ImGui::SameLine(); const char* perflog_label = "Perf Tool"; float filter_width = ImGui::GetContentRegionAvail().x; float perf_stress_factor_width = (30 * dpi_scale); if (group == ImGuiTestGroup_Perfs) { filter_width -= style.ItemSpacing.x + perf_stress_factor_width; filter_width -= style.ItemSpacing.x + style.FramePadding.x * 2 + ImGui::CalcTextSize(perflog_label).x; } filter_width -= ImGui::CalcTextSize("(?)").x + style.ItemSpacing.x; ImGui::SetNextItemWidth(ImMax(20.0f, filter_width)); #if IMGUI_VERSION_NUM >= 19066 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip | ImGuiInputFlags_RouteFromRootWindow); #endif ImGui::InputText("##filter", filter); ImGui::SameLine(); ImGui::TextDisabled("(?)"); ImGui::SetItemTooltip("Query is composed of one or more comma-separated filter terms with optional modifiers.\n" "Available modifiers:\n" "- '-' prefix excludes tests matched by the term.\n" "- '^' prefix anchors term matching to the start of the string.\n" "- '$' suffix anchors term matching to the end of the string."); if (group == ImGuiTestGroup_Perfs) { ImGui::SameLine(); ImGui::SetNextItemWidth(perf_stress_factor_width); ImGui::DragInt("##PerfStress", &e->IO.PerfStressAmount, 0.1f, 1, 20, "x%d"); ImGui::SetItemTooltip("Increase workload of performance tests (higher means longer run)."); // FIXME: Move? ImGui::SameLine(); if (ImGui::Button(perflog_label)) { e->UiPerfToolOpen = true; ImGui::FocusWindow(ImGui::FindWindowByName("Dear ImGui Perf Tool")); } } int tests_completed = 0; int tests_succeeded = 0; int tests_failed = 0; ImVector tests_to_remove; if (ImGui::BeginTable("Tests", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_SizingFixedFit)) { ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("Status"); ImGui::TableSetupColumn("Category"); ImGui::TableSetupColumn("Test", ImGuiTableColumnFlags_WidthStretch); ImGui::TableHeadersRow(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6, 4) * dpi_scale); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 0) * dpi_scale); //ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(100, 10) * dpi_scale); for (int test_n = 0; test_n < e->TestsAll.Size; test_n++) { ImGuiTest* test = e->TestsAll[test_n]; if (!ShowTestGroupFilterTest(e, group, filter->c_str(), test)) continue; ImGuiTestOutput* test_output = &test->Output; ImGuiTestContext* test_context = (e->TestContext && e->TestContext->Test == test) ? e->TestContext : nullptr; // Running context, if any ImGui::TableNextRow(); ImGui::PushID(test_n); // Colors match general test status colors defined below. ImVec4 status_color; switch (test_output->Status) { case ImGuiTestStatus_Error: status_color = ImVec4(0.9f, 0.1f, 0.1f, 1.0f); tests_completed++; tests_failed++; break; case ImGuiTestStatus_Success: status_color = ImVec4(0.1f, 0.9f, 0.1f, 1.0f); tests_completed++; tests_succeeded++; break; case ImGuiTestStatus_Queued: case ImGuiTestStatus_Running: case ImGuiTestStatus_Suspended: if (test_context && (test_context->RunFlags & ImGuiTestRunFlags_GuiFuncOnly)) status_color = ImVec4(0.8f, 0.0f, 0.8f, 1.0f); else status_color = ImVec4(0.8f, 0.4f, 0.1f, 1.0f); break; default: status_color = ImVec4(0.4f, 0.4f, 0.4f, 1.0f); break; } ImGui::TableNextColumn(); TestStatusButton("status", status_color, test_output->Status == ImGuiTestStatus_Running || test_output->Status == ImGuiTestStatus_Suspended, -1); ImGui::SameLine(); bool queue_test = false; bool queue_gui_func_toggle = false; bool select_test = false; if (test_output->Status == ImGuiTestStatus_Suspended) { // Resume IM_SUSPEND_TESTFUNC // FIXME: Terrible user experience to have this here. if (ImGui::Button("Con###Run")) test_output->Status = ImGuiTestStatus_Running; ImGui::SetItemTooltip("CTRL+Space to continue."); if (ImGui::IsKeyPressed(ImGuiKey_Space) && io.KeyCtrl) test_output->Status = ImGuiTestStatus_Running; } else { if (ImGui::Button("Run###Run")) queue_test = select_test = true; } ImGui::TableNextColumn(); if (ImGui::Selectable(test->Category, test == e->UiSelectedTest, ImGuiSelectableFlags_SpanAllColumns | (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnNav)) select_test = true; // Double-click to run test, CTRL+Double-click to run GUI function const bool is_running_gui_func = (test_context && (test_context->RunFlags & ImGuiTestRunFlags_GuiFuncOnly)); const bool has_gui_func = (test->GuiFunc != nullptr); if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) { if (ImGui::GetIO().KeyCtrl) queue_gui_func_toggle = true; else queue_test = true; } /*if (ImGui::IsItemHovered() && test->TestLog.size() > 0) { ImGui::BeginTooltip(); DrawTestLog(engine, test, false); ImGui::EndTooltip(); }*/ if (e->UiSelectAndScrollToTest == test) ImGui::SetScrollHereY(); bool view_source = false; if (ImGui::BeginPopupContextItem()) { select_test = true; if (ImGui::MenuItem("Run test")) queue_test = true; if (ImGui::MenuItem("Run GUI func", "Ctrl+DblClick", is_running_gui_func, has_gui_func)) queue_gui_func_toggle = true; ImGui::Separator(); const bool open_source_available = (test->SourceFile != nullptr) && (e->IO.SrcFileOpenFunc != nullptr); Str128 buf; if (test->SourceFile != nullptr) // This is normally set by IM_REGISTER_TEST() but custom registration may omit it. buf.setf("Open source (%s:%d)", ImPathFindFilename(test->SourceFile), test->SourceLine); else buf.set("Open source"); if (ImGui::MenuItem(buf.c_str(), nullptr, false, open_source_available)) ImGuiTestEngine_OpenSourceFile(e, test->SourceFile, test->SourceLine); if (ImGui::MenuItem("View source...", nullptr, false, test->SourceFile != nullptr)) view_source = true; if (group == ImGuiTestGroup_Perfs && ImGui::MenuItem("View perflog")) { e->PerfTool->ViewOnly(test->Name); e->UiPerfToolOpen = true; } ImGui::Separator(); if (ImGui::MenuItem("Copy name", nullptr, false)) ImGui::SetClipboardText(test->Name); if (test_output->Status == ImGuiTestStatus_Error) if (ImGui::MenuItem("Copy names of all failing tests")) { Str256 failing_tests; GetFailingTestsAsString(e, group, ',', &failing_tests); ImGui::SetClipboardText(failing_tests.c_str()); } ImGuiTestLog* test_log = &test_output->Log; if (ImGui::BeginMenu("Copy log", !test_log->IsEmpty())) { for (int level_n = ImGuiTestVerboseLevel_Error; level_n < ImGuiTestVerboseLevel_COUNT; level_n++) { ImGuiTestVerboseLevel level = (ImGuiTestVerboseLevel)level_n; int count = test_log->ExtractLinesForVerboseLevels((ImGuiTestVerboseLevel)0, level, nullptr); if (ImGui::MenuItem(Str64f("%s (%d lines)", ImGuiTestEngine_GetVerboseLevelName(level), count).c_str(), nullptr, false, count > 0)) { ImGuiTextBuffer buffer; test_log->ExtractLinesForVerboseLevels((ImGuiTestVerboseLevel)0, level, &buffer); ImGui::SetClipboardText(buffer.c_str()); } } ImGui::EndMenu(); } if (ImGui::MenuItem("Clear log", nullptr, false, !test_log->IsEmpty())) test_log->Clear(); // [DEBUG] Simple way to exercise ImGuiTestEngine_UnregisterTest() //ImGui::Separator(); //if (ImGui::MenuItem("Remove test")) // tests_to_remove.push_back(test); ImGui::EndPopup(); } // Process source popup static ImGuiTextBuffer source_blurb; static int goto_line = -1; if (view_source) { source_blurb.clear(); size_t file_size = 0; char* file_data = (char*)ImFileLoadToMemory(test->SourceFile, "rb", &file_size); if (file_data) source_blurb.append(file_data, file_data + file_size); else source_blurb.append(""); goto_line = test->SourceLine; ImGui::OpenPopup("Source"); } if (ImGui::BeginPopup("Source")) { const ImVec2 start_pos = ImGui::GetCursorScreenPos(); const float line_height = ImGui::GetTextLineHeight(); if (goto_line != -1) ImGui::SetScrollY(ImMax((goto_line - 5) * line_height, 0.0f)); goto_line = -1; ImRect r(0.0f, (test->SourceLine - 1) * line_height, ImGui::GetWindowWidth(), (test->SourceLineEnd - 1) * line_height); ImGui::GetWindowDrawList()->AddRectFilled(start_pos + r.Min, start_pos + r.Max, IM_COL32(80, 80, 150, 100)); ImGui::TextUnformatted(source_blurb.c_str(), source_blurb.end()); ImGui::EndPopup(); } ImGui::TableNextColumn(); ImGui::TextUnformatted(test->Name); // Process selection if (select_test) e->UiSelectedTest = test; // Process queuing if (queue_gui_func_toggle && is_running_gui_func) ImGuiTestEngine_AbortCurrentTest(e); else if (queue_gui_func_toggle && !e->IO.IsRunningTests) ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_RunFromGui | ImGuiTestRunFlags_GuiFuncOnly); if (queue_test && !e->IO.IsRunningTests) ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_RunFromGui); ImGui::PopID(); } ImGui::Spacing(); ImGui::PopStyleVar(2); ImGui::EndTable(); } // Process removal for (ImGuiTest* test : tests_to_remove) ImGuiTestEngine_UnregisterTest(e, test); // Display test status recap (colors match per-test run button colors defined above) { ImVec4 status_color; if (tests_failed > 0) status_color = ImVec4(0.9f, 0.1f, 0.1f, 1.0f); // Red else if (e->IO.IsRunningTests) status_color = ImVec4(0.8f, 0.4f, 0.1f, 1.0f); else if (tests_succeeded > 0 && tests_completed == tests_succeeded) status_color = ImVec4(0.1f, 0.9f, 0.1f, 1.0f); else status_color = ImVec4(0.4f, 0.4f, 0.4f, 1.0f); //ImVec2 cursor_pos_bkp = ImGui::GetCursorPos(); ImGui::SetCursorPos(status_button_pos); TestStatusButton("status", status_color, false, tests_failed > 0 ? tests_failed : -1);// e->IO.IsRunningTests); ImGui::SetItemTooltip("Filtered: %d\n- OK: %d\n- Errors: %d", tests_completed, tests_succeeded, tests_failed); //ImGui::SetCursorPos(cursor_pos_bkp); // Restore cursor position for rendering further widgets } } static void ImGuiTestEngine_ShowLogAndTools(ImGuiTestEngine* engine) { ImGuiContext& g = *GImGui; const float dpi_scale = GetDpiScale(); if (!ImGui::BeginTabBar("##tools")) return; if (ImGui::BeginTabItem("LOG")) { ImGuiTest* selected_test = engine->UiSelectedTest; if (selected_test != nullptr) ImGui::Text("Log for '%s' '%s'", selected_test->Category, selected_test->Name); else ImGui::Text("N/A"); if (ImGui::SmallButton("Clear")) if (selected_test) selected_test->Output.Log.Clear(); ImGui::SameLine(); if (ImGui::SmallButton("Copy to clipboard")) if (engine->UiSelectedTest) ImGui::SetClipboardText(selected_test->Output.Log.Buffer.c_str()); ImGui::Separator(); ImGui::BeginChild("Log"); if (engine->UiSelectedTest) { DrawTestLog(engine, engine->UiSelectedTest); if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) ImGui::SetScrollHereY(); } ImGui::EndChild(); ImGui::EndTabItem(); } // Options if (ImGui::BeginTabItem("OPTIONS")) { ImGuiIO& io = ImGui::GetIO(); ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Text("TestEngine: HookItems: %d, HookPushId: %d, InfoTasks: %d", g.TestEngineHookItems, #if IMGUI_VERSION_NUM < 19229 g.DebugHookIdInfo != 0, #else g.DebugHookIdInfoId != 0, #endif engine->InfoTasks.Size); ImGui::Separator(); if (ImGui::Button("Reboot UI context")) engine->ToolDebugRebootUiContext = true; const ImGuiInputTextCallback filter_callback = [](ImGuiInputTextCallbackData* data) { return (data->EventChar == ',' || data->EventChar == ';') ? 1 : 0; }; ImGui::InputText("Branch/Annotation", engine->IO.GitBranchName, IM_ARRAYSIZE(engine->IO.GitBranchName), ImGuiInputTextFlags_CallbackCharFilter, filter_callback, nullptr); ImGui::SetItemTooltip("This will be stored in the CSV file for performance tools."); ImGui::Separator(); if (ImGui::TreeNode("Screen/video capture")) { ImGui::Checkbox("Capture when requested by API", &engine->IO.ConfigCaptureEnabled); ImGui::SetItemTooltip("Enable or disable screen capture API completely."); ImGui::Checkbox("Capture screen on error", &engine->IO.ConfigCaptureOnError); ImGui::SetItemTooltip("Capture a screenshot on test failure."); // Fields modified by in this call will be synced to engine->CaptureContext. engine->CaptureTool._ShowEncoderConfigFields(&engine->CaptureContext); ImGui::TreePop(); } if (ImGui::TreeNode("Performances")) { ImGui::Checkbox("Slow down whole app", &engine->ToolSlowDown); ImGui::SameLine(); ImGui::SetNextItemWidth(70 * dpi_scale); ImGui::SliderInt("##ms", &engine->ToolSlowDownMs, 0, 400, "%d ms"); // FIXME-TESTS: Need to be visualizing the samples/spikes. double dt_1 = 1.0 / ImGui::GetIO().Framerate; double fps_now = 1.0 / dt_1; double dt_100 = engine->PerfDeltaTime100.GetAverage(); double dt_500 = engine->PerfDeltaTime500.GetAverage(); //if (engine->PerfRefDeltaTime <= 0.0 && engine->PerfRefDeltaTime.IsFull()) // engine->PerfRefDeltaTime = dt_2000; ImGui::Checkbox("Unthrolled", &engine->IO.ConfigNoThrottle); ImGui::SameLine(); if (ImGui::Button("Pick ref dt")) engine->PerfRefDeltaTime = dt_500; double dt_ref = engine->PerfRefDeltaTime; ImGui::Text("[ref dt] %6.3f ms", engine->PerfRefDeltaTime * 1000); ImGui::Text("[last 001] %6.3f ms (%.1f FPS) ++ %6.3f ms", dt_1 * 1000.0, 1.0 / dt_1, (dt_1 - dt_ref) * 1000); ImGui::Text("[last 100] %6.3f ms (%.1f FPS) ++ %6.3f ms ~ converging in %.1f secs", dt_100 * 1000.0, 1.0 / dt_100, (dt_1 - dt_ref) * 1000, 100.0 / fps_now); ImGui::Text("[last 500] %6.3f ms (%.1f FPS) ++ %6.3f ms ~ converging in %.1f secs", dt_500 * 1000.0, 1.0 / dt_500, (dt_1 - dt_ref) * 1000, 500.0 / fps_now); //ImGui::PlotLines("Last 100", &engine->PerfDeltaTime100.Samples.Data, engine->PerfDeltaTime100.Samples.Size, engine->PerfDeltaTime100.Idx, nullptr, 0.0f, dt_1000 * 1.10f, ImVec2(0.0f, ImGui::GetFontSize())); ImVec2 plot_size(0.0f, ImGui::GetFrameHeight() * 3); ImMovingAverage* ma = &engine->PerfDeltaTime500; ImGui::PlotLines("Last 500", [](void* data, int n) { ImMovingAverage* ma = (ImMovingAverage*)data; return (float)(ma->Samples[n] * 1000); }, ma, ma->Samples.Size, 0 * ma->Idx, nullptr, 0.0f, (float)(ImMax(dt_100, dt_500) * 1000.0 * 1.2f), plot_size); ImGui::TreePop(); } if (ImGui::TreeNode("Dear ImGui Configuration Flags")) { ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); #ifdef IMGUI_HAS_DOCK ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar); #endif ImGui::TreePop(); } ImGui::EndTabItem(); } ImGui::EndTabBar(); } static void ImGuiTestEngine_ShowTestTool(ImGuiTestEngine* engine, bool* p_open) { const float dpi_scale = GetDpiScale(); ImGui::SetNextWindowSize(ImVec2(ImGui::GetFontSize() * 50, ImGui::GetFontSize() * 40), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Dear ImGui Test Engine", p_open, ImGuiWindowFlags_MenuBar)) { ImGui::End(); return; } bool run = false; if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Tests")) { // FIXME: This idiom showcases an issue with menus vs shortcuts. Would be nice if e.g. we could activate a shortcut? run = ImGui::MenuItem("Run Visible", "Ctrl+R"); ImGui::MenuItem("Filter", "Ctrl+F"); if (p_open != NULL && ImGui::MenuItem("Close")) *p_open = false; ImGui::EndMenu(); } if (ImGui::BeginMenu("Tools")) { ImGuiContext& g = *GImGui; ImGui::MenuItem("Metrics/Debugger", "", &engine->UiMetricsOpen); ImGui::MenuItem("Debug Log", "", &engine->UiDebugLogOpen); ImGui::MenuItem("Stack Tool", "", &engine->UiStackToolOpen); ImGui::MenuItem("Item Picker", "", &g.DebugItemPickerActive); ImGui::Separator(); ImGui::MenuItem("Capture Tool", "", &engine->UiCaptureToolOpen); ImGui::MenuItem("Perf Tool", "", &engine->UiPerfToolOpen); ImGui::EndMenu(); } ImGui::EndMenuBar(); } ImGui::SetNextItemWidth(90 * dpi_scale); if (ImGui::BeginCombo("##RunSpeed", ImGuiTestEngine_GetRunSpeedName(engine->IO.ConfigRunSpeed), ImGuiComboFlags_None)) { for (ImGuiTestRunSpeed level = (ImGuiTestRunSpeed)0; level < ImGuiTestRunSpeed_COUNT; level = (ImGuiTestRunSpeed)(level + 1)) if (ImGui::Selectable(ImGuiTestEngine_GetRunSpeedName(level), engine->IO.ConfigRunSpeed == level)) engine->IO.ConfigRunSpeed = level; ImGui::EndCombo(); } ImGui::SetItemTooltip( "Running speed\n" "- Fast: Run tests as fast as possible (no delay/vsync, teleport mouse, etc.).\n" "- Normal: Run tests at human watchable speed (for debugging).\n" "- Cinematic: Run tests with pauses between actions (for e.g. tutorials)." ); ImGui::SameLine(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); // (Would be good if we exposed horizontal layout mode..) ImGui::Checkbox("Stop", &engine->IO.ConfigStopOnError); ImGui::SetItemTooltip("When hitting an error:\n- Stop running other tests."); ImGui::SameLine(); ImGui::Checkbox("DbgBrk", &engine->IO.ConfigBreakOnError); ImGui::SetItemTooltip("When hitting an error:\n- Break in debugger."); ImGui::SameLine(); ImGui::Checkbox("Capture", &engine->IO.ConfigCaptureOnError); ImGui::SetItemTooltip("When hitting an error:\n- Capture screen to PNG. Right-click filename in Test Log to open."); ImGui::SameLine(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); ImGui::Checkbox("KeepGUI", &engine->IO.ConfigKeepGuiFunc); ImGui::SetItemTooltip("After running single test or hitting an error:\n- Keep GUI function visible and interactive.\n- Hold ESC to abort a running GUI function."); ImGui::SameLine(); bool keep_focus = !engine->IO.ConfigRestoreFocusAfterTests; if (ImGui::Checkbox("KeepFocus", &keep_focus)) engine->IO.ConfigRestoreFocusAfterTests = !keep_focus; ImGui::SetItemTooltip("After running tests:\n- Keep GUI current focus, instead of restoring focus to this window."); ImGui::SameLine(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); ImGui::SetNextItemWidth(70 * dpi_scale); if (ImGui::BeginCombo("##Verbose", ImGuiTestEngine_GetVerboseLevelName(engine->IO.ConfigVerboseLevel), ImGuiComboFlags_None)) { for (ImGuiTestVerboseLevel level = (ImGuiTestVerboseLevel)0; level < ImGuiTestVerboseLevel_COUNT; level = (ImGuiTestVerboseLevel)(level + 1)) if (ImGui::Selectable(ImGuiTestEngine_GetVerboseLevelName(level), engine->IO.ConfigVerboseLevel == level)) engine->IO.ConfigVerboseLevel = engine->IO.ConfigVerboseLevelOnError = level; ImGui::EndCombo(); } ImGui::SetItemTooltip("Verbose level."); //ImGui::PopStyleVar(); ImGui::Separator(); // SPLITTER // FIXME-OPT: A better splitter API supporting arbitrary number of splits would be useful. float list_height = 0.0f; float& log_height = engine->UiLogHeight; ImGui::Splitter("splitter", &list_height, &log_height, ImGuiAxis_Y, +1); // TESTS ImGui::BeginChild("List", ImVec2(0, list_height), false, ImGuiWindowFlags_NoScrollbar); if (ImGui::BeginTabBar("##Tests", ImGuiTabBarFlags_NoTooltip)) // Add _NoPushId flag in TabBar? { if (ImGui::BeginTabItem("TESTS", nullptr, ImGuiTabItemFlags_NoPushId)) { ShowTestGroup(engine, ImGuiTestGroup_Tests, engine->UiFilterTests, run); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("PERFS", nullptr, ImGuiTabItemFlags_NoPushId)) { ShowTestGroup(engine, ImGuiTestGroup_Perfs, engine->UiFilterPerfs, run); ImGui::EndTabItem(); } ImGui::EndTabBar(); } ImGui::EndChild(); engine->UiSelectAndScrollToTest = nullptr; // LOG & TOOLS ImGui::BeginChild("Log", ImVec2(0, log_height)); ImGuiTestEngine_ShowLogAndTools(engine); ImGui::EndChild(); ImGui::End(); } void ImGuiTestEngine_ShowTestEngineWindows(ImGuiTestEngine* e, bool* p_open) { if (e->TestsSourceLinesDirty) ImGuiTestEngine_UpdateTestsSourceLines(e); // Test Tool ImGuiTestEngine_ShowTestTool(e, p_open); // Stack Tool #if IMGUI_VERSION_NUM < 18993 if (e->UiStackToolOpen) ImGui::ShowStackToolWindow(&e->UiStackToolOpen); #else if (e->UiStackToolOpen) ImGui::ShowIDStackToolWindow(&e->UiStackToolOpen); #endif // Capture Tool if (e->UiCaptureToolOpen) e->CaptureTool.ShowCaptureToolWindow(&e->CaptureContext, &e->UiCaptureToolOpen); // Performance tool if (e->UiPerfToolOpen) e->PerfTool->ShowPerfToolWindow(e, &e->UiPerfToolOpen);; // Show Dear ImGui windows // (we cannot show demo window here because it could lead to duplicate display, which demo windows isn't guarded for) if (e->UiMetricsOpen) ImGui::ShowMetricsWindow(&e->UiMetricsOpen); if (e->UiDebugLogOpen) ImGui::ShowDebugLogWindow(&e->UiDebugLogOpen); } void ImGuiTestEngine_OpenSourceFile(ImGuiTestEngine* e, const char* source_filename, int source_line_no) { ImGuiTestEngineIO& e_io = ImGuiTestEngine_GetIO(e); if (e_io.SrcFileOpenFunc == nullptr) ImOsOpenInShell(source_filename); // This is never used by imgui_test_suite but we provide it as a second layer of convenience for test engine users. else e_io.SrcFileOpenFunc(source_filename, source_line_no, e_io.SrcFileOpenUserData); // Debugger output which may be double-clicked // Print after opener so it appears in a neat place below e.g. DLL loading. if (ImGui::GetIO().ConfigDebugIsDebuggerPresent) ImOsOutputDebugString(Str256f("%s(%d): opening from user action.\n", source_filename, source_line_no).c_str()); } ================================================ FILE: lib/third_party/imgui/imgui_test_engine/source/imgui_te_utils.cpp ================================================ // dear imgui test engine // (helpers/utilities. do NOT use this as a general purpose library) // This file is governed by the "Dear ImGui Test Engine License". // Details of the license are provided in the LICENSE.txt file in the same directory. #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #include "imgui_te_utils.h" #include "imgui.h" #include "imgui_internal.h" #define STR_IMPLEMENTATION #include "thirdparty/Str/Str.h" #if defined(_WIN32) #if !defined(_WINDOWS_) #define WIN32_LEAN_AND_MEAN #include #endif #include // ShellExecuteA() #include #else #include #include #endif #ifndef _MSC_VER #include #include // stat() #endif #ifdef __APPLE__ #include #endif #if defined(__linux) || defined(__linux__) || defined(__MACH__) || defined(__MSL__) || defined(__MINGW32__) #include // pthread_setname_np() #endif #include // high_resolution_clock::now() #include // this_thread::sleep_for() //----------------------------------------------------------------------------- // Hashing Helpers //----------------------------------------------------------------------------- // - ImHashDecoratedPathParseLiteral() [internal] // - ImHashDecoratedPath() // - ImFindNextDecoratedPartInPath() //----------------------------------------------------------------------------- // - Parse literals encoded as "$$xxxx/" and incorporate into our hash based on type. // - $$ not passed by caller. static ImGuiID ImHashDecoratedPathParseLiteral(ImGuiID crc, const unsigned char* str, const unsigned char* str_end, const unsigned char** out_str_remaining) { // Parse type (default to int) ImGuiDataType type = ImGuiDataType_S32; if (*str == '(') { // "$$(int)????" where ???? is s32 or u32 if (str + 5 < str_end && memcmp(str, "(int)", 5) == 0) { type = ImGuiDataType_S32; str += 5; } // "$$(ptr)0x????" where ???? is ptr size else if (str + 7 < str_end && memcmp(str, "(ptr)0x", 7) == 0) { type = ImGuiDataType_Pointer; str += 7; } } // Parse value switch (type) { case ImGuiDataType_S32: { // e.g. "$$(int)123" for s32/u32/ImGuiID, same as PushID(int) int v = 0; { int negative = 0; if (str < str_end && *str == '-') { negative = 1; str++; } if (str < str_end && *str == '+') { str++; } for (char c = *str; str < str_end; c = *(++str)) { if (c >= '0' && c <= '9') { v = (v * 10) + (c - '0'); } else break; } if (negative) v = -v; } crc = ~ImHashData(&v, sizeof(int), ~crc); break; } case ImGuiDataType_Pointer: { // e.g. "$$(ptr)0x1234FFFF" for pointers, same as PushID(void*) intptr_t v = 0; { for (char c = *str; str < str_end; c = *(++str)) { if (c >= '0' && c <= '9') { v = (v << 4) + (c - '0'); } else if (c >= 'A' && c <= 'F') { v = (v << 4) + 10 + (c - 'A'); } else if (c >= 'a' && c <= 'f') { v = (v << 4) + 10 + (c - 'a'); } else break; } } crc = ~ImHashData(&v, sizeof(void*), ~crc); break; } } // "$$xxxx" must always be either end of string, either leading to a next section e.g. "$$xxxx/" IM_ASSERT(str == str_end || *str == '/'); *out_str_remaining = str; return crc; } // Hash "hello/world" as if it was "helloworld" // To hash a forward slash we need to use "hello\\/world" // IM_ASSERT(ImHashDecoratedPath("Hello/world") == ImHashStr("Helloworld", 0)); // IM_ASSERT(ImHashDecoratedPath("Hello\\/world") == ImHashStr("Hello/world", 0)); // IM_ASSERT(ImHashDecoratedPath("$$1") == (n = 1, ImHashData(&n, sizeof(int)))); // Adapted from ImHash(). Not particularly fast! static const ImU32 GImGuiTestEngineCrc32LookupTable[256] = { #if (IMGUI_VERSION_NUM < 19152) || defined(IMGUI_USE_LEGACY_CRC32_ADLER) 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, #else 0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24, 0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B, 0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA, 0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595, 0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198, 0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7, 0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46, 0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829, 0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C, 0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033, 0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982, 0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED, 0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0, 0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F, 0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E, 0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351 #endif }; ImGuiID ImHashDecoratedPath(const char* str, const char* str_end, ImGuiID seed) { const ImU32* crc32_lut = GImGuiTestEngineCrc32LookupTable; // Prefixing the string with / ignore the seed if (str != str_end && str[0] == '/') seed = 0; seed = ~seed; ImU32 crc = seed; // Focus for non-zero terminated string for consistency if (str_end == nullptr) str_end = str + strlen(str); bool inhibit_one = false; bool new_section = true; const unsigned char* current = (const unsigned char*)str; while (current < (const unsigned char*)str_end) { const unsigned char c = *current++; // Backslash to inhibit special behavior of following character if (c == '\\' && !inhibit_one) { inhibit_one = true; continue; } // Forward slashes are ignored unless prefixed with a backward slash if (c == '/' && !inhibit_one) { inhibit_one = false; new_section = true; seed = crc; // Set seed to the new path continue; } // $$ at the beginning of a section to encode literals. // - Currently: "$$????" = hash of 1 as int // - May add pointers and other types. if (c == '$' && current[0] == '$' && !inhibit_one && new_section) { crc = ImHashDecoratedPathParseLiteral(crc, current + 1, (const unsigned char*)str_end, ¤t); continue; } // Reset the hash when encountering ### if (c == '#' && current[0] == '#' && current[1] == '#') crc = seed; // Hash byte crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; inhibit_one = new_section = false; } return ~crc; } // Returns a next element of decorated hash path. // "//hello/world/child" --> "world/child" // "world/child" --> "child" // This is a helper for code needing to do some parsing of individual nodes in a path. // Note: we need the (unsigned char*) stuff in order to keep code similar to ImHashDecoratedPath(). They are not really necessary in this function tho. const char* ImFindNextDecoratedPartInPath(const char* str, const char* str_end) { const unsigned char* current = (const unsigned char*)str; while (*current == '/') current++; bool inhibit_one = false; while (true) { if (str_end != nullptr && current == (const unsigned char*)str_end) break; const unsigned char c = *current++; if (c == 0) break; if (c == '\\' && !inhibit_one) { inhibit_one = true; continue; } // Forward slashes are ignored unless prefixed with a backward slash if (c == '/' && !inhibit_one) return (const char*)current; inhibit_one = false; } return nullptr; } //----------------------------------------------------------------------------- // File/Directory Helpers //----------------------------------------------------------------------------- // - ImFileExist() // - ImFileCreateDirectoryChain() // - ImFileFindInParents() // - ImFileLoadSourceBlurb() //----------------------------------------------------------------------------- #if _WIN32 static const char IM_DIR_SEPARATOR = '\\'; static void ImUtf8ToWideChar(const char* multi_byte, ImVector* buf) { const int wsize = ::MultiByteToWideChar(CP_UTF8, 0, multi_byte, -1, nullptr, 0); buf->resize(wsize); ::MultiByteToWideChar(CP_UTF8, 0, multi_byte, -1, (wchar_t*)buf->Data, wsize); } #else static const char IM_DIR_SEPARATOR = '/'; #endif bool ImFileExist(const char* filename) { struct stat dir_stat; int ret = stat(filename, &dir_stat); return (ret == 0); } bool ImFileDelete(const char* filename) { #if _WIN32 ImVector buf; ImUtf8ToWideChar(filename, &buf); return ::DeleteFileW(&buf[0]) == TRUE; #else unlink(filename); #endif return false; } // Create directories for specified path. Slashes will be replaced with platform directory separators. // e.g. ImFileCreateDirectoryChain("aaaa/bbbb/cccc.png") // will try to create "aaaa/" then "aaaa/bbbb/". bool ImFileCreateDirectoryChain(const char* path, const char* path_end) { IM_ASSERT(path != nullptr); IM_ASSERT(path[0] != 0); if (path_end == nullptr) path_end = path + strlen(path); // Copy in a local, zero-terminated buffer size_t path_len = (size_t)(path_end - path); char* path_local = (char*)IM_ALLOC(path_len + 1); memcpy(path_local, path, path_len); path_local[path_len] = 0; #if defined(_WIN32) ImVector buf; #endif // Modification of passed file_name allows us to avoid extra temporary memory allocation. // strtok() pokes \0 into places where slashes are, we create a directory using directory_name and restore slash. for (char* token = strtok(path_local, "\\/"); token != nullptr; token = strtok(nullptr, "\\/")) { // strtok() replaces slashes with NULLs. Overwrite removed slashes here with the type of slashes the OS needs (win32 functions need backslashes). if (token != path_local) *(token - 1) = IM_DIR_SEPARATOR; #if defined(_WIN32) // Use ::CreateDirectoryW() because ::CreateDirectoryA() treat filenames in the local code-page instead of UTF-8 // We cannot use ImWchar, which can be 32bits if IMGUI_USE_WCHAR32 (and CreateDirectoryW require 16bits wchar) int filename_wsize = MultiByteToWideChar(CP_UTF8, 0, path_local, -1, nullptr, 0); buf.resize(filename_wsize); MultiByteToWideChar(CP_UTF8, 0, path_local, -1, &buf[0], filename_wsize); if (!::CreateDirectoryW((wchar_t*)&buf[0], nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) #else if (mkdir(path_local, S_IRWXU) != 0 && errno != EEXIST) #endif { IM_FREE(path_local); return false; } } IM_FREE(path_local); return true; } bool ImFileFindInParents(const char* sub_path, int max_parent_count, Str* output) { IM_ASSERT(sub_path != nullptr); IM_ASSERT(output != nullptr); for (int parent_level = 0; parent_level < max_parent_count; parent_level++) { output->clear(); for (int j = 0; j < parent_level; j++) output->append("../"); output->append(sub_path); if (ImFileExist(output->c_str())) return true; } output->clear(); return false; } bool ImFileLoadSourceBlurb(const char* file_name, int line_no_start, int line_no_end, ImGuiTextBuffer* out_buf) { size_t file_size = 0; char* file_begin = (char*)ImFileLoadToMemory(file_name, "rb", &file_size, 1); if (file_begin == nullptr) return false; char* file_end = file_begin + file_size; int line_no = 0; const char* test_src_begin = nullptr; const char* test_src_end = nullptr; for (const char* p = file_begin; p < file_end; ) { line_no++; const char* line_begin = p; const char* line_end = ImStrchrRange(line_begin + 1, file_end, '\n'); if (line_end == nullptr) line_end = file_end; if (line_no >= line_no_start && line_no <= line_no_end) { if (test_src_begin == nullptr) test_src_begin = line_begin; test_src_end = ImMax(test_src_end, line_end); } p = line_end + 1; } if (test_src_begin != nullptr) out_buf->append(test_src_begin, test_src_end); else out_buf->clear(); ImGui::MemFree(file_begin); return true; } //----------------------------------------------------------------------------- // Path Helpers //----------------------------------------------------------------------------- // - ImPathFindFilename() // - ImPathFindFileExt() // - ImPathFixSeparatorsForCurrentOS() //----------------------------------------------------------------------------- const char* ImPathFindFilename(const char* path, const char* path_end) { IM_ASSERT(path != nullptr); if (!path_end) path_end = path + strlen(path); const char* p = path_end; while (p > path) { if (p[-1] == '/' || p[-1] == '\\') break; p--; } return p; } // "folder/filename" -> return pointer to "" (end of string) // "folder/filename.png" -> return pointer to ".png" // "folder/filename.png.bak" -> return pointer to ".png.bak" const char* ImPathFindExtension(const char* path, const char* path_end) { if (!path_end) path_end = path + strlen(path); const char* filename = ImPathFindFilename(path, path_end); const char* p = filename; while (p < path_end) { if (p[0] == '.') break; p++; } return p; } void ImPathFixSeparatorsForCurrentOS(char* buf) { #ifdef _WIN32 for (char* p = buf; *p != 0; p++) if (*p == '/') *p = '\\'; #else for (char* p = buf; *p != 0; p++) if (*p == '\\') *p = '/'; #endif } //----------------------------------------------------------------------------- // String Helpers //----------------------------------------------------------------------------- static const char* ImStrStr(const char* haystack, size_t hlen, const char* needle, int nlen) { const char* end = haystack + hlen; const char* p = haystack; while ((p = (const char*)memchr(p, *needle, end - p)) != nullptr) { if (end - p < nlen) return nullptr; if (memcmp(p, needle, nlen) == 0) return p; p++; } return nullptr; } void ImStrReplace(Str* s, const char* find, const char* repl) { IM_ASSERT(find != nullptr && *find); IM_ASSERT(repl != nullptr); int find_len = (int)strlen(find); int repl_len = (int)strlen(repl); int repl_diff = repl_len - find_len; // Estimate required length of new buffer if string size increases. int need_capacity = s->capacity(); int num_matches = INT_MAX; if (repl_diff > 0) { num_matches = 0; need_capacity = s->length(); for (char* p = s->c_str(), *end = s->c_str() + s->length(); p != nullptr && p < end;) { p = (char*)ImStrStr(p, end - p, find, find_len); if (p) { need_capacity += repl_diff; p += find_len; num_matches++; } } } if (num_matches == 0) return; const char* not_owned_data = s->owned() ? nullptr : s->c_str(); if (!s->owned() || need_capacity > s->capacity()) s->reserve(need_capacity); if (not_owned_data != nullptr) s->set(not_owned_data); // Replace data. for (char* p = s->c_str(), *end = s->c_str() + s->length(); p != nullptr && p < end && num_matches--;) { p = (char*)ImStrStr(p, end - p, find, find_len); if (p) { memmove(p + repl_len, p + find_len, end - p - find_len + 1); memcpy(p, repl, repl_len); p += repl_len; end += repl_diff; } } } const char* ImStrchrRangeWithEscaping(const char* str, const char* str_end, char find_c) { while (str < str_end) { const char c = *str; if (c == '\\') { str += 2; continue; } if (c == find_c) return str; str++; } return nullptr; } // Suboptimal but ok for the data size we are dealing with (see commit on 2022/08/22 for a faster and more complicated version) void ImStrXmlEscape(Str* s) { ImStrReplace(s, "&", "&"); ImStrReplace(s, "<", "<"); ImStrReplace(s, ">", ">"); ImStrReplace(s, "\"", """); ImStrReplace(s, "\'", "'"); } // Based on code from https://github.com/EddieBreeg/C_b64 by @EddieBreeg. int ImStrBase64Encode(const unsigned char* src, char* dst, int length) { static const char* b64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; int i, j, k, l, encoded_len = 0; while (length > 0) { switch (length) { case 1: i = src[0] >> 2; j = (src[0] & 3) << 4; k = 64; l = 64; break; case 2: i = src[0] >> 2; j = ((src[0] & 3) << 4) | (src[1] >> 4); k = (src[1] & 15) << 2; l = 64; break; default: i = src[0] >> 2; j = ((src[0] & 3) << 4) | (src[1] >> 4); k = ((src[1] & 0xf) << 2) | (src[2] >> 6 & 3); l = src[2] & 0x3f; break; } dst[0] = b64Table[i]; dst[1] = b64Table[j]; dst[2] = b64Table[k]; dst[3] = b64Table[l]; src += 3; dst += 4; length -= 3; encoded_len += 4; } return encoded_len; } //----------------------------------------------------------------------------- // Parsing Helpers //----------------------------------------------------------------------------- // - ImParseSplitCommandLine() // - ImParseFindIniSection() //----------------------------------------------------------------------------- void ImParseExtractArgcArgvFromCommandLine(int* out_argc, char const*** out_argv, const char* cmd_line) { size_t cmd_line_len = strlen(cmd_line); int n = 1; { const char* p = cmd_line; while (*p != 0) { const char* arg = p; while (*arg == ' ') arg++; const char* arg_end = strchr(arg, ' '); if (arg_end == nullptr) p = arg_end = cmd_line + cmd_line_len; else p = arg_end + 1; n++; } } int argc = n; char const** argv = (char const**)malloc(sizeof(char*) * ((size_t)argc + 1) + (cmd_line_len + 1)); IM_ASSERT(argv != nullptr); char* cmd_line_dup = (char*)argv + sizeof(char*) * ((size_t)argc + 1); strcpy(cmd_line_dup, cmd_line); { argv[0] = "main.exe"; argv[argc] = nullptr; char* p = cmd_line_dup; for (n = 1; n < argc; n++) { char* arg = p; char* arg_end = strchr(arg, ' '); if (arg_end == nullptr) p = arg_end = cmd_line_dup + cmd_line_len; else p = arg_end + 1; argv[n] = arg; arg_end[0] = 0; } } *out_argc = argc; *out_argv = argv; } bool ImParseFindIniSection(const char* ini_config, const char* header, ImVector* result) { IM_ASSERT(ini_config != nullptr); IM_ASSERT(header != nullptr); IM_ASSERT(result != nullptr); size_t ini_len = strlen(ini_config); size_t header_len = strlen(header); IM_ASSERT(header_len > 0); if (ini_len == 0) return false; const char* section_start = strstr(ini_config, header); if (section_start == nullptr) return false; const char* section_end = strstr(section_start + header_len, "\n["); if (section_end == nullptr) section_end = section_start + ini_len; // "\n[" matches next header start on all platforms, but it cuts new line marker in half on windows. if (*(section_end - 1) == '\r') --section_end; size_t section_len = (size_t)(section_end - section_start); result->resize((int)section_len + 1); ImStrncpy(result->Data, section_start, section_len); return true; } //----------------------------------------------------------------------------- // Time Helpers //----------------------------------------------------------------------------- // - ImTimeGetInMicroseconds() // - ImTimestampToISO8601() //----------------------------------------------------------------------------- uint64_t ImTimeGetInMicroseconds() { // Trying std::chrono out of unfettered optimism that it may actually work.. using namespace std; chrono::microseconds ms = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch()); return (uint64_t)ms.count(); } void ImTimestampToISO8601(uint64_t timestamp, Str* out_date) { time_t unix_time = (time_t)(timestamp / 1000000); // Convert to seconds. tm* time = gmtime(&unix_time); const char* time_format = "%Y-%m-%dT%H:%M:%S"; // max_size "maximum number of characters to be copied to ptr, including the terminating null-character." // return "returns the total number of characters copied to ptr (not including the terminating null-character)" size_t size_req = strftime(out_date->c_str(), out_date->capacity() + 1, time_format, time); if (size_req > (size_t)out_date->capacity()) { out_date->reserve((int)size_req); strftime(out_date->c_str(), out_date->capacity() + 1, time_format, time); } } //----------------------------------------------------------------------------- // Threading Helpers //----------------------------------------------------------------------------- // - ImThreadSleepInMilliseconds() // - ImThreadSetCurrentThreadDescription() //----------------------------------------------------------------------------- void ImThreadSleepInMilliseconds(int ms) { using namespace std; this_thread::sleep_for(chrono::milliseconds(ms)); } #if defined(_MSC_VER) // Helper function for setting thread name on Win32 // This is a separate function because __try cannot coexist with local objects that need destructors called on stack unwind static void ImThreadSetCurrentThreadDescriptionWin32OldStyle(const char* description) { // Old-style Win32 thread name setting method // See https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code const DWORD MS_VC_EXCEPTION = 0x406D1388; #pragma pack(push,8) typedef struct tagTHREADNAME_INFO { DWORD dwType; // Must be 0x1000. LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread). DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; #pragma pack(pop) THREADNAME_INFO info; info.dwType = 0x1000; info.szName = description; info.dwThreadID = (DWORD)-1; info.dwFlags = 0; #pragma warning(push) #pragma warning(disable: 6320 6322) __try { RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); } __except (EXCEPTION_EXECUTE_HANDLER) { } #pragma warning(pop) } #endif // #ifdef _WIN32 // Set the description (name) of the current thread for debugging purposes void ImThreadSetCurrentThreadDescription(const char* description) { #if defined(_MSC_VER) // Windows + Visual Studio // New-style thread name setting // Only supported from Win 10 version 1607/Server 2016 onwards, hence the need for dynamic linking typedef HRESULT(WINAPI* SetThreadDescriptionFunc)(HANDLE hThread, PCWSTR lpThreadDescription); SetThreadDescriptionFunc set_thread_description = (SetThreadDescriptionFunc)::GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetThreadDescription"); if (set_thread_description) { ImVector buf; const int description_wsize = ImTextCountCharsFromUtf8(description, nullptr) + 1; buf.resize(description_wsize); ImTextStrFromUtf8(&buf[0], description_wsize, description, nullptr); set_thread_description(::GetCurrentThread(), (wchar_t*)&buf[0]); } // Also do the old-style method too even if the new-style one worked, as the two work in slightly different sets of circumstances ImThreadSetCurrentThreadDescriptionWin32OldStyle(description); #elif defined(__linux) || defined(__linux__) || defined(__MINGW32__) // Linux or MingW pthread_setname_np(pthread_self(), description); #elif defined(__MACH__) || defined(__MSL__) // OSX pthread_setname_np(description); #else // This is a nice-to-have rather than critical functionality, so fail silently if we don't support this platform #endif } //----------------------------------------------------------------------------- // Build info helpers //----------------------------------------------------------------------------- // - ImBuildGetCompilationInfo() // - ImBuildGetGitBranchName() //----------------------------------------------------------------------------- // Turn __DATE__ "Jan 10 2019" into "2019-01-10" static void ImBuildParseDateFromCompilerIntoYMD(const char* in_date, char* out_buf, size_t out_buf_size) { char month_str[5]; int year, month, day; sscanf(in_date, "%3s %d %d", month_str, &day, &year); const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; const char* p = strstr(month_names, month_str); month = p ? (int)(1 + (p - month_names) / 3) : 0; ImFormatString(out_buf, out_buf_size, "%04d-%02d-%02d", year, month, day); } // Those strings are used to output easily identifiable markers in compare logs. We only need to support what we use for testing. // We can probably grab info in eaplatform.h/eacompiler.h etc. in EASTL const ImBuildInfo* ImBuildGetCompilationInfo() { static ImBuildInfo build_info; if (build_info.Type[0] == '\0') { // Build Type #if defined(DEBUG) || defined(_DEBUG) build_info.Type = "Debug"; #else build_info.Type = "Release"; #endif // CPU #if defined(_M_X86) || defined(_M_IX86) || defined(__i386) || defined(__i386__) || defined(_X86_) || defined(_M_AMD64) || defined(_AMD64_) || defined(__x86_64__) build_info.Cpu = (sizeof(size_t) == 4) ? "X86" : "X64"; #elif defined(__aarch64__) || (defined(_M_ARM64) && defined(_WIN64)) build_info.Cpu = "ARM64"; #elif defined(__EMSCRIPTEN__) build_info.Cpu = "WebAsm"; #else build_info.Cpu = (sizeof(size_t) == 4) ? "Unknown32" : "Unknown64"; #endif // Platform/OS #if defined(_WIN32) build_info.OS = "Windows"; #elif defined(__linux) || defined(__linux__) build_info.OS = "Linux"; #elif defined(__MACH__) || defined(__MSL__) build_info.OS = "OSX"; #elif defined(__ORBIS__) build_info.OS = "PS4"; #elif defined(__PROSPERO__) build_info.OS = "PS5"; #elif defined(_DURANGO) build_info.OS = "XboxOne"; #else build_info.OS = "Unknown"; #endif // Compiler #if defined(_MSC_VER) build_info.Compiler = "MSVC"; #elif defined(__clang__) build_info.Compiler = "Clang"; #elif defined(__GNUC__) build_info.Compiler = "GCC"; #else build_info.Compiler = "Unknown"; #endif // Date/Time ImBuildParseDateFromCompilerIntoYMD(__DATE__, build_info.Date, IM_ARRAYSIZE(build_info.Date)); build_info.Time = __TIME__; } return &build_info; } bool ImBuildFindGitBranchName(const char* git_repo_path, Str* branch_name) { IM_ASSERT(git_repo_path != nullptr); IM_ASSERT(branch_name != nullptr); Str256f head_path("%s/.git/HEAD", git_repo_path); size_t head_size = 0; bool result = false; if (char* git_head = (char*)ImFileLoadToMemory(head_path.c_str(), "r", &head_size, 1)) { const char prefix[] = "ref: refs/heads/"; // Branch name is prefixed with this in HEAD file. const int prefix_length = IM_ARRAYSIZE(prefix) - 1; strtok(git_head, "\r\n"); // Trim new line if (head_size > prefix_length && strncmp(git_head, prefix, prefix_length) == 0) { // "ref: refs/heads/master" -> "master" branch_name->set(git_head + prefix_length); } else { // Should be git hash, keep first 8 characters (see #42) branch_name->setf("%.8s", git_head); } result = true; IM_FREE(git_head); } return result; } //----------------------------------------------------------------------------- // Operating System Helpers //----------------------------------------------------------------------------- // - ImOsCreateProcess() // - ImOsPOpen() // - ImOsPClose() // - ImOsOpenInShell() // - ImOsConsoleSetTextColor() // - ImOsIsDebuggerPresent() //----------------------------------------------------------------------------- bool ImOsCreateProcess(const char* cmd_line) { #if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE STARTUPINFOA siStartInfo; PROCESS_INFORMATION piProcInfo; ZeroMemory(&siStartInfo, sizeof(STARTUPINFOA)); char* cmd_line_copy = ImStrdup(cmd_line); BOOL ret = ::CreateProcessA(nullptr, cmd_line_copy, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo); free(cmd_line_copy); ::CloseHandle(siStartInfo.hStdInput); ::CloseHandle(siStartInfo.hStdOutput); ::CloseHandle(siStartInfo.hStdError); ::CloseHandle(piProcInfo.hProcess); ::CloseHandle(piProcInfo.hThread); return ret != 0; #else IM_UNUSED(cmd_line); IM_ASSERT(0); return false; #endif } FILE* ImOsPOpen(const char* cmd_line, const char* mode) { IM_ASSERT(cmd_line != nullptr && *cmd_line); IM_ASSERT(mode != nullptr && *mode); #if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE ImVector w_cmd_line; ImVector w_mode; ImUtf8ToWideChar(cmd_line, &w_cmd_line); ImUtf8ToWideChar(mode, &w_mode); w_mode.resize(w_mode.Size + 1); wcscat(w_mode.Data, L"b"); // Windows requires 'b' mode while unixes do not support it and default to binary. return _wpopen(w_cmd_line.Data, w_mode.Data); #elif !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE return popen(cmd_line, mode); #else IM_ASSERT(0); return NULL; #endif } void ImOsPClose(FILE* fp) { IM_ASSERT(fp != nullptr); #if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE _pclose(fp); #elif !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE pclose(fp); #else IM_ASSERT(0); #endif } void ImOsOpenInShell(const char* path) { Str256 command(path); #if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE ImPathFixSeparatorsForCurrentOS(command.c_str()); ::ShellExecuteA(nullptr, "open", command.c_str(), nullptr, nullptr, SW_SHOWDEFAULT); #elif !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE #if __APPLE__ const char* open_executable = "open"; #else const char* open_executable = "xdg-open"; #endif command.setf("%s \"%s\"", open_executable, path); ImPathFixSeparatorsForCurrentOS(command.c_str()); system(command.c_str()); #else IM_UNUSED(path); IM_ASSERT(0); #endif } void ImOsConsoleSetTextColor(ImOsConsoleStream stream, ImOsConsoleTextColor color) { #if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE HANDLE hConsole = 0; switch (stream) { case ImOsConsoleStream_StandardOutput: hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE); break; case ImOsConsoleStream_StandardError: hConsole = ::GetStdHandle(STD_ERROR_HANDLE); break; } WORD wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; switch (color) { case ImOsConsoleTextColor_Black: wAttributes = 0x00; break; case ImOsConsoleTextColor_White: wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; case ImOsConsoleTextColor_BrightWhite: wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; case ImOsConsoleTextColor_BrightRed: wAttributes = FOREGROUND_RED | FOREGROUND_INTENSITY; break; case ImOsConsoleTextColor_BrightGreen: wAttributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; case ImOsConsoleTextColor_BrightBlue: wAttributes = FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; case ImOsConsoleTextColor_BrightYellow: wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; default: IM_ASSERT(0); } ::SetConsoleTextAttribute(hConsole, wAttributes); #elif defined(__linux) || defined(__linux__) || defined(__MACH__) || defined(__MSL__) // FIXME: check system capabilities (with environment variable TERM) FILE* handle = 0; switch (stream) { case ImOsConsoleStream_StandardOutput: handle = stdout; break; case ImOsConsoleStream_StandardError: handle = stderr; break; } const char* modifier = ""; switch (color) { case ImOsConsoleTextColor_Black: modifier = "\033[30m"; break; case ImOsConsoleTextColor_White: modifier = "\033[0m"; break; case ImOsConsoleTextColor_BrightWhite: modifier = "\033[1;37m"; break; case ImOsConsoleTextColor_BrightRed: modifier = "\033[1;31m"; break; case ImOsConsoleTextColor_BrightGreen: modifier = "\033[1;32m"; break; case ImOsConsoleTextColor_BrightBlue: modifier = "\033[1;34m"; break; case ImOsConsoleTextColor_BrightYellow: modifier = "\033[1;33m"; break; default: IM_ASSERT(0); } fprintf(handle, "%s", modifier); #else IM_UNUSED(stream); IM_UNUSED(color); #endif } bool ImOsIsDebuggerPresent() { #ifdef _WIN32 return ::IsDebuggerPresent() != 0; #elif defined(__linux__) int debugger_pid = 0; char buf[2048]; // TracerPid is located near the start of the file. If end of the buffer gets cut off thats fine. FILE* fp = fopen("/proc/self/status", "rb"); // Can not use ImFileLoadToMemory because size detection of /proc/self/status would fail. if (fp == nullptr) return false; fread(buf, 1, IM_ARRAYSIZE(buf), fp); fclose(fp); buf[IM_ARRAYSIZE(buf) - 1] = 0; if (char* tracer_pid = strstr(buf, "TracerPid:")) { tracer_pid += 10; // Skip label while (isspace(*tracer_pid)) tracer_pid++; debugger_pid = atoi(tracer_pid); } return debugger_pid != 0; #elif defined(__APPLE__) // https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x int junk; int mib[4]; struct kinfo_proc info; size_t size; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. info.kp_proc.p_flag = 0; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); size = sizeof(info); junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0); IM_ASSERT(junk == 0); // We're being debugged if the P_TRACED flag is set. return (info.kp_proc.p_flag & P_TRACED) != 0; #else // FIXME return false; #endif } void ImOsOutputDebugString(const char* message) { #ifdef _WIN32 OutputDebugStringA(message); #else IM_UNUSED(message); #endif } //----------------------------------------------------------------------------- // Str.h + InputText bindings //----------------------------------------------------------------------------- struct InputTextCallbackStr_UserData { Str* StrObj; ImGuiInputTextCallback ChainCallback; void* ChainCallbackUserData; }; static int InputTextCallbackStr(ImGuiInputTextCallbackData* data) { InputTextCallbackStr_UserData* user_data = (InputTextCallbackStr_UserData*)data->UserData; if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) { // Resize string callback // If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want. Str* str = user_data->StrObj; IM_ASSERT(data->Buf == str->c_str()); str->reserve(data->BufTextLen); data->Buf = (char*)str->c_str(); } else if (user_data->ChainCallback) { // Forward to user callback, if any data->UserData = user_data->ChainCallbackUserData; return user_data->ChainCallback(data); } return 0; } // Draw an extra colored frame over the previous item // Similar to DebugDrawItemRect() but use Max(1.0f, FrameBorderSize) void ImGui::ItemErrorFrame(ImU32 col) { ImGuiContext& g = *GetCurrentContext(); ImDrawList* drawlist = GetWindowDrawList(); ImGuiStyle& style = GetStyle(); // FIXME: GetItemRectMin() / GetItemRectMax() will include label. NavRect is not probably defined :( drawlist->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, GetColorU32(col), style.FrameRounding, ImDrawFlags_None, ImMax(1.0f, style.FrameBorderSize)); } bool ImGui::InputText(const char* label, Str* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); flags |= ImGuiInputTextFlags_CallbackResize; InputTextCallbackStr_UserData cb_user_data; cb_user_data.StrObj = str; cb_user_data.ChainCallback = callback; cb_user_data.ChainCallbackUserData = user_data; return InputText(label, (char*)str->c_str(), (size_t)str->capacity() + 1, flags, InputTextCallbackStr, &cb_user_data); } bool ImGui::InputTextWithHint(const char* label, const char* hint, Str* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); flags |= ImGuiInputTextFlags_CallbackResize; InputTextCallbackStr_UserData cb_user_data; cb_user_data.StrObj = str; cb_user_data.ChainCallback = callback; cb_user_data.ChainCallbackUserData = user_data; return InputTextWithHint(label, hint, (char*)str->c_str(), (size_t)str->capacity() + 1, flags, InputTextCallbackStr, &cb_user_data); } bool ImGui::InputTextMultiline(const char* label, Str* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); flags |= ImGuiInputTextFlags_CallbackResize; InputTextCallbackStr_UserData cb_user_data; cb_user_data.StrObj = str; cb_user_data.ChainCallback = callback; cb_user_data.ChainCallbackUserData = user_data; return InputTextMultiline(label, (char*)str->c_str(), (size_t)str->capacity() + 1, size, flags, InputTextCallbackStr, &cb_user_data); } // anchor parameter indicates which split would retain it's constant size. // anchor = 0 - both splits resize when parent container size changes. Both value_1 and value_2 should be persistent. // anchor = -1 - top/left split would have a constant size. bottom/right split would resize when parent container size changes. value_1 should be persistent, value_2 will always be recalculated from value_1. // anchor = +1 - bottom/right split would have a constant size. top/left split would resize when parent container size changes. value_2 should be persistent, value_1 will always be recalculated from value_2. bool ImGui::Splitter(const char* id, float* value_1, float* value_2, int axis, int anchor, float min_size_0, float min_size_1) { // FIXME-DOGFOODING: This needs further refining. // FIXME-SCROLL: When resizing either we'd like to keep scroll focus on something (e.g. last clicked item for list, bottom for log) // See https://github.com/ocornut/imgui/issues/319 ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; ImGuiWindow* window = ImGui::GetCurrentWindow(); if (min_size_0 < 0) min_size_0 = ImGui::GetFrameHeight(); if (min_size_1) min_size_1 = ImGui::GetFrameHeight(); IM_ASSERT(axis == ImGuiAxis_X || axis == ImGuiAxis_Y); float& v_1 = *value_1; float& v_2 = *value_2; ImRect splitter_bb; const float avail = axis == ImGuiAxis_X ? ImGui::GetContentRegionAvail().x - style.ItemSpacing.x : ImGui::GetContentRegionAvail().y - style.ItemSpacing.y; if (anchor < 0) { v_2 = ImMax(avail - v_1, min_size_1); // First split is constant size. } else if (anchor > 0) { v_1 = ImMax(avail - v_2, min_size_0); // Second split is constant size. } else { float r = v_1 / (v_1 + v_2); // Both splits maintain same relative size to parent. v_1 = IM_ROUND(avail * r) - 1; v_2 = IM_ROUND(avail * (1.0f - r)) - 1; } if (axis == ImGuiAxis_X) { float x = window->DC.CursorPos.x + v_1 + IM_ROUND(style.ItemSpacing.x * 0.5f); splitter_bb = ImRect(x - 1, window->WorkRect.Min.y, x + 1, window->WorkRect.Max.y); } else if (axis == ImGuiAxis_Y) { float y = window->DC.CursorPos.y + v_1 + IM_ROUND(style.ItemSpacing.y * 0.5f); splitter_bb = ImRect(window->WorkRect.Min.x, y - 1, window->WorkRect.Max.x, y + 1); } return ImGui::SplitterBehavior(splitter_bb, ImGui::GetID(id), (ImGuiAxis)axis, &v_1, &v_2, min_size_0, min_size_1, 3.0f); } // FIXME-TESTS: Should eventually remove. ImFont* ImGui::FindFontByPrefix(const char* prefix) { ImGuiContext& g = *GImGui; for (ImFont* font : g.IO.Fonts->Fonts) #if IMGUI_VERSION_NUM < 19184 if (strncmp(font->ConfigData->Name, prefix, strlen(prefix)) == 0) #elif defined(IMGUI_HAS_TEXTURES) if (strncmp(font->Sources[0]->Name, prefix, strlen(prefix)) == 0) #else if (strncmp(font->Sources[0].Name, prefix, strlen(prefix)) == 0) #endif return font; return nullptr; } // Legacy version support #if IMGUI_VERSION_NUM < 18924 const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { return tab_bar->GetTabName(tab); } #endif #if IMGUI_VERSION_NUM < 18927 ImGuiID ImGui::TableGetInstanceID(ImGuiTable* table, int instance_no) { // Changed in #6140 return table->ID + instance_no; } #endif ImGuiID TableGetHeaderID(ImGuiTable* table, const char* column, int instance_no) { IM_ASSERT(table != nullptr); int column_n = -1; for (int n = 0; n < table->Columns.size() && column_n < 0; n++) if (strcmp(ImGui::TableGetColumnName(table, n), column) == 0) column_n = n; IM_ASSERT(column_n != -1); return TableGetHeaderID(table, column_n, instance_no); } ImGuiID TableGetHeaderID(ImGuiTable* table, int column_n, int instance_no) { IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); const ImGuiID table_instance_id = ImGui::TableGetInstanceID(table, instance_no); const char* column_name = ImGui::TableGetColumnName(table, column_n); #if IMGUI_VERSION_NUM >= 18927 const int column_id_differencier = column_n; #else const int column_id_differencier = instance_no * table->ColumnsCount + column_n; #endif const int column_id = ImHashData(&column_id_differencier, sizeof(column_id_differencier), table_instance_id); return ImHashData(column_name, strlen(column_name), column_id); } // FIXME: Could be moved to core as an internal function? void TableDiscardInstanceAndSettings(ImGuiID table_id) { ImGuiContext& g = *GImGui; IM_ASSERT(g.CurrentTable == nullptr); if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(table_id)) settings->ID = 0; if (ImGuiTable* table = ImGui::TableFindByID(table_id)) ImGui::TableRemove(table); // FIXME-TABLE: We should be able to use TableResetSettings() instead of TableRemove()! Maybe less of a clean slate but would be good to check that it does the job //ImGui::TableResetSettings(table); } // Helper to verify ImDrawData integrity of buffer count (broke before e.g. #6716) void DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data) { #if IMGUI_VERSION_NUM >= 18973 int total_vtx_count = 0; int total_idx_count = 0; for (ImDrawList* draw_list : draw_data->CmdLists) { total_vtx_count += draw_list->VtxBuffer.Size; total_idx_count += draw_list->IdxBuffer.Size; } IM_UNUSED(total_vtx_count); IM_UNUSED(total_idx_count); IM_ASSERT(total_vtx_count == draw_data->TotalVtxCount); IM_ASSERT(total_idx_count == draw_data->TotalIdxCount); #else IM_UNUSED(draw_data); #endif } //----------------------------------------------------------------------------- // Simple CSV parser //----------------------------------------------------------------------------- void ImGuiCsvParser::Clear() { Rows = Columns = 0; if (_Data != nullptr) IM_FREE(_Data); _Data = nullptr; _Index.clear(); } bool ImGuiCsvParser::Load(const char* filename) { size_t len = 0; _Data = (char*)ImFileLoadToMemory(filename, "rb", &len, 1); if (_Data == nullptr) return false; int columns = 1; if (Columns > 0) { columns = Columns; // User-provided expected column count. } else { for (const char* c = _Data; *c != '\n' && *c != '\0'; c++) // Count columns. Quoted columns with commas are not supported. if (*c == ',') columns++; } // Count rows. Extra new lines anywhere in the file are ignored. int max_rows = 0; for (const char* c = _Data, *end = c + len; c < end; c++) if ((*c == '\n' && c[1] != '\r' && c[1] != '\n') || *c == '\0') max_rows++; if (columns == 0 || max_rows == 0) return false; // Create index _Index.resize(columns * max_rows); int col = 0; char* col_data = _Data; for (char* c = _Data; *c != '\0'; c++) { const bool is_comma = (*c == ','); const bool is_eol = (*c == '\n' || *c == '\r'); const bool is_eof = (*c == '\0'); if (is_comma || is_eol || is_eof) { _Index[Rows * columns + col] = col_data; col_data = c + 1; if (is_comma) { col++; } else { if (col + 1 == columns) Rows++; else fprintf(stderr, "%s: Unexpected number of columns on line %d, ignoring.\n", filename, Rows + 1); // FIXME col = 0; } *c = 0; if (is_eol) while (c[1] == '\r' || c[1] == '\n') c++; } } Columns = columns; return true; } //----------------------------------------------------------------------------- ================================================ FILE: lib/third_party/imgui/imnodes/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) # https://github.com/Nelarius/imnodes project(imgui_imnodes) set(CMAKE_CXX_STANDARD 23) if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD) add_library(imgui_imnodes OBJECT source/imnodes.cpp ) target_include_directories(imgui_imnodes PUBLIC include ) target_link_libraries(imgui_imnodes PRIVATE imgui_includes) endif() target_include_directories(imgui_all_includes INTERFACE include) ================================================ FILE: lib/third_party/imgui/imnodes/LICENSE.md ================================================ MIT License Copyright (c) 2019 Johann Muszynski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: lib/third_party/imgui/imnodes/README.md ================================================

imnodes

A small, dependency-free node editor extension for dear imgui.

[![Build Status](https://github.com/nelarius/imnodes/workflows/Build/badge.svg)](https://github.com/nelarius/imnodes/actions?workflow=Build) Imnodes aims to provide a simple, immediate-mode interface for creating a node editor within an ImGui window. Imnodes provides simple, customizable building blocks that a user needs to build their node editor. Features: * Create nodes, links, and pins in an immediate-mode style. The user controls all the state. * Nest ImGui widgets inside nodes * Simple distribution, just copy-paste `imnodes.h`, `imnodes_internal.h`, and `imnodes.cpp` into your project along side ImGui. ## Examples This repository includes a few example files, under `example/`. They are intended as simple examples giving you an idea of what you can build with imnodes. If you need to build the examples, you can use the provided CMake script to do so. ```bash # Initialize the vcpkg submodule $ git submodule update --init # Run the generation step and build $ cmake -B build-release/ -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake $ cmake --build build-release -- -j ``` Note that this has not been tested on Linux and is likely to fail on the platform. ## A brief tour Here is a small overview of how the extension is used. For more information on example usage, scroll to the bottom of the README. Before anything can be done, the library must be initialized. This can be done at the same time as `dear imgui` initialization. ```cpp ImGui::CreateContext(); ImNodes::CreateContext(); // elsewhere in the code... ImNodes::DestroyContext(); ImGui::DestroyContext(); ``` The node editor is a workspace which contains nodes. The node editor must be instantiated within a window, like any other UI element. ```cpp ImGui::Begin("node editor"); ImNodes::BeginNodeEditor(); ImNodes::EndNodeEditor(); ImGui::End(); ``` Now you should have a workspace with a grid visible in the window. An empty node can now be instantiated: ```cpp const int hardcoded_node_id = 1; ImNodes::BeginNodeEditor(); ImNodes::BeginNode(hardcoded_node_id); ImGui::Dummy(ImVec2(80.0f, 45.0f)); ImNodes::EndNode(); ImNodes::EndNodeEditor(); ``` Nodes, like windows in `dear imgui` must be uniquely identified. But we can't use the node titles for identification, because it should be possible to have many nodes of the same name in the workspace. Instead, you just use integers for identification. Attributes are the UI content of the node. An attribute will have a pin (the little circle) on either side of the node. There are two types of attributes: input, and output attributes. Input attribute pins are on the left side of the node, and output attribute pins are on the right. Like nodes, pins must be uniquely identified. ```cpp ImNodes::BeginNode(hardcoded_node_id); const int output_attr_id = 2; ImNodes::BeginOutputAttribute(output_attr_id); // in between Begin|EndAttribute calls, you can call ImGui // UI functions ImGui::Text("output pin"); ImNodes::EndOutputAttribute(); ImNodes::EndNode(); ``` The extension doesn't really care what is in the attribute. It just renders the pin for the attribute, and allows the user to create links between pins. A title bar can be added to the node using `BeginNodeTitleBar` and `EndNodeTitleBar`. Like attributes, you place your title bar's content between the function calls. Note that these functions have to be called before adding attributes or other `dear imgui` UI elements to the node, since the node's layout is built in order, top-to-bottom. ```cpp ImNodes::BeginNode(hardcoded_node_id); ImNodes::BeginNodeTitleBar(); ImGui::TextUnformatted("output node"); ImNodes::EndNodeTitleBar(); // pins and other node UI content omitted... ImNodes::EndNode(); ``` The user has to render their own links between nodes as well. A link is a curve which connects two attributes. A link is just a pair of attribute ids. And like nodes and attributes, links too have to be identified by unique integer values: ```cpp std::vector> links; // elsewhere in the code... for (int i = 0; i < links.size(); ++i) { const std::pair p = links[i]; // in this case, we just use the array index of the link // as the unique identifier ImNodes::Link(i, p.first, p.second); } ``` After `EndNodeEditor` has been called, you can check if a link was created during the frame with the function call `IsLinkCreated`: ```cpp int start_attr, end_attr; if (ImNodes::IsLinkCreated(&start_attr, &end_attr)) { links.push_back(std::make_pair(start_attr, end_attr)); } ``` In addition to checking for new links, you can also check whether UI elements are being hovered over by the mouse cursor: ```cpp int node_id; if (ImNodes::IsNodeHovered(&node_id)) { node_hovered = node_id; } ``` You can also check to see if any node has been selected. Nodes can be clicked on, or they can be selected by clicking and dragging the box selector over them. ```cpp // Note that since many nodes can be selected at once, we first need to query the number of // selected nodes before getting them. const int num_selected_nodes = ImNodes::NumSelectedNodes(); if (num_selected_nodes > 0) { std::vector selected_nodes; selected_nodes.resize(num_selected_nodes); ImNodes::GetSelectedNodes(selected_nodes.data()); } ``` See `imnodes.h` for more UI event-related functions. Like `dear imgui`, the style of the UI can be changed. You can set the color style of individual nodes, pins, and links mid-frame by calling `ImNodes::PushColorStyle` and `ImNodes::PopColorStyle`. ```cpp // set the titlebar color of an individual node ImNodes::PushColorStyle( ImNodesCol_TitleBar, IM_COL32(11, 109, 191, 255)); ImNodes::PushColorStyle( ImNodesCol_TitleBarSelected, IM_COL32(81, 148, 204, 255)); ImNodes::BeginNode(hardcoded_node_id); // node internals here... ImNodes::EndNode(); ImNodes::PopColorStyle(); ImNodes::PopColorStyle(); ``` If the style is not being set mid-frame, `ImNodes::GetStyle` can be called instead, and the values can be set into the style array directly. ```cpp // set the titlebar color for all nodes ImNodesStyle& style = ImNodes::GetStyle(); style.colors[ImNodesCol_TitleBar] = IM_COL32(232, 27, 86, 255); style.colors[ImNodesCol_TitleBarSelected] = IM_COL32(241, 108, 146, 255); ``` To handle quicker navigation of large graphs you can use an interactive mini-map overlay. The mini-map can be zoomed and scrolled. Editor nodes will track the panning of the mini-map accordingly. ```cpp ImGui::Begin("node editor"); ImNodes::BeginNodeEditor(); // add nodes... // must be called right before EndNodeEditor ImNodes::MiniMap(); ImNodes::EndNodeEditor(); ImGui::End(); ``` The relative sizing and corner location of the mini-map in the editor space can be specified like so: ```cpp // MiniMap is a square region with a side length that is 20% the largest editor canvas dimension // See ImNodesMiniMapLocation_ for other corner locations ImNodes::MiniMap(0.2f, ImNodesMiniMapLocation_TopRight); ``` The mini-map also supports limited node hovering customization through a user-defined callback. ```cpp // User callback void mini_map_node_hovering_callback(int node_id, void* user_data) { ImGui::SetTooltip("This is node %d", node_id); } // Later on... ImNodes::MiniMap(0.2f, ImNodesMiniMapLocation_TopRight, mini_map_node_hovering_callback, custom_user_data); // 'custom_user_data' can be used to supply extra information needed for drawing within the callback ``` ## Customizing ImNodes ImNodes can be customized by providing an `imnodes_config.h` header and specifying defining `IMNODES_USER_CONFIG=imnodes_config.h` when compiling. It is currently possible to override the type of the minimap hovering callback function. This is useful when generating bindings for another language. Here's an example imnodes_config.h, which generates a pybind wrapper for the callback. ```cpp #pragma once #include namespace pybind11 { inline bool PyWrapper_Check(PyObject *o) { return true; } class wrapper : public object { public: PYBIND11_OBJECT_DEFAULT(wrapper, object, PyWrapper_Check) wrapper(void* x) { m_ptr = (PyObject*)x; } explicit operator bool() const { return m_ptr != nullptr && m_ptr != Py_None; } }; } //namespace pybind11 namespace py = pybind11; #define ImNodesMiniMapNodeHoveringCallback py::wrapper #define ImNodesMiniMapNodeHoveringCallbackUserData py::wrapper ``` ## Known issues * `ImGui::Separator()` spans the current window span. As a result, using a separator inside a node will result in the separator spilling out of the node into the node editor grid. ## Further information See the `examples/` directory to see library usage in greater detail. * simple.cpp is a simple hello-world style program which displays two nodes * save_load.cpp is enables you to add and remove nodes and links, and serializes/deserializes them, so that the program state is retained between restarting the program * color_node_editor.cpp is a more complete example, which shows how a simple node editor is implemented with a graph. ================================================ FILE: lib/third_party/imgui/imnodes/include/imnodes.h ================================================ #pragma once #include #include #ifdef IMNODES_USER_CONFIG #include IMNODES_USER_CONFIG #endif #ifndef IMNODES_NAMESPACE #define IMNODES_NAMESPACE ImNodes #endif typedef int ImNodesCol; // -> enum ImNodesCol_ typedef int ImNodesStyleVar; // -> enum ImNodesStyleVar_ typedef int ImNodesStyleFlags; // -> enum ImNodesStyleFlags_ typedef int ImNodesPinShape; // -> enum ImNodesPinShape_ typedef int ImNodesAttributeFlags; // -> enum ImNodesAttributeFlags_ typedef int ImNodesMiniMapLocation; // -> enum ImNodesMiniMapLocation_ enum ImNodesCol_ { ImNodesCol_NodeBackground = 0, ImNodesCol_NodeBackgroundHovered, ImNodesCol_NodeBackgroundSelected, ImNodesCol_NodeOutline, ImNodesCol_TitleBar, ImNodesCol_TitleBarHovered, ImNodesCol_TitleBarSelected, ImNodesCol_Link, ImNodesCol_LinkHovered, ImNodesCol_LinkSelected, ImNodesCol_Pin, ImNodesCol_PinHovered, ImNodesCol_BoxSelector, ImNodesCol_BoxSelectorOutline, ImNodesCol_GridBackground, ImNodesCol_GridLine, ImNodesCol_GridLinePrimary, ImNodesCol_MiniMapBackground, ImNodesCol_MiniMapBackgroundHovered, ImNodesCol_MiniMapOutline, ImNodesCol_MiniMapOutlineHovered, ImNodesCol_MiniMapNodeBackground, ImNodesCol_MiniMapNodeBackgroundHovered, ImNodesCol_MiniMapNodeBackgroundSelected, ImNodesCol_MiniMapNodeOutline, ImNodesCol_MiniMapLink, ImNodesCol_MiniMapLinkSelected, ImNodesCol_MiniMapCanvas, ImNodesCol_MiniMapCanvasOutline, ImNodesCol_COUNT }; enum ImNodesStyleVar_ { ImNodesStyleVar_GridSpacing = 0, ImNodesStyleVar_NodeCornerRounding, ImNodesStyleVar_NodePadding, ImNodesStyleVar_NodeBorderThickness, ImNodesStyleVar_LinkThickness, ImNodesStyleVar_LinkLineSegmentsPerLength, ImNodesStyleVar_LinkHoverDistance, ImNodesStyleVar_PinCircleRadius, ImNodesStyleVar_PinQuadSideLength, ImNodesStyleVar_PinTriangleSideLength, ImNodesStyleVar_PinLineThickness, ImNodesStyleVar_PinHoverRadius, ImNodesStyleVar_PinOffset, ImNodesStyleVar_MiniMapPadding, ImNodesStyleVar_MiniMapOffset, ImNodesStyleVar_COUNT }; enum ImNodesStyleFlags_ { ImNodesStyleFlags_None = 0, ImNodesStyleFlags_NodeOutline = 1 << 0, ImNodesStyleFlags_GridLines = 1 << 2, ImNodesStyleFlags_GridLinesPrimary = 1 << 3, ImNodesStyleFlags_GridSnapping = 1 << 4 }; enum ImNodesPinShape_ { ImNodesPinShape_Circle, ImNodesPinShape_CircleFilled, ImNodesPinShape_Triangle, ImNodesPinShape_TriangleFilled, ImNodesPinShape_Quad, ImNodesPinShape_QuadFilled }; // This enum controls the way the attribute pins behave. enum ImNodesAttributeFlags_ { ImNodesAttributeFlags_None = 0, // Allow detaching a link by left-clicking and dragging the link at a pin it is connected to. // NOTE: the user has to actually delete the link for this to work. A deleted link can be // detected by calling IsLinkDestroyed() after EndNodeEditor(). ImNodesAttributeFlags_EnableLinkDetachWithDragClick = 1 << 0, // Visual snapping of an in progress link will trigger IsLink Created/Destroyed events. Allows // for previewing the creation of a link while dragging it across attributes. See here for demo: // https://github.com/Nelarius/imnodes/issues/41#issuecomment-647132113 NOTE: the user has to // actually delete the link for this to work. A deleted link can be detected by calling // IsLinkDestroyed() after EndNodeEditor(). ImNodesAttributeFlags_EnableLinkCreationOnSnap = 1 << 1 }; struct ImNodesIO { struct EmulateThreeButtonMouse { EmulateThreeButtonMouse(); // The keyboard modifier to use in combination with mouse left click to pan the editor view. // Set to NULL by default. To enable this feature, set the modifier to point to a boolean // indicating the state of a modifier. For example, // // ImNodes::GetIO().EmulateThreeButtonMouse.Modifier = &ImGui::GetIO().KeyAlt; const bool* Modifier; } EmulateThreeButtonMouse; struct LinkDetachWithModifierClick { LinkDetachWithModifierClick(); // Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL // by default. To enable the feature, set the modifier to point to a boolean indicating the // state of a modifier. For example, // // ImNodes::GetIO().LinkDetachWithModifierClick.Modifier = &ImGui::GetIO().KeyCtrl; // // Left-clicking a link with this modifier pressed will detach that link. NOTE: the user has // to actually delete the link for this to work. A deleted link can be detected by calling // IsLinkDestroyed() after EndNodeEditor(). const bool* Modifier; } LinkDetachWithModifierClick; struct MultipleSelectModifier { MultipleSelectModifier(); // Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL // by default. To enable the feature, set the modifier to point to a boolean indicating the // state of a modifier. For example, // // ImNodes::GetIO().MultipleSelectModifier.Modifier = &ImGui::GetIO().KeyCtrl; // // Left-clicking a node with this modifier pressed will add the node to the list of // currently selected nodes. If this value is NULL, the Ctrl key will be used. const bool* Modifier; } MultipleSelectModifier; // Holding alt mouse button pans the node area, by default middle mouse button will be used // Set based on ImGuiMouseButton values int AltMouseButton; // Panning speed when dragging an element and mouse is outside the main editor view. float AutoPanningSpeed; ImNodesIO(); }; struct ImNodesStyle { float GridSpacing; float NodeCornerRounding; ImVec2 NodePadding; float NodeBorderThickness; float LinkThickness; float LinkLineSegmentsPerLength; float LinkHoverDistance; // The following variables control the look and behavior of the pins. The default size of each // pin shape is balanced to occupy approximately the same surface area on the screen. // The circle radius used when the pin shape is either ImNodesPinShape_Circle or // ImNodesPinShape_CircleFilled. float PinCircleRadius; // The quad side length used when the shape is either ImNodesPinShape_Quad or // ImNodesPinShape_QuadFilled. float PinQuadSideLength; // The equilateral triangle side length used when the pin shape is either // ImNodesPinShape_Triangle or ImNodesPinShape_TriangleFilled. float PinTriangleSideLength; // The thickness of the line used when the pin shape is not filled. float PinLineThickness; // The radius from the pin's center position inside of which it is detected as being hovered // over. float PinHoverRadius; // Offsets the pins' positions from the edge of the node to the outside of the node. float PinOffset; // Mini-map padding size between mini-map edge and mini-map content. ImVec2 MiniMapPadding; // Mini-map offset from the screen side. ImVec2 MiniMapOffset; // By default, ImNodesStyleFlags_NodeOutline and ImNodesStyleFlags_Gridlines are enabled. ImNodesStyleFlags Flags; // Set these mid-frame using Push/PopColorStyle. You can index this color array with with a // ImNodesCol value. unsigned int Colors[ImNodesCol_COUNT]; ImNodesStyle(); }; enum ImNodesMiniMapLocation_ { ImNodesMiniMapLocation_BottomLeft, ImNodesMiniMapLocation_BottomRight, ImNodesMiniMapLocation_TopLeft, ImNodesMiniMapLocation_TopRight, }; struct ImGuiContext; struct ImVec2; struct ImNodesContext; // An editor context corresponds to a set of nodes in a single workspace (created with a single // Begin/EndNodeEditor pair) // // By default, the library creates an editor context behind the scenes, so using any of the imnodes // functions doesn't require you to explicitly create a context. struct ImNodesEditorContext; // Callback type used to specify special behavior when hovering a node in the minimap #ifndef ImNodesMiniMapNodeHoveringCallback typedef void (*ImNodesMiniMapNodeHoveringCallback)(int, void*); #endif #ifndef ImNodesMiniMapNodeHoveringCallbackUserData typedef void* ImNodesMiniMapNodeHoveringCallbackUserData; #endif namespace IMNODES_NAMESPACE { // Call this function if you are compiling imnodes in to a dll, separate from ImGui. Calling this // function sets the GImGui global variable, which is not shared across dll boundaries. void SetImGuiContext(ImGuiContext* ctx); ImNodesContext* CreateContext(); void DestroyContext(ImNodesContext* ctx = NULL); // NULL = destroy current context ImNodesContext* GetCurrentContext(); void SetCurrentContext(ImNodesContext* ctx); ImNodesEditorContext* EditorContextCreate(); void EditorContextFree(ImNodesEditorContext*); void EditorContextSet(ImNodesEditorContext*); ImVec2 EditorContextGetPanning(); void EditorContextResetPanning(const ImVec2& pos); void EditorContextMoveToNode(const int node_id); ImNodesIO& GetIO(); // Returns the global style struct. See the struct declaration for default values. ImNodesStyle& GetStyle(); // Style presets matching the dear imgui styles of the same name. If dest is NULL, the active // context's ImNodesStyle instance will be used as the destination. void StyleColorsDark(ImNodesStyle* dest = NULL); // on by default void StyleColorsClassic(ImNodesStyle* dest = NULL); void StyleColorsLight(ImNodesStyle* dest = NULL); // The top-level function call. Call this before calling BeginNode/EndNode. Calling this function // will result the node editor grid workspace being rendered. void BeginNodeEditor(); void EndNodeEditor(); // Add a navigable minimap to the editor; call before EndNodeEditor after all // nodes and links have been specified void MiniMap( const float minimap_size_fraction = 0.2f, const ImNodesMiniMapLocation location = ImNodesMiniMapLocation_TopLeft, const ImNodesMiniMapNodeHoveringCallback node_hovering_callback = NULL, const ImNodesMiniMapNodeHoveringCallbackUserData node_hovering_callback_data = NULL); // Use PushColorStyle and PopColorStyle to modify ImNodesStyle::Colors mid-frame. void PushColorStyle(ImNodesCol item, unsigned int color); void PopColorStyle(); void PushStyleVar(ImNodesStyleVar style_item, float value); void PushStyleVar(ImNodesStyleVar style_item, const ImVec2& value); void PopStyleVar(int count = 1); // id can be any positive or negative integer, but INT_MIN is currently reserved for internal use. void BeginNode(int id); void EndNode(); ImVec2 GetNodeDimensions(int id); // Place your node title bar content (such as the node title, using ImGui::Text) between the // following function calls. These functions have to be called before adding any attributes, or the // layout of the node will be incorrect. void BeginNodeTitleBar(); void EndNodeTitleBar(); // Attributes are ImGui UI elements embedded within the node. Attributes can have pin shapes // rendered next to them. Links are created between pins. // // The activity status of an attribute can be checked via the IsAttributeActive() and // IsAnyAttributeActive() function calls. This is one easy way of checking for any changes made to // an attribute's drag float UI, for instance. // // Each attribute id must be unique. // Create an input attribute block. The pin is rendered on left side. void BeginInputAttribute(int id, ImNodesPinShape shape = ImNodesPinShape_CircleFilled); void EndInputAttribute(); // Create an output attribute block. The pin is rendered on the right side. void BeginOutputAttribute(int id, ImNodesPinShape shape = ImNodesPinShape_CircleFilled); void EndOutputAttribute(); // Create a static attribute block. A static attribute has no pin, and therefore can't be linked to // anything. However, you can still use IsAttributeActive() and IsAnyAttributeActive() to check for // attribute activity. void BeginStaticAttribute(int id); void EndStaticAttribute(); // Push a single AttributeFlags value. By default, only AttributeFlags_None is set. void PushAttributeFlag(ImNodesAttributeFlags flag); void PopAttributeFlag(); // Render a link between attributes. // The attributes ids used here must match the ids used in Begin(Input|Output)Attribute function // calls. The order of start_attr and end_attr doesn't make a difference for rendering the link. void Link(int id, int start_attribute_id, int end_attribute_id); // Enable or disable the ability to click and drag a specific node. void SetNodeDraggable(int node_id, const bool draggable); // The node's position can be expressed in three coordinate systems: // * screen space coordinates, -- the origin is the upper left corner of the window. // * editor space coordinates -- the origin is the upper left corner of the node editor window // * grid space coordinates, -- the origin is the upper left corner of the node editor window, // translated by the current editor panning vector (see EditorContextGetPanning() and // EditorContextResetPanning()) // Use the following functions to get and set the node's coordinates in these coordinate systems. void SetNodeScreenSpacePos(int node_id, const ImVec2& screen_space_pos); void SetNodeEditorSpacePos(int node_id, const ImVec2& editor_space_pos); void SetNodeGridSpacePos(int node_id, const ImVec2& grid_pos); ImVec2 GetNodeScreenSpacePos(const int node_id); ImVec2 GetNodeEditorSpacePos(const int node_id); ImVec2 GetNodeGridSpacePos(const int node_id); // If ImNodesStyleFlags_GridSnapping is enabled, snap the specified node's origin to the grid. void SnapNodeToGrid(int node_id); // Returns true if the current node editor canvas is being hovered over by the mouse, and is not // blocked by any other windows. bool IsEditorHovered(); // The following functions return true if a UI element is being hovered over by the mouse cursor. // Assigns the id of the UI element being hovered over to the function argument. Use these functions // after EndNodeEditor() has been called. bool IsNodeHovered(int* node_id); bool IsLinkHovered(int* link_id); bool IsPinHovered(int* attribute_id); // Use The following two functions to query the number of selected nodes or links in the current // editor. Use after calling EndNodeEditor(). int NumSelectedNodes(); int NumSelectedLinks(); // Get the selected node/link ids. The pointer argument should point to an integer array with at // least as many elements as the respective NumSelectedNodes/NumSelectedLinks function call // returned. void GetSelectedNodes(int* node_ids); void GetSelectedLinks(int* link_ids); // Clears the list of selected nodes/links. Useful if you want to delete a selected node or link. void ClearNodeSelection(); void ClearLinkSelection(); // Use the following functions to add or remove individual nodes or links from the current editors // selection. Note that all functions require the id to be an existing valid id for this editor. // Select-functions has the precondition that the object is currently considered unselected. // Clear-functions has the precondition that the object is currently considered selected. // Preconditions listed above can be checked via IsNodeSelected/IsLinkSelected if not already // known. void SelectNode(int node_id); void ClearNodeSelection(int node_id); bool IsNodeSelected(int node_id); void SelectLink(int link_id); void ClearLinkSelection(int link_id); bool IsLinkSelected(int link_id); // Was the previous attribute active? This will continuously return true while the left mouse button // is being pressed over the UI content of the attribute. bool IsAttributeActive(); // Was any attribute active? If so, sets the active attribute id to the output function argument. bool IsAnyAttributeActive(int* attribute_id = NULL); // Use the following functions to query a change of state for an existing link, or new link. Call // these after EndNodeEditor(). // Did the user start dragging a new link from a pin? bool IsLinkStarted(int* started_at_attribute_id); // Did the user drop the dragged link before attaching it to a pin? // There are two different kinds of situations to consider when handling this event: // 1) a link which is created at a pin and then dropped // 2) an existing link which is detached from a pin and then dropped // Use the including_detached_links flag to control whether this function triggers when the user // detaches a link and drops it. bool IsLinkDropped(int* started_at_attribute_id = NULL, bool including_detached_links = true); // Did the user finish creating a new link? bool IsLinkCreated( int* started_at_attribute_id, int* ended_at_attribute_id, bool* created_from_snap = NULL); bool IsLinkCreated( int* started_at_node_id, int* started_at_attribute_id, int* ended_at_node_id, int* ended_at_attribute_id, bool* created_from_snap = NULL); // Was an existing link detached from a pin by the user? The detached link's id is assigned to the // output argument link_id. bool IsLinkDestroyed(int* link_id); // Use the following functions to write the editor context's state to a string, or directly to a // file. The editor context is serialized in the INI file format. const char* SaveCurrentEditorStateToIniString(size_t* data_size = NULL); const char* SaveEditorStateToIniString( const ImNodesEditorContext* editor, size_t* data_size = NULL); void LoadCurrentEditorStateFromIniString(const char* data, size_t data_size); void LoadEditorStateFromIniString(ImNodesEditorContext* editor, const char* data, size_t data_size); void SaveCurrentEditorStateToIniFile(const char* file_name); void SaveEditorStateToIniFile(const ImNodesEditorContext* editor, const char* file_name); void LoadCurrentEditorStateFromIniFile(const char* file_name); void LoadEditorStateFromIniFile(ImNodesEditorContext* editor, const char* file_name); } // namespace IMNODES_NAMESPACE ================================================ FILE: lib/third_party/imgui/imnodes/include/imnodes_internal.h ================================================ #pragma once #include "imnodes.h" #include #define IMGUI_DEFINE_MATH_OPERATORS #include #include // the structure of this file: // // [SECTION] internal enums // [SECTION] internal data structures // [SECTION] global and editor context structs // [SECTION] object pool implementation struct ImNodesContext; IMGUI_API extern ImNodesContext* GImNodes; // [SECTION] internal enums typedef int ImNodesScope; typedef int ImNodesAttributeType; typedef int ImNodesUIState; typedef int ImNodesClickInteractionType; typedef int ImNodesLinkCreationType; enum ImNodesScope_ { ImNodesScope_None = 1, ImNodesScope_Editor = 1 << 1, ImNodesScope_Node = 1 << 2, ImNodesScope_Attribute = 1 << 3 }; enum ImNodesAttributeType_ { ImNodesAttributeType_None, ImNodesAttributeType_Input, ImNodesAttributeType_Output }; enum ImNodesUIState_ { ImNodesUIState_None = 0, ImNodesUIState_LinkStarted = 1 << 0, ImNodesUIState_LinkDropped = 1 << 1, ImNodesUIState_LinkCreated = 1 << 2 }; enum ImNodesClickInteractionType_ { ImNodesClickInteractionType_Node, ImNodesClickInteractionType_Link, ImNodesClickInteractionType_LinkCreation, ImNodesClickInteractionType_Panning, ImNodesClickInteractionType_BoxSelection, ImNodesClickInteractionType_ImGuiItem, ImNodesClickInteractionType_None }; enum ImNodesLinkCreationType_ { ImNodesLinkCreationType_Standard, ImNodesLinkCreationType_FromDetach }; // [SECTION] internal data structures // The object T must have the following interface: // // struct T // { // T(); // // int id; // }; template struct ImObjectPool { ImVector Pool; ImVector InUse; ImVector FreeList; ImGuiStorage IdMap; ImObjectPool() : Pool(), InUse(), FreeList(), IdMap() {} }; // Emulates std::optional using the sentinel value `INVALID_INDEX`. struct ImOptionalIndex { ImOptionalIndex() : _Index(INVALID_INDEX) {} ImOptionalIndex(const int value) : _Index(value) {} // Observers inline bool HasValue() const { return _Index != INVALID_INDEX; } inline int Value() const { IM_ASSERT(HasValue()); return _Index; } // Modifiers inline ImOptionalIndex& operator=(const int value) { _Index = value; return *this; } inline void Reset() { _Index = INVALID_INDEX; } inline bool operator==(const ImOptionalIndex& rhs) const { return _Index == rhs._Index; } inline bool operator==(const int rhs) const { return _Index == rhs; } inline bool operator!=(const ImOptionalIndex& rhs) const { return _Index != rhs._Index; } inline bool operator!=(const int rhs) const { return _Index != rhs; } static const int INVALID_INDEX = -1; private: int _Index; }; struct ImNodeData { int Id; ImVec2 Origin; // The node origin is in editor space ImRect TitleBarContentRect; ImRect Rect; struct { ImU32 Background, BackgroundHovered, BackgroundSelected, Outline, Titlebar, TitlebarHovered, TitlebarSelected; } ColorStyle; struct { float CornerRounding; ImVec2 Padding; float BorderThickness; } LayoutStyle; ImVector PinIndices; bool Draggable; ImNodeData(const int node_id) : Id(node_id), Origin(0.0f, 0.0f), TitleBarContentRect(), Rect(ImVec2(0.0f, 0.0f), ImVec2(0.0f, 0.0f)), ColorStyle(), LayoutStyle(), PinIndices(), Draggable(true) { } ~ImNodeData() { Id = INT_MIN; } }; struct ImPinData { int Id; int ParentNodeIdx; ImRect AttributeRect; ImNodesAttributeType Type; ImNodesPinShape Shape; ImVec2 Pos; // screen-space coordinates int Flags; struct { ImU32 Background, Hovered; } ColorStyle; ImPinData(const int pin_id) : Id(pin_id), ParentNodeIdx(), AttributeRect(), Type(ImNodesAttributeType_None), Shape(ImNodesPinShape_CircleFilled), Pos(), Flags(ImNodesAttributeFlags_None), ColorStyle() { } }; struct ImLinkData { int Id; int StartPinIdx, EndPinIdx; struct { ImU32 Base, Hovered, Selected; } ColorStyle; ImLinkData(const int link_id) : Id(link_id), StartPinIdx(), EndPinIdx(), ColorStyle() {} }; struct ImClickInteractionState { ImNodesClickInteractionType Type; struct { int StartPinIdx; ImOptionalIndex EndPinIdx; ImNodesLinkCreationType Type; } LinkCreation; struct { ImRect Rect; // Coordinates in grid space } BoxSelector; ImClickInteractionState() : Type(ImNodesClickInteractionType_None) {} }; struct ImNodesColElement { ImU32 Color; ImNodesCol Item; ImNodesColElement(const ImU32 c, const ImNodesCol s) : Color(c), Item(s) {} }; struct ImNodesStyleVarElement { ImNodesStyleVar Item; float FloatValue[2]; ImNodesStyleVarElement(const ImNodesStyleVar variable, const float value) : Item(variable) { FloatValue[0] = value; } ImNodesStyleVarElement(const ImNodesStyleVar variable, const ImVec2 value) : Item(variable) { FloatValue[0] = value.x; FloatValue[1] = value.y; } }; // [SECTION] global and editor context structs struct ImNodesEditorContext { ImObjectPool Nodes; ImObjectPool Pins; ImObjectPool Links; ImVector NodeDepthOrder; // ui related fields ImVec2 Panning; ImVec2 AutoPanningDelta; // Minimum and maximum extents of all content in grid space. Valid after final // ImNodes::EndNode() call. ImRect GridContentBounds; ImVector SelectedNodeIndices; ImVector SelectedLinkIndices; // Relative origins of selected nodes for snapping of dragged nodes ImVector SelectedNodeOffsets; // Offset of the primary node origin relative to the mouse cursor. ImVec2 PrimaryNodeOffset; ImClickInteractionState ClickInteraction; // Mini-map state set by MiniMap() bool MiniMapEnabled; ImNodesMiniMapLocation MiniMapLocation; float MiniMapSizeFraction; ImNodesMiniMapNodeHoveringCallback MiniMapNodeHoveringCallback; ImNodesMiniMapNodeHoveringCallbackUserData MiniMapNodeHoveringCallbackUserData; // Mini-map state set during EndNodeEditor() call ImRect MiniMapRectScreenSpace; ImRect MiniMapContentScreenSpace; float MiniMapScaling; ImNodesEditorContext() : Nodes(), Pins(), Links(), Panning(0.f, 0.f), SelectedNodeIndices(), SelectedLinkIndices(), SelectedNodeOffsets(), PrimaryNodeOffset(0.f, 0.f), ClickInteraction(), MiniMapEnabled(false), MiniMapSizeFraction(0.0f), MiniMapNodeHoveringCallback(NULL), MiniMapNodeHoveringCallbackUserData(NULL), MiniMapScaling(0.0f) { } }; struct ImNodesContext { ImNodesEditorContext* DefaultEditorCtx; ImNodesEditorContext* EditorCtx; // Canvas draw list and helper state ImDrawList* CanvasDrawList; ImGuiStorage NodeIdxToSubmissionIdx; ImVector NodeIdxSubmissionOrder; ImVector NodeIndicesOverlappingWithMouse; ImVector OccludedPinIndices; // Canvas extents ImVec2 CanvasOriginScreenSpace; ImRect CanvasRectScreenSpace; // Debug helpers ImNodesScope CurrentScope; // Configuration state ImNodesIO Io; ImNodesStyle Style; ImVector ColorModifierStack; ImVector StyleModifierStack; ImGuiTextBuffer TextBuffer; int CurrentAttributeFlags; ImVector AttributeFlagStack; // UI element state int CurrentNodeIdx; int CurrentPinIdx; int CurrentAttributeId; ImOptionalIndex HoveredNodeIdx; ImOptionalIndex HoveredLinkIdx; ImOptionalIndex HoveredPinIdx; ImOptionalIndex DeletedLinkIdx; ImOptionalIndex SnapLinkIdx; // Event helper state // TODO: this should be a part of a state machine, and not a member of the global struct. // Unclear what parts of the code this relates to. int ImNodesUIState; int ActiveAttributeId; bool ActiveAttribute; // ImGui::IO cache ImVec2 MousePos; bool LeftMouseClicked; bool LeftMouseReleased; bool AltMouseClicked; bool LeftMouseDragging; bool AltMouseDragging; float AltMouseScrollDelta; bool MultipleSelectModifier; }; namespace IMNODES_NAMESPACE { static inline ImNodesEditorContext& EditorContextGet() { // No editor context was set! Did you forget to call ImNodes::CreateContext()? IM_ASSERT(GImNodes->EditorCtx != NULL); return *GImNodes->EditorCtx; } // [SECTION] ObjectPool implementation template static inline int ObjectPoolFind(const ImObjectPool& objects, const int id) { const int index = objects.IdMap.GetInt(static_cast(id), -1); return index; } template static inline void ObjectPoolUpdate(ImObjectPool& objects) { for (int i = 0; i < objects.InUse.size(); ++i) { const int id = objects.Pool[i].Id; if (!objects.InUse[i] && objects.IdMap.GetInt(id, -1) == i) { objects.IdMap.SetInt(id, -1); objects.FreeList.push_back(i); (objects.Pool.Data + i)->~T(); } } } template<> inline void ObjectPoolUpdate(ImObjectPool& nodes) { for (int i = 0; i < nodes.InUse.size(); ++i) { if (nodes.InUse[i]) { nodes.Pool[i].PinIndices.clear(); } else { const int id = nodes.Pool[i].Id; if (nodes.IdMap.GetInt(id, -1) == i) { // Remove node idx form depth stack the first time we detect that this idx slot is // unused ImVector& depth_stack = EditorContextGet().NodeDepthOrder; const int* const elem = depth_stack.find(i); IM_ASSERT(elem != depth_stack.end()); depth_stack.erase(elem); nodes.IdMap.SetInt(id, -1); nodes.FreeList.push_back(i); (nodes.Pool.Data + i)->~ImNodeData(); } } } } template static inline void ObjectPoolReset(ImObjectPool& objects) { if (!objects.InUse.empty()) { memset(objects.InUse.Data, 0, objects.InUse.size_in_bytes()); } } template static inline int ObjectPoolFindOrCreateIndex(ImObjectPool& objects, const int id) { int index = objects.IdMap.GetInt(static_cast(id), -1); // Construct new object if (index == -1) { if (objects.FreeList.empty()) { index = objects.Pool.size(); IM_ASSERT(objects.Pool.size() == objects.InUse.size()); const int new_size = objects.Pool.size() + 1; objects.Pool.resize(new_size); objects.InUse.resize(new_size); } else { index = objects.FreeList.back(); objects.FreeList.pop_back(); } IM_PLACEMENT_NEW(objects.Pool.Data + index) T(id); objects.IdMap.SetInt(static_cast(id), index); } // Flag it as used objects.InUse[index] = true; return index; } template<> inline int ObjectPoolFindOrCreateIndex(ImObjectPool& nodes, const int node_id) { int node_idx = nodes.IdMap.GetInt(static_cast(node_id), -1); // Construct new node if (node_idx == -1) { if (nodes.FreeList.empty()) { node_idx = nodes.Pool.size(); IM_ASSERT(nodes.Pool.size() == nodes.InUse.size()); const int new_size = nodes.Pool.size() + 1; nodes.Pool.resize(new_size); nodes.InUse.resize(new_size); } else { node_idx = nodes.FreeList.back(); nodes.FreeList.pop_back(); } IM_PLACEMENT_NEW(nodes.Pool.Data + node_idx) ImNodeData(node_id); nodes.IdMap.SetInt(static_cast(node_id), node_idx); ImNodesEditorContext& editor = EditorContextGet(); editor.NodeDepthOrder.push_back(node_idx); } // Flag node as used nodes.InUse[node_idx] = true; return node_idx; } template static inline T& ObjectPoolFindOrCreateObject(ImObjectPool& objects, const int id) { const int index = ObjectPoolFindOrCreateIndex(objects, id); return objects.Pool[index]; } } // namespace IMNODES_NAMESPACE ================================================ FILE: lib/third_party/imgui/imnodes/source/imnodes.cpp ================================================ // the structure of this file: // // [SECTION] bezier curve helpers // [SECTION] draw list helper // [SECTION] ui state logic // [SECTION] render helpers // [SECTION] API implementation #include "imnodes.h" #include "imnodes_internal.h" #define IMGUI_DEFINE_MATH_OPERATORS #include // Check minimum ImGui version #define MINIMUM_COMPATIBLE_IMGUI_VERSION 17400 #if IMGUI_VERSION_NUM < MINIMUM_COMPATIBLE_IMGUI_VERSION #error "Minimum ImGui version requirement not met -- please use a newer version!" #endif #include #include #include #include #include // for fwrite, ssprintf, sscanf #include #include // strlen, strncmp // Use secure CRT function variants to avoid MSVC compiler errors #ifdef _MSC_VER #define sscanf sscanf_s #endif IMGUI_API ImNodesContext* GImNodes = NULL; namespace IMNODES_NAMESPACE { namespace { // [SECTION] bezier curve helpers struct CubicBezier { ImVec2 P0, P1, P2, P3; int NumSegments; }; inline ImVec2 EvalCubicBezier( const float t, const ImVec2& P0, const ImVec2& P1, const ImVec2& P2, const ImVec2& P3) { // B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3 const float u = 1.0f - t; const float b0 = u * u * u; const float b1 = 3 * u * u * t; const float b2 = 3 * u * t * t; const float b3 = t * t * t; return ImVec2( b0 * P0.x + b1 * P1.x + b2 * P2.x + b3 * P3.x, b0 * P0.y + b1 * P1.y + b2 * P2.y + b3 * P3.y); } // Calculates the closest point along each bezier curve segment. ImVec2 GetClosestPointOnCubicBezier(const int num_segments, const ImVec2& p, const CubicBezier& cb) { IM_ASSERT(num_segments > 0); ImVec2 p_last = cb.P0; ImVec2 p_closest; float p_closest_dist = FLT_MAX; float t_step = 1.0f / (float)num_segments; for (int i = 1; i <= num_segments; ++i) { ImVec2 p_current = EvalCubicBezier(t_step * i, cb.P0, cb.P1, cb.P2, cb.P3); ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); float dist = ImLengthSqr(p - p_line); if (dist < p_closest_dist) { p_closest = p_line; p_closest_dist = dist; } p_last = p_current; } return p_closest; } inline float GetDistanceToCubicBezier( const ImVec2& pos, const CubicBezier& cubic_bezier, const int num_segments) { const ImVec2 point_on_curve = GetClosestPointOnCubicBezier(num_segments, pos, cubic_bezier); const ImVec2 to_curve = point_on_curve - pos; return ImSqrt(ImLengthSqr(to_curve)); } inline ImRect GetContainingRectForCubicBezier(const CubicBezier& cb) { const ImVec2 min = ImVec2(ImMin(cb.P0.x, cb.P3.x), ImMin(cb.P0.y, cb.P3.y)); const ImVec2 max = ImVec2(ImMax(cb.P0.x, cb.P3.x), ImMax(cb.P0.y, cb.P3.y)); const float hover_distance = GImNodes->Style.LinkHoverDistance; ImRect rect(min, max); rect.Add(cb.P1); rect.Add(cb.P2); rect.Expand(ImVec2(hover_distance, hover_distance)); return rect; } inline CubicBezier GetCubicBezier( ImVec2 start, ImVec2 end, const ImNodesAttributeType start_type, const float line_segments_per_length) { IM_ASSERT( (start_type == ImNodesAttributeType_Input) || (start_type == ImNodesAttributeType_Output)); if (start_type == ImNodesAttributeType_Input) { ImSwap(start, end); } const float link_length = ImSqrt(ImLengthSqr(end - start)); const ImVec2 offset = ImVec2(0.25f * link_length, 0.f); CubicBezier cubic_bezier; cubic_bezier.P0 = start; cubic_bezier.P1 = start + offset; cubic_bezier.P2 = end - offset; cubic_bezier.P3 = end; cubic_bezier.NumSegments = ImMax(static_cast(link_length * line_segments_per_length), 1); return cubic_bezier; } inline float EvalImplicitLineEq(const ImVec2& p1, const ImVec2& p2, const ImVec2& p) { return (p2.y - p1.y) * p.x + (p1.x - p2.x) * p.y + (p2.x * p1.y - p1.x * p2.y); } inline int Sign(float val) { return int(val > 0.0f) - int(val < 0.0f); } inline bool RectangleOverlapsLineSegment(const ImRect& rect, const ImVec2& p1, const ImVec2& p2) { // Trivial case: rectangle contains an endpoint if (rect.Contains(p1) || rect.Contains(p2)) { return true; } // Flip rectangle if necessary ImRect flip_rect = rect; if (flip_rect.Min.x > flip_rect.Max.x) { ImSwap(flip_rect.Min.x, flip_rect.Max.x); } if (flip_rect.Min.y > flip_rect.Max.y) { ImSwap(flip_rect.Min.y, flip_rect.Max.y); } // Trivial case: line segment lies to one particular side of rectangle if ((p1.x < flip_rect.Min.x && p2.x < flip_rect.Min.x) || (p1.x > flip_rect.Max.x && p2.x > flip_rect.Max.x) || (p1.y < flip_rect.Min.y && p2.y < flip_rect.Min.y) || (p1.y > flip_rect.Max.y && p2.y > flip_rect.Max.y)) { return false; } const int corner_signs[4] = { Sign(EvalImplicitLineEq(p1, p2, flip_rect.Min)), Sign(EvalImplicitLineEq(p1, p2, ImVec2(flip_rect.Max.x, flip_rect.Min.y))), Sign(EvalImplicitLineEq(p1, p2, ImVec2(flip_rect.Min.x, flip_rect.Max.y))), Sign(EvalImplicitLineEq(p1, p2, flip_rect.Max))}; int sum = 0; int sum_abs = 0; for (int i = 0; i < 4; ++i) { sum += corner_signs[i]; sum_abs += abs(corner_signs[i]); } // At least one corner of rectangle lies on a different side of line segment return abs(sum) != sum_abs; } inline bool RectangleOverlapsBezier(const ImRect& rectangle, const CubicBezier& cubic_bezier) { ImVec2 current = EvalCubicBezier(0.f, cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3); const float dt = 1.0f / cubic_bezier.NumSegments; for (int s = 0; s < cubic_bezier.NumSegments; ++s) { ImVec2 next = EvalCubicBezier( static_cast((s + 1) * dt), cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3); if (RectangleOverlapsLineSegment(rectangle, current, next)) { return true; } current = next; } return false; } inline bool RectangleOverlapsLink( const ImRect& rectangle, const ImVec2& start, const ImVec2& end, const ImNodesAttributeType start_type) { // First level: simple rejection test via rectangle overlap: ImRect lrect = ImRect(start, end); if (lrect.Min.x > lrect.Max.x) { ImSwap(lrect.Min.x, lrect.Max.x); } if (lrect.Min.y > lrect.Max.y) { ImSwap(lrect.Min.y, lrect.Max.y); } if (rectangle.Overlaps(lrect)) { // First, check if either one or both endpoinds are trivially contained // in the rectangle if (rectangle.Contains(start) || rectangle.Contains(end)) { return true; } // Second level of refinement: do a more expensive test against the // link const CubicBezier cubic_bezier = GetCubicBezier(start, end, start_type, GImNodes->Style.LinkLineSegmentsPerLength); return RectangleOverlapsBezier(rectangle, cubic_bezier); } return false; } // [SECTION] coordinate space conversion helpers inline ImVec2 ScreenSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return v - GImNodes->CanvasOriginScreenSpace - editor.Panning; } inline ImRect ScreenSpaceToGridSpace(const ImNodesEditorContext& editor, const ImRect& r) { return ImRect(ScreenSpaceToGridSpace(editor, r.Min), ScreenSpaceToGridSpace(editor, r.Max)); } inline ImVec2 GridSpaceToScreenSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return v + GImNodes->CanvasOriginScreenSpace + editor.Panning; } inline ImVec2 GridSpaceToEditorSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return v + editor.Panning; } inline ImVec2 EditorSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return v - editor.Panning; } inline ImVec2 EditorSpaceToScreenSpace(const ImVec2& v) { return GImNodes->CanvasOriginScreenSpace + v; } inline ImVec2 MiniMapSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return (v - editor.MiniMapContentScreenSpace.Min) / editor.MiniMapScaling + editor.GridContentBounds.Min; } inline ImVec2 ScreenSpaceToMiniMapSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return (ScreenSpaceToGridSpace(editor, v) - editor.GridContentBounds.Min) * editor.MiniMapScaling + editor.MiniMapContentScreenSpace.Min; } inline ImRect ScreenSpaceToMiniMapSpace(const ImNodesEditorContext& editor, const ImRect& r) { return ImRect( ScreenSpaceToMiniMapSpace(editor, r.Min), ScreenSpaceToMiniMapSpace(editor, r.Max)); } // [SECTION] draw list helper void ImDrawListGrowChannels(ImDrawList* draw_list, const int num_channels) { ImDrawListSplitter& splitter = draw_list->_Splitter; if (splitter._Count == 1) { splitter.Split(draw_list, num_channels + 1); return; } // NOTE: this logic has been lifted from ImDrawListSplitter::Split with slight modifications // to allow nested splits. The main modification is that we only create new ImDrawChannel // instances after splitter._Count, instead of over the whole splitter._Channels array like // the regular ImDrawListSplitter::Split method does. const int old_channel_capacity = splitter._Channels.Size; // NOTE: _Channels is not resized down, and therefore _Count <= _Channels.size()! const int old_channel_count = splitter._Count; const int requested_channel_count = old_channel_count + num_channels; if (old_channel_capacity < old_channel_count + num_channels) { splitter._Channels.resize(requested_channel_count); } splitter._Count = requested_channel_count; for (int i = old_channel_count; i < requested_channel_count; ++i) { ImDrawChannel& channel = splitter._Channels[i]; // If we're inside the old capacity region of the array, we need to reuse the existing // memory of the command and index buffers. if (i < old_channel_capacity) { channel._CmdBuffer.resize(0); channel._IdxBuffer.resize(0); } // Else, we need to construct new draw channels. else { IM_PLACEMENT_NEW(&channel) ImDrawChannel(); } { ImDrawCmd draw_cmd; draw_cmd.ClipRect = draw_list->_ClipRectStack.back(); draw_cmd.TexRef = draw_list->_TextureStack.back(); channel._CmdBuffer.push_back(draw_cmd); } } } void ImDrawListSplitterSwapChannels( ImDrawListSplitter& splitter, const int lhs_idx, const int rhs_idx) { if (lhs_idx == rhs_idx) { return; } IM_ASSERT(lhs_idx >= 0 && lhs_idx < splitter._Count); IM_ASSERT(rhs_idx >= 0 && rhs_idx < splitter._Count); ImDrawChannel& lhs_channel = splitter._Channels[lhs_idx]; ImDrawChannel& rhs_channel = splitter._Channels[rhs_idx]; lhs_channel._CmdBuffer.swap(rhs_channel._CmdBuffer); lhs_channel._IdxBuffer.swap(rhs_channel._IdxBuffer); const int current_channel = splitter._Current; if (current_channel == lhs_idx) { splitter._Current = rhs_idx; } else if (current_channel == rhs_idx) { splitter._Current = lhs_idx; } } void DrawListSet(ImDrawList* window_draw_list) { GImNodes->CanvasDrawList = window_draw_list; GImNodes->NodeIdxToSubmissionIdx.Clear(); GImNodes->NodeIdxSubmissionOrder.clear(); } // The draw list channels are structured as follows. First we have our base channel, the canvas grid // on which we render the grid lines in BeginNodeEditor(). The base channel is the reason // draw_list_submission_idx_to_background_channel_idx offsets the index by one. Each BeginNode() // call appends two new draw channels, for the node background and foreground. The node foreground // is the channel into which the node's ImGui content is rendered. Finally, in EndNodeEditor() we // append one last draw channel for rendering the selection box and the incomplete link on top of // everything else. // // +----------+----------+----------+----------+----------+----------+ // | | | | | | | // |canvas |node |node |... |... |click | // |grid |background|foreground| | |interaction // | | | | | | | // +----------+----------+----------+----------+----------+----------+ // | | // | submission idx | // | | // ----------------------- void DrawListAddNode(const int node_idx) { GImNodes->NodeIdxToSubmissionIdx.SetInt( static_cast(node_idx), GImNodes->NodeIdxSubmissionOrder.Size); GImNodes->NodeIdxSubmissionOrder.push_back(node_idx); ImDrawListGrowChannels(GImNodes->CanvasDrawList, 2); } void DrawListAppendClickInteractionChannel() { // NOTE: don't use this function outside of EndNodeEditor. Using this before all nodes have been // added will screw up the node draw order. ImDrawListGrowChannels(GImNodes->CanvasDrawList, 1); } int DrawListSubmissionIdxToBackgroundChannelIdx(const int submission_idx) { // NOTE: the first channel is the canvas background, i.e. the grid return 1 + 2 * submission_idx; } int DrawListSubmissionIdxToForegroundChannelIdx(const int submission_idx) { return DrawListSubmissionIdxToBackgroundChannelIdx(submission_idx) + 1; } void DrawListActivateClickInteractionChannel() { GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel( GImNodes->CanvasDrawList, GImNodes->CanvasDrawList->_Splitter._Count - 1); } void DrawListActivateCurrentNodeForeground() { const int foreground_channel_idx = DrawListSubmissionIdxToForegroundChannelIdx(GImNodes->NodeIdxSubmissionOrder.Size - 1); GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel( GImNodes->CanvasDrawList, foreground_channel_idx); } void DrawListActivateNodeBackground(const int node_idx) { const int submission_idx = GImNodes->NodeIdxToSubmissionIdx.GetInt(static_cast(node_idx), -1); // There is a discrepancy in the submitted node count and the rendered node count! Did you call // one of the following functions // * EditorContextMoveToNode // * SetNodeScreenSpacePos // * SetNodeGridSpacePos // * SetNodeDraggable // after the BeginNode/EndNode function calls? IM_ASSERT(submission_idx != -1); const int background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(submission_idx); GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel( GImNodes->CanvasDrawList, background_channel_idx); } void DrawListSwapSubmissionIndices(const int lhs_idx, const int rhs_idx) { IM_ASSERT(lhs_idx != rhs_idx); const int lhs_foreground_channel_idx = DrawListSubmissionIdxToForegroundChannelIdx(lhs_idx); const int lhs_background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(lhs_idx); const int rhs_foreground_channel_idx = DrawListSubmissionIdxToForegroundChannelIdx(rhs_idx); const int rhs_background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(rhs_idx); ImDrawListSplitterSwapChannels( GImNodes->CanvasDrawList->_Splitter, lhs_background_channel_idx, rhs_background_channel_idx); ImDrawListSplitterSwapChannels( GImNodes->CanvasDrawList->_Splitter, lhs_foreground_channel_idx, rhs_foreground_channel_idx); } void DrawListSortChannelsByDepth(const ImVector& node_idx_depth_order) { if (GImNodes->NodeIdxToSubmissionIdx.Data.Size < 2) { return; } IM_ASSERT(node_idx_depth_order.Size == GImNodes->NodeIdxSubmissionOrder.Size); int start_idx = node_idx_depth_order.Size - 1; while (node_idx_depth_order[start_idx] == GImNodes->NodeIdxSubmissionOrder[start_idx]) { if (--start_idx == 0) { // early out if submission order and depth order are the same return; } } // TODO: this is an O(N^2) algorithm. It might be worthwhile revisiting this to see if the time // complexity can be reduced. for (int depth_idx = start_idx; depth_idx > 0; --depth_idx) { const int node_idx = node_idx_depth_order[depth_idx]; // Find the current index of the node_idx in the submission order array int submission_idx = -1; for (int i = 0; i < GImNodes->NodeIdxSubmissionOrder.Size; ++i) { if (GImNodes->NodeIdxSubmissionOrder[i] == node_idx) { submission_idx = i; break; } } IM_ASSERT(submission_idx >= 0); if (submission_idx == depth_idx) { continue; } for (int j = submission_idx; j < depth_idx; ++j) { DrawListSwapSubmissionIndices(j, j + 1); ImSwap(GImNodes->NodeIdxSubmissionOrder[j], GImNodes->NodeIdxSubmissionOrder[j + 1]); } } } // [SECTION] ui state logic ImVec2 GetScreenSpacePinCoordinates( const ImRect& node_rect, const ImRect& attribute_rect, const ImNodesAttributeType type) { IM_ASSERT(type == ImNodesAttributeType_Input || type == ImNodesAttributeType_Output); const float x = type == ImNodesAttributeType_Input ? (node_rect.Min.x - GImNodes->Style.PinOffset) : (node_rect.Max.x + GImNodes->Style.PinOffset); return ImVec2(x, 0.5f * (attribute_rect.Min.y + attribute_rect.Max.y)); } ImVec2 GetScreenSpacePinCoordinates(const ImNodesEditorContext& editor, const ImPinData& pin) { const ImRect& parent_node_rect = editor.Nodes.Pool[pin.ParentNodeIdx].Rect; return GetScreenSpacePinCoordinates(parent_node_rect, pin.AttributeRect, pin.Type); } bool MouseInCanvas() { // This flag should be true either when hovering or clicking something in the canvas. const bool is_window_hovered_or_focused = ImGui::IsWindowHovered() || ImGui::IsWindowFocused(); return is_window_hovered_or_focused && GImNodes->CanvasRectScreenSpace.Contains(ImGui::GetMousePos()); } void BeginNodeSelection(ImNodesEditorContext& editor, const int node_idx) { // Don't start selecting a node if we are e.g. already creating and dragging // a new link! New link creation can happen when the mouse is clicked over // a node, but within the hover radius of a pin. if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None) { return; } editor.ClickInteraction.Type = ImNodesClickInteractionType_Node; // If the node is not already contained in the selection, then we want only // the interaction node to be selected, effective immediately. // // If the multiple selection modifier is active, we want to add this node // to the current list of selected nodes. // // Otherwise, we want to allow for the possibility of multiple nodes to be // moved at once. if (!editor.SelectedNodeIndices.contains(node_idx)) { editor.SelectedLinkIndices.clear(); if (!GImNodes->MultipleSelectModifier) { editor.SelectedNodeIndices.clear(); } editor.SelectedNodeIndices.push_back(node_idx); // Ensure that individually selected nodes get rendered on top ImVector& depth_stack = editor.NodeDepthOrder; const int* const elem = depth_stack.find(node_idx); IM_ASSERT(elem != depth_stack.end()); depth_stack.erase(elem); depth_stack.push_back(node_idx); } // Deselect a previously-selected node else if (GImNodes->MultipleSelectModifier) { const int* const node_ptr = editor.SelectedNodeIndices.find(node_idx); editor.SelectedNodeIndices.erase(node_ptr); // Don't allow dragging after deselecting editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } // To support snapping of multiple nodes, we need to store the offset of // each node in the selection to the origin of the dragged node. const ImVec2 ref_origin = editor.Nodes.Pool[node_idx].Origin; editor.PrimaryNodeOffset = ref_origin + GImNodes->CanvasOriginScreenSpace + editor.Panning - GImNodes->MousePos; editor.SelectedNodeOffsets.clear(); for (int idx = 0; idx < editor.SelectedNodeIndices.Size; idx++) { const int node = editor.SelectedNodeIndices[idx]; const ImVec2 node_origin = editor.Nodes.Pool[node].Origin - ref_origin; editor.SelectedNodeOffsets.push_back(node_origin); } } void BeginLinkSelection(ImNodesEditorContext& editor, const int link_idx) { editor.ClickInteraction.Type = ImNodesClickInteractionType_Link; // When a link is selected, clear all other selections, and insert the link // as the sole selection. editor.SelectedNodeIndices.clear(); editor.SelectedLinkIndices.clear(); editor.SelectedLinkIndices.push_back(link_idx); } void BeginLinkDetach(ImNodesEditorContext& editor, const int link_idx, const int detach_pin_idx) { const ImLinkData& link = editor.Links.Pool[link_idx]; ImClickInteractionState& state = editor.ClickInteraction; state.Type = ImNodesClickInteractionType_LinkCreation; state.LinkCreation.EndPinIdx.Reset(); state.LinkCreation.StartPinIdx = detach_pin_idx == link.StartPinIdx ? link.EndPinIdx : link.StartPinIdx; GImNodes->DeletedLinkIdx = link_idx; } void BeginLinkCreation(ImNodesEditorContext& editor, const int hovered_pin_idx) { editor.ClickInteraction.Type = ImNodesClickInteractionType_LinkCreation; editor.ClickInteraction.LinkCreation.StartPinIdx = hovered_pin_idx; editor.ClickInteraction.LinkCreation.EndPinIdx.Reset(); editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_Standard; GImNodes->ImNodesUIState |= ImNodesUIState_LinkStarted; } void BeginLinkInteraction( ImNodesEditorContext& editor, const int link_idx, const ImOptionalIndex pin_idx = ImOptionalIndex()) { // Check if we are clicking the link with the modifier pressed. // This will in a link detach via clicking. const bool modifier_pressed = GImNodes->Io.LinkDetachWithModifierClick.Modifier == NULL ? false : *GImNodes->Io.LinkDetachWithModifierClick.Modifier; if (modifier_pressed) { const ImLinkData& link = editor.Links.Pool[link_idx]; const ImPinData& start_pin = editor.Pins.Pool[link.StartPinIdx]; const ImPinData& end_pin = editor.Pins.Pool[link.EndPinIdx]; const ImVec2& mouse_pos = GImNodes->MousePos; const float dist_to_start = ImLengthSqr(start_pin.Pos - mouse_pos); const float dist_to_end = ImLengthSqr(end_pin.Pos - mouse_pos); const int closest_pin_idx = dist_to_start < dist_to_end ? link.StartPinIdx : link.EndPinIdx; editor.ClickInteraction.Type = ImNodesClickInteractionType_LinkCreation; BeginLinkDetach(editor, link_idx, closest_pin_idx); editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_FromDetach; } else { if (pin_idx.HasValue()) { const int hovered_pin_flags = editor.Pins.Pool[pin_idx.Value()].Flags; // Check the 'click and drag to detach' case. if (hovered_pin_flags & ImNodesAttributeFlags_EnableLinkDetachWithDragClick) { BeginLinkDetach(editor, link_idx, pin_idx.Value()); editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_FromDetach; } else { BeginLinkCreation(editor, pin_idx.Value()); } } else { BeginLinkSelection(editor, link_idx); } } } static inline bool IsMiniMapHovered(); void BeginCanvasInteraction(ImNodesEditorContext& editor) { const bool any_ui_element_hovered = GImNodes->HoveredNodeIdx.HasValue() || GImNodes->HoveredLinkIdx.HasValue() || GImNodes->HoveredPinIdx.HasValue() || ImGui::IsAnyItemHovered(); const bool mouse_not_in_canvas = !MouseInCanvas(); if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None || any_ui_element_hovered || mouse_not_in_canvas) { return; } const bool started_panning = GImNodes->AltMouseClicked; if (started_panning) { editor.ClickInteraction.Type = ImNodesClickInteractionType_Panning; } else if (GImNodes->LeftMouseClicked) { editor.ClickInteraction.Type = ImNodesClickInteractionType_BoxSelection; editor.ClickInteraction.BoxSelector.Rect.Min = ScreenSpaceToGridSpace(editor, GImNodes->MousePos); } } void BoxSelectorUpdateSelection(ImNodesEditorContext& editor, ImRect box_rect) { // Invert box selector coordinates as needed if (box_rect.Min.x > box_rect.Max.x) { ImSwap(box_rect.Min.x, box_rect.Max.x); } if (box_rect.Min.y > box_rect.Max.y) { ImSwap(box_rect.Min.y, box_rect.Max.y); } // Update node selection editor.SelectedNodeIndices.clear(); // Test for overlap against node rectangles for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx) { if (editor.Nodes.InUse[node_idx]) { ImNodeData& node = editor.Nodes.Pool[node_idx]; if (box_rect.Overlaps(node.Rect)) { editor.SelectedNodeIndices.push_back(node_idx); } } } // Update link selection editor.SelectedLinkIndices.clear(); // Test for overlap against links for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx) { if (editor.Links.InUse[link_idx]) { const ImLinkData& link = editor.Links.Pool[link_idx]; const ImPinData& pin_start = editor.Pins.Pool[link.StartPinIdx]; const ImPinData& pin_end = editor.Pins.Pool[link.EndPinIdx]; const ImRect& node_start_rect = editor.Nodes.Pool[pin_start.ParentNodeIdx].Rect; const ImRect& node_end_rect = editor.Nodes.Pool[pin_end.ParentNodeIdx].Rect; const ImVec2 start = GetScreenSpacePinCoordinates( node_start_rect, pin_start.AttributeRect, pin_start.Type); const ImVec2 end = GetScreenSpacePinCoordinates(node_end_rect, pin_end.AttributeRect, pin_end.Type); // Test if (RectangleOverlapsLink(box_rect, start, end, pin_start.Type)) { editor.SelectedLinkIndices.push_back(link_idx); } } } } ImVec2 SnapOriginToGrid(ImVec2 origin) { if (GImNodes->Style.Flags & ImNodesStyleFlags_GridSnapping) { const float spacing = GImNodes->Style.GridSpacing; const float spacing2 = spacing * 0.5f; // Snap the origin to the nearest grid point in any direction float modx = fmodf(fabsf(origin.x) + spacing2, spacing) - spacing2; float mody = fmodf(fabsf(origin.y) + spacing2, spacing) - spacing2; origin.x += (origin.x < 0.f) ? modx : -modx; origin.y += (origin.y < 0.f) ? mody : -mody; } return origin; } void TranslateSelectedNodes(ImNodesEditorContext& editor) { if (GImNodes->LeftMouseDragging) { // If we have grid snap enabled, don't start moving nodes until we've moved the mouse // slightly const bool shouldTranslate = (GImNodes->Style.Flags & ImNodesStyleFlags_GridSnapping) ? ImGui::GetIO().MouseDragMaxDistanceSqr[0] > 5.0 : true; const ImVec2 origin = SnapOriginToGrid( GImNodes->MousePos - GImNodes->CanvasOriginScreenSpace - editor.Panning + editor.PrimaryNodeOffset); for (int i = 0; i < editor.SelectedNodeIndices.size(); ++i) { const ImVec2 node_rel = editor.SelectedNodeOffsets[i]; const int node_idx = editor.SelectedNodeIndices[i]; ImNodeData& node = editor.Nodes.Pool[node_idx]; if (node.Draggable && shouldTranslate) { node.Origin = origin + node_rel + editor.AutoPanningDelta; } } } } struct LinkPredicate { bool operator()(const ImLinkData& lhs, const ImLinkData& rhs) const { // Do a unique compare by sorting the pins' addresses. // This catches duplicate links, whether they are in the // same direction or not. // Sorting by pin index should have the uniqueness guarantees as sorting // by id -- each unique id will get one slot in the link pool array. int lhs_start = lhs.StartPinIdx; int lhs_end = lhs.EndPinIdx; int rhs_start = rhs.StartPinIdx; int rhs_end = rhs.EndPinIdx; if (lhs_start > lhs_end) { ImSwap(lhs_start, lhs_end); } if (rhs_start > rhs_end) { ImSwap(rhs_start, rhs_end); } return lhs_start == rhs_start && lhs_end == rhs_end; } }; ImOptionalIndex FindDuplicateLink( const ImNodesEditorContext& editor, const int start_pin_idx, const int end_pin_idx) { ImLinkData test_link(0); test_link.StartPinIdx = start_pin_idx; test_link.EndPinIdx = end_pin_idx; for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx) { const ImLinkData& link = editor.Links.Pool[link_idx]; if (LinkPredicate()(test_link, link) && editor.Links.InUse[link_idx]) { return ImOptionalIndex(link_idx); } } return ImOptionalIndex(); } bool ShouldLinkSnapToPin( const ImNodesEditorContext& editor, const ImPinData& start_pin, const int hovered_pin_idx, const ImOptionalIndex duplicate_link) { const ImPinData& end_pin = editor.Pins.Pool[hovered_pin_idx]; // The end pin must be in a different node if (start_pin.ParentNodeIdx == end_pin.ParentNodeIdx) { return false; } // The end pin must be of a different type if (start_pin.Type == end_pin.Type) { return false; } // The link to be created must not be a duplicate, unless it is the link which was created on // snap. In that case we want to snap, since we want it to appear visually as if the created // link remains snapped to the pin. if (duplicate_link.HasValue() && !(duplicate_link == GImNodes->SnapLinkIdx)) { return false; } return true; } void ClickInteractionUpdate(ImNodesEditorContext& editor) { switch (editor.ClickInteraction.Type) { case ImNodesClickInteractionType_BoxSelection: { editor.ClickInteraction.BoxSelector.Rect.Max = ScreenSpaceToGridSpace(editor, GImNodes->MousePos); ImRect box_rect = editor.ClickInteraction.BoxSelector.Rect; box_rect.Min = GridSpaceToScreenSpace(editor, box_rect.Min); box_rect.Max = GridSpaceToScreenSpace(editor, box_rect.Max); BoxSelectorUpdateSelection(editor, box_rect); const ImU32 box_selector_color = GImNodes->Style.Colors[ImNodesCol_BoxSelector]; const ImU32 box_selector_outline = GImNodes->Style.Colors[ImNodesCol_BoxSelectorOutline]; GImNodes->CanvasDrawList->AddRectFilled(box_rect.Min, box_rect.Max, box_selector_color); GImNodes->CanvasDrawList->AddRect(box_rect.Min, box_rect.Max, box_selector_outline); if (GImNodes->LeftMouseReleased) { ImVector& depth_stack = editor.NodeDepthOrder; const ImVector& selected_idxs = editor.SelectedNodeIndices; // Bump the selected node indices, in order, to the top of the depth stack. // NOTE: this algorithm has worst case time complexity of O(N^2), if the node selection // is ~ N (due to selected_idxs.contains()). if ((selected_idxs.Size > 0) && (selected_idxs.Size < depth_stack.Size)) { int num_moved = 0; // The number of indices moved. Stop after selected_idxs.Size for (int i = 0; i < depth_stack.Size - selected_idxs.Size; ++i) { for (int node_idx = depth_stack[i]; selected_idxs.contains(node_idx); node_idx = depth_stack[i]) { depth_stack.erase(depth_stack.begin() + static_cast(i)); depth_stack.push_back(node_idx); ++num_moved; } if (num_moved == selected_idxs.Size) { break; } } } editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } break; case ImNodesClickInteractionType_Node: { TranslateSelectedNodes(editor); if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } break; case ImNodesClickInteractionType_Link: { if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } break; case ImNodesClickInteractionType_LinkCreation: { const ImPinData& start_pin = editor.Pins.Pool[editor.ClickInteraction.LinkCreation.StartPinIdx]; const ImOptionalIndex maybe_duplicate_link_idx = GImNodes->HoveredPinIdx.HasValue() ? FindDuplicateLink( editor, editor.ClickInteraction.LinkCreation.StartPinIdx, GImNodes->HoveredPinIdx.Value()) : ImOptionalIndex(); const bool should_snap = GImNodes->HoveredPinIdx.HasValue() && ShouldLinkSnapToPin( editor, start_pin, GImNodes->HoveredPinIdx.Value(), maybe_duplicate_link_idx); // If we created on snap and the hovered pin is empty or changed, then we need signal that // the link's state has changed. const bool snapping_pin_changed = editor.ClickInteraction.LinkCreation.EndPinIdx.HasValue() && !(GImNodes->HoveredPinIdx == editor.ClickInteraction.LinkCreation.EndPinIdx); // Detach the link that was created by this link event if it's no longer in snap range if (snapping_pin_changed && GImNodes->SnapLinkIdx.HasValue()) { BeginLinkDetach( editor, GImNodes->SnapLinkIdx.Value(), editor.ClickInteraction.LinkCreation.EndPinIdx.Value()); } const ImVec2 start_pos = GetScreenSpacePinCoordinates(editor, start_pin); // If we are within the hover radius of a receiving pin, snap the link // endpoint to it const ImVec2 end_pos = should_snap ? GetScreenSpacePinCoordinates( editor, editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()]) : GImNodes->MousePos; const CubicBezier cubic_bezier = GetCubicBezier( start_pos, end_pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength); #if IMGUI_VERSION_NUM < 18000 GImNodes->CanvasDrawList->AddBezierCurve( #else GImNodes->CanvasDrawList->AddBezierCubic( #endif cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3, GImNodes->Style.Colors[ImNodesCol_Link], GImNodes->Style.LinkThickness, cubic_bezier.NumSegments); const bool link_creation_on_snap = GImNodes->HoveredPinIdx.HasValue() && (editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()].Flags & ImNodesAttributeFlags_EnableLinkCreationOnSnap); if (!should_snap) { editor.ClickInteraction.LinkCreation.EndPinIdx.Reset(); } const bool create_link = should_snap && (GImNodes->LeftMouseReleased || link_creation_on_snap); if (create_link && !maybe_duplicate_link_idx.HasValue()) { // Avoid send OnLinkCreated() events every frame if the snap link is not saved // (only applies for EnableLinkCreationOnSnap) if (!GImNodes->LeftMouseReleased && editor.ClickInteraction.LinkCreation.EndPinIdx == GImNodes->HoveredPinIdx) { break; } GImNodes->ImNodesUIState |= ImNodesUIState_LinkCreated; editor.ClickInteraction.LinkCreation.EndPinIdx = GImNodes->HoveredPinIdx.Value(); } if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; if (!create_link) { GImNodes->ImNodesUIState |= ImNodesUIState_LinkDropped; } } } break; case ImNodesClickInteractionType_Panning: { const bool dragging = GImNodes->AltMouseDragging; if (dragging) { editor.Panning += ImGui::GetIO().MouseDelta; } else { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } break; case ImNodesClickInteractionType_ImGuiItem: { if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } case ImNodesClickInteractionType_None: break; default: IM_ASSERT(!"Unreachable code!"); break; } } void ResolveOccludedPins(const ImNodesEditorContext& editor, ImVector& occluded_pin_indices) { const ImVector& depth_stack = editor.NodeDepthOrder; occluded_pin_indices.resize(0); if (depth_stack.Size < 2) { return; } // For each node in the depth stack for (int depth_idx = 0; depth_idx < (depth_stack.Size - 1); ++depth_idx) { const ImNodeData& node_below = editor.Nodes.Pool[depth_stack[depth_idx]]; // Iterate over the rest of the depth stack to find nodes overlapping the pins for (int next_depth_idx = depth_idx + 1; next_depth_idx < depth_stack.Size; ++next_depth_idx) { const ImRect& rect_above = editor.Nodes.Pool[depth_stack[next_depth_idx]].Rect; // Iterate over each pin for (int idx = 0; idx < node_below.PinIndices.Size; ++idx) { const int pin_idx = node_below.PinIndices[idx]; const ImVec2& pin_pos = editor.Pins.Pool[pin_idx].Pos; if (rect_above.Contains(pin_pos)) { occluded_pin_indices.push_back(pin_idx); } } } } } ImOptionalIndex ResolveHoveredPin( const ImObjectPool& pins, const ImVector& occluded_pin_indices) { float smallest_distance = FLT_MAX; ImOptionalIndex pin_idx_with_smallest_distance; const float hover_radius_sqr = GImNodes->Style.PinHoverRadius * GImNodes->Style.PinHoverRadius; for (int idx = 0; idx < pins.Pool.Size; ++idx) { if (!pins.InUse[idx]) { continue; } if (occluded_pin_indices.contains(idx)) { continue; } const ImVec2& pin_pos = pins.Pool[idx].Pos; const float distance_sqr = ImLengthSqr(pin_pos - GImNodes->MousePos); // TODO: GImNodes->Style.PinHoverRadius needs to be copied into pin data and the pin-local // value used here. This is no longer called in BeginAttribute/EndAttribute scope and the // detected pin might have a different hover radius than what the user had when calling // BeginAttribute/EndAttribute. if (distance_sqr < hover_radius_sqr && distance_sqr < smallest_distance) { smallest_distance = distance_sqr; pin_idx_with_smallest_distance = idx; } } return pin_idx_with_smallest_distance; } ImOptionalIndex ResolveHoveredNode(const ImVector& depth_stack) { if (GImNodes->NodeIndicesOverlappingWithMouse.size() == 0) { return ImOptionalIndex(); } if (GImNodes->NodeIndicesOverlappingWithMouse.size() == 1) { return ImOptionalIndex(GImNodes->NodeIndicesOverlappingWithMouse[0]); } int largest_depth_idx = -1; int node_idx_on_top = -1; for (int i = 0; i < GImNodes->NodeIndicesOverlappingWithMouse.size(); ++i) { const int node_idx = GImNodes->NodeIndicesOverlappingWithMouse[i]; for (int depth_idx = 0; depth_idx < depth_stack.size(); ++depth_idx) { if (depth_stack[depth_idx] == node_idx && (depth_idx > largest_depth_idx)) { largest_depth_idx = depth_idx; node_idx_on_top = node_idx; } } } IM_ASSERT(node_idx_on_top != -1); return ImOptionalIndex(node_idx_on_top); } ImOptionalIndex ResolveHoveredLink( const ImObjectPool& links, const ImObjectPool& pins) { float smallest_distance = FLT_MAX; ImOptionalIndex link_idx_with_smallest_distance; // There are two ways a link can be detected as "hovered". // 1. The link is within hover distance to the mouse. The closest such link is selected as being // hovered over. // 2. If the link is connected to the currently hovered pin. // // The latter is a requirement for link detaching with drag click to work, as both a link and // pin are required to be hovered over for the feature to work. for (int idx = 0; idx < links.Pool.Size; ++idx) { if (!links.InUse[idx]) { continue; } const ImLinkData& link = links.Pool[idx]; const ImPinData& start_pin = pins.Pool[link.StartPinIdx]; const ImPinData& end_pin = pins.Pool[link.EndPinIdx]; // If there is a hovered pin links can only be considered hovered if they use that pin if (GImNodes->HoveredPinIdx.HasValue()) { if (GImNodes->HoveredPinIdx == link.StartPinIdx || GImNodes->HoveredPinIdx == link.EndPinIdx) { return idx; } continue; } // TODO: the calculated CubicBeziers could be cached since we generate them again when // rendering the links const CubicBezier cubic_bezier = GetCubicBezier( start_pin.Pos, end_pin.Pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength); // The distance test { const ImRect link_rect = GetContainingRectForCubicBezier(cubic_bezier); // First, do a simple bounding box test against the box containing the link // to see whether calculating the distance to the link is worth doing. if (link_rect.Contains(GImNodes->MousePos)) { const float distance = GetDistanceToCubicBezier( GImNodes->MousePos, cubic_bezier, cubic_bezier.NumSegments); // TODO: GImNodes->Style.LinkHoverDistance could be also copied into ImLinkData, // since we're not calling this function in the same scope as ImNodes::Link(). The // rendered/detected link might have a different hover distance than what the user // had specified when calling Link() if (distance < GImNodes->Style.LinkHoverDistance && distance < smallest_distance) { smallest_distance = distance; link_idx_with_smallest_distance = idx; } } } } return link_idx_with_smallest_distance; } // [SECTION] render helpers inline ImRect GetItemRect() { return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); } inline ImVec2 GetNodeTitleBarOrigin(const ImNodeData& node) { return node.Origin + node.LayoutStyle.Padding; } inline ImVec2 GetNodeContentOrigin(const ImNodeData& node) { const ImVec2 title_bar_height = ImVec2(0.f, node.TitleBarContentRect.GetHeight() + 2.0f * node.LayoutStyle.Padding.y); return node.Origin + title_bar_height + node.LayoutStyle.Padding; } inline ImRect GetNodeTitleRect(const ImNodeData& node) { ImRect expanded_title_rect = node.TitleBarContentRect; expanded_title_rect.Expand(node.LayoutStyle.Padding); return ImRect( expanded_title_rect.Min, expanded_title_rect.Min + ImVec2(node.Rect.GetWidth(), 0.f) + ImVec2(0.f, expanded_title_rect.GetHeight())); } void DrawGrid(ImNodesEditorContext& editor, const ImVec2& canvas_size) { const ImVec2 offset = editor.Panning; ImU32 line_color = GImNodes->Style.Colors[ImNodesCol_GridLine]; ImU32 line_color_prim = GImNodes->Style.Colors[ImNodesCol_GridLinePrimary]; bool draw_primary = GImNodes->Style.Flags & ImNodesStyleFlags_GridLinesPrimary; for (float x = fmodf(offset.x, GImNodes->Style.GridSpacing); x < canvas_size.x; x += GImNodes->Style.GridSpacing) { GImNodes->CanvasDrawList->AddLine( EditorSpaceToScreenSpace(ImVec2(x, 0.0f)), EditorSpaceToScreenSpace(ImVec2(x, canvas_size.y)), offset.x - x == 0.f && draw_primary ? line_color_prim : line_color); } for (float y = fmodf(offset.y, GImNodes->Style.GridSpacing); y < canvas_size.y; y += GImNodes->Style.GridSpacing) { GImNodes->CanvasDrawList->AddLine( EditorSpaceToScreenSpace(ImVec2(0.0f, y)), EditorSpaceToScreenSpace(ImVec2(canvas_size.x, y)), offset.y - y == 0.f && draw_primary ? line_color_prim : line_color); } } struct QuadOffsets { ImVec2 TopLeft, BottomLeft, BottomRight, TopRight; }; QuadOffsets CalculateQuadOffsets(const float side_length) { const float half_side = 0.5f * side_length; QuadOffsets offset; offset.TopLeft = ImVec2(-half_side, half_side); offset.BottomLeft = ImVec2(-half_side, -half_side); offset.BottomRight = ImVec2(half_side, -half_side); offset.TopRight = ImVec2(half_side, half_side); return offset; } struct TriangleOffsets { ImVec2 TopLeft, BottomLeft, Right; }; TriangleOffsets CalculateTriangleOffsets(const float side_length) { // Calculates the Vec2 offsets from an equilateral triangle's midpoint to // its vertices. Here is how the left_offset and right_offset are // calculated. // // For an equilateral triangle of side length s, the // triangle's height, h, is h = s * sqrt(3) / 2. // // The length from the base to the midpoint is (1 / 3) * h. The length from // the midpoint to the triangle vertex is (2 / 3) * h. const float sqrt_3 = sqrtf(3.0f); const float left_offset = -0.1666666666667f * sqrt_3 * side_length; const float right_offset = 0.333333333333f * sqrt_3 * side_length; const float vertical_offset = 0.5f * side_length; TriangleOffsets offset; offset.TopLeft = ImVec2(left_offset, vertical_offset); offset.BottomLeft = ImVec2(left_offset, -vertical_offset); offset.Right = ImVec2(right_offset, 0.f); return offset; } void DrawPinShape(const ImVec2& pin_pos, const ImPinData& pin, const ImU32 pin_color) { static const int CIRCLE_NUM_SEGMENTS = 8; switch (pin.Shape) { case ImNodesPinShape_Circle: { GImNodes->CanvasDrawList->AddCircle( pin_pos, GImNodes->Style.PinCircleRadius, pin_color, CIRCLE_NUM_SEGMENTS, GImNodes->Style.PinLineThickness); } break; case ImNodesPinShape_CircleFilled: { GImNodes->CanvasDrawList->AddCircleFilled( pin_pos, GImNodes->Style.PinCircleRadius, pin_color, CIRCLE_NUM_SEGMENTS); } break; case ImNodesPinShape_Quad: { const QuadOffsets offset = CalculateQuadOffsets(GImNodes->Style.PinQuadSideLength); GImNodes->CanvasDrawList->AddQuad( pin_pos + offset.TopLeft, pin_pos + offset.BottomLeft, pin_pos + offset.BottomRight, pin_pos + offset.TopRight, pin_color, GImNodes->Style.PinLineThickness); } break; case ImNodesPinShape_QuadFilled: { const QuadOffsets offset = CalculateQuadOffsets(GImNodes->Style.PinQuadSideLength); GImNodes->CanvasDrawList->AddQuadFilled( pin_pos + offset.TopLeft, pin_pos + offset.BottomLeft, pin_pos + offset.BottomRight, pin_pos + offset.TopRight, pin_color); } break; case ImNodesPinShape_Triangle: { const TriangleOffsets offset = CalculateTriangleOffsets(GImNodes->Style.PinTriangleSideLength); GImNodes->CanvasDrawList->AddTriangle( pin_pos + offset.TopLeft, pin_pos + offset.BottomLeft, pin_pos + offset.Right, pin_color, // NOTE: for some weird reason, the line drawn by AddTriangle is // much thinner than the lines drawn by AddCircle or AddQuad. // Multiplying the line thickness by two seemed to solve the // problem at a few different thickness values. 2.f * GImNodes->Style.PinLineThickness); } break; case ImNodesPinShape_TriangleFilled: { const TriangleOffsets offset = CalculateTriangleOffsets(GImNodes->Style.PinTriangleSideLength); GImNodes->CanvasDrawList->AddTriangleFilled( pin_pos + offset.TopLeft, pin_pos + offset.BottomLeft, pin_pos + offset.Right, pin_color); } break; default: IM_ASSERT(!"Invalid PinShape value!"); break; } } void DrawPin(ImNodesEditorContext& editor, const int pin_idx) { ImPinData& pin = editor.Pins.Pool[pin_idx]; const ImRect& parent_node_rect = editor.Nodes.Pool[pin.ParentNodeIdx].Rect; pin.Pos = GetScreenSpacePinCoordinates(parent_node_rect, pin.AttributeRect, pin.Type); ImU32 pin_color = pin.ColorStyle.Background; if (GImNodes->HoveredPinIdx == pin_idx) { pin_color = pin.ColorStyle.Hovered; } DrawPinShape(pin.Pos, pin, pin_color); } void DrawNode(ImNodesEditorContext& editor, const int node_idx) { const ImNodeData& node = editor.Nodes.Pool[node_idx]; ImGui::SetCursorPos(node.Origin + editor.Panning); const bool node_hovered = GImNodes->HoveredNodeIdx == node_idx && editor.ClickInteraction.Type != ImNodesClickInteractionType_BoxSelection; ImU32 node_background = node.ColorStyle.Background; ImU32 titlebar_background = node.ColorStyle.Titlebar; if (editor.SelectedNodeIndices.contains(node_idx)) { node_background = node.ColorStyle.BackgroundSelected; titlebar_background = node.ColorStyle.TitlebarSelected; } else if (node_hovered) { node_background = node.ColorStyle.BackgroundHovered; titlebar_background = node.ColorStyle.TitlebarHovered; } { // node base GImNodes->CanvasDrawList->AddRectFilled( node.Rect.Min, node.Rect.Max, node_background, node.LayoutStyle.CornerRounding); // title bar: if (node.TitleBarContentRect.GetHeight() > 0.f) { ImRect title_bar_rect = GetNodeTitleRect(node); #if IMGUI_VERSION_NUM < 18200 GImNodes->CanvasDrawList->AddRectFilled( title_bar_rect.Min, title_bar_rect.Max, titlebar_background, node.LayoutStyle.CornerRounding, ImDrawCornerFlags_Top); #else GImNodes->CanvasDrawList->AddRectFilled( title_bar_rect.Min, title_bar_rect.Max, titlebar_background, node.LayoutStyle.CornerRounding, ImDrawFlags_RoundCornersTop); #endif } if ((GImNodes->Style.Flags & ImNodesStyleFlags_NodeOutline) != 0) { #if IMGUI_VERSION_NUM < 18200 GImNodes->CanvasDrawList->AddRect( node.Rect.Min, node.Rect.Max, node.ColorStyle.Outline, node.LayoutStyle.CornerRounding, ImDrawCornerFlags_All, node.LayoutStyle.BorderThickness); #else GImNodes->CanvasDrawList->AddRect( node.Rect.Min, node.Rect.Max, node.ColorStyle.Outline, node.LayoutStyle.CornerRounding, ImDrawFlags_RoundCornersAll, node.LayoutStyle.BorderThickness); #endif } } for (int i = 0; i < node.PinIndices.size(); ++i) { DrawPin(editor, node.PinIndices[i]); } if (node_hovered) { GImNodes->HoveredNodeIdx = node_idx; } } void DrawLink(ImNodesEditorContext& editor, const int link_idx) { const ImLinkData& link = editor.Links.Pool[link_idx]; const ImPinData& start_pin = editor.Pins.Pool[link.StartPinIdx]; const ImPinData& end_pin = editor.Pins.Pool[link.EndPinIdx]; const CubicBezier cubic_bezier = GetCubicBezier( start_pin.Pos, end_pin.Pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength); const bool link_hovered = GImNodes->HoveredLinkIdx == link_idx && editor.ClickInteraction.Type != ImNodesClickInteractionType_BoxSelection; if (link_hovered) { GImNodes->HoveredLinkIdx = link_idx; } // It's possible for a link to be deleted in begin_link_interaction. A user // may detach a link, resulting in the link wire snapping to the mouse // position. // // In other words, skip rendering the link if it was deleted. if (GImNodes->DeletedLinkIdx == link_idx) { return; } ImU32 link_color = link.ColorStyle.Base; if (editor.SelectedLinkIndices.contains(link_idx)) { link_color = link.ColorStyle.Selected; } else if (link_hovered) { link_color = link.ColorStyle.Hovered; } #if IMGUI_VERSION_NUM < 18000 GImNodes->CanvasDrawList->AddBezierCurve( #else GImNodes->CanvasDrawList->AddBezierCubic( #endif cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3, link_color, GImNodes->Style.LinkThickness, cubic_bezier.NumSegments); } void BeginPinAttribute( const int id, const ImNodesAttributeType type, const ImNodesPinShape shape, const int node_idx) { // Make sure to call BeginNode() before calling // BeginAttribute() IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); GImNodes->CurrentScope = ImNodesScope_Attribute; ImGui::BeginGroup(); ImGui::PushID(id); ImNodesEditorContext& editor = EditorContextGet(); GImNodes->CurrentAttributeId = id; const int pin_idx = ObjectPoolFindOrCreateIndex(editor.Pins, id); GImNodes->CurrentPinIdx = pin_idx; ImPinData& pin = editor.Pins.Pool[pin_idx]; pin.Id = id; pin.ParentNodeIdx = node_idx; pin.Type = type; pin.Shape = shape; pin.Flags = GImNodes->CurrentAttributeFlags; pin.ColorStyle.Background = GImNodes->Style.Colors[ImNodesCol_Pin]; pin.ColorStyle.Hovered = GImNodes->Style.Colors[ImNodesCol_PinHovered]; } void EndPinAttribute() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Attribute); GImNodes->CurrentScope = ImNodesScope_Node; ImGui::PopID(); ImGui::EndGroup(); if (ImGui::IsItemActive()) { GImNodes->ActiveAttribute = true; GImNodes->ActiveAttributeId = GImNodes->CurrentAttributeId; } ImNodesEditorContext& editor = EditorContextGet(); ImPinData& pin = editor.Pins.Pool[GImNodes->CurrentPinIdx]; ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx]; pin.AttributeRect = GetItemRect(); node.PinIndices.push_back(GImNodes->CurrentPinIdx); } void Initialize(ImNodesContext* context) { context->CanvasOriginScreenSpace = ImVec2(0.0f, 0.0f); context->CanvasRectScreenSpace = ImRect(ImVec2(0.f, 0.f), ImVec2(0.f, 0.f)); context->CurrentScope = ImNodesScope_None; context->CurrentPinIdx = INT_MAX; context->CurrentNodeIdx = INT_MAX; context->DefaultEditorCtx = EditorContextCreate(); context->EditorCtx = context->DefaultEditorCtx; context->CurrentAttributeFlags = ImNodesAttributeFlags_None; context->AttributeFlagStack.push_back(GImNodes->CurrentAttributeFlags); StyleColorsDark(&context->Style); } void Shutdown(ImNodesContext* ctx) { EditorContextFree(ctx->DefaultEditorCtx); } // [SECTION] minimap static inline bool IsMiniMapActive() { ImNodesEditorContext& editor = EditorContextGet(); return editor.MiniMapEnabled && editor.MiniMapSizeFraction > 0.0f; } static inline bool IsMiniMapHovered() { ImNodesEditorContext& editor = EditorContextGet(); return IsMiniMapActive() && ImGui::IsMouseHoveringRect( editor.MiniMapRectScreenSpace.Min, editor.MiniMapRectScreenSpace.Max); } static inline void CalcMiniMapLayout() { ImNodesEditorContext& editor = EditorContextGet(); const ImVec2 offset = GImNodes->Style.MiniMapOffset; const ImVec2 border = GImNodes->Style.MiniMapPadding; const ImRect editor_rect = GImNodes->CanvasRectScreenSpace; // Compute the size of the mini-map area ImVec2 mini_map_size; float mini_map_scaling; { const ImVec2 max_size = ImFloor(editor_rect.GetSize() * editor.MiniMapSizeFraction - border * 2.0f); const float max_size_aspect_ratio = max_size.x / max_size.y; const ImVec2 grid_content_size = editor.GridContentBounds.IsInverted() ? max_size : ImFloor(editor.GridContentBounds.GetSize()); const float grid_content_aspect_ratio = grid_content_size.x / grid_content_size.y; mini_map_size = ImFloor( grid_content_aspect_ratio > max_size_aspect_ratio ? ImVec2(max_size.x, max_size.x / grid_content_aspect_ratio) : ImVec2(max_size.y * grid_content_aspect_ratio, max_size.y)); mini_map_scaling = mini_map_size.x / grid_content_size.x; } // Compute location of the mini-map ImVec2 mini_map_pos; { ImVec2 align; switch (editor.MiniMapLocation) { case ImNodesMiniMapLocation_BottomRight: align.x = 1.0f; align.y = 1.0f; break; case ImNodesMiniMapLocation_BottomLeft: align.x = 0.0f; align.y = 1.0f; break; case ImNodesMiniMapLocation_TopRight: align.x = 1.0f; align.y = 0.0f; break; case ImNodesMiniMapLocation_TopLeft: // [[fallthrough]] default: align.x = 0.0f; align.y = 0.0f; break; } const ImVec2 top_left_pos = editor_rect.Min + offset + border; const ImVec2 bottom_right_pos = editor_rect.Max - offset - border - mini_map_size; mini_map_pos = ImFloor(ImLerp(top_left_pos, bottom_right_pos, align)); } editor.MiniMapRectScreenSpace = ImRect(mini_map_pos - border, mini_map_pos + mini_map_size + border); editor.MiniMapContentScreenSpace = ImRect(mini_map_pos, mini_map_pos + mini_map_size); editor.MiniMapScaling = mini_map_scaling; } static void MiniMapDrawNode(ImNodesEditorContext& editor, const int node_idx) { const ImNodeData& node = editor.Nodes.Pool[node_idx]; const ImRect node_rect = ScreenSpaceToMiniMapSpace(editor, node.Rect); // Round to near whole pixel value for corner-rounding to prevent visual glitches const float mini_map_node_rounding = floorf(node.LayoutStyle.CornerRounding * editor.MiniMapScaling); ImU32 mini_map_node_background; if (editor.ClickInteraction.Type == ImNodesClickInteractionType_None && ImGui::IsMouseHoveringRect(node_rect.Min, node_rect.Max)) { mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackgroundHovered]; // Run user callback when hovering a mini-map node if (editor.MiniMapNodeHoveringCallback) { editor.MiniMapNodeHoveringCallback(node.Id, editor.MiniMapNodeHoveringCallbackUserData); } } else if (editor.SelectedNodeIndices.contains(node_idx)) { mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackgroundSelected]; } else { mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackground]; } const ImU32 mini_map_node_outline = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeOutline]; GImNodes->CanvasDrawList->AddRectFilled( node_rect.Min, node_rect.Max, mini_map_node_background, mini_map_node_rounding); GImNodes->CanvasDrawList->AddRect( node_rect.Min, node_rect.Max, mini_map_node_outline, mini_map_node_rounding); } static void MiniMapDrawLink(ImNodesEditorContext& editor, const int link_idx) { const ImLinkData& link = editor.Links.Pool[link_idx]; const ImPinData& start_pin = editor.Pins.Pool[link.StartPinIdx]; const ImPinData& end_pin = editor.Pins.Pool[link.EndPinIdx]; const CubicBezier cubic_bezier = GetCubicBezier( ScreenSpaceToMiniMapSpace(editor, start_pin.Pos), ScreenSpaceToMiniMapSpace(editor, end_pin.Pos), start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength / editor.MiniMapScaling); // It's possible for a link to be deleted in begin_link_interaction. A user // may detach a link, resulting in the link wire snapping to the mouse // position. // // In other words, skip rendering the link if it was deleted. if (GImNodes->DeletedLinkIdx == link_idx) { return; } const ImU32 link_color = GImNodes->Style.Colors [editor.SelectedLinkIndices.contains(link_idx) ? ImNodesCol_MiniMapLinkSelected : ImNodesCol_MiniMapLink]; #if IMGUI_VERSION_NUM < 18000 GImNodes->CanvasDrawList->AddBezierCurve( #else GImNodes->CanvasDrawList->AddBezierCubic( #endif cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3, link_color, GImNodes->Style.LinkThickness * editor.MiniMapScaling, cubic_bezier.NumSegments); } static void MiniMapUpdate() { ImNodesEditorContext& editor = EditorContextGet(); ImU32 mini_map_background; if (IsMiniMapHovered()) { mini_map_background = GImNodes->Style.Colors[ImNodesCol_MiniMapBackgroundHovered]; } else { mini_map_background = GImNodes->Style.Colors[ImNodesCol_MiniMapBackground]; } // Create a child window bellow mini-map, so it blocks all mouse interaction on canvas. int flags = ImGuiWindowFlags_NoBackground; ImGui::SetCursorScreenPos(editor.MiniMapRectScreenSpace.Min); ImGui::BeginChild("minimap", editor.MiniMapRectScreenSpace.GetSize(), false, flags); const ImRect& mini_map_rect = editor.MiniMapRectScreenSpace; // Draw minimap background and border GImNodes->CanvasDrawList->AddRectFilled( mini_map_rect.Min, mini_map_rect.Max, mini_map_background); GImNodes->CanvasDrawList->AddRect( mini_map_rect.Min, mini_map_rect.Max, GImNodes->Style.Colors[ImNodesCol_MiniMapOutline]); // Clip draw list items to mini-map rect (after drawing background/outline) GImNodes->CanvasDrawList->PushClipRect( mini_map_rect.Min, mini_map_rect.Max, true /* intersect with editor clip-rect */); // Draw links first so they appear under nodes, and we can use the same draw channel for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx) { if (editor.Links.InUse[link_idx]) { MiniMapDrawLink(editor, link_idx); } } for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx) { if (editor.Nodes.InUse[node_idx]) { MiniMapDrawNode(editor, node_idx); } } // Draw editor canvas rect inside mini-map { const ImU32 canvas_color = GImNodes->Style.Colors[ImNodesCol_MiniMapCanvas]; const ImU32 outline_color = GImNodes->Style.Colors[ImNodesCol_MiniMapCanvasOutline]; const ImRect rect = ScreenSpaceToMiniMapSpace(editor, GImNodes->CanvasRectScreenSpace); GImNodes->CanvasDrawList->AddRectFilled(rect.Min, rect.Max, canvas_color); GImNodes->CanvasDrawList->AddRect(rect.Min, rect.Max, outline_color); } // Have to pop mini-map clip rect GImNodes->CanvasDrawList->PopClipRect(); bool mini_map_is_hovered = ImGui::IsWindowHovered(); ImGui::EndChild(); bool center_on_click = mini_map_is_hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left) && editor.ClickInteraction.Type == ImNodesClickInteractionType_None && !GImNodes->NodeIdxSubmissionOrder.empty(); if (center_on_click) { ImVec2 target = MiniMapSpaceToGridSpace(editor, ImGui::GetMousePos()); ImVec2 center = GImNodes->CanvasRectScreenSpace.GetSize() * 0.5f; editor.Panning = ImFloor(center - target); } // Reset callback info after use editor.MiniMapNodeHoveringCallback = NULL; editor.MiniMapNodeHoveringCallbackUserData = NULL; } // [SECTION] selection helpers template void SelectObject(const ImObjectPool& objects, ImVector& selected_indices, const int id) { const int idx = ObjectPoolFind(objects, id); IM_ASSERT(idx >= 0); IM_ASSERT(selected_indices.find(idx) == selected_indices.end()); selected_indices.push_back(idx); } template void ClearObjectSelection( const ImObjectPool& objects, ImVector& selected_indices, const int id) { const int idx = ObjectPoolFind(objects, id); IM_ASSERT(idx >= 0); IM_ASSERT(selected_indices.find(idx) != selected_indices.end()); selected_indices.find_erase_unsorted(idx); } template bool IsObjectSelected(const ImObjectPool& objects, ImVector& selected_indices, const int id) { const int idx = ObjectPoolFind(objects, id); return selected_indices.find(idx) != selected_indices.end(); } } // namespace } // namespace IMNODES_NAMESPACE // [SECTION] API implementation ImNodesIO::EmulateThreeButtonMouse::EmulateThreeButtonMouse() : Modifier(NULL) {} ImNodesIO::LinkDetachWithModifierClick::LinkDetachWithModifierClick() : Modifier(NULL) {} ImNodesIO::MultipleSelectModifier::MultipleSelectModifier() : Modifier(NULL) {} ImNodesIO::ImNodesIO() : EmulateThreeButtonMouse(), LinkDetachWithModifierClick(), AltMouseButton(ImGuiMouseButton_Middle), AutoPanningSpeed(1000.0f) { } ImNodesStyle::ImNodesStyle() : GridSpacing(24.f), NodeCornerRounding(4.f), NodePadding(8.f, 8.f), NodeBorderThickness(1.f), LinkThickness(3.f), LinkLineSegmentsPerLength(0.1f), LinkHoverDistance(10.f), PinCircleRadius(4.f), PinQuadSideLength(7.f), PinTriangleSideLength(9.5), PinLineThickness(1.f), PinHoverRadius(10.f), PinOffset(0.f), MiniMapPadding(8.0f, 8.0f), MiniMapOffset(4.0f, 4.0f), Flags(ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines), Colors() { } namespace IMNODES_NAMESPACE { ImNodesContext* CreateContext() { ImNodesContext* ctx = IM_NEW(ImNodesContext)(); if (GImNodes == NULL) SetCurrentContext(ctx); Initialize(ctx); return ctx; } void DestroyContext(ImNodesContext* ctx) { if (ctx == NULL) ctx = GImNodes; Shutdown(ctx); if (GImNodes == ctx) SetCurrentContext(NULL); IM_DELETE(ctx); } ImNodesContext* GetCurrentContext() { return GImNodes; } void SetCurrentContext(ImNodesContext* ctx) { GImNodes = ctx; } ImNodesEditorContext* EditorContextCreate() { void* mem = ImGui::MemAlloc(sizeof(ImNodesEditorContext)); new (mem) ImNodesEditorContext(); return (ImNodesEditorContext*)mem; } void EditorContextFree(ImNodesEditorContext* ctx) { ctx->~ImNodesEditorContext(); ImGui::MemFree(ctx); } void EditorContextSet(ImNodesEditorContext* ctx) { GImNodes->EditorCtx = ctx; } ImVec2 EditorContextGetPanning() { const ImNodesEditorContext& editor = EditorContextGet(); return editor.Panning; } void EditorContextResetPanning(const ImVec2& pos) { ImNodesEditorContext& editor = EditorContextGet(); editor.Panning = pos; } void EditorContextMoveToNode(const int node_id) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); editor.Panning.x = -node.Origin.x; editor.Panning.y = -node.Origin.y; } void SetImGuiContext(ImGuiContext* ctx) { ImGui::SetCurrentContext(ctx); } ImNodesIO& GetIO() { return GImNodes->Io; } ImNodesStyle& GetStyle() { return GImNodes->Style; } void StyleColorsDark(ImNodesStyle* dest) { if (dest == nullptr) { dest = &GImNodes->Style; } dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(50, 50, 50, 255); dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(75, 75, 75, 255); dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(75, 75, 75, 255); dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255); // title bar colors match ImGui's titlebg colors dest->Colors[ImNodesCol_TitleBar] = IM_COL32(41, 74, 122, 255); dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(66, 150, 250, 255); dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(66, 150, 250, 255); // link colors match ImGui's slider grab colors dest->Colors[ImNodesCol_Link] = IM_COL32(61, 133, 224, 200); dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(66, 150, 250, 255); dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(66, 150, 250, 255); // pin colors match ImGui's button colors dest->Colors[ImNodesCol_Pin] = IM_COL32(53, 150, 250, 180); dest->Colors[ImNodesCol_PinHovered] = IM_COL32(53, 150, 250, 255); dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(61, 133, 224, 30); dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(61, 133, 224, 150); dest->Colors[ImNodesCol_GridBackground] = IM_COL32(40, 40, 50, 200); dest->Colors[ImNodesCol_GridLine] = IM_COL32(200, 200, 200, 40); dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(240, 240, 240, 60); // minimap colors dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 150); dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200); dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100); dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200); dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered] = IM_COL32(200, 200, 200, 255); dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered]; dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link]; dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected]; dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25); dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200); } void StyleColorsClassic(ImNodesStyle* dest) { if (dest == nullptr) { dest = &GImNodes->Style; } dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(50, 50, 50, 255); dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(75, 75, 75, 255); dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(75, 75, 75, 255); dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255); dest->Colors[ImNodesCol_TitleBar] = IM_COL32(69, 69, 138, 255); dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(82, 82, 161, 255); dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(82, 82, 161, 255); dest->Colors[ImNodesCol_Link] = IM_COL32(255, 255, 255, 100); dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(105, 99, 204, 153); dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(105, 99, 204, 153); dest->Colors[ImNodesCol_Pin] = IM_COL32(89, 102, 156, 170); dest->Colors[ImNodesCol_PinHovered] = IM_COL32(102, 122, 179, 200); dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(82, 82, 161, 100); dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(82, 82, 161, 255); dest->Colors[ImNodesCol_GridBackground] = IM_COL32(40, 40, 50, 200); dest->Colors[ImNodesCol_GridLine] = IM_COL32(200, 200, 200, 40); dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(240, 240, 240, 60); // minimap colors dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 100); dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200); dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100); dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200); dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered]; dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = IM_COL32(200, 200, 240, 255); dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link]; dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected]; dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25); dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200); } void StyleColorsLight(ImNodesStyle* dest) { if (dest == nullptr) { dest = &GImNodes->Style; } dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(240, 240, 240, 255); dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(240, 240, 240, 255); dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(240, 240, 240, 255); dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255); dest->Colors[ImNodesCol_TitleBar] = IM_COL32(248, 248, 248, 255); dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(209, 209, 209, 255); dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(209, 209, 209, 255); // original imgui values: 66, 150, 250 dest->Colors[ImNodesCol_Link] = IM_COL32(66, 150, 250, 100); // original imgui values: 117, 138, 204 dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(66, 150, 250, 242); dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(66, 150, 250, 242); // original imgui values: 66, 150, 250 dest->Colors[ImNodesCol_Pin] = IM_COL32(66, 150, 250, 160); dest->Colors[ImNodesCol_PinHovered] = IM_COL32(66, 150, 250, 255); dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(90, 170, 250, 30); dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(90, 170, 250, 150); dest->Colors[ImNodesCol_GridBackground] = IM_COL32(225, 225, 225, 255); dest->Colors[ImNodesCol_GridLine] = IM_COL32(180, 180, 180, 100); dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(120, 120, 120, 100); // minimap colors dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 100); dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200); dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100); dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200); dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered]; dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = IM_COL32(200, 200, 240, 255); dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link]; dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected]; dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25); dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200); } void BeginNodeEditor() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); GImNodes->CurrentScope = ImNodesScope_Editor; // Reset state from previous pass ImNodesEditorContext& editor = EditorContextGet(); editor.AutoPanningDelta = ImVec2(0, 0); editor.GridContentBounds = ImRect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); editor.MiniMapEnabled = false; ObjectPoolReset(editor.Nodes); ObjectPoolReset(editor.Pins); ObjectPoolReset(editor.Links); GImNodes->HoveredNodeIdx.Reset(); GImNodes->HoveredLinkIdx.Reset(); GImNodes->HoveredPinIdx.Reset(); GImNodes->DeletedLinkIdx.Reset(); GImNodes->SnapLinkIdx.Reset(); GImNodes->NodeIndicesOverlappingWithMouse.clear(); GImNodes->ImNodesUIState = ImNodesUIState_None; GImNodes->MousePos = ImGui::GetIO().MousePos; GImNodes->LeftMouseClicked = ImGui::IsMouseClicked(0); GImNodes->LeftMouseReleased = ImGui::IsMouseReleased(0); GImNodes->LeftMouseDragging = ImGui::IsMouseDragging(0, 0.0f); GImNodes->AltMouseClicked = (GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL && *GImNodes->Io.EmulateThreeButtonMouse.Modifier && GImNodes->LeftMouseClicked) || ImGui::IsMouseClicked(GImNodes->Io.AltMouseButton); GImNodes->AltMouseDragging = (GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL && GImNodes->LeftMouseDragging && (*GImNodes->Io.EmulateThreeButtonMouse.Modifier)) || ImGui::IsMouseDragging(GImNodes->Io.AltMouseButton, 0.0f); GImNodes->AltMouseScrollDelta = ImGui::GetIO().MouseWheel; GImNodes->MultipleSelectModifier = (GImNodes->Io.MultipleSelectModifier.Modifier != NULL ? *GImNodes->Io.MultipleSelectModifier.Modifier : ImGui::GetIO().KeyCtrl); GImNodes->ActiveAttribute = false; ImGui::BeginGroup(); { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.f, 1.f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.f, 0.f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, GImNodes->Style.Colors[ImNodesCol_GridBackground]); ImGui::BeginChild( "scrolling_region", ImVec2(0.f, 0.f), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollWithMouse); GImNodes->CanvasOriginScreenSpace = ImGui::GetCursorScreenPos(); // NOTE: we have to fetch the canvas draw list *after* we call // BeginChild(), otherwise the ImGui UI elements are going to be // rendered into the parent window draw list. DrawListSet(ImGui::GetWindowDrawList()); { const ImVec2 canvas_size = ImGui::GetWindowSize(); GImNodes->CanvasRectScreenSpace = ImRect( EditorSpaceToScreenSpace(ImVec2(0.f, 0.f)), EditorSpaceToScreenSpace(canvas_size)); if (GImNodes->Style.Flags & ImNodesStyleFlags_GridLines) { DrawGrid(editor, canvas_size); } } } } void EndNodeEditor() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor); GImNodes->CurrentScope = ImNodesScope_None; ImNodesEditorContext& editor = EditorContextGet(); bool no_grid_content = editor.GridContentBounds.IsInverted(); if (no_grid_content) { editor.GridContentBounds = ScreenSpaceToGridSpace(editor, GImNodes->CanvasRectScreenSpace); } // Detect ImGui interaction first, because it blocks interaction with the rest of the UI if (GImNodes->LeftMouseClicked && ImGui::IsAnyItemActive()) { editor.ClickInteraction.Type = ImNodesClickInteractionType_ImGuiItem; } // Detect which UI element is being hovered over. Detection is done in a hierarchical fashion, // because a UI element being hovered excludes any other as being hovered over. // Don't do hovering detection for nodes/links/pins when interacting with the mini-map, since // its an *overlay* with its own interaction behavior and must have precedence during mouse // interaction. if ((editor.ClickInteraction.Type == ImNodesClickInteractionType_None || editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation) && MouseInCanvas() && !IsMiniMapHovered()) { // Pins needs some special care. We need to check the depth stack to see which pins are // being occluded by other nodes. ResolveOccludedPins(editor, GImNodes->OccludedPinIndices); GImNodes->HoveredPinIdx = ResolveHoveredPin(editor.Pins, GImNodes->OccludedPinIndices); if (!GImNodes->HoveredPinIdx.HasValue()) { // Resolve which node is actually on top and being hovered using the depth stack. GImNodes->HoveredNodeIdx = ResolveHoveredNode(editor.NodeDepthOrder); } // We don't check for hovered pins here, because if we want to detach a link by clicking and // dragging, we need to have both a link and pin hovered. if (!GImNodes->HoveredNodeIdx.HasValue()) { GImNodes->HoveredLinkIdx = ResolveHoveredLink(editor.Links, editor.Pins); } } for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx) { if (editor.Nodes.InUse[node_idx]) { DrawListActivateNodeBackground(node_idx); DrawNode(editor, node_idx); } } // In order to render the links underneath the nodes, we want to first select the bottom draw // channel. GImNodes->CanvasDrawList->ChannelsSetCurrent(0); for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx) { if (editor.Links.InUse[link_idx]) { DrawLink(editor, link_idx); } } // Render the click interaction UI elements (partial links, box selector) on top of everything // else. DrawListAppendClickInteractionChannel(); DrawListActivateClickInteractionChannel(); if (IsMiniMapActive()) { CalcMiniMapLayout(); MiniMapUpdate(); } // Handle node graph interaction if (!IsMiniMapHovered()) { if (GImNodes->LeftMouseClicked && GImNodes->HoveredLinkIdx.HasValue()) { BeginLinkInteraction(editor, GImNodes->HoveredLinkIdx.Value(), GImNodes->HoveredPinIdx); } else if (GImNodes->LeftMouseClicked && GImNodes->HoveredPinIdx.HasValue()) { BeginLinkCreation(editor, GImNodes->HoveredPinIdx.Value()); } else if (GImNodes->LeftMouseClicked && GImNodes->HoveredNodeIdx.HasValue()) { BeginNodeSelection(editor, GImNodes->HoveredNodeIdx.Value()); } else if ( GImNodes->LeftMouseClicked || GImNodes->LeftMouseReleased || GImNodes->AltMouseClicked || GImNodes->AltMouseScrollDelta != 0.f) { BeginCanvasInteraction(editor); } bool should_auto_pan = editor.ClickInteraction.Type == ImNodesClickInteractionType_BoxSelection || editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation || editor.ClickInteraction.Type == ImNodesClickInteractionType_Node; if (should_auto_pan && !MouseInCanvas()) { ImVec2 mouse = ImGui::GetMousePos(); ImVec2 center = GImNodes->CanvasRectScreenSpace.GetCenter(); ImVec2 direction = (center - mouse); direction = direction * ImInvLength(direction, 0.0); editor.AutoPanningDelta = direction * ImGui::GetIO().DeltaTime * GImNodes->Io.AutoPanningSpeed; editor.Panning += editor.AutoPanningDelta; } } ClickInteractionUpdate(editor); // At this point, draw commands have been issued for all nodes (and pins). Update the node pool // to detect unused node slots and remove those indices from the depth stack before sorting the // node draw commands by depth. ObjectPoolUpdate(editor.Nodes); ObjectPoolUpdate(editor.Pins); DrawListSortChannelsByDepth(editor.NodeDepthOrder); // After the links have been rendered, the link pool can be updated as well. ObjectPoolUpdate(editor.Links); // Finally, merge the draw channels GImNodes->CanvasDrawList->ChannelsMerge(); // pop style ImGui::EndChild(); // end scrolling region ImGui::PopStyleColor(); // pop child window background color ImGui::PopStyleVar(); // pop window padding ImGui::PopStyleVar(); // pop frame padding ImGui::EndGroup(); } void MiniMap( const float minimap_size_fraction, const ImNodesMiniMapLocation location, const ImNodesMiniMapNodeHoveringCallback node_hovering_callback, const ImNodesMiniMapNodeHoveringCallbackUserData node_hovering_callback_data) { // Check that editor size fraction is sane; must be in the range (0, 1] IM_ASSERT(minimap_size_fraction > 0.f && minimap_size_fraction <= 1.f); // Remember to call before EndNodeEditor IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor); ImNodesEditorContext& editor = EditorContextGet(); editor.MiniMapEnabled = true; editor.MiniMapSizeFraction = minimap_size_fraction; editor.MiniMapLocation = location; // Set node hovering callback information editor.MiniMapNodeHoveringCallback = node_hovering_callback; editor.MiniMapNodeHoveringCallbackUserData = node_hovering_callback_data; // Actual drawing/updating of the MiniMap is done in EndNodeEditor so that // mini map is draw over everything and all pin/link positions are updated // correctly relative to their respective nodes. Hence, we must store some of // of the state for the mini map in GImNodes for the actual drawing/updating } void BeginNode(const int node_id) { // Remember to call BeginNodeEditor before calling BeginNode IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor); GImNodes->CurrentScope = ImNodesScope_Node; ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFindOrCreateIndex(editor.Nodes, node_id); GImNodes->CurrentNodeIdx = node_idx; ImNodeData& node = editor.Nodes.Pool[node_idx]; node.ColorStyle.Background = GImNodes->Style.Colors[ImNodesCol_NodeBackground]; node.ColorStyle.BackgroundHovered = GImNodes->Style.Colors[ImNodesCol_NodeBackgroundHovered]; node.ColorStyle.BackgroundSelected = GImNodes->Style.Colors[ImNodesCol_NodeBackgroundSelected]; node.ColorStyle.Outline = GImNodes->Style.Colors[ImNodesCol_NodeOutline]; node.ColorStyle.Titlebar = GImNodes->Style.Colors[ImNodesCol_TitleBar]; node.ColorStyle.TitlebarHovered = GImNodes->Style.Colors[ImNodesCol_TitleBarHovered]; node.ColorStyle.TitlebarSelected = GImNodes->Style.Colors[ImNodesCol_TitleBarSelected]; node.LayoutStyle.CornerRounding = GImNodes->Style.NodeCornerRounding; node.LayoutStyle.Padding = GImNodes->Style.NodePadding; node.LayoutStyle.BorderThickness = GImNodes->Style.NodeBorderThickness; // ImGui::SetCursorPos sets the cursor position, local to the current widget // (in this case, the child object started in BeginNodeEditor). Use // ImGui::SetCursorScreenPos to set the screen space coordinates directly. ImGui::SetCursorPos(GridSpaceToEditorSpace(editor, GetNodeTitleBarOrigin(node))); DrawListAddNode(node_idx); DrawListActivateCurrentNodeForeground(); ImGui::PushID(node.Id); ImGui::BeginGroup(); } void EndNode() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); GImNodes->CurrentScope = ImNodesScope_Editor; ImNodesEditorContext& editor = EditorContextGet(); // The node's rectangle depends on the ImGui UI group size. ImGui::EndGroup(); ImGui::PopID(); ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx]; node.Rect = GetItemRect(); node.Rect.Expand(node.LayoutStyle.Padding); editor.GridContentBounds.Add(node.Origin); editor.GridContentBounds.Add(node.Origin + node.Rect.GetSize()); if (node.Rect.Contains(GImNodes->MousePos)) { GImNodes->NodeIndicesOverlappingWithMouse.push_back(GImNodes->CurrentNodeIdx); } } ImVec2 GetNodeDimensions(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFind(editor.Nodes, node_id); IM_ASSERT(node_idx != -1); // invalid node_id const ImNodeData& node = editor.Nodes.Pool[node_idx]; return node.Rect.GetSize(); } void BeginNodeTitleBar() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); ImGui::BeginGroup(); } void EndNodeTitleBar() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); ImGui::EndGroup(); ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx]; node.TitleBarContentRect = GetItemRect(); ImGui::ItemAdd(GetNodeTitleRect(node), ImGui::GetID("title_bar")); ImGui::SetCursorPos(GridSpaceToEditorSpace(editor, GetNodeContentOrigin(node))); } void BeginInputAttribute(const int id, const ImNodesPinShape shape) { BeginPinAttribute(id, ImNodesAttributeType_Input, shape, GImNodes->CurrentNodeIdx); } void EndInputAttribute() { EndPinAttribute(); } void BeginOutputAttribute(const int id, const ImNodesPinShape shape) { BeginPinAttribute(id, ImNodesAttributeType_Output, shape, GImNodes->CurrentNodeIdx); } void EndOutputAttribute() { EndPinAttribute(); } void BeginStaticAttribute(const int id) { // Make sure to call BeginNode() before calling BeginAttribute() IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); GImNodes->CurrentScope = ImNodesScope_Attribute; GImNodes->CurrentAttributeId = id; ImGui::BeginGroup(); ImGui::PushID(id); } void EndStaticAttribute() { // Make sure to call BeginNode() before calling BeginAttribute() IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Attribute); GImNodes->CurrentScope = ImNodesScope_Node; ImGui::PopID(); ImGui::EndGroup(); if (ImGui::IsItemActive()) { GImNodes->ActiveAttribute = true; GImNodes->ActiveAttributeId = GImNodes->CurrentAttributeId; } } void PushAttributeFlag(const ImNodesAttributeFlags flag) { GImNodes->CurrentAttributeFlags |= flag; GImNodes->AttributeFlagStack.push_back(GImNodes->CurrentAttributeFlags); } void PopAttributeFlag() { // PopAttributeFlag called without a matching PushAttributeFlag! // The bottom value is always the default value, pushed in Initialize(). IM_ASSERT(GImNodes->AttributeFlagStack.size() > 1); GImNodes->AttributeFlagStack.pop_back(); GImNodes->CurrentAttributeFlags = GImNodes->AttributeFlagStack.back(); } void Link(const int id, const int start_attr_id, const int end_attr_id) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor); ImNodesEditorContext& editor = EditorContextGet(); ImLinkData& link = ObjectPoolFindOrCreateObject(editor.Links, id); link.Id = id; link.StartPinIdx = ObjectPoolFindOrCreateIndex(editor.Pins, start_attr_id); link.EndPinIdx = ObjectPoolFindOrCreateIndex(editor.Pins, end_attr_id); link.ColorStyle.Base = GImNodes->Style.Colors[ImNodesCol_Link]; link.ColorStyle.Hovered = GImNodes->Style.Colors[ImNodesCol_LinkHovered]; link.ColorStyle.Selected = GImNodes->Style.Colors[ImNodesCol_LinkSelected]; // Check if this link was created by the current link event if ((editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation && editor.Pins.Pool[link.EndPinIdx].Flags & ImNodesAttributeFlags_EnableLinkCreationOnSnap && editor.ClickInteraction.LinkCreation.StartPinIdx == link.StartPinIdx && editor.ClickInteraction.LinkCreation.EndPinIdx == link.EndPinIdx) || (editor.ClickInteraction.LinkCreation.StartPinIdx == link.EndPinIdx && editor.ClickInteraction.LinkCreation.EndPinIdx == link.StartPinIdx)) { GImNodes->SnapLinkIdx = ObjectPoolFindOrCreateIndex(editor.Links, id); } } void PushColorStyle(const ImNodesCol item, unsigned int color) { GImNodes->ColorModifierStack.push_back(ImNodesColElement(GImNodes->Style.Colors[item], item)); GImNodes->Style.Colors[item] = color; } void PopColorStyle() { IM_ASSERT(GImNodes->ColorModifierStack.size() > 0); const ImNodesColElement elem = GImNodes->ColorModifierStack.back(); GImNodes->Style.Colors[elem.Item] = elem.Color; GImNodes->ColorModifierStack.pop_back(); } struct ImNodesStyleVarInfo { ImGuiDataType Type; ImU32 Count; ImU32 Offset; void* GetVarPtr(ImNodesStyle* style) const { return (void*)((unsigned char*)style + Offset); } }; static const ImNodesStyleVarInfo GStyleVarInfo[] = { // ImNodesStyleVar_GridSpacing {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, GridSpacing)}, // ImNodesStyleVar_NodeCornerRounding {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, NodeCornerRounding)}, // ImNodesStyleVar_NodePadding {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, NodePadding)}, // ImNodesStyleVar_NodeBorderThickness {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, NodeBorderThickness)}, // ImNodesStyleVar_LinkThickness {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkThickness)}, // ImNodesStyleVar_LinkLineSegmentsPerLength {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkLineSegmentsPerLength)}, // ImNodesStyleVar_LinkHoverDistance {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkHoverDistance)}, // ImNodesStyleVar_PinCircleRadius {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinCircleRadius)}, // ImNodesStyleVar_PinQuadSideLength {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinQuadSideLength)}, // ImNodesStyleVar_PinTriangleSideLength {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinTriangleSideLength)}, // ImNodesStyleVar_PinLineThickness {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinLineThickness)}, // ImNodesStyleVar_PinHoverRadius {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinHoverRadius)}, // ImNodesStyleVar_PinOffset {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinOffset)}, // ImNodesStyleVar_MiniMapPadding {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, MiniMapPadding)}, // ImNodesStyleVar_MiniMapOffset {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, MiniMapOffset)}, }; static const ImNodesStyleVarInfo* GetStyleVarInfo(ImNodesStyleVar idx) { IM_ASSERT(idx >= 0 && idx < ImNodesStyleVar_COUNT); IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImNodesStyleVar_COUNT); return &GStyleVarInfo[idx]; } void PushStyleVar(const ImNodesStyleVar item, const float value) { const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(item); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { float& style_var = *(float*)var_info->GetVarPtr(&GImNodes->Style); GImNodes->StyleModifierStack.push_back(ImNodesStyleVarElement(item, style_var)); style_var = value; return; } IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); } void PushStyleVar(const ImNodesStyleVar item, const ImVec2& value) { const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(item); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) { ImVec2& style_var = *(ImVec2*)var_info->GetVarPtr(&GImNodes->Style); GImNodes->StyleModifierStack.push_back(ImNodesStyleVarElement(item, style_var)); style_var = value; return; } IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); } void PopStyleVar(int count) { while (count > 0) { IM_ASSERT(GImNodes->StyleModifierStack.size() > 0); const ImNodesStyleVarElement style_backup = GImNodes->StyleModifierStack.back(); GImNodes->StyleModifierStack.pop_back(); const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(style_backup.Item); void* style_var = var_info->GetVarPtr(&GImNodes->Style); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { ((float*)style_var)[0] = style_backup.FloatValue[0]; } else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) { ((float*)style_var)[0] = style_backup.FloatValue[0]; ((float*)style_var)[1] = style_backup.FloatValue[1]; } count--; } } void SetNodeScreenSpacePos(const int node_id, const ImVec2& screen_space_pos) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Origin = ScreenSpaceToGridSpace(editor, screen_space_pos); } void SetNodeEditorSpacePos(const int node_id, const ImVec2& editor_space_pos) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Origin = EditorSpaceToGridSpace(editor, editor_space_pos); } void SetNodeGridSpacePos(const int node_id, const ImVec2& grid_pos) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Origin = grid_pos; } void SetNodeDraggable(const int node_id, const bool draggable) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Draggable = draggable; } ImVec2 GetNodeScreenSpacePos(const int node_id) { ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFind(editor.Nodes, node_id); IM_ASSERT(node_idx != -1); ImNodeData& node = editor.Nodes.Pool[node_idx]; return GridSpaceToScreenSpace(editor, node.Origin); } ImVec2 GetNodeEditorSpacePos(const int node_id) { ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFind(editor.Nodes, node_id); IM_ASSERT(node_idx != -1); ImNodeData& node = editor.Nodes.Pool[node_idx]; return GridSpaceToEditorSpace(editor, node.Origin); } ImVec2 GetNodeGridSpacePos(const int node_id) { ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFind(editor.Nodes, node_id); IM_ASSERT(node_idx != -1); ImNodeData& node = editor.Nodes.Pool[node_idx]; return node.Origin; } void SnapNodeToGrid(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Origin = SnapOriginToGrid(node.Origin); } bool IsEditorHovered() { return MouseInCanvas(); } bool IsNodeHovered(int* const node_id) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(node_id != NULL); const bool is_hovered = GImNodes->HoveredNodeIdx.HasValue(); if (is_hovered) { const ImNodesEditorContext& editor = EditorContextGet(); *node_id = editor.Nodes.Pool[GImNodes->HoveredNodeIdx.Value()].Id; } return is_hovered; } bool IsLinkHovered(int* const link_id) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(link_id != NULL); const bool is_hovered = GImNodes->HoveredLinkIdx.HasValue(); if (is_hovered) { const ImNodesEditorContext& editor = EditorContextGet(); *link_id = editor.Links.Pool[GImNodes->HoveredLinkIdx.Value()].Id; } return is_hovered; } bool IsPinHovered(int* const attr) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(attr != NULL); const bool is_hovered = GImNodes->HoveredPinIdx.HasValue(); if (is_hovered) { const ImNodesEditorContext& editor = EditorContextGet(); *attr = editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()].Id; } return is_hovered; } int NumSelectedNodes() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); const ImNodesEditorContext& editor = EditorContextGet(); return editor.SelectedNodeIndices.size(); } int NumSelectedLinks() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); const ImNodesEditorContext& editor = EditorContextGet(); return editor.SelectedLinkIndices.size(); } void GetSelectedNodes(int* node_ids) { IM_ASSERT(node_ids != NULL); const ImNodesEditorContext& editor = EditorContextGet(); for (int i = 0; i < editor.SelectedNodeIndices.size(); ++i) { const int node_idx = editor.SelectedNodeIndices[i]; node_ids[i] = editor.Nodes.Pool[node_idx].Id; } } void GetSelectedLinks(int* link_ids) { IM_ASSERT(link_ids != NULL); const ImNodesEditorContext& editor = EditorContextGet(); for (int i = 0; i < editor.SelectedLinkIndices.size(); ++i) { const int link_idx = editor.SelectedLinkIndices[i]; link_ids[i] = editor.Links.Pool[link_idx].Id; } } void ClearNodeSelection() { ImNodesEditorContext& editor = EditorContextGet(); editor.SelectedNodeIndices.clear(); } void ClearNodeSelection(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); ClearObjectSelection(editor.Nodes, editor.SelectedNodeIndices, node_id); } void ClearLinkSelection() { ImNodesEditorContext& editor = EditorContextGet(); editor.SelectedLinkIndices.clear(); } void ClearLinkSelection(int link_id) { ImNodesEditorContext& editor = EditorContextGet(); ClearObjectSelection(editor.Links, editor.SelectedLinkIndices, link_id); } void SelectNode(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); SelectObject(editor.Nodes, editor.SelectedNodeIndices, node_id); } void SelectLink(int link_id) { ImNodesEditorContext& editor = EditorContextGet(); SelectObject(editor.Links, editor.SelectedLinkIndices, link_id); } bool IsNodeSelected(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); return IsObjectSelected(editor.Nodes, editor.SelectedNodeIndices, node_id); } bool IsLinkSelected(int link_id) { ImNodesEditorContext& editor = EditorContextGet(); return IsObjectSelected(editor.Links, editor.SelectedLinkIndices, link_id); } bool IsAttributeActive() { IM_ASSERT((GImNodes->CurrentScope & ImNodesScope_Node) != 0); if (!GImNodes->ActiveAttribute) { return false; } return GImNodes->ActiveAttributeId == GImNodes->CurrentAttributeId; } bool IsAnyAttributeActive(int* const attribute_id) { IM_ASSERT((GImNodes->CurrentScope & (ImNodesScope_Node | ImNodesScope_Attribute)) == 0); if (!GImNodes->ActiveAttribute) { return false; } if (attribute_id != NULL) { *attribute_id = GImNodes->ActiveAttributeId; } return true; } bool IsLinkStarted(int* const started_at_id) { // Call this function after EndNodeEditor()! IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(started_at_id != NULL); const bool is_started = (GImNodes->ImNodesUIState & ImNodesUIState_LinkStarted) != 0; if (is_started) { const ImNodesEditorContext& editor = EditorContextGet(); const int pin_idx = editor.ClickInteraction.LinkCreation.StartPinIdx; *started_at_id = editor.Pins.Pool[pin_idx].Id; } return is_started; } bool IsLinkDropped(int* const started_at_id, const bool including_detached_links) { // Call this function after EndNodeEditor()! IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); const ImNodesEditorContext& editor = EditorContextGet(); const bool link_dropped = (GImNodes->ImNodesUIState & ImNodesUIState_LinkDropped) != 0 && (including_detached_links || editor.ClickInteraction.LinkCreation.Type != ImNodesLinkCreationType_FromDetach); if (link_dropped && started_at_id) { const int pin_idx = editor.ClickInteraction.LinkCreation.StartPinIdx; *started_at_id = editor.Pins.Pool[pin_idx].Id; } return link_dropped; } bool IsLinkCreated( int* const started_at_pin_id, int* const ended_at_pin_id, bool* const created_from_snap) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(started_at_pin_id != NULL); IM_ASSERT(ended_at_pin_id != NULL); const bool is_created = (GImNodes->ImNodesUIState & ImNodesUIState_LinkCreated) != 0; if (is_created) { const ImNodesEditorContext& editor = EditorContextGet(); const int start_idx = editor.ClickInteraction.LinkCreation.StartPinIdx; const int end_idx = editor.ClickInteraction.LinkCreation.EndPinIdx.Value(); const ImPinData& start_pin = editor.Pins.Pool[start_idx]; const ImPinData& end_pin = editor.Pins.Pool[end_idx]; if (start_pin.Type == ImNodesAttributeType_Output) { *started_at_pin_id = start_pin.Id; *ended_at_pin_id = end_pin.Id; } else { *started_at_pin_id = end_pin.Id; *ended_at_pin_id = start_pin.Id; } if (created_from_snap) { *created_from_snap = editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation; } } return is_created; } bool IsLinkCreated( int* started_at_node_id, int* started_at_pin_id, int* ended_at_node_id, int* ended_at_pin_id, bool* created_from_snap) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(started_at_node_id != NULL); IM_ASSERT(started_at_pin_id != NULL); IM_ASSERT(ended_at_node_id != NULL); IM_ASSERT(ended_at_pin_id != NULL); const bool is_created = (GImNodes->ImNodesUIState & ImNodesUIState_LinkCreated) != 0; if (is_created) { const ImNodesEditorContext& editor = EditorContextGet(); const int start_idx = editor.ClickInteraction.LinkCreation.StartPinIdx; const int end_idx = editor.ClickInteraction.LinkCreation.EndPinIdx.Value(); const ImPinData& start_pin = editor.Pins.Pool[start_idx]; const ImNodeData& start_node = editor.Nodes.Pool[start_pin.ParentNodeIdx]; const ImPinData& end_pin = editor.Pins.Pool[end_idx]; const ImNodeData& end_node = editor.Nodes.Pool[end_pin.ParentNodeIdx]; if (start_pin.Type == ImNodesAttributeType_Output) { *started_at_pin_id = start_pin.Id; *started_at_node_id = start_node.Id; *ended_at_pin_id = end_pin.Id; *ended_at_node_id = end_node.Id; } else { *started_at_pin_id = end_pin.Id; *started_at_node_id = end_node.Id; *ended_at_pin_id = start_pin.Id; *ended_at_node_id = start_node.Id; } if (created_from_snap) { *created_from_snap = editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation; } } return is_created; } bool IsLinkDestroyed(int* const link_id) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); const bool link_destroyed = GImNodes->DeletedLinkIdx.HasValue(); if (link_destroyed) { const ImNodesEditorContext& editor = EditorContextGet(); const int link_idx = GImNodes->DeletedLinkIdx.Value(); *link_id = editor.Links.Pool[link_idx].Id; } return link_destroyed; } namespace { void NodeLineHandler(ImNodesEditorContext& editor, const char* const line) { int id; int x, y; if (sscanf(line, "[node.%i", &id) == 1) { const int node_idx = ObjectPoolFindOrCreateIndex(editor.Nodes, id); GImNodes->CurrentNodeIdx = node_idx; ImNodeData& node = editor.Nodes.Pool[node_idx]; node.Id = id; } else if (sscanf(line, "origin=%i,%i", &x, &y) == 2) { ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx]; node.Origin = SnapOriginToGrid(ImVec2((float)x, (float)y)); } } void EditorLineHandler(ImNodesEditorContext& editor, const char* const line) { (void)sscanf(line, "panning=%f,%f", &editor.Panning.x, &editor.Panning.y); } } // namespace const char* SaveCurrentEditorStateToIniString(size_t* const data_size) { return SaveEditorStateToIniString(&EditorContextGet(), data_size); } const char* SaveEditorStateToIniString( const ImNodesEditorContext* const editor_ptr, size_t* const data_size) { IM_ASSERT(editor_ptr != NULL); const ImNodesEditorContext& editor = *editor_ptr; GImNodes->TextBuffer.clear(); // TODO: check to make sure that the estimate is the upper bound of element GImNodes->TextBuffer.reserve(64 * editor.Nodes.Pool.size()); GImNodes->TextBuffer.appendf( "[editor]\npanning=%i,%i\n", (int)editor.Panning.x, (int)editor.Panning.y); for (int i = 0; i < editor.Nodes.Pool.size(); i++) { if (editor.Nodes.InUse[i]) { const ImNodeData& node = editor.Nodes.Pool[i]; GImNodes->TextBuffer.appendf("\n[node.%d]\n", node.Id); GImNodes->TextBuffer.appendf("origin=%i,%i\n", (int)node.Origin.x, (int)node.Origin.y); } } if (data_size != NULL) { *data_size = GImNodes->TextBuffer.size(); } return GImNodes->TextBuffer.c_str(); } void LoadCurrentEditorStateFromIniString(const char* const data, const size_t data_size) { LoadEditorStateFromIniString(&EditorContextGet(), data, data_size); } void LoadEditorStateFromIniString( ImNodesEditorContext* const editor_ptr, const char* const data, const size_t data_size) { if (data_size == 0u) { return; } ImNodesEditorContext& editor = editor_ptr == NULL ? EditorContextGet() : *editor_ptr; char* buf = (char*)ImGui::MemAlloc(data_size + 1); const char* buf_end = buf + data_size; memcpy(buf, data, data_size); buf[data_size] = 0; void (*line_handler)(ImNodesEditorContext&, const char*); line_handler = NULL; char* line_end = NULL; for (char* line = buf; line < buf_end; line = line_end + 1) { while (*line == '\n' || *line == '\r') { line++; } line_end = line; while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') { line_end++; } line_end[0] = 0; if (*line == ';' || *line == '\0') { continue; } if (line[0] == '[' && line_end[-1] == ']') { line_end[-1] = 0; if (strncmp(line + 1, "node", 4) == 0) { line_handler = NodeLineHandler; } else if (strcmp(line + 1, "editor") == 0) { line_handler = EditorLineHandler; } } if (line_handler != NULL) { line_handler(editor, line); } } ImGui::MemFree(buf); } void SaveCurrentEditorStateToIniFile(const char* const file_name) { SaveEditorStateToIniFile(&EditorContextGet(), file_name); } void SaveEditorStateToIniFile(const ImNodesEditorContext* const editor, const char* const file_name) { size_t data_size = 0u; const char* data = SaveEditorStateToIniString(editor, &data_size); FILE* file = ImFileOpen(file_name, "wt"); if (!file) { return; } fwrite(data, sizeof(char), data_size, file); fclose(file); } void LoadCurrentEditorStateFromIniFile(const char* const file_name) { LoadEditorStateFromIniFile(&EditorContextGet(), file_name); } void LoadEditorStateFromIniFile(ImNodesEditorContext* const editor, const char* const file_name) { size_t data_size = 0u; char* file_data = (char*)ImFileLoadToMemory(file_name, "rb", &data_size); if (!file_data) { return; } LoadEditorStateFromIniString(editor, file_data, data_size); ImGui::MemFree(file_data); } } // namespace IMNODES_NAMESPACE ================================================ FILE: lib/third_party/imgui/implot/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) # https://github.com/epezent/implot project(imgui_implot) set(CMAKE_CXX_STANDARD 23) if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD) add_library(imgui_implot OBJECT source/implot.cpp source/implot_items.cpp source/implot_demo.cpp ) target_include_directories(imgui_implot PUBLIC include ) target_link_libraries(imgui_implot PRIVATE imgui_includes) endif() target_include_directories(imgui_all_includes INTERFACE include) ================================================ FILE: lib/third_party/imgui/implot/LICENSE.txt ================================================ MIT License Copyright (c) 2020 Evan Pezent Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: lib/third_party/imgui/implot/README.md ================================================ # ImPlot ImPlot is an immediate mode, GPU accelerated plotting library for [Dear ImGui](https://github.com/ocornut/imgui). It aims to provide a first-class API that ImGui fans will love. ImPlot is well suited for visualizing program data in real-time or creating interactive plots, and requires minimal code to integrate. Just like ImGui, it does not burden the end user with GUI state management, avoids STL containers and C++ headers, and has no external dependencies except for ImGui itself. ## Features - GPU accelerated rendering - multiple plot types: - line plots - shaded plots - scatter plots - vertical/horizontal/stacked bars graphs - vertical/horizontal error bars - stem plots - stair plots - pie charts - heatmap charts - 1D/2D histograms - images - and more likely to come - mix/match multiple plot items on a single plot - configurable axes ranges and scaling (linear/log) - subplots - time formatted x-axes (US formatted or ISO 8601) - reversible and lockable axes - multiple x-axes and y-axes - controls for zooming, panning, box selection, and auto-fitting data - controls for creating persistent query ranges (see demo) - several plot styling options: 10 marker types, adjustable marker sizes, line weights, outline colors, fill colors, etc. - 16 built-in colormaps and support for and user-added colormaps - optional plot titles, axis labels, and grid labels - optional and configurable legends with toggle buttons to quickly show/hide plot items - default styling based on current ImGui theme, or completely custom plot styles - customizable data getters and data striding (just like ImGui:PlotLine) - accepts data as float, double, and 8, 16, 32, and 64-bit signed/unsigned integral types - and more! (see Announcements [2022](https://github.com/epezent/implot/discussions/370)/[2021](https://github.com/epezent/implot/issues/168)/[2020](https://github.com/epezent/implot/issues/48)) ## Usage The API is used just like any other ImGui `BeginX`/`EndX` pair. First, start a new plot with `ImPlot::BeginPlot()`. Next, plot as many items as you want with the provided `PlotX` functions (e.g. `PlotLine()`, `PlotBars()`, `PlotScatter()`, etc). Finally, wrap things up with a call to `ImPlot::EndPlot()`. That's it! ```cpp int bar_data[11] = ...; float x_data[1000] = ...; float y_data[1000] = ...; ImGui::Begin("My Window"); if (ImPlot::BeginPlot("My Plot")) { ImPlot::PlotBars("My Bar Plot", bar_data, 11); ImPlot::PlotLine("My Line Plot", x_data, y_data, 1000); ... ImPlot::EndPlot(); } ImGui::End(); ``` ![Usage](https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/example.PNG) Of course, there's much more you can do with ImPlot... ## Demos A comprehensive example of ImPlot's features can be found in `implot_demo.cpp`. Add this file to your sources and call `ImPlot::ShowDemoWindow()` somewhere in your update loop. You are encouraged to use this file as a reference when needing to implement various plot types. The demo is always updated to show new plot types and features as they are added, so check back with each release! An online version of the demo is hosted [here](https://traineq.org/implot_demo/src/implot_demo.html). You can view the plots and the source code that generated them. Note that this demo may not always be up to date and is not as performant as a desktop implementation, but it should give you a general taste of what's possible with ImPlot. Special thanks to [pthom](https://github.com/pthom) for creating and hosting this! More sophisticated demos requiring lengthier code and/or third-party libraries can be found in a separate repository: [implot_demos](https://github.com/epezent/implot_demos). Here, you will find advanced signal processing and ImPlot usage in action. Please read the `Contributing` section of that repository if you have an idea for a new demo! ## Integration 0) Set up an [ImGui](https://github.com/ocornut/imgui) environment if you don't already have one. 1) Add `implot.h`, `implot_internal.h`, `implot.cpp`, `implot_items.cpp` and optionally `implot_demo.cpp` to your sources. Alternatively, you can get ImPlot using [vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/implot). 2) Create and destroy an `ImPlotContext` wherever you do so for your `ImGuiContext`: ```cpp ImGui::CreateContext(); ImPlot::CreateContext(); ... ImPlot::DestroyContext(); ImGui::DestroyContext(); ``` You should be good to go! ## Installing ImPlot using vcpkg You can download and install ImPlot using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: ```bash git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install ./vcpkg install implot ``` The ImPlot port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. ## Extremely Important Note Dear ImGui uses **16-bit indexing by default**, so high-density ImPlot widgets like `ImPlot::PlotHeatmap()` may produce too many vertices into `ImDrawList`, which causes an assertion failure and will result in data truncation and/or visual glitches. Therefore, it is **HIGHLY** recommended that you EITHER: - **Option 1:** Enable 32-bit indices by uncommenting `#define ImDrawIdx unsigned int` in your ImGui [`imconfig.h`](https://github.com/ocornut/imgui/blob/master/imconfig.h#L89) file. - **Option 2:** Handle the `ImGuiBackendFlags_RendererHasVtxOffset` flag in your renderer if you must use 16-bit indices. Many of the default ImGui rendering backends already support `ImGuiBackendFlags_RendererHasVtxOffset`. Refer to [this issue](https://github.com/ocornut/imgui/issues/2591) for more information. ## FAQ **Q: Why?** A: ImGui is an incredibly powerful tool for rapid prototyping and development, but provides only limited mechanisms for data visualization. Two dimensional plots are ubiquitous and useful to almost any application. Being able to visualize your data in real-time will give you insight and better understanding of your application. **Q: Is ImPlot the right plotting library for me?** A: If you're looking to generate publication quality plots and/or export plots to a file, ImPlot is NOT the library for you! ImPlot is geared toward plotting application data at realtime speeds with high levels of interactivity. ImPlot does its best to create pretty plots (indeed, there are quite a few styling options available), but it will always favor function over form. **Q: Where is the documentation?** A: The API is thoroughly commented in `implot.h`, and the demo in `implot_demo.cpp` should be more than enough to get you started. Also take a look at the [implot_demos](https://github.com/epezent/implot_demos) repository. **Q: Is ImPlot suitable for plotting large datasets?** A: Yes, within reason. You can plot tens to hundreds of thousands of points without issue, but don't expect millions to be a buttery smooth experience. That said, you can always downsample extremely large datasets by telling ImPlot to stride your data at larger intervals if needed. Also try the experimental `backends` branch which aims to provide GPU acceleration support. **Q: What data types can I plot?** A: ImPlot plotting functions accept most scalar types: `float`, `double`, `int8`, `uint8`, `int16`, `uint16`, `int32`, `uint32`, `int64`, `uint64`. Arrays of custom structs or classes (e.g. `Vector2f` or similar) are easily passed to ImPlot functions using the built-in striding features (see `implot.h` for documentation), and many plotters provide a "getter" overload which accepts data generating callbacks. You can fully customize the list of accepted types by defining `IMPLOT_CUSTOM_NUMERIC_TYPES` at compile time: see doc in `implot_items.cpp`. **Q: Can plot styles be modified?** A: Yes. Data colormaps and various styling colors and variables can be pushed/popped or modified permanently on startup. Three default styles are available, as well as an automatic style that attempts to match you ImGui style. **Q: Does ImPlot support logarithmic scaling or time formatting?** A: Yep! Both logscale and timescale are supported. **Q: Does ImPlot support multiple y-axes? x-axes?** A: Yes. Up to three x-axes and three y-axes can be enabled. **Q: Does ImPlot support [insert plot type]?** A: Maybe. Check the demo, gallery, or Announcements ([2020](https://github.com/epezent/implot/issues/48)/[2021](https://github.com/epezent/implot/issues/168))to see if your desired plot type is shown. If not, consider submitting an issue or better yet, a PR! **Q: Does ImPlot support 3D plots?** A: No, and likely never will since ImGui only deals in 2D rendering. **Q: Does ImPlot provide analytic tools?** A: Not exactly, but it does give you the ability to query plot sub-ranges, with which you can process your data however you like. **Q: Can plots be exported/saved to image?** A: Not currently. Use your OS's screen capturing mechanisms if you need to capture a plot. ImPlot is not suitable for rendering publication quality plots; it is only intended to be used as a visualization tool. Post-process your data with MATLAB or matplotlib for these purposes. **Q: Why are my plot lines showing aliasing?** A: You probably need to enable `ImGuiStyle::AntiAliasedLinesUseTex` (or possibly `ImGuiStyle:AntiAliasedLines`). If those settings are already enabled, then you must ensure your backend supports texture based anti-aliasing (i.e. uses bilinear sampling). Most of the default ImGui backends support this feature out of the box. Learn more [here](https://github.com/ocornut/imgui/issues/3245). Alternatively, you can enable MSAA at the application level if your hardware supports it (4x should do). **Q: Can I compile ImPlot as a dynamic library?** A: Like ImGui, it is recommended that you compile and link ImPlot as a *static* library or directly as a part of your sources. However, if you must and are compiling ImPlot and ImGui as separate DLLs, make sure you set the current *ImGui* context with `ImPlot::SetImGuiContext(ImGuiContext* ctx)`. This ensures that global ImGui variables are correctly shared across the DLL boundary. **Q: Can ImPlot be used with other languages/bindings?** A: Yes, you can use the generated C binding, [cimplot](https://github.com/cimgui/cimplot) with most high level languages. [DearPyGui](https://github.com/hoffstadt/DearPyGui) provides a Python wrapper, among other things. [DearImGui/DearImPlot](https://github.com/aybe/DearImGui) provides bindings for .NET. [imgui-java](https://github.com/SpaiR/imgui-java) provides bindings for Java. [ImPlot.jl](https://github.com/wsphillips/ImPlot.jl) provides bindings for Julia. A Rust binding, [implot-rs](https://github.com/4bb4/implot-rs), is currently in the works. An example using Emscripten can be found [here](https://github.com/pthom/implot_demo). ================================================ FILE: lib/third_party/imgui/implot/include/implot.h ================================================ // MIT License // Copyright (c) 2023 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.17 // Table of Contents: // // [SECTION] Macros and Defines // [SECTION] Enums and Types // [SECTION] Callbacks // [SECTION] Contexts // [SECTION] Begin/End Plot // [SECTION] Begin/End Subplot // [SECTION] Setup // [SECTION] SetNext // [SECTION] Plot Items // [SECTION] Plot Tools // [SECTION] Plot Utils // [SECTION] Legend Utils // [SECTION] Drag and Drop // [SECTION] Styling // [SECTION] Colormaps // [SECTION] Input Mapping // [SECTION] Miscellaneous // [SECTION] Demo // [SECTION] Obsolete API #pragma once #include "imgui.h" //----------------------------------------------------------------------------- // [SECTION] Macros and Defines //----------------------------------------------------------------------------- // Define attributes of all API symbols declarations (e.g. for DLL under Windows) // Using ImPlot via a shared library is not recommended, because we don't guarantee // backward nor forward ABI compatibility and also function call overhead. If you // do use ImPlot as a DLL, be sure to call SetImGuiContext (see Miscellanous section). #ifndef IMPLOT_API #define IMPLOT_API #endif // ImPlot version string. #define IMPLOT_VERSION "0.17" // Indicates variable should deduced automatically. #define IMPLOT_AUTO -1 // Special color used to indicate that a color should be deduced automatically. #define IMPLOT_AUTO_COL ImVec4(0,0,0,-1) // Macro for templated plotting functions; keeps header clean. #define IMPLOT_TMP template IMPLOT_API //----------------------------------------------------------------------------- // [SECTION] Enums and Types //----------------------------------------------------------------------------- // Forward declarations struct ImPlotContext; // ImPlot context (opaque struct, see implot_internal.h) // Enums/Flags typedef int ImAxis; // -> enum ImAxis_ typedef int ImPlotFlags; // -> enum ImPlotFlags_ typedef int ImPlotAxisFlags; // -> enum ImPlotAxisFlags_ typedef int ImPlotSubplotFlags; // -> enum ImPlotSubplotFlags_ typedef int ImPlotLegendFlags; // -> enum ImPlotLegendFlags_ typedef int ImPlotMouseTextFlags; // -> enum ImPlotMouseTextFlags_ typedef int ImPlotDragToolFlags; // -> ImPlotDragToolFlags_ typedef int ImPlotColormapScaleFlags; // -> ImPlotColormapScaleFlags_ typedef int ImPlotItemFlags; // -> ImPlotItemFlags_ typedef int ImPlotLineFlags; // -> ImPlotLineFlags_ typedef int ImPlotScatterFlags; // -> ImPlotScatterFlags typedef int ImPlotStairsFlags; // -> ImPlotStairsFlags_ typedef int ImPlotShadedFlags; // -> ImPlotShadedFlags_ typedef int ImPlotBarsFlags; // -> ImPlotBarsFlags_ typedef int ImPlotBarGroupsFlags; // -> ImPlotBarGroupsFlags_ typedef int ImPlotErrorBarsFlags; // -> ImPlotErrorBarsFlags_ typedef int ImPlotStemsFlags; // -> ImPlotStemsFlags_ typedef int ImPlotInfLinesFlags; // -> ImPlotInfLinesFlags_ typedef int ImPlotPieChartFlags; // -> ImPlotPieChartFlags_ typedef int ImPlotHeatmapFlags; // -> ImPlotHeatmapFlags_ typedef int ImPlotHistogramFlags; // -> ImPlotHistogramFlags_ typedef int ImPlotDigitalFlags; // -> ImPlotDigitalFlags_ typedef int ImPlotImageFlags; // -> ImPlotImageFlags_ typedef int ImPlotTextFlags; // -> ImPlotTextFlags_ typedef int ImPlotDummyFlags; // -> ImPlotDummyFlags_ typedef int ImPlotCond; // -> enum ImPlotCond_ typedef int ImPlotCol; // -> enum ImPlotCol_ typedef int ImPlotStyleVar; // -> enum ImPlotStyleVar_ typedef int ImPlotScale; // -> enum ImPlotScale_ typedef int ImPlotMarker; // -> enum ImPlotMarker_ typedef int ImPlotColormap; // -> enum ImPlotColormap_ typedef int ImPlotLocation; // -> enum ImPlotLocation_ typedef int ImPlotBin; // -> enum ImPlotBin_ // Axis indices. The values assigned may change; NEVER hardcode these. enum ImAxis_ { // horizontal axes ImAxis_X1 = 0, // enabled by default ImAxis_X2, // disabled by default ImAxis_X3, // disabled by default // vertical axes ImAxis_Y1, // enabled by default ImAxis_Y2, // disabled by default ImAxis_Y3, // disabled by default // bookeeping ImAxis_COUNT }; // Options for plots (see BeginPlot). enum ImPlotFlags_ { ImPlotFlags_None = 0, // default ImPlotFlags_NoTitle = 1 << 0, // the plot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MyPlot") ImPlotFlags_NoLegend = 1 << 1, // the legend will not be displayed ImPlotFlags_NoMouseText = 1 << 2, // the mouse position, in plot coordinates, will not be displayed inside of the plot ImPlotFlags_NoInputs = 1 << 3, // the user will not be able to interact with the plot ImPlotFlags_NoMenus = 1 << 4, // the user will not be able to open context menus ImPlotFlags_NoBoxSelect = 1 << 5, // the user will not be able to box-select ImPlotFlags_NoFrame = 1 << 6, // the ImGui frame will not be rendered ImPlotFlags_Equal = 1 << 7, // x and y axes pairs will be constrained to have the same units/pixel ImPlotFlags_Crosshairs = 1 << 8, // the default mouse cursor will be replaced with a crosshair when hovered ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText }; // Options for plot axes (see SetupAxis). enum ImPlotAxisFlags_ { ImPlotAxisFlags_None = 0, // default ImPlotAxisFlags_NoLabel = 1 << 0, // the axis label will not be displayed (axis labels are also hidden if the supplied string name is nullptr) ImPlotAxisFlags_NoGridLines = 1 << 1, // no grid lines will be displayed ImPlotAxisFlags_NoTickMarks = 1 << 2, // no tick marks will be displayed ImPlotAxisFlags_NoTickLabels = 1 << 3, // no text labels will be displayed ImPlotAxisFlags_NoInitialFit = 1 << 4, // axis will not be initially fit to data extents on the first rendered frame ImPlotAxisFlags_NoMenus = 1 << 5, // the user will not be able to open context menus with right-click ImPlotAxisFlags_NoSideSwitch = 1 << 6, // the user will not be able to switch the axis side by dragging it ImPlotAxisFlags_NoHighlight = 1 << 7, // the axis will not have its background highlighted when hovered or held ImPlotAxisFlags_Opposite = 1 << 8, // axis ticks and labels will be rendered on the conventionally opposite side (i.e, right or top) ImPlotAxisFlags_Foreground = 1 << 9, // grid lines will be displayed in the foreground (i.e. on top of data) instead of the background ImPlotAxisFlags_Invert = 1 << 10, // the axis will be inverted ImPlotAxisFlags_AutoFit = 1 << 11, // axis will be auto-fitting to data extents ImPlotAxisFlags_RangeFit = 1 << 12, // axis will only fit points if the point is in the visible range of the **orthogonal** axis ImPlotAxisFlags_PanStretch = 1 << 13, // panning in a locked or constrained state will cause the axis to stretch if possible ImPlotAxisFlags_LockMin = 1 << 14, // the axis minimum value will be locked when panning/zooming ImPlotAxisFlags_LockMax = 1 << 15, // the axis maximum value will be locked when panning/zooming ImPlotAxisFlags_Lock = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax, ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_AuxDefault = ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_Opposite }; // Options for subplots (see BeginSubplot) enum ImPlotSubplotFlags_ { ImPlotSubplotFlags_None = 0, // default ImPlotSubplotFlags_NoTitle = 1 << 0, // the subplot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MySubplot") ImPlotSubplotFlags_NoLegend = 1 << 1, // the legend will not be displayed (only applicable if ImPlotSubplotFlags_ShareItems is enabled) ImPlotSubplotFlags_NoMenus = 1 << 2, // the user will not be able to open context menus with right-click ImPlotSubplotFlags_NoResize = 1 << 3, // resize splitters between subplot cells will be not be provided ImPlotSubplotFlags_NoAlign = 1 << 4, // subplot edges will not be aligned vertically or horizontally ImPlotSubplotFlags_ShareItems = 1 << 5, // items across all subplots will be shared and rendered into a single legend entry ImPlotSubplotFlags_LinkRows = 1 << 6, // link the y-axis limits of all plots in each row (does not apply to auxiliary axes) ImPlotSubplotFlags_LinkCols = 1 << 7, // link the x-axis limits of all plots in each column (does not apply to auxiliary axes) ImPlotSubplotFlags_LinkAllX = 1 << 8, // link the x-axis limits in every plot in the subplot (does not apply to auxiliary axes) ImPlotSubplotFlags_LinkAllY = 1 << 9, // link the y-axis limits in every plot in the subplot (does not apply to auxiliary axes) ImPlotSubplotFlags_ColMajor = 1 << 10 // subplots are added in column major order instead of the default row major order }; // Options for legends (see SetupLegend) enum ImPlotLegendFlags_ { ImPlotLegendFlags_None = 0, // default ImPlotLegendFlags_NoButtons = 1 << 0, // legend icons will not function as hide/show buttons ImPlotLegendFlags_NoHighlightItem = 1 << 1, // plot items will not be highlighted when their legend entry is hovered ImPlotLegendFlags_NoHighlightAxis = 1 << 2, // axes will not be highlighted when legend entries are hovered (only relevant if x/y-axis count > 1) ImPlotLegendFlags_NoMenus = 1 << 3, // the user will not be able to open context menus with right-click ImPlotLegendFlags_Outside = 1 << 4, // legend will be rendered outside of the plot area ImPlotLegendFlags_Horizontal = 1 << 5, // legend entries will be displayed horizontally ImPlotLegendFlags_Sort = 1 << 6, // legend entries will be displayed in alphabetical order }; // Options for mouse hover text (see SetupMouseText) enum ImPlotMouseTextFlags_ { ImPlotMouseTextFlags_None = 0, // default ImPlotMouseTextFlags_NoAuxAxes = 1 << 0, // only show the mouse position for primary axes ImPlotMouseTextFlags_NoFormat = 1 << 1, // axes label formatters won't be used to render text ImPlotMouseTextFlags_ShowAlways = 1 << 2, // always display mouse position even if plot not hovered }; // Options for DragPoint, DragLine, DragRect enum ImPlotDragToolFlags_ { ImPlotDragToolFlags_None = 0, // default ImPlotDragToolFlags_NoCursors = 1 << 0, // drag tools won't change cursor icons when hovered or held ImPlotDragToolFlags_NoFit = 1 << 1, // the drag tool won't be considered for plot fits ImPlotDragToolFlags_NoInputs = 1 << 2, // lock the tool from user inputs ImPlotDragToolFlags_Delayed = 1 << 3, // tool rendering will be delayed one frame; useful when applying position-constraints }; // Flags for ColormapScale enum ImPlotColormapScaleFlags_ { ImPlotColormapScaleFlags_None = 0, // default ImPlotColormapScaleFlags_NoLabel = 1 << 0, // the colormap axis label will not be displayed ImPlotColormapScaleFlags_Opposite = 1 << 1, // render the colormap label and tick labels on the opposite side ImPlotColormapScaleFlags_Invert = 1 << 2, // invert the colormap bar and axis scale (this only affects rendering; if you only want to reverse the scale mapping, make scale_min > scale_max) }; // Flags for ANY PlotX function enum ImPlotItemFlags_ { ImPlotItemFlags_None = 0, ImPlotItemFlags_NoLegend = 1 << 0, // the item won't have a legend entry displayed ImPlotItemFlags_NoFit = 1 << 1, // the item won't be considered for plot fits }; // Flags for PlotLine enum ImPlotLineFlags_ { ImPlotLineFlags_None = 0, // default ImPlotLineFlags_Segments = 1 << 10, // a line segment will be rendered from every two consecutive points ImPlotLineFlags_Loop = 1 << 11, // the last and first point will be connected to form a closed loop ImPlotLineFlags_SkipNaN = 1 << 12, // NaNs values will be skipped instead of rendered as missing data ImPlotLineFlags_NoClip = 1 << 13, // markers (if displayed) on the edge of a plot will not be clipped ImPlotLineFlags_Shaded = 1 << 14, // a filled region between the line and horizontal origin will be rendered; use PlotShaded for more advanced cases }; // Flags for PlotScatter enum ImPlotScatterFlags_ { ImPlotScatterFlags_None = 0, // default ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped }; // Flags for PlotStairs enum ImPlotStairsFlags_ { ImPlotStairsFlags_None = 0, // default ImPlotStairsFlags_PreStep = 1 << 10, // the y value is continued constantly to the left from every x position, i.e. the interval (x[i-1], x[i]] has the value y[i] ImPlotStairsFlags_Shaded = 1 << 11 // a filled region between the stairs and horizontal origin will be rendered; use PlotShaded for more advanced cases }; // Flags for PlotShaded (placeholder) enum ImPlotShadedFlags_ { ImPlotShadedFlags_None = 0 // default }; // Flags for PlotBars enum ImPlotBarsFlags_ { ImPlotBarsFlags_None = 0, // default ImPlotBarsFlags_Horizontal = 1 << 10, // bars will be rendered horizontally on the current y-axis }; // Flags for PlotBarGroups enum ImPlotBarGroupsFlags_ { ImPlotBarGroupsFlags_None = 0, // default ImPlotBarGroupsFlags_Horizontal = 1 << 10, // bar groups will be rendered horizontally on the current y-axis ImPlotBarGroupsFlags_Stacked = 1 << 11, // items in a group will be stacked on top of each other }; // Flags for PlotErrorBars enum ImPlotErrorBarsFlags_ { ImPlotErrorBarsFlags_None = 0, // default ImPlotErrorBarsFlags_Horizontal = 1 << 10, // error bars will be rendered horizontally on the current y-axis }; // Flags for PlotStems enum ImPlotStemsFlags_ { ImPlotStemsFlags_None = 0, // default ImPlotStemsFlags_Horizontal = 1 << 10, // stems will be rendered horizontally on the current y-axis }; // Flags for PlotInfLines enum ImPlotInfLinesFlags_ { ImPlotInfLinesFlags_None = 0, // default ImPlotInfLinesFlags_Horizontal = 1 << 10 // lines will be rendered horizontally on the current y-axis }; // Flags for PlotPieChart enum ImPlotPieChartFlags_ { ImPlotPieChartFlags_None = 0, // default ImPlotPieChartFlags_Normalize = 1 << 10, // force normalization of pie chart values (i.e. always make a full circle if sum < 0) ImPlotPieChartFlags_IgnoreHidden = 1 << 11 // ignore hidden slices when drawing the pie chart (as if they were not there) }; // Flags for PlotHeatmap enum ImPlotHeatmapFlags_ { ImPlotHeatmapFlags_None = 0, // default ImPlotHeatmapFlags_ColMajor = 1 << 10, // data will be read in column major order }; // Flags for PlotHistogram and PlotHistogram2D enum ImPlotHistogramFlags_ { ImPlotHistogramFlags_None = 0, // default ImPlotHistogramFlags_Horizontal = 1 << 10, // histogram bars will be rendered horizontally (not supported by PlotHistogram2D) ImPlotHistogramFlags_Cumulative = 1 << 11, // each bin will contain its count plus the counts of all previous bins (not supported by PlotHistogram2D) ImPlotHistogramFlags_Density = 1 << 12, // counts will be normalized, i.e. the PDF will be visualized, or the CDF will be visualized if Cumulative is also set ImPlotHistogramFlags_NoOutliers = 1 << 13, // exclude values outside the specifed histogram range from the count toward normalizing and cumulative counts ImPlotHistogramFlags_ColMajor = 1 << 14 // data will be read in column major order (not supported by PlotHistogram) }; // Flags for PlotDigital (placeholder) enum ImPlotDigitalFlags_ { ImPlotDigitalFlags_None = 0 // default }; // Flags for PlotImage (placeholder) enum ImPlotImageFlags_ { ImPlotImageFlags_None = 0 // default }; // Flags for PlotText enum ImPlotTextFlags_ { ImPlotTextFlags_None = 0, // default ImPlotTextFlags_Vertical = 1 << 10 // text will be rendered vertically }; // Flags for PlotDummy (placeholder) enum ImPlotDummyFlags_ { ImPlotDummyFlags_None = 0 // default }; // Represents a condition for SetupAxisLimits etc. (same as ImGuiCond, but we only support a subset of those enums) enum ImPlotCond_ { ImPlotCond_None = ImGuiCond_None, // No condition (always set the variable), same as _Always ImPlotCond_Always = ImGuiCond_Always, // No condition (always set the variable) ImPlotCond_Once = ImGuiCond_Once, // Set the variable once per runtime session (only the first call will succeed) }; // Plot styling colors. enum ImPlotCol_ { // item styling colors ImPlotCol_Line, // plot line/outline color (defaults to next unused color in current colormap) ImPlotCol_Fill, // plot fill color for bars (defaults to the current line color) ImPlotCol_MarkerOutline, // marker outline color (defaults to the current line color) ImPlotCol_MarkerFill, // marker fill color (defaults to the current line color) ImPlotCol_ErrorBar, // error bar color (defaults to ImGuiCol_Text) // plot styling colors ImPlotCol_FrameBg, // plot frame background color (defaults to ImGuiCol_FrameBg) ImPlotCol_PlotBg, // plot area background color (defaults to ImGuiCol_WindowBg) ImPlotCol_PlotBorder, // plot area border color (defaults to ImGuiCol_Border) ImPlotCol_LegendBg, // legend background color (defaults to ImGuiCol_PopupBg) ImPlotCol_LegendBorder, // legend border color (defaults to ImPlotCol_PlotBorder) ImPlotCol_LegendText, // legend text color (defaults to ImPlotCol_InlayText) ImPlotCol_TitleText, // plot title text color (defaults to ImGuiCol_Text) ImPlotCol_InlayText, // color of text appearing inside of plots (defaults to ImGuiCol_Text) ImPlotCol_AxisText, // axis label and tick lables color (defaults to ImGuiCol_Text) ImPlotCol_AxisGrid, // axis grid color (defaults to 25% ImPlotCol_AxisText) ImPlotCol_AxisTick, // axis tick color (defaults to AxisGrid) ImPlotCol_AxisBg, // background color of axis hover region (defaults to transparent) ImPlotCol_AxisBgHovered, // axis hover color (defaults to ImGuiCol_ButtonHovered) ImPlotCol_AxisBgActive, // axis active color (defaults to ImGuiCol_ButtonActive) ImPlotCol_Selection, // box-selection color (defaults to yellow) ImPlotCol_Crosshairs, // crosshairs color (defaults to ImPlotCol_PlotBorder) ImPlotCol_COUNT }; // Plot styling variables. enum ImPlotStyleVar_ { // item styling variables ImPlotStyleVar_LineWeight, // float, plot item line weight in pixels ImPlotStyleVar_Marker, // int, marker specification ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") ImPlotStyleVar_MarkerWeight, // float, plot outline weight of markers in pixels ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels ImPlotStyleVar_DigitalBitGap, // float, digital channels bit padding gap in pixels // plot styling variables ImPlotStyleVar_PlotBorderSize, // float, thickness of border around plot area ImPlotStyleVar_MinorAlpha, // float, alpha multiplier applied to minor axis grid lines ImPlotStyleVar_MajorTickLen, // ImVec2, major tick lengths for X and Y axes ImPlotStyleVar_MinorTickLen, // ImVec2, minor tick lengths for X and Y axes ImPlotStyleVar_MajorTickSize, // ImVec2, line thickness of major ticks ImPlotStyleVar_MinorTickSize, // ImVec2, line thickness of minor ticks ImPlotStyleVar_MajorGridSize, // ImVec2, line thickness of major grid lines ImPlotStyleVar_MinorGridSize, // ImVec2, line thickness of minor grid lines ImPlotStyleVar_PlotPadding, // ImVec2, padding between widget frame and plot area, labels, or outside legends (i.e. main padding) ImPlotStyleVar_LabelPadding, // ImVec2, padding between axes labels, tick labels, and plot edge ImPlotStyleVar_LegendPadding, // ImVec2, legend padding from plot edges ImPlotStyleVar_LegendInnerPadding, // ImVec2, legend inner padding from legend edges ImPlotStyleVar_LegendSpacing, // ImVec2, spacing between legend entries ImPlotStyleVar_MousePosPadding, // ImVec2, padding between plot edge and interior info text ImPlotStyleVar_AnnotationPadding, // ImVec2, text padding around annotation labels ImPlotStyleVar_FitPadding, // ImVec2, additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) ImPlotStyleVar_PlotDefaultSize, // ImVec2, default size used when ImVec2(0,0) is passed to BeginPlot ImPlotStyleVar_PlotMinSize, // ImVec2, minimum size plot frame can be when shrunk ImPlotStyleVar_COUNT }; // Axis scale enum ImPlotScale_ { ImPlotScale_Linear = 0, // default linear scale ImPlotScale_Time, // date/time scale ImPlotScale_Log10, // base 10 logartithmic scale ImPlotScale_SymLog, // symmetric log scale }; // Marker specifications. enum ImPlotMarker_ { ImPlotMarker_None = -1, // no marker ImPlotMarker_Circle, // a circle marker (default) ImPlotMarker_Square, // a square maker ImPlotMarker_Diamond, // a diamond marker ImPlotMarker_Up, // an upward-pointing triangle marker ImPlotMarker_Down, // an downward-pointing triangle marker ImPlotMarker_Left, // an leftward-pointing triangle marker ImPlotMarker_Right, // an rightward-pointing triangle marker ImPlotMarker_Cross, // a cross marker (not fillable) ImPlotMarker_Plus, // a plus marker (not fillable) ImPlotMarker_Asterisk, // a asterisk marker (not fillable) ImPlotMarker_COUNT }; // Built-in colormaps enum ImPlotColormap_ { ImPlotColormap_Deep = 0, // a.k.a. seaborn deep (qual=true, n=10) (default) ImPlotColormap_Dark = 1, // a.k.a. matplotlib "Set1" (qual=true, n=9 ) ImPlotColormap_Pastel = 2, // a.k.a. matplotlib "Pastel1" (qual=true, n=9 ) ImPlotColormap_Paired = 3, // a.k.a. matplotlib "Paired" (qual=true, n=12) ImPlotColormap_Viridis = 4, // a.k.a. matplotlib "viridis" (qual=false, n=11) ImPlotColormap_Plasma = 5, // a.k.a. matplotlib "plasma" (qual=false, n=11) ImPlotColormap_Hot = 6, // a.k.a. matplotlib/MATLAB "hot" (qual=false, n=11) ImPlotColormap_Cool = 7, // a.k.a. matplotlib/MATLAB "cool" (qual=false, n=11) ImPlotColormap_Pink = 8, // a.k.a. matplotlib/MATLAB "pink" (qual=false, n=11) ImPlotColormap_Jet = 9, // a.k.a. MATLAB "jet" (qual=false, n=11) ImPlotColormap_Twilight = 10, // a.k.a. matplotlib "twilight" (qual=false, n=11) ImPlotColormap_RdBu = 11, // red/blue, Color Brewer (qual=false, n=11) ImPlotColormap_BrBG = 12, // brown/blue-green, Color Brewer (qual=false, n=11) ImPlotColormap_PiYG = 13, // pink/yellow-green, Color Brewer (qual=false, n=11) ImPlotColormap_Spectral = 14, // color spectrum, Color Brewer (qual=false, n=11) ImPlotColormap_Greys = 15, // white/black (qual=false, n=2 ) }; // Used to position items on a plot (e.g. legends, labels, etc.) enum ImPlotLocation_ { ImPlotLocation_Center = 0, // center-center ImPlotLocation_North = 1 << 0, // top-center ImPlotLocation_South = 1 << 1, // bottom-center ImPlotLocation_West = 1 << 2, // center-left ImPlotLocation_East = 1 << 3, // center-right ImPlotLocation_NorthWest = ImPlotLocation_North | ImPlotLocation_West, // top-left ImPlotLocation_NorthEast = ImPlotLocation_North | ImPlotLocation_East, // top-right ImPlotLocation_SouthWest = ImPlotLocation_South | ImPlotLocation_West, // bottom-left ImPlotLocation_SouthEast = ImPlotLocation_South | ImPlotLocation_East // bottom-right }; // Enums for different automatic histogram binning methods (k = bin count or w = bin width) enum ImPlotBin_ { ImPlotBin_Sqrt = -1, // k = sqrt(n) ImPlotBin_Sturges = -2, // k = 1 + log2(n) ImPlotBin_Rice = -3, // k = 2 * cbrt(n) ImPlotBin_Scott = -4, // w = 3.49 * sigma / cbrt(n) }; // Double precision version of ImVec2 used by ImPlot. Extensible by end users. IM_MSVC_RUNTIME_CHECKS_OFF struct ImPlotPoint { double x, y; constexpr ImPlotPoint() : x(0.0), y(0.0) { } constexpr ImPlotPoint(double _x, double _y) : x(_x), y(_y) { } constexpr ImPlotPoint(const ImVec2& p) : x((double)p.x), y((double)p.y) { } double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return ((double*)(void*)(char*)this)[idx]; } double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return ((const double*)(const void*)(const char*)this)[idx]; } #ifdef IMPLOT_POINT_CLASS_EXTRA IMPLOT_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h // to convert back and forth between your math types and ImPlotPoint. #endif }; IM_MSVC_RUNTIME_CHECKS_RESTORE // Range defined by a min/max value. struct ImPlotRange { double Min, Max; constexpr ImPlotRange() : Min(0.0), Max(0.0) { } constexpr ImPlotRange(double _min, double _max) : Min(_min), Max(_max) { } bool Contains(double value) const { return value >= Min && value <= Max; } double Size() const { return Max - Min; } double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; } }; // Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max(). struct ImPlotRect { ImPlotRange X, Y; constexpr ImPlotRect() : X(0.0,0.0), Y(0.0,0.0) { } constexpr ImPlotRect(double x_min, double x_max, double y_min, double y_max) : X(x_min, x_max), Y(y_min, y_max) { } bool Contains(const ImPlotPoint& p) const { return Contains(p.x, p.y); } bool Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); } ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); } ImPlotPoint Clamp(const ImPlotPoint& p) { return Clamp(p.x, p.y); } ImPlotPoint Clamp(double x, double y) { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); } ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); } ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); } }; // Plot style structure struct ImPlotStyle { // item styling variables float LineWeight; // = 1, item line weight in pixels int Marker; // = ImPlotMarker_None, marker specification float MarkerSize; // = 4, marker size in pixels (roughly the marker's "radius") float MarkerWeight; // = 1, outline weight of markers in pixels float FillAlpha; // = 1, alpha modifier applied to plot fills float ErrorBarSize; // = 5, error bar whisker width in pixels float ErrorBarWeight; // = 1.5, error bar whisker weight in pixels float DigitalBitHeight; // = 8, digital channels bit height (at y = 1.0f) in pixels float DigitalBitGap; // = 4, digital channels bit padding gap in pixels // plot styling variables float PlotBorderSize; // = 1, line thickness of border around plot area float MinorAlpha; // = 0.25 alpha multiplier applied to minor axis grid lines ImVec2 MajorTickLen; // = 10,10 major tick lengths for X and Y axes ImVec2 MinorTickLen; // = 5,5 minor tick lengths for X and Y axes ImVec2 MajorTickSize; // = 1,1 line thickness of major ticks ImVec2 MinorTickSize; // = 1,1 line thickness of minor ticks ImVec2 MajorGridSize; // = 1,1 line thickness of major grid lines ImVec2 MinorGridSize; // = 1,1 line thickness of minor grid lines ImVec2 PlotPadding; // = 10,10 padding between widget frame and plot area, labels, or outside legends (i.e. main padding) ImVec2 LabelPadding; // = 5,5 padding between axes labels, tick labels, and plot edge ImVec2 LegendPadding; // = 10,10 legend padding from plot edges ImVec2 LegendInnerPadding; // = 5,5 legend inner padding from legend edges ImVec2 LegendSpacing; // = 5,0 spacing between legend entries ImVec2 MousePosPadding; // = 10,10 padding between plot edge and interior mouse location text ImVec2 AnnotationPadding; // = 2,2 text padding around annotation labels ImVec2 FitPadding; // = 0,0 additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) ImVec2 PlotDefaultSize; // = 400,300 default size used when ImVec2(0,0) is passed to BeginPlot ImVec2 PlotMinSize; // = 200,150 minimum size plot frame can be when shrunk // style colors ImVec4 Colors[ImPlotCol_COUNT]; // Array of styling colors. Indexable with ImPlotCol_ enums. // colormap ImPlotColormap Colormap; // The current colormap. Set this to either an ImPlotColormap_ enum or an index returned by AddColormap. // settings/flags bool UseLocalTime; // = false, axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled bool UseISO8601; // = false, dates will be formatted according to ISO 8601 where applicable (e.g. YYYY-MM-DD, YYYY-MM, --MM-DD, etc.) bool Use24HourClock; // = false, times will be formatted using a 24 hour clock IMPLOT_API ImPlotStyle(); }; // Support for legacy versions #if (IMGUI_VERSION_NUM < 18716) // Renamed in 1.88 #define ImGuiMod_None 0 #define ImGuiMod_Ctrl ImGuiKeyModFlags_Ctrl #define ImGuiMod_Shift ImGuiKeyModFlags_Shift #define ImGuiMod_Alt ImGuiKeyModFlags_Alt #define ImGuiMod_Super ImGuiKeyModFlags_Super #elif (IMGUI_VERSION_NUM < 18823) // Renamed in 1.89, sorry #define ImGuiMod_None 0 #define ImGuiMod_Ctrl ImGuiModFlags_Ctrl #define ImGuiMod_Shift ImGuiModFlags_Shift #define ImGuiMod_Alt ImGuiModFlags_Alt #define ImGuiMod_Super ImGuiModFlags_Super #endif // Input mapping structure. Default values listed. See also MapInputDefault, MapInputReverse. struct ImPlotInputMap { ImGuiMouseButton Pan; // LMB enables panning when held, int PanMod; // none optional modifier that must be held for panning/fitting ImGuiMouseButton Fit; // LMB initiates fit when double clicked ImGuiMouseButton Select; // RMB begins box selection when pressed and confirms selection when released ImGuiMouseButton SelectCancel; // LMB cancels active box selection when pressed; cannot be same as Select int SelectMod; // none optional modifier that must be held for box selection int SelectHorzMod; // Alt expands active box selection horizontally to plot edge when held int SelectVertMod; // Shift expands active box selection vertically to plot edge when held ImGuiMouseButton Menu; // RMB opens context menus (if enabled) when clicked int OverrideMod; // Ctrl when held, all input is ignored; used to enable axis/plots as DND sources int ZoomMod; // none optional modifier that must be held for scroll wheel zooming float ZoomRate; // 0.1f zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click); make negative to invert IMPLOT_API ImPlotInputMap(); }; //----------------------------------------------------------------------------- // [SECTION] Callbacks //----------------------------------------------------------------------------- // Callback signature for axis tick label formatter. typedef int (*ImPlotFormatter)(double value, char* buff, int size, void* user_data); // Callback signature for data getter. typedef ImPlotPoint (*ImPlotGetter)(int idx, void* user_data); // Callback signature for axis transform. typedef double (*ImPlotTransform)(double value, void* user_data); namespace ImPlot { //----------------------------------------------------------------------------- // [SECTION] Contexts //----------------------------------------------------------------------------- // Creates a new ImPlot context. Call this after ImGui::CreateContext. IMPLOT_API ImPlotContext* CreateContext(); // Destroys an ImPlot context. Call this before ImGui::DestroyContext. nullptr = destroy current context. IMPLOT_API void DestroyContext(ImPlotContext* ctx = nullptr); // Returns the current ImPlot context. nullptr if no context has ben set. IMPLOT_API ImPlotContext* GetCurrentContext(); // Sets the current ImPlot context. IMPLOT_API void SetCurrentContext(ImPlotContext* ctx); // Sets the current **ImGui** context. This is ONLY necessary if you are compiling // ImPlot as a DLL (not recommended) separate from your ImGui compilation. It // sets the global variable GImGui, which is not shared across DLL boundaries. // See GImGui documentation in imgui.cpp for more details. IMPLOT_API void SetImGuiContext(ImGuiContext* ctx); //----------------------------------------------------------------------------- // [SECTION] Begin/End Plot //----------------------------------------------------------------------------- // Starts a 2D plotting context. If this function returns true, EndPlot() MUST // be called! You are encouraged to use the following convention: // // if (BeginPlot(...)) { // PlotLine(...); // ... // EndPlot(); // } // // Important notes: // // - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID // collisions or don't want to display a title in the plot, use double hashes // (e.g. "MyPlot##HiddenIdText" or "##NoTitle"). // - #size is the **frame** size of the plot widget, not the plot area. The default // size of plots (i.e. when ImVec2(0,0)) can be modified in your ImPlotStyle. IMPLOT_API bool BeginPlot(const char* title_id, const ImVec2& size=ImVec2(-1,0), ImPlotFlags flags=0); // Only call EndPlot() if BeginPlot() returns true! Typically called at the end // of an if statement conditioned on BeginPlot(). See example above. IMPLOT_API void EndPlot(); //----------------------------------------------------------------------------- // [SECTION] Begin/End Subplots //----------------------------------------------------------------------------- // Starts a subdivided plotting context. If the function returns true, // EndSubplots() MUST be called! Call BeginPlot/EndPlot AT MOST [rows*cols] // times in between the begining and end of the subplot context. Plots are // added in row major order. // // Example: // // if (BeginSubplots("My Subplot",2,3,ImVec2(800,400)) { // for (int i = 0; i < 6; ++i) { // if (BeginPlot(...)) { // ImPlot::PlotLine(...); // ... // EndPlot(); // } // } // EndSubplots(); // } // // Produces: // // [0] | [1] | [2] // ----|-----|---- // [3] | [4] | [5] // // Important notes: // // - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID // collisions or don't want to display a title in the plot, use double hashes // (e.g. "MySubplot##HiddenIdText" or "##NoTitle"). // - #rows and #cols must be greater than 0. // - #size is the size of the entire grid of subplots, not the individual plots // - #row_ratios and #col_ratios must have AT LEAST #rows and #cols elements, // respectively. These are the sizes of the rows and columns expressed in ratios. // If the user adjusts the dimensions, the arrays are updated with new ratios. // // Important notes regarding BeginPlot from inside of BeginSubplots: // // - The #title_id parameter of _BeginPlot_ (see above) does NOT have to be // unique when called inside of a subplot context. Subplot IDs are hashed // for your convenience so you don't have call PushID or generate unique title // strings. Simply pass an empty string to BeginPlot unless you want to title // each subplot. // - The #size parameter of _BeginPlot_ (see above) is ignored when inside of a // subplot context. The actual size of the subplot will be based on the // #size value you pass to _BeginSubplots_ and #row/#col_ratios if provided. IMPLOT_API bool BeginSubplots(const char* title_id, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags = 0, float* row_ratios = nullptr, float* col_ratios = nullptr); // Only call EndSubplots() if BeginSubplots() returns true! Typically called at the end // of an if statement conditioned on BeginSublots(). See example above. IMPLOT_API void EndSubplots(); //----------------------------------------------------------------------------- // [SECTION] Setup //----------------------------------------------------------------------------- // The following API allows you to setup and customize various aspects of the // current plot. The functions should be called immediately after BeginPlot // and before any other API calls. Typical usage is as follows: // if (BeginPlot(...)) { 1) begin a new plot // SetupAxis(ImAxis_X1, "My X-Axis"); 2) make Setup calls // SetupAxis(ImAxis_Y1, "My Y-Axis"); // SetupLegend(ImPlotLocation_North); // ... // SetupFinish(); 3) [optional] explicitly finish setup // PlotLine(...); 4) plot items // ... // EndPlot(); 5) end the plot // } // // Important notes: // // - Always call Setup code at the top of your BeginPlot conditional statement. // - Setup is locked once you start plotting or explicitly call SetupFinish. // Do NOT call Setup code after you begin plotting or after you make // any non-Setup API calls (e.g. utils like PlotToPixels also lock Setup) // - Calling SetupFinish is OPTIONAL, but probably good practice. If you do not // call it yourself, then the first subsequent plotting or utility function will // call it for you. // Enables an axis or sets the label and/or flags for an existing axis. Leave #label = nullptr for no label. IMPLOT_API void SetupAxis(ImAxis axis, const char* label=nullptr, ImPlotAxisFlags flags=0); // Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. Inversion with v_min > v_max is not supported; use SetupAxisLimits instead. IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); // Links an axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot. IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max); // Sets the format of numeric axis labels via formater specifier (default="%g"). Formated values will be double (i.e. use %f). IMPLOT_API void SetupAxisFormat(ImAxis axis, const char* fmt); // Sets the format of numeric axis labels via formatter callback. Given #value, write a label into #buff. Optionally pass user data. IMPLOT_API void SetupAxisFormat(ImAxis axis, ImPlotFormatter formatter, void* data=nullptr); // Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true. IMPLOT_API void SetupAxisTicks(ImAxis axis, const double* values, int n_ticks, const char* const labels[]=nullptr, bool keep_default=false); // Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true. IMPLOT_API void SetupAxisTicks(ImAxis axis, double v_min, double v_max, int n_ticks, const char* const labels[]=nullptr, bool keep_default=false); // Sets an axis' scale using built-in options. IMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotScale scale); // Sets an axis' scale using user supplied forward and inverse transfroms. IMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotTransform forward, ImPlotTransform inverse, void* data=nullptr); // Sets an axis' limits constraints. IMPLOT_API void SetupAxisLimitsConstraints(ImAxis axis, double v_min, double v_max); // Sets an axis' zoom constraints. IMPLOT_API void SetupAxisZoomConstraints(ImAxis axis, double z_min, double z_max); // Sets the label and/or flags for primary X and Y axes (shorthand for two calls to SetupAxis). IMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags=0, ImPlotAxisFlags y_flags=0); // Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); // Sets up the plot legend. This can also be called immediately after BeginSubplots when using ImPlotSubplotFlags_ShareItems. IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0); // Set the location of the current plot's mouse position text (default = South|East). IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0); // Explicitly finalize plot setup. Once you call this, you cannot make anymore Setup calls for the current plot! // Note that calling this function is OPTIONAL; it will be called by the first subsequent setup-locking API call. IMPLOT_API void SetupFinish(); //----------------------------------------------------------------------------- // [SECTION] SetNext //----------------------------------------------------------------------------- // Though you should default to the `Setup` API above, there are some scenarios // where (re)configuring a plot or axis before `BeginPlot` is needed (e.g. if // using a preceding button or slider widget to change the plot limits). In // this case, you can use the `SetNext` API below. While this is not as feature // rich as the Setup API, most common needs are provided. These functions can be // called anwhere except for inside of `Begin/EndPlot`. For example: // if (ImGui::Button("Center Plot")) // ImPlot::SetNextPlotLimits(-1,1,-1,1); // if (ImPlot::BeginPlot(...)) { // ... // ImPlot::EndPlot(); // } // // Important notes: // // - You must still enable non-default axes with SetupAxis for these functions // to work properly. // Sets an upcoming axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. IMPLOT_API void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); // Links an upcoming axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot! IMPLOT_API void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max); // Set an upcoming axis to auto fit to its data. IMPLOT_API void SetNextAxisToFit(ImAxis axis); // Sets the upcoming primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). IMPLOT_API void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); // Sets all upcoming axes to auto fit to their data. IMPLOT_API void SetNextAxesToFit(); //----------------------------------------------------------------------------- // [SECTION] Plot Items //----------------------------------------------------------------------------- // The main plotting API is provied below. Call these functions between // Begin/EndPlot and after any Setup API calls. Each plots data on the current // x and y axes, which can be changed with `SetAxis/Axes`. // // The templated functions are explicitly instantiated in implot_items.cpp. // They are not intended to be used generically with custom types. You will get // a linker error if you try! All functions support the following scalar types: // // float, double, ImS8, ImU8, ImS16, ImU16, ImS32, ImU32, ImS64, ImU64 // // // If you need to plot custom or non-homogenous data you have a few options: // // 1. If your data is a simple struct/class (e.g. Vector2f), you can use striding. // This is the most performant option if applicable. // // struct Vector2f { float X, Y; }; // ... // Vector2f data[42]; // ImPlot::PlotLine("line", &data[0].x, &data[0].y, 42, 0, 0, sizeof(Vector2f)); // // 2. Write a custom getter C function or C++ lambda and pass it and optionally your data to // an ImPlot function post-fixed with a G (e.g. PlotScatterG). This has a slight performance // cost, but probably not enough to worry about unless your data is very large. Examples: // // ImPlotPoint MyDataGetter(void* data, int idx) { // MyData* my_data = (MyData*)data; // ImPlotPoint p; // p.x = my_data->GetTime(idx); // p.y = my_data->GetValue(idx); // return p // } // ... // auto my_lambda = [](int idx, void*) { // double t = idx / 999.0; // return ImPlotPoint(t, 0.5+0.5*std::sin(2*PI*10*t)); // }; // ... // if (ImPlot::BeginPlot("MyPlot")) { // MyData my_data; // ImPlot::PlotScatterG("scatter", MyDataGetter, &my_data, my_data.Size()); // ImPlot::PlotLineG("line", my_lambda, nullptr, 1000); // ImPlot::EndPlot(); // } // // NB: All types are converted to double before plotting. You may lose information // if you try plotting extremely large 64-bit integral types. Proceed with caution! // Plots a standard 2D line plot. IMPLOT_TMP void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotLineG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotLineFlags flags=0); // Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle. IMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotScatterFlags flags=0); // Plots a a stairstep graph. The y value is continued constantly to the right from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i] IMPLOT_TMP void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotStairsG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotStairsFlags flags=0); // Plots a shaded (filled) region between two lines, or a line and a horizontal reference. Set yref to +/-INFINITY for infinite fill extents. IMPLOT_TMP void PlotShaded(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double xstart=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double yref=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotShadedG(const char* label_id, ImPlotGetter getter1, void* data1, ImPlotGetter getter2, void* data2, int count, ImPlotShadedFlags flags=0); // Plots a bar graph. Vertical by default. #bar_size and #shift are in plot units. IMPLOT_TMP void PlotBars(const char* label_id, const T* values, int count, double bar_size=0.67, double shift=0, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double bar_size, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotBarsG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_size, ImPlotBarsFlags flags=0); // Plots a group of bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements. IMPLOT_TMP void PlotBarGroups(const char* const label_ids[], const T* values, int item_count, int group_count, double group_size=0.67, double shift=0, ImPlotBarGroupsFlags flags=0); // Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot. IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T)); // Plots stems. Vertical by default. IMPLOT_TMP void PlotStems(const char* label_id, const T* values, int count, double ref=0, double scale=1, double start=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double ref=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T)); // Plots infinite vertical or horizontal lines (e.g. for references or asymptotes). IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T)); // Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to nullptr for no labels. IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data=nullptr, double angle0=90, ImPlotPieChartFlags flags=0); IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. IMPLOT_TMP double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, double bar_scale=1.0, ImPlotRange range=ImPlotRange(), ImPlotHistogramFlags flags=0); // Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #range is left unspecified, the min/max of // #xs an #ys will be used as the ranges. Otherwise, outlier values outside of range are not binned. The largest bin count or density is returned. IMPLOT_TMP double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, ImPlotRect range=ImPlotRect(), ImPlotHistogramFlags flags=0); // Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot. IMPLOT_TMP void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, ImPlotDigitalFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotDigitalFlags flags=0); // Plots an axis-aligned image. #bounds_min/bounds_max are in plot coordinates (y-up) and #uv0/uv1 are in texture coordinates (y-down). #ifdef IMGUI_HAS_TEXTURES IMPLOT_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), ImPlotImageFlags flags = 0); #else IMPLOT_API void PlotImage(const char* label_id, ImTextureID tex_ref, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1), ImPlotImageFlags flags=0); #endif // Plots a centered text label at point x,y with an optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...). IMPLOT_API void PlotText(const char* text, double x, double y, const ImVec2& pix_offset=ImVec2(0,0), ImPlotTextFlags flags=0); // Plots a dummy item (i.e. adds a legend entry colored by ImPlotCol_Line) IMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0); //----------------------------------------------------------------------------- // [SECTION] Plot Tools //----------------------------------------------------------------------------- // The following can be used to render interactive elements and/or annotations. // Like the item plotting functions above, they apply to the current x and y // axes, which can be changed with `SetAxis/SetAxes`. These functions return true // when user interaction causes the provided coordinates to change. Additional // user interactions can be retrieved through the optional output parameters. // Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr); // Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr); // Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr); // Shows a draggable and resizeable rectangle. IMPLOT_API bool DragRect(int id, double* x1, double* y1, double* x2, double* y2, const ImVec4& col, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr); // Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top. IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false); IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, ...) IM_FMTARGS(6); IMPLOT_API void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, va_list args) IM_FMTLIST(6); // Shows a x-axis tag at the specified coordinate value. IMPLOT_API void TagX(double x, const ImVec4& col, bool round = false); IMPLOT_API void TagX(double x, const ImVec4& col, const char* fmt, ...) IM_FMTARGS(3); IMPLOT_API void TagXV(double x, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3); // Shows a y-axis tag at the specified coordinate value. IMPLOT_API void TagY(double y, const ImVec4& col, bool round = false); IMPLOT_API void TagY(double y, const ImVec4& col, const char* fmt, ...) IM_FMTARGS(3); IMPLOT_API void TagYV(double y, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3); //----------------------------------------------------------------------------- // [SECTION] Plot Utils //----------------------------------------------------------------------------- // Select which axis/axes will be used for subsequent plot elements. IMPLOT_API void SetAxis(ImAxis axis); IMPLOT_API void SetAxes(ImAxis x_axis, ImAxis y_axis); // Convert pixels to a position in the current plot's coordinate system. Passing IMPLOT_AUTO uses the current axes. IMPLOT_API ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); IMPLOT_API ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Convert a position in the current plot's coordinate system to pixels. Passing IMPLOT_AUTO uses the current axes. IMPLOT_API ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); IMPLOT_API ImVec2 PlotToPixels(double x, double y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Get the current Plot position (top-left) in pixels. IMPLOT_API ImVec2 GetPlotPos(); // Get the curent Plot size in pixels. IMPLOT_API ImVec2 GetPlotSize(); // Returns the mouse position in x,y coordinates of the current plot. Passing IMPLOT_AUTO uses the current axes. IMPLOT_API ImPlotPoint GetPlotMousePos(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Returns the current plot axis range. IMPLOT_API ImPlotRect GetPlotLimits(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Returns true if the plot area in the current plot is hovered. IMPLOT_API bool IsPlotHovered(); // Returns true if the axis label area in the current plot is hovered. IMPLOT_API bool IsAxisHovered(ImAxis axis); // Returns true if the bounding frame of a subplot is hovered. IMPLOT_API bool IsSubplotsHovered(); // Returns true if the current plot is being box selected. IMPLOT_API bool IsPlotSelected(); // Returns the current plot box selection bounds. Passing IMPLOT_AUTO uses the current axes. IMPLOT_API ImPlotRect GetPlotSelection(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Cancels a the current plot box selection. IMPLOT_API void CancelPlotSelection(); // Hides or shows the next plot item (i.e. as if it were toggled from the legend). // Use ImPlotCond_Always if you need to forcefully set this every frame. IMPLOT_API void HideNextItem(bool hidden = true, ImPlotCond cond = ImPlotCond_Once); // Use the following around calls to Begin/EndPlot to align l/r/t/b padding. // Consider using Begin/EndSubplots first. They are more feature rich and // accomplish the same behaviour by default. The functions below offer lower // level control of plot alignment. // Align axis padding over multiple plots in a single row or column. #group_id must // be unique. If this function returns true, EndAlignedPlots() must be called. IMPLOT_API bool BeginAlignedPlots(const char* group_id, bool vertical = true); // Only call EndAlignedPlots() if BeginAlignedPlots() returns true! IMPLOT_API void EndAlignedPlots(); //----------------------------------------------------------------------------- // [SECTION] Legend Utils //----------------------------------------------------------------------------- // Begin a popup for a legend entry. IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button=1); // End a popup for a legend entry. IMPLOT_API void EndLegendPopup(); // Returns true if a plot item legend entry is hovered. IMPLOT_API bool IsLegendEntryHovered(const char* label_id); //----------------------------------------------------------------------------- // [SECTION] Drag and Drop //----------------------------------------------------------------------------- // Turns the current plot's plotting area into a drag and drop target. Don't forget to call EndDragDropTarget! IMPLOT_API bool BeginDragDropTargetPlot(); // Turns the current plot's X-axis into a drag and drop target. Don't forget to call EndDragDropTarget! IMPLOT_API bool BeginDragDropTargetAxis(ImAxis axis); // Turns the current plot's legend into a drag and drop target. Don't forget to call EndDragDropTarget! IMPLOT_API bool BeginDragDropTargetLegend(); // Ends a drag and drop target (currently just an alias for ImGui::EndDragDropTarget). IMPLOT_API void EndDragDropTarget(); // NB: By default, plot and axes drag and drop *sources* require holding the Ctrl modifier to initiate the drag. // You can change the modifier if desired. If ImGuiMod_None is provided, the axes will be locked from panning. // Turns the current plot's plotting area into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! IMPLOT_API bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags=0); // Turns the current plot's X-axis into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! IMPLOT_API bool BeginDragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags=0); // Turns an item in the current plot's legend into drag and drop source. Don't forget to call EndDragDropSource! IMPLOT_API bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags=0); // Ends a drag and drop source (currently just an alias for ImGui::EndDragDropSource). IMPLOT_API void EndDragDropSource(); //----------------------------------------------------------------------------- // [SECTION] Styling //----------------------------------------------------------------------------- // Styling colors in ImPlot works similarly to styling colors in ImGui, but // with one important difference. Like ImGui, all style colors are stored in an // indexable array in ImPlotStyle. You can permanently modify these values through // GetStyle().Colors, or temporarily modify them with Push/Pop functions below. // However, by default all style colors in ImPlot default to a special color // IMPLOT_AUTO_COL. The behavior of this color depends upon the style color to // which it as applied: // // 1) For style colors associated with plot items (e.g. ImPlotCol_Line), // IMPLOT_AUTO_COL tells ImPlot to color the item with the next unused // color in the current colormap. Thus, every item will have a different // color up to the number of colors in the colormap, at which point the // colormap will roll over. For most use cases, you should not need to // set these style colors to anything but IMPLOT_COL_AUTO; you are // probably better off changing the current colormap. However, if you // need to explicitly color a particular item you may either Push/Pop // the style color around the item in question, or use the SetNextXXXStyle // API below. If you permanently set one of these style colors to a specific // color, or forget to call Pop, then all subsequent items will be styled // with the color you set. // // 2) For style colors associated with plot styling (e.g. ImPlotCol_PlotBg), // IMPLOT_AUTO_COL tells ImPlot to set that color from color data in your // **ImGuiStyle**. The ImGuiCol_ that these style colors default to are // detailed above, and in general have been mapped to produce plots visually // consistent with your current ImGui style. Of course, you are free to // manually set these colors to whatever you like, and further can Push/Pop // them around individual plots for plot-specific styling (e.g. coloring axes). // Provides access to plot style structure for permanant modifications to colors, sizes, etc. IMPLOT_API ImPlotStyle& GetStyle(); // Style plot colors for current ImGui style (default). IMPLOT_API void StyleColorsAuto(ImPlotStyle* dst = nullptr); // Style plot colors for ImGui "Classic". IMPLOT_API void StyleColorsClassic(ImPlotStyle* dst = nullptr); // Style plot colors for ImGui "Dark". IMPLOT_API void StyleColorsDark(ImPlotStyle* dst = nullptr); // Style plot colors for ImGui "Light". IMPLOT_API void StyleColorsLight(ImPlotStyle* dst = nullptr); // Use PushStyleX to temporarily modify your ImPlotStyle. The modification // will last until the matching call to PopStyleX. You MUST call a pop for // every push, otherwise you will leak memory! This behaves just like ImGui. // Temporarily modify a style color. Don't forget to call PopStyleColor! IMPLOT_API void PushStyleColor(ImPlotCol idx, ImU32 col); IMPLOT_API void PushStyleColor(ImPlotCol idx, const ImVec4& col); // Undo temporary style color modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopStyleColor(int count = 1); // Temporarily modify a style variable of float type. Don't forget to call PopStyleVar! IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, float val); // Temporarily modify a style variable of int type. Don't forget to call PopStyleVar! IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, int val); // Temporarily modify a style variable of ImVec2 type. Don't forget to call PopStyleVar! IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val); // Undo temporary style variable modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopStyleVar(int count = 1); // The following can be used to modify the style of the next plot item ONLY. They do // NOT require calls to PopStyleX. Leave style attributes you don't want modified to // IMPLOT_AUTO or IMPLOT_AUTO_COL. Automatic styles will be deduced from the current // values in your ImPlotStyle or from Colormap data. // Set the line color and weight for the next item only. IMPLOT_API void SetNextLineStyle(const ImVec4& col = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO); // Set the fill color for the next item only. IMPLOT_API void SetNextFillStyle(const ImVec4& col = IMPLOT_AUTO_COL, float alpha_mod = IMPLOT_AUTO); // Set the marker style for the next item only. IMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size = IMPLOT_AUTO, const ImVec4& fill = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO, const ImVec4& outline = IMPLOT_AUTO_COL); // Set the error bar style for the next item only. IMPLOT_API void SetNextErrorBarStyle(const ImVec4& col = IMPLOT_AUTO_COL, float size = IMPLOT_AUTO, float weight = IMPLOT_AUTO); // Gets the last item primary color (i.e. its legend icon color) IMPLOT_API ImVec4 GetLastItemColor(); // Returns the null terminated string name for an ImPlotCol. IMPLOT_API const char* GetStyleColorName(ImPlotCol idx); // Returns the null terminated string name for an ImPlotMarker. IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); //----------------------------------------------------------------------------- // [SECTION] Colormaps //----------------------------------------------------------------------------- // Item styling is based on colormaps when the relevant ImPlotCol_XXX is set to // IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can // add and then push/pop your own colormaps as well. To permanently set a colormap, // modify the Colormap index member of your ImPlotStyle. // Colormap data will be ignored and a custom color will be used if you have done one of the following: // 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. // 2) Pushed an item style color using PushStyleColor(). // 3) Set the next item style with a SetNextXXXStyle function. // Add a new colormap. The color data will be copied. The colormap can be used by pushing either the returned index or the // string name with PushColormap. The colormap name must be unique and the size must be greater than 1. You will receive // an assert otherwise! By default colormaps are considered to be qualitative (i.e. discrete). If you want to create a // continuous colormap, set #qual=false. This will treat the colors you provide as keys, and ImPlot will build a linearly // interpolated lookup table. The memory footprint of this table will be exactly ((size-1)*255+1)*4 bytes. IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* cols, int size, bool qual=true); IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* cols, int size, bool qual=true); // Returns the number of available colormaps (i.e. the built-in + user-added count). IMPLOT_API int GetColormapCount(); // Returns a null terminated string name for a colormap given an index. Returns nullptr if index is invalid. IMPLOT_API const char* GetColormapName(ImPlotColormap cmap); // Returns an index number for a colormap given a valid string name. Returns -1 if name is invalid. IMPLOT_API ImPlotColormap GetColormapIndex(const char* name); // Temporarily switch to one of the built-in (i.e. ImPlotColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap! IMPLOT_API void PushColormap(ImPlotColormap cmap); // Push a colormap by string name. Use built-in names such as "Default", "Deep", "Jet", etc. or a string you provided to AddColormap. Don't forget to call PopColormap! IMPLOT_API void PushColormap(const char* name); // Undo temporary colormap modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopColormap(int count = 1); // Returns the next color from the current colormap and advances the colormap for the current plot. // Can also be used with no return value to skip colors if desired. You need to call this between Begin/EndPlot! IMPLOT_API ImVec4 NextColormapColor(); // Colormap utils. If cmap = IMPLOT_AUTO (default), the current colormap is assumed. // Pass an explicit colormap index (built-in or user-added) to specify otherwise. // Returns the size of a colormap. IMPLOT_API int GetColormapSize(ImPlotColormap cmap = IMPLOT_AUTO); // Returns a color from a colormap given an index >= 0 (modulo will be performed). IMPLOT_API ImVec4 GetColormapColor(int idx, ImPlotColormap cmap = IMPLOT_AUTO); // Sample a color from the current colormap given t between 0 and 1. IMPLOT_API ImVec4 SampleColormap(float t, ImPlotColormap cmap = IMPLOT_AUTO); // Shows a vertical color scale with linear spaced ticks using the specified color map. Use double hashes to hide label (e.g. "##NoLabel"). If scale_min > scale_max, the scale to color mapping will be reversed. IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), const char* format = "%g", ImPlotColormapScaleFlags flags = 0, ImPlotColormap cmap = IMPLOT_AUTO); // Shows a horizontal slider with a colormap gradient background. Optionally returns the color sampled at t in [0 1]. IMPLOT_API bool ColormapSlider(const char* label, float* t, ImVec4* out = nullptr, const char* format = "", ImPlotColormap cmap = IMPLOT_AUTO); // Shows a button with a colormap gradient brackground. IMPLOT_API bool ColormapButton(const char* label, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO); // When items in a plot sample their color from a colormap, the color is cached and does not change // unless explicitly overriden. Therefore, if you change the colormap after the item has already been plotted, // item colors will NOT update. If you need item colors to resample the new colormap, then use this // function to bust the cached colors. If #plot_title_id is nullptr, then every item in EVERY existing plot // will be cache busted. Otherwise only the plot specified by #plot_title_id will be busted. For the // latter, this function must be called in the same ImGui ID scope that the plot is in. You should rarely if ever // need this function, but it is available for applications that require runtime colormap swaps (e.g. Heatmaps demo). IMPLOT_API void BustColorCache(const char* plot_title_id = nullptr); //----------------------------------------------------------------------------- // [SECTION] Input Mapping //----------------------------------------------------------------------------- // Provides access to input mapping structure for permanant modifications to controls for pan, select, etc. IMPLOT_API ImPlotInputMap& GetInputMap(); // Default input mapping: pan = LMB drag, box select = RMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll. IMPLOT_API void MapInputDefault(ImPlotInputMap* dst = nullptr); // Reverse input mapping: pan = RMB drag, box select = LMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll. IMPLOT_API void MapInputReverse(ImPlotInputMap* dst = nullptr); //----------------------------------------------------------------------------- // [SECTION] Miscellaneous //----------------------------------------------------------------------------- // Render icons similar to those that appear in legends (nifty for data lists). IMPLOT_API void ItemIcon(const ImVec4& col); IMPLOT_API void ItemIcon(ImU32 col); IMPLOT_API void ColormapIcon(ImPlotColormap cmap); // Get the plot draw list for custom rendering to the current plot area. Call between Begin/EndPlot. IMPLOT_API ImDrawList* GetPlotDrawList(); // Push clip rect for rendering to current plot area. The rect can be expanded or contracted by #expand pixels. Call between Begin/EndPlot. IMPLOT_API void PushPlotClipRect(float expand=0); // Pop plot clip rect. Call between Begin/EndPlot. IMPLOT_API void PopPlotClipRect(); // Shows ImPlot style selector dropdown menu. IMPLOT_API bool ShowStyleSelector(const char* label); // Shows ImPlot colormap selector dropdown menu. IMPLOT_API bool ShowColormapSelector(const char* label); // Shows ImPlot input map selector dropdown menu. IMPLOT_API bool ShowInputMapSelector(const char* label); // Shows ImPlot style editor block (not a window). IMPLOT_API void ShowStyleEditor(ImPlotStyle* ref = nullptr); // Add basic help/info block for end users (not a window). IMPLOT_API void ShowUserGuide(); // Shows ImPlot metrics/debug information window. IMPLOT_API void ShowMetricsWindow(bool* p_popen = nullptr); //----------------------------------------------------------------------------- // [SECTION] Demo //----------------------------------------------------------------------------- // Shows the ImPlot demo window (add implot_demo.cpp to your sources!) IMPLOT_API void ShowDemoWindow(bool* p_open = nullptr); } // namespace ImPlot //----------------------------------------------------------------------------- // [SECTION] Obsolete API //----------------------------------------------------------------------------- // The following functions will be removed! Keep your copy of implot up to date! // Occasionally set '#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS' to stay ahead. // If you absolutely must use these functions and do not want to receive compiler // warnings, set '#define IMPLOT_DISABLE_OBSOLETE_WARNINGS'. #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMPLOT_DISABLE_DEPRECATED_WARNINGS #if __cplusplus > 201402L #define IMPLOT_DEPRECATED(method) [[deprecated]] method #elif defined( __GNUC__ ) && !defined( __INTEL_COMPILER ) && ( __GNUC__ > 3 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) ) #define IMPLOT_DEPRECATED(method) method __attribute__( ( deprecated ) ) #elif defined( _MSC_VER ) #define IMPLOT_DEPRECATED(method) __declspec(deprecated) method #else #define IMPLOT_DEPRECATED(method) method #endif #else #define IMPLOT_DEPRECATED(method) method #endif enum ImPlotFlagsObsolete_ { ImPlotFlags_YAxis2 = 1 << 20, ImPlotFlags_YAxis3 = 1 << 21, }; namespace ImPlot { // OBSOLETED in v0.13 -> PLANNED REMOVAL in v1.0 IMPLOT_DEPRECATED( IMPLOT_API bool BeginPlot(const char* title_id, const char* x_label, // = nullptr, const char* y_label, // = nullptr, const ImVec2& size = ImVec2(-1,0), ImPlotFlags flags = ImPlotFlags_None, ImPlotAxisFlags x_flags = 0, ImPlotAxisFlags y_flags = 0, ImPlotAxisFlags y2_flags = ImPlotAxisFlags_AuxDefault, ImPlotAxisFlags y3_flags = ImPlotAxisFlags_AuxDefault, const char* y2_label = nullptr, const char* y3_label = nullptr) ); } // namespace ImPlot #endif ================================================ FILE: lib/third_party/imgui/implot/include/implot_internal.h ================================================ // MIT License // Copyright (c) 2023 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.17 // You may use this file to debug, understand or extend ImPlot features but we // don't provide any guarantee of forward compatibility! //----------------------------------------------------------------------------- // [SECTION] Header Mess //----------------------------------------------------------------------------- #pragma once #include #include "imgui_internal.h" #include #ifndef IMPLOT_VERSION #error Must include implot.h before implot_internal.h #endif // Support for pre-1.84 versions. ImPool's GetSize() -> GetBufSize() #if (IMGUI_VERSION_NUM < 18303) #define GetBufSize GetSize #endif //----------------------------------------------------------------------------- // [SECTION] Constants //----------------------------------------------------------------------------- // Constants can be changed unless stated otherwise. We may move some of these // to ImPlotStyleVar_ over time. // Mimimum allowable timestamp value 01/01/1970 @ 12:00am (UTC) (DO NOT DECREASE THIS) #define IMPLOT_MIN_TIME 0 // Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS) #define IMPLOT_MAX_TIME 32503680000 // Default label format for axis labels #define IMPLOT_LABEL_FORMAT "%g" // Max character size for tick labels #define IMPLOT_LABEL_MAX_SIZE 32 //----------------------------------------------------------------------------- // [SECTION] Macros //----------------------------------------------------------------------------- #define IMPLOT_NUM_X_AXES ImAxis_Y1 #define IMPLOT_NUM_Y_AXES (ImAxis_COUNT - IMPLOT_NUM_X_AXES) // Split ImU32 color into RGB components [0 255] #define IM_COL32_SPLIT_RGB(col,r,g,b) \ ImU32 r = ((col >> IM_COL32_R_SHIFT) & 0xFF); \ ImU32 g = ((col >> IM_COL32_G_SHIFT) & 0xFF); \ ImU32 b = ((col >> IM_COL32_B_SHIFT) & 0xFF); //----------------------------------------------------------------------------- // [SECTION] Forward Declarations //----------------------------------------------------------------------------- struct ImPlotTick; struct ImPlotAxis; struct ImPlotAxisColor; struct ImPlotItem; struct ImPlotLegend; struct ImPlotPlot; struct ImPlotNextPlotData; struct ImPlotTicker; //----------------------------------------------------------------------------- // [SECTION] Context Pointer //----------------------------------------------------------------------------- #ifndef GImPlot extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer #endif //----------------------------------------------------------------------------- // [SECTION] Generic Helpers //----------------------------------------------------------------------------- // Computes the common (base-10) logarithm static inline float ImLog10(float x) { return log10f(x); } static inline double ImLog10(double x) { return log10(x); } static inline float ImSinh(float x) { return sinhf(x); } static inline double ImSinh(double x) { return sinh(x); } static inline float ImAsinh(float x) { return asinhf(x); } static inline double ImAsinh(double x) { return asinh(x); } // Returns true if a flag is set template static inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; } // Flips a flag in a flagset template static inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; } // Linearly remaps x from [x0 x1] to [y0 y1]. template static inline T ImRemap(T x, T x0, T x1, T y0, T y1) { return y0 + (x - x0) * (y1 - y0) / (x1 - x0); } // Linear rempas x from [x0 x1] to [0 1] template static inline T ImRemap01(T x, T x0, T x1) { return (x - x0) / (x1 - x0); } // Returns always positive modulo (assumes r != 0) static inline int ImPosMod(int l, int r) { return (l % r + r) % r; } // Returns true if val is NAN static inline bool ImNan(double val) { return std::isnan(val); } // Returns true if val is NAN or INFINITY static inline bool ImNanOrInf(double val) { return !(val >= -DBL_MAX && val <= DBL_MAX) || ImNan(val); } // Turns NANs to 0s static inline double ImConstrainNan(double val) { return ImNan(val) ? 0 : val; } // Turns infinity to floating point maximums static inline double ImConstrainInf(double val) { return val >= DBL_MAX ? DBL_MAX : val <= -DBL_MAX ? - DBL_MAX : val; } // Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?) static inline double ImConstrainLog(double val) { return val <= 0 ? 0.001f : val; } // Turns numbers less than 0 to zero static inline double ImConstrainTime(double val) { return val < IMPLOT_MIN_TIME ? IMPLOT_MIN_TIME : (val > IMPLOT_MAX_TIME ? IMPLOT_MAX_TIME : val); } // True if two numbers are approximately equal using units in the last place. static inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) { return ImAbs(v1-v2) < DBL_EPSILON * ImAbs(v1+v2) * ulp || ImAbs(v1-v2) < DBL_MIN; } // Finds min value in an unsorted array template static inline T ImMinArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] < m) { m = values[i]; } } return m; } // Finds the max value in an unsorted array template static inline T ImMaxArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] > m) { m = values[i]; } } return m; } // Finds the min and max value in an unsorted array template static inline void ImMinMaxArray(const T* values, int count, T* min_out, T* max_out) { T Min = values[0]; T Max = values[0]; for (int i = 1; i < count; ++i) { if (values[i] < Min) { Min = values[i]; } if (values[i] > Max) { Max = values[i]; } } *min_out = Min; *max_out = Max; } // Finds the sim of an array template static inline T ImSum(const T* values, int count) { T sum = 0; for (int i = 0; i < count; ++i) sum += values[i]; return sum; } // Finds the mean of an array template static inline double ImMean(const T* values, int count) { double den = 1.0 / count; double mu = 0; for (int i = 0; i < count; ++i) mu += (double)values[i] * den; return mu; } // Finds the sample standard deviation of an array template static inline double ImStdDev(const T* values, int count) { double den = 1.0 / (count - 1.0); double mu = ImMean(values, count); double x = 0; for (int i = 0; i < count; ++i) x += ((double)values[i] - mu) * ((double)values[i] - mu) * den; return sqrt(x); } // Mix color a and b by factor s in [0 256] static inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) { #ifdef IMPLOT_MIX64 const ImU32 af = 256-s; const ImU32 bf = s; const ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24); const ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); const ImU64 mix = (al * af + bl * bf); return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); #else const ImU32 af = 256-s; const ImU32 bf = s; const ImU32 al = (a & 0x00ff00ff); const ImU32 ah = (a & 0xff00ff00) >> 8; const ImU32 bl = (b & 0x00ff00ff); const ImU32 bh = (b & 0xff00ff00) >> 8; const ImU32 ml = (al * af + bl * bf); const ImU32 mh = (ah * af + bh * bf); return (mh & 0xff00ff00) | ((ml & 0xff00ff00) >> 8); #endif } // Lerp across an array of 32-bit collors given t in [0.0 1.0] static inline ImU32 ImLerpU32(const ImU32* colors, int size, float t) { int i1 = (int)((size - 1 ) * t); int i2 = i1 + 1; if (i2 == size || size == 1) return colors[i1]; float den = 1.0f / (size - 1); float t1 = i1 * den; float t2 = i2 * den; float tr = ImRemap01(t, t1, t2); return ImMixU32(colors[i1], colors[i2], (ImU32)(tr*256)); } // Set alpha channel of 32-bit color from float in range [0.0 1.0] static inline ImU32 ImAlphaU32(ImU32 col, float alpha) { return col & ~((ImU32)((1.0f-alpha)*255)< static inline bool ImOverlaps(T min_a, T max_a, T min_b, T max_b) { return min_a <= max_b && min_b <= max_a; } //----------------------------------------------------------------------------- // [SECTION] ImPlot Enums //----------------------------------------------------------------------------- typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_ typedef int ImPlotDateFmt; // -> enum ImPlotDateFmt_ typedef int ImPlotTimeFmt; // -> enum ImPlotTimeFmt_ enum ImPlotTimeUnit_ { ImPlotTimeUnit_Us, // microsecond ImPlotTimeUnit_Ms, // millisecond ImPlotTimeUnit_S, // second ImPlotTimeUnit_Min, // minute ImPlotTimeUnit_Hr, // hour ImPlotTimeUnit_Day, // day ImPlotTimeUnit_Mo, // month ImPlotTimeUnit_Yr, // year ImPlotTimeUnit_COUNT }; enum ImPlotDateFmt_ { // default [ ISO 8601 ] ImPlotDateFmt_None = 0, ImPlotDateFmt_DayMo, // 10/3 [ --10-03 ] ImPlotDateFmt_DayMoYr, // 10/3/91 [ 1991-10-03 ] ImPlotDateFmt_MoYr, // Oct 1991 [ 1991-10 ] ImPlotDateFmt_Mo, // Oct [ --10 ] ImPlotDateFmt_Yr // 1991 [ 1991 ] }; enum ImPlotTimeFmt_ { // default [ 24 Hour Clock ] ImPlotTimeFmt_None = 0, ImPlotTimeFmt_Us, // .428 552 [ .428 552 ] ImPlotTimeFmt_SUs, // :29.428 552 [ :29.428 552 ] ImPlotTimeFmt_SMs, // :29.428 [ :29.428 ] ImPlotTimeFmt_S, // :29 [ :29 ] ImPlotTimeFmt_MinSMs, // 21:29.428 [ 21:29.428 ] ImPlotTimeFmt_HrMinSMs, // 7:21:29.428pm [ 19:21:29.428 ] ImPlotTimeFmt_HrMinS, // 7:21:29pm [ 19:21:29 ] ImPlotTimeFmt_HrMin, // 7:21pm [ 19:21 ] ImPlotTimeFmt_Hr // 7pm [ 19:00 ] }; //----------------------------------------------------------------------------- // [SECTION] Callbacks //----------------------------------------------------------------------------- typedef void (*ImPlotLocator)(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); //----------------------------------------------------------------------------- // [SECTION] Structs //----------------------------------------------------------------------------- // Combined date/time format spec struct ImPlotDateTimeSpec { ImPlotDateTimeSpec() {} ImPlotDateTimeSpec(ImPlotDateFmt date_fmt, ImPlotTimeFmt time_fmt, bool use_24_hr_clk = false, bool use_iso_8601 = false) { Date = date_fmt; Time = time_fmt; UseISO8601 = use_iso_8601; Use24HourClock = use_24_hr_clk; } ImPlotDateFmt Date; ImPlotTimeFmt Time; bool UseISO8601; bool Use24HourClock; }; // Two part timestamp struct. struct ImPlotTime { time_t S; // second part int Us; // microsecond part ImPlotTime() { S = 0; Us = 0; } ImPlotTime(time_t s, int us = 0) { S = s + us / 1000000; Us = us % 1000000; } void RollOver() { S = S + Us / 1000000; Us = Us % 1000000; } double ToDouble() const { return (double)S + (double)Us / 1000000.0; } static ImPlotTime FromDouble(double t) { return ImPlotTime((time_t)t, (int)(t * 1000000 - floor(t) * 1000000)); } }; static inline ImPlotTime operator+(const ImPlotTime& lhs, const ImPlotTime& rhs) { return ImPlotTime(lhs.S + rhs.S, lhs.Us + rhs.Us); } static inline ImPlotTime operator-(const ImPlotTime& lhs, const ImPlotTime& rhs) { return ImPlotTime(lhs.S - rhs.S, lhs.Us - rhs.Us); } static inline bool operator==(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs.S == rhs.S && lhs.Us == rhs.Us; } static inline bool operator<(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs.S == rhs.S ? lhs.Us < rhs.Us : lhs.S < rhs.S; } static inline bool operator>(const ImPlotTime& lhs, const ImPlotTime& rhs) { return rhs < lhs; } static inline bool operator<=(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs < rhs || lhs == rhs; } static inline bool operator>=(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs > rhs || lhs == rhs; } // Colormap data storage struct ImPlotColormapData { ImVector Keys; ImVector KeyCounts; ImVector KeyOffsets; ImVector Tables; ImVector TableSizes; ImVector TableOffsets; ImGuiTextBuffer Text; ImVector TextOffsets; ImVector Quals; ImGuiStorage Map; int Count; ImPlotColormapData() { Count = 0; } int Append(const char* name, const ImU32* keys, int count, bool qual) { if (GetIndex(name) != -1) return -1; KeyOffsets.push_back(Keys.size()); KeyCounts.push_back(count); Keys.reserve(Keys.size()+count); for (int i = 0; i < count; ++i) Keys.push_back(keys[i]); TextOffsets.push_back(Text.size()); Text.append(name, name + strlen(name) + 1); Quals.push_back(qual); ImGuiID id = ImHashStr(name); int idx = Count++; Map.SetInt(id,idx); _AppendTable(idx); return idx; } void _AppendTable(ImPlotColormap cmap) { int key_count = GetKeyCount(cmap); const ImU32* keys = GetKeys(cmap); int off = Tables.size(); TableOffsets.push_back(off); if (IsQual(cmap)) { Tables.reserve(key_count); for (int i = 0; i < key_count; ++i) Tables.push_back(keys[i]); TableSizes.push_back(key_count); } else { int max_size = 255 * (key_count-1) + 1; Tables.reserve(off + max_size); // ImU32 last = keys[0]; // Tables.push_back(last); // int n = 1; for (int i = 0; i < key_count-1; ++i) { for (int s = 0; s < 255; ++s) { ImU32 a = keys[i]; ImU32 b = keys[i+1]; ImU32 c = ImMixU32(a,b,s); // if (c != last) { Tables.push_back(c); // last = c; // n++; // } } } ImU32 c = keys[key_count-1]; // if (c != last) { Tables.push_back(c); // n++; // } // TableSizes.push_back(n); TableSizes.push_back(max_size); } } void RebuildTables() { Tables.resize(0); TableSizes.resize(0); TableOffsets.resize(0); for (int i = 0; i < Count; ++i) _AppendTable(i); } inline bool IsQual(ImPlotColormap cmap) const { return Quals[cmap]; } inline const char* GetName(ImPlotColormap cmap) const { return cmap < Count ? Text.Buf.Data + TextOffsets[cmap] : nullptr; } inline ImPlotColormap GetIndex(const char* name) const { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } inline const ImU32* GetKeys(ImPlotColormap cmap) const { return &Keys[KeyOffsets[cmap]]; } inline int GetKeyCount(ImPlotColormap cmap) const { return KeyCounts[cmap]; } inline ImU32 GetKeyColor(ImPlotColormap cmap, int idx) const { return Keys[KeyOffsets[cmap]+idx]; } inline void SetKeyColor(ImPlotColormap cmap, int idx, ImU32 value) { Keys[KeyOffsets[cmap]+idx] = value; RebuildTables(); } inline const ImU32* GetTable(ImPlotColormap cmap) const { return &Tables[TableOffsets[cmap]]; } inline int GetTableSize(ImPlotColormap cmap) const { return TableSizes[cmap]; } inline ImU32 GetTableColor(ImPlotColormap cmap, int idx) const { return Tables[TableOffsets[cmap]+idx]; } inline ImU32 LerpTable(ImPlotColormap cmap, float t) const { int off = TableOffsets[cmap]; int siz = TableSizes[cmap]; int idx = Quals[cmap] ? ImClamp((int)(siz*t),0,siz-1) : (int)((siz - 1) * t + 0.5f); return Tables[off + idx]; } }; // ImPlotPoint with positive/negative error values struct ImPlotPointError { double X, Y, Neg, Pos; ImPlotPointError(double x, double y, double neg, double pos) { X = x; Y = y; Neg = neg; Pos = pos; } }; // Interior plot label/annotation struct ImPlotAnnotation { ImVec2 Pos; ImVec2 Offset; ImU32 ColorBg; ImU32 ColorFg; int TextOffset; bool Clamp; ImPlotAnnotation() { ColorBg = ColorFg = 0; TextOffset = 0; Clamp = false; } }; // Collection of plot labels struct ImPlotAnnotationCollection { ImVector Annotations; ImGuiTextBuffer TextBuffer; int Size; ImPlotAnnotationCollection() { Reset(); } void AppendV(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt, va_list args) IM_FMTLIST(7) { ImPlotAnnotation an; an.Pos = pos; an.Offset = off; an.ColorBg = bg; an.ColorFg = fg; an.TextOffset = TextBuffer.size(); an.Clamp = clamp; Annotations.push_back(an); TextBuffer.appendfv(fmt, args); const char nul[] = ""; TextBuffer.append(nul,nul+1); Size++; } void Append(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt, ...) IM_FMTARGS(7) { va_list args; va_start(args, fmt); AppendV(pos, off, bg, fg, clamp, fmt, args); va_end(args); } const char* GetText(int idx) { return TextBuffer.Buf.Data + Annotations[idx].TextOffset; } void Reset() { Annotations.shrink(0); TextBuffer.Buf.shrink(0); Size = 0; } }; struct ImPlotTag { ImAxis Axis; double Value; ImU32 ColorBg; ImU32 ColorFg; int TextOffset; }; struct ImPlotTagCollection { ImVector Tags; ImGuiTextBuffer TextBuffer; int Size; ImPlotTagCollection() { Reset(); } void AppendV(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, va_list args) IM_FMTLIST(6) { ImPlotTag tag; tag.Axis = axis; tag.Value = value; tag.ColorBg = bg; tag.ColorFg = fg; tag.TextOffset = TextBuffer.size(); Tags.push_back(tag); TextBuffer.appendfv(fmt, args); const char nul[] = ""; TextBuffer.append(nul,nul+1); Size++; } void Append(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, ...) IM_FMTARGS(6) { va_list args; va_start(args, fmt); AppendV(axis, value, bg, fg, fmt, args); va_end(args); } const char* GetText(int idx) { return TextBuffer.Buf.Data + Tags[idx].TextOffset; } void Reset() { Tags.shrink(0); TextBuffer.Buf.shrink(0); Size = 0; } }; // Tick mark info struct ImPlotTick { double PlotPos; float PixelPos; ImVec2 LabelSize; int TextOffset; bool Major; bool ShowLabel; int Level; int Idx; ImPlotTick(double value, bool major, int level, bool show_label) { PixelPos = 0; PlotPos = value; Major = major; ShowLabel = show_label; Level = level; TextOffset = -1; } }; // Collection of ticks struct ImPlotTicker { ImVector Ticks; ImGuiTextBuffer TextBuffer; ImVec2 MaxSize; ImVec2 LateSize; int Levels; ImPlotTicker() { Reset(); } ImPlotTick& AddTick(double value, bool major, int level, bool show_label, const char* label) { ImPlotTick tick(value, major, level, show_label); if (show_label && label != nullptr) { tick.TextOffset = TextBuffer.size(); TextBuffer.append(label, label + strlen(label) + 1); tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); } return AddTick(tick); } ImPlotTick& AddTick(double value, bool major, int level, bool show_label, ImPlotFormatter formatter, void* data) { ImPlotTick tick(value, major, level, show_label); if (show_label && formatter != nullptr) { char buff[IMPLOT_LABEL_MAX_SIZE]; tick.TextOffset = TextBuffer.size(); formatter(tick.PlotPos, buff, sizeof(buff), data); TextBuffer.append(buff, buff + strlen(buff) + 1); tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); } return AddTick(tick); } inline ImPlotTick& AddTick(ImPlotTick tick) { if (tick.ShowLabel) { MaxSize.x = tick.LabelSize.x > MaxSize.x ? tick.LabelSize.x : MaxSize.x; MaxSize.y = tick.LabelSize.y > MaxSize.y ? tick.LabelSize.y : MaxSize.y; } tick.Idx = Ticks.size(); Ticks.push_back(tick); return Ticks.back(); } const char* GetText(int idx) const { return TextBuffer.Buf.Data + Ticks[idx].TextOffset; } const char* GetText(const ImPlotTick& tick) { return GetText(tick.Idx); } void OverrideSizeLate(const ImVec2& size) { LateSize.x = size.x > LateSize.x ? size.x : LateSize.x; LateSize.y = size.y > LateSize.y ? size.y : LateSize.y; } void Reset() { Ticks.shrink(0); TextBuffer.Buf.shrink(0); MaxSize = LateSize; LateSize = ImVec2(0,0); Levels = 1; } int TickCount() const { return Ticks.Size; } }; // Axis state information that must persist after EndPlot struct ImPlotAxis { ImGuiID ID; ImPlotAxisFlags Flags; ImPlotAxisFlags PreviousFlags; ImPlotRange Range; ImPlotCond RangeCond; ImPlotScale Scale; ImPlotRange FitExtents; ImPlotAxis* OrthoAxis; ImPlotRange ConstraintRange; ImPlotRange ConstraintZoom; ImPlotTicker Ticker; ImPlotFormatter Formatter; void* FormatterData; char FormatSpec[16]; ImPlotLocator Locator; double* LinkedMin; double* LinkedMax; int PickerLevel; ImPlotTime PickerTimeMin, PickerTimeMax; ImPlotTransform TransformForward; ImPlotTransform TransformInverse; void* TransformData; float PixelMin, PixelMax; double ScaleMin, ScaleMax; double ScaleToPixel; float Datum1, Datum2; ImRect HoverRect; int LabelOffset; ImU32 ColorMaj, ColorMin, ColorTick, ColorTxt, ColorBg, ColorHov, ColorAct, ColorHiLi; bool Enabled; bool Vertical; bool FitThisFrame; bool HasRange; bool HasFormatSpec; bool ShowDefaultTicks; bool Hovered; bool Held; ImPlotAxis() { ID = 0; Flags = PreviousFlags = ImPlotAxisFlags_None; Range.Min = 0; Range.Max = 1; Scale = ImPlotScale_Linear; TransformForward = TransformInverse = nullptr; TransformData = nullptr; FitExtents.Min = HUGE_VAL; FitExtents.Max = -HUGE_VAL; OrthoAxis = nullptr; ConstraintRange = ImPlotRange(-INFINITY,INFINITY); ConstraintZoom = ImPlotRange(DBL_MIN,INFINITY); LinkedMin = LinkedMax = nullptr; PickerLevel = 0; Datum1 = Datum2 = 0; PixelMin = PixelMax = 0; LabelOffset = -1; ColorMaj = ColorMin = ColorTick = ColorTxt = ColorBg = ColorHov = ColorAct = 0; ColorHiLi = IM_COL32_BLACK_TRANS; Formatter = nullptr; FormatterData = nullptr; Locator = nullptr; Enabled = Hovered = Held = FitThisFrame = HasRange = HasFormatSpec = false; ShowDefaultTicks = true; } inline void Reset() { Enabled = false; Scale = ImPlotScale_Linear; TransformForward = TransformInverse = nullptr; TransformData = nullptr; LabelOffset = -1; HasFormatSpec = false; Formatter = nullptr; FormatterData = nullptr; Locator = nullptr; ShowDefaultTicks = true; FitThisFrame = false; FitExtents.Min = HUGE_VAL; FitExtents.Max = -HUGE_VAL; OrthoAxis = nullptr; ConstraintRange = ImPlotRange(-INFINITY,INFINITY); ConstraintZoom = ImPlotRange(DBL_MIN,INFINITY); Ticker.Reset(); } inline bool SetMin(double _min, bool force=false) { if (!force && IsLockedMin()) return false; _min = ImConstrainNan(ImConstrainInf(_min)); if (_min < ConstraintRange.Min) _min = ConstraintRange.Min; double z = Range.Max - _min; if (z < ConstraintZoom.Min) _min = Range.Max - ConstraintZoom.Min; if (z > ConstraintZoom.Max) _min = Range.Max - ConstraintZoom.Max; if (_min >= Range.Max) return false; Range.Min = _min; PickerTimeMin = ImPlotTime::FromDouble(Range.Min); UpdateTransformCache(); return true; }; inline bool SetMax(double _max, bool force=false) { if (!force && IsLockedMax()) return false; _max = ImConstrainNan(ImConstrainInf(_max)); if (_max > ConstraintRange.Max) _max = ConstraintRange.Max; double z = _max - Range.Min; if (z < ConstraintZoom.Min) _max = Range.Min + ConstraintZoom.Min; if (z > ConstraintZoom.Max) _max = Range.Min + ConstraintZoom.Max; if (_max <= Range.Min) return false; Range.Max = _max; PickerTimeMax = ImPlotTime::FromDouble(Range.Max); UpdateTransformCache(); return true; }; inline void SetRange(double v1, double v2) { Range.Min = ImMin(v1,v2); Range.Max = ImMax(v1,v2); Constrain(); PickerTimeMin = ImPlotTime::FromDouble(Range.Min); PickerTimeMax = ImPlotTime::FromDouble(Range.Max); UpdateTransformCache(); } inline void SetRange(const ImPlotRange& range) { SetRange(range.Min, range.Max); } inline void SetAspect(double unit_per_pix) { double new_size = unit_per_pix * PixelSize(); double delta = (new_size - Range.Size()) * 0.5; if (IsLocked()) return; else if (IsLockedMin() && !IsLockedMax()) SetRange(Range.Min, Range.Max + 2*delta); else if (!IsLockedMin() && IsLockedMax()) SetRange(Range.Min - 2*delta, Range.Max); else SetRange(Range.Min - delta, Range.Max + delta); } inline float PixelSize() const { return ImAbs(PixelMax - PixelMin); } inline double GetAspect() const { return Range.Size() / PixelSize(); } inline void Constrain() { Range.Min = ImConstrainNan(ImConstrainInf(Range.Min)); Range.Max = ImConstrainNan(ImConstrainInf(Range.Max)); if (Range.Min < ConstraintRange.Min) Range.Min = ConstraintRange.Min; if (Range.Max > ConstraintRange.Max) Range.Max = ConstraintRange.Max; double z = Range.Size(); if (z < ConstraintZoom.Min) { double delta = (ConstraintZoom.Min - z) * 0.5; Range.Min -= delta; Range.Max += delta; } if (z > ConstraintZoom.Max) { double delta = (z - ConstraintZoom.Max) * 0.5; Range.Min += delta; Range.Max -= delta; } if (Range.Max <= Range.Min) Range.Max = Range.Min + DBL_EPSILON; } inline void UpdateTransformCache() { ScaleToPixel = (PixelMax - PixelMin) / Range.Size(); if (TransformForward != nullptr) { ScaleMin = TransformForward(Range.Min, TransformData); ScaleMax = TransformForward(Range.Max, TransformData); } else { ScaleMin = Range.Min; ScaleMax = Range.Max; } } inline float PlotToPixels(double plt) const { if (TransformForward != nullptr) { double s = TransformForward(plt, TransformData); double t = (s - ScaleMin) / (ScaleMax - ScaleMin); plt = Range.Min + Range.Size() * t; } return (float)(PixelMin + ScaleToPixel * (plt - Range.Min)); } inline double PixelsToPlot(float pix) const { double plt = (pix - PixelMin) / ScaleToPixel + Range.Min; if (TransformInverse != nullptr) { double t = (plt - Range.Min) / Range.Size(); double s = t * (ScaleMax - ScaleMin) + ScaleMin; plt = TransformInverse(s, TransformData); } return plt; } inline void ExtendFit(double v) { if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) { FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; } } inline void ExtendFitWith(ImPlotAxis& alt, double v, double v_alt) { if (ImHasFlag(Flags, ImPlotAxisFlags_RangeFit) && !alt.Range.Contains(v_alt)) return; if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) { FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; } } inline void ApplyFit(float padding) { const double ext_size = FitExtents.Size() * 0.5; FitExtents.Min -= ext_size * padding; FitExtents.Max += ext_size * padding; if (!IsLockedMin() && !ImNanOrInf(FitExtents.Min)) Range.Min = FitExtents.Min; if (!IsLockedMax() && !ImNanOrInf(FitExtents.Max)) Range.Max = FitExtents.Max; if (ImAlmostEqual(Range.Min, Range.Max)) { Range.Max += 0.5; Range.Min -= 0.5; } Constrain(); UpdateTransformCache(); } inline bool HasLabel() const { return LabelOffset != -1 && !ImHasFlag(Flags, ImPlotAxisFlags_NoLabel); } inline bool HasGridLines() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoGridLines); } inline bool HasTickLabels() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickLabels); } inline bool HasTickMarks() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickMarks); } inline bool WillRender() const { return Enabled && (HasGridLines() || HasTickLabels() || HasTickMarks()); } inline bool IsOpposite() const { return ImHasFlag(Flags, ImPlotAxisFlags_Opposite); } inline bool IsInverted() const { return ImHasFlag(Flags, ImPlotAxisFlags_Invert); } inline bool IsForeground() const { return ImHasFlag(Flags, ImPlotAxisFlags_Foreground); } inline bool IsAutoFitting() const { return ImHasFlag(Flags, ImPlotAxisFlags_AutoFit); } inline bool CanInitFit() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoInitialFit) && !HasRange && !LinkedMin && !LinkedMax; } inline bool IsRangeLocked() const { return HasRange && RangeCond == ImPlotCond_Always; } inline bool IsLockedMin() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMin); } inline bool IsLockedMax() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMax); } inline bool IsLocked() const { return IsLockedMin() && IsLockedMax(); } inline bool IsInputLockedMin() const { return IsLockedMin() || IsAutoFitting(); } inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); } inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); } inline bool HasMenus() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoMenus); } inline bool IsPanLocked(bool increasing) { if (ImHasFlag(Flags, ImPlotAxisFlags_PanStretch)) { return IsInputLocked(); } else { if (IsLockedMin() || IsLockedMax() || IsAutoFitting()) return false; if (increasing) return Range.Max == ConstraintRange.Max; else return Range.Min == ConstraintRange.Min; } } void PushLinks() { if (LinkedMin) { *LinkedMin = Range.Min; } if (LinkedMax) { *LinkedMax = Range.Max; } } void PullLinks() { if (LinkedMin && LinkedMax) { SetRange(*LinkedMin, *LinkedMax); } else if (LinkedMin) { SetMin(*LinkedMin,true); } else if (LinkedMax) { SetMax(*LinkedMax,true); } } }; // Align plots group data struct ImPlotAlignmentData { bool Vertical; float PadA; float PadB; float PadAMax; float PadBMax; ImPlotAlignmentData() { Vertical = true; PadA = PadB = PadAMax = PadBMax = 0; } void Begin() { PadAMax = PadBMax = 0; } void Update(float& pad_a, float& pad_b, float& delta_a, float& delta_b) { float bak_a = pad_a; float bak_b = pad_b; if (PadAMax < pad_a) { PadAMax = pad_a; } if (PadBMax < pad_b) { PadBMax = pad_b; } if (pad_a < PadA) { pad_a = PadA; delta_a = pad_a - bak_a; } else { delta_a = 0; } if (pad_b < PadB) { pad_b = PadB; delta_b = pad_b - bak_b; } else { delta_b = 0; } } void End() { PadA = PadAMax; PadB = PadBMax; } void Reset() { PadA = PadB = PadAMax = PadBMax = 0; } }; // State information for Plot items struct ImPlotItem { ImGuiID ID; ImU32 Color; ImRect LegendHoverRect; int NameOffset; bool Show; bool LegendHovered; bool SeenThisFrame; ImPlotItem() { ID = 0; Color = IM_COL32_WHITE; NameOffset = -1; Show = true; SeenThisFrame = false; LegendHovered = false; } ~ImPlotItem() { ID = 0; } }; // Holds Legend state struct ImPlotLegend { ImPlotLegendFlags Flags; ImPlotLegendFlags PreviousFlags; ImPlotLocation Location; ImPlotLocation PreviousLocation; ImVec2 Scroll; ImVector Indices; ImGuiTextBuffer Labels; ImRect Rect; ImRect RectClamped; bool Hovered; bool Held; bool CanGoInside; ImPlotLegend() { Flags = PreviousFlags = ImPlotLegendFlags_None; CanGoInside = true; Hovered = Held = false; Location = PreviousLocation = ImPlotLocation_NorthWest; Scroll = ImVec2(0,0); } void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); } }; // Holds Items and Legend data struct ImPlotItemGroup { ImGuiID ID; ImPlotLegend Legend; ImPool ItemPool; int ColormapIdx; ImPlotItemGroup() { ID = 0; ColormapIdx = 0; } int GetItemCount() const { return ItemPool.GetBufSize(); } ImGuiID GetItemID(const char* label_id) { return ImGui::GetID(label_id); /* GetIDWithSeed */ } ImPlotItem* GetItem(ImGuiID id) { return ItemPool.GetByKey(id); } ImPlotItem* GetItem(const char* label_id) { return GetItem(GetItemID(label_id)); } ImPlotItem* GetOrAddItem(ImGuiID id) { return ItemPool.GetOrAddByKey(id); } ImPlotItem* GetItemByIndex(int i) { return ItemPool.GetByIndex(i); } int GetItemIndex(ImPlotItem* item) { return ItemPool.GetIndex(item); } int GetLegendCount() const { return Legend.Indices.size(); } ImPlotItem* GetLegendItem(int i) { return ItemPool.GetByIndex(Legend.Indices[i]); } const char* GetLegendLabel(int i) { return Legend.Labels.Buf.Data + GetLegendItem(i)->NameOffset; } void Reset() { ItemPool.Clear(); Legend.Reset(); ColormapIdx = 0; } }; // Holds Plot state information that must persist after EndPlot struct ImPlotPlot { ImGuiID ID; ImPlotFlags Flags; ImPlotFlags PreviousFlags; ImPlotLocation MouseTextLocation; ImPlotMouseTextFlags MouseTextFlags; ImPlotAxis Axes[ImAxis_COUNT]; ImGuiTextBuffer TextBuffer; ImPlotItemGroup Items; ImAxis CurrentX; ImAxis CurrentY; ImRect FrameRect; ImRect CanvasRect; ImRect PlotRect; ImRect AxesRect; ImRect SelectRect; ImVec2 SelectStart; int TitleOffset; bool JustCreated; bool Initialized; bool SetupLocked; bool FitThisFrame; bool Hovered; bool Held; bool Selecting; bool Selected; bool ContextLocked; ImPlotPlot() { Flags = PreviousFlags = ImPlotFlags_None; for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) XAxis(i).Vertical = false; for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) YAxis(i).Vertical = true; SelectStart = ImVec2(0,0); CurrentX = ImAxis_X1; CurrentY = ImAxis_Y1; MouseTextLocation = ImPlotLocation_South | ImPlotLocation_East; MouseTextFlags = ImPlotMouseTextFlags_None; TitleOffset = -1; JustCreated = true; Initialized = SetupLocked = FitThisFrame = false; Hovered = Held = Selected = Selecting = ContextLocked = false; } inline bool IsInputLocked() const { for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { if (!XAxis(i).IsInputLocked()) return false; } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { if (!YAxis(i).IsInputLocked()) return false; } return true; } inline void ClearTextBuffer() { TextBuffer.Buf.shrink(0); } inline void SetTitle(const char* title) { if (title && ImGui::FindRenderedTextEnd(title, nullptr) != title) { TitleOffset = TextBuffer.size(); TextBuffer.append(title, title + strlen(title) + 1); } else { TitleOffset = -1; } } inline bool HasTitle() const { return TitleOffset != -1 && !ImHasFlag(Flags, ImPlotFlags_NoTitle); } inline const char* GetTitle() const { return TextBuffer.Buf.Data + TitleOffset; } inline ImPlotAxis& XAxis(int i) { return Axes[ImAxis_X1 + i]; } inline const ImPlotAxis& XAxis(int i) const { return Axes[ImAxis_X1 + i]; } inline ImPlotAxis& YAxis(int i) { return Axes[ImAxis_Y1 + i]; } inline const ImPlotAxis& YAxis(int i) const { return Axes[ImAxis_Y1 + i]; } inline int EnabledAxesX() { int cnt = 0; for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) cnt += XAxis(i).Enabled; return cnt; } inline int EnabledAxesY() { int cnt = 0; for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) cnt += YAxis(i).Enabled; return cnt; } inline void SetAxisLabel(ImPlotAxis& axis, const char* label) { if (label && ImGui::FindRenderedTextEnd(label, nullptr) != label) { axis.LabelOffset = TextBuffer.size(); TextBuffer.append(label, label + strlen(label) + 1); } else { axis.LabelOffset = -1; } } inline const char* GetAxisLabel(const ImPlotAxis& axis) const { return TextBuffer.Buf.Data + axis.LabelOffset; } }; // Holds subplot data that must persist after EndSubplot struct ImPlotSubplot { ImGuiID ID; ImPlotSubplotFlags Flags; ImPlotSubplotFlags PreviousFlags; ImPlotItemGroup Items; int Rows; int Cols; int CurrentIdx; ImRect FrameRect; ImRect GridRect; ImVec2 CellSize; ImVector RowAlignmentData; ImVector ColAlignmentData; ImVector RowRatios; ImVector ColRatios; ImVector RowLinkData; ImVector ColLinkData; float TempSizes[2]; bool FrameHovered; bool HasTitle; ImPlotSubplot() { ID = 0; Flags = PreviousFlags = ImPlotSubplotFlags_None; Rows = Cols = CurrentIdx = 0; Items.Legend.Location = ImPlotLocation_North; Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside; Items.Legend.CanGoInside = false; TempSizes[0] = TempSizes[1] = 0; FrameHovered = false; HasTitle = false; } }; // Temporary data storage for upcoming plot struct ImPlotNextPlotData { ImPlotCond RangeCond[ImAxis_COUNT]; ImPlotRange Range[ImAxis_COUNT]; bool HasRange[ImAxis_COUNT]; bool Fit[ImAxis_COUNT]; double* LinkedMin[ImAxis_COUNT]; double* LinkedMax[ImAxis_COUNT]; ImPlotNextPlotData() { Reset(); } void Reset() { for (int i = 0; i < ImAxis_COUNT; ++i) { HasRange[i] = false; Fit[i] = false; LinkedMin[i] = LinkedMax[i] = nullptr; } } }; // Temporary data storage for upcoming item struct ImPlotNextItemData { ImVec4 Colors[5]; // ImPlotCol_Line, ImPlotCol_Fill, ImPlotCol_MarkerOutline, ImPlotCol_MarkerFill, ImPlotCol_ErrorBar float LineWeight; ImPlotMarker Marker; float MarkerSize; float MarkerWeight; float FillAlpha; float ErrorBarSize; float ErrorBarWeight; float DigitalBitHeight; float DigitalBitGap; bool RenderLine; bool RenderFill; bool RenderMarkerLine; bool RenderMarkerFill; bool HasHidden; bool Hidden; ImPlotCond HiddenCond; ImPlotNextItemData() { Reset(); } void Reset() { for (int i = 0; i < 5; ++i) Colors[i] = IMPLOT_AUTO_COL; LineWeight = MarkerSize = MarkerWeight = FillAlpha = ErrorBarSize = ErrorBarWeight = DigitalBitHeight = DigitalBitGap = IMPLOT_AUTO; Marker = IMPLOT_AUTO; HasHidden = Hidden = false; } }; // Holds state information that must persist between calls to BeginPlot()/EndPlot() struct ImPlotContext { // Plot States ImPool Plots; ImPool Subplots; ImPlotPlot* CurrentPlot; ImPlotSubplot* CurrentSubplot; ImPlotItemGroup* CurrentItems; ImPlotItem* CurrentItem; ImPlotItem* PreviousItem; // Tick Marks and Labels ImPlotTicker CTicker; // Annotation and Tabs ImPlotAnnotationCollection Annotations; ImPlotTagCollection Tags; // Style and Colormaps ImPlotStyle Style; ImVector ColorModifiers; ImVector StyleModifiers; ImPlotColormapData ColormapData; ImVector ColormapModifiers; // Time tm Tm; // Temp data for general use ImVector TempDouble1, TempDouble2; ImVector TempInt1; // Misc int DigitalPlotItemCnt; int DigitalPlotOffset; ImPlotNextPlotData NextPlotData; ImPlotNextItemData NextItemData; ImPlotInputMap InputMap; bool OpenContextThisFrame; ImGuiTextBuffer MousePosStringBuilder; ImPlotItemGroup* SortItems; // Align plots ImPool AlignmentData; ImPlotAlignmentData* CurrentAlignmentH; ImPlotAlignmentData* CurrentAlignmentV; }; //----------------------------------------------------------------------------- // [SECTION] Internal API // No guarantee of forward compatibility here! //----------------------------------------------------------------------------- namespace ImPlot { //----------------------------------------------------------------------------- // [SECTION] Context Utils //----------------------------------------------------------------------------- // Initializes an ImPlotContext IMPLOT_API void Initialize(ImPlotContext* ctx); // Resets an ImPlot context for the next call to BeginPlot IMPLOT_API void ResetCtxForNextPlot(ImPlotContext* ctx); // Resets an ImPlot context for the next call to BeginAlignedPlots IMPLOT_API void ResetCtxForNextAlignedPlots(ImPlotContext* ctx); // Resets an ImPlot context for the next call to BeginSubplot IMPLOT_API void ResetCtxForNextSubplot(ImPlotContext* ctx); //----------------------------------------------------------------------------- // [SECTION] Plot Utils //----------------------------------------------------------------------------- // Gets a plot from the current ImPlotContext IMPLOT_API ImPlotPlot* GetPlot(const char* title); // Gets the current plot from the current ImPlotContext IMPLOT_API ImPlotPlot* GetCurrentPlot(); // Busts the cache for every plot in the current context IMPLOT_API void BustPlotCache(); // Shows a plot's context menu. IMPLOT_API void ShowPlotContextMenu(ImPlotPlot& plot); //----------------------------------------------------------------------------- // [SECTION] Setup Utils //----------------------------------------------------------------------------- // Lock Setup and call SetupFinish if necessary. static inline void SetupLock() { ImPlotContext& gp = *GImPlot; if (!gp.CurrentPlot->SetupLocked) SetupFinish(); gp.CurrentPlot->SetupLocked = true; } //----------------------------------------------------------------------------- // [SECTION] Subplot Utils //----------------------------------------------------------------------------- // Advances to next subplot IMPLOT_API void SubplotNextCell(); // Shows a subplot's context menu. IMPLOT_API void ShowSubplotsContextMenu(ImPlotSubplot& subplot); //----------------------------------------------------------------------------- // [SECTION] Item Utils //----------------------------------------------------------------------------- // Begins a new item. Returns false if the item should not be plotted. Pushes PlotClipRect. IMPLOT_API bool BeginItem(const char* label_id, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO); // Same as above but with fitting functionality. template bool BeginItemEx(const char* label_id, const _Fitter& fitter, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO) { if (BeginItem(label_id, flags, recolor_from)) { ImPlotPlot& plot = *GetCurrentPlot(); if (plot.FitThisFrame && !ImHasFlag(flags, ImPlotItemFlags_NoFit)) fitter.Fit(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]); return true; } return false; } // Ends an item (call only if BeginItem returns true). Pops PlotClipRect. IMPLOT_API void EndItem(); // Register or get an existing item from the current plot. IMPLOT_API ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created = nullptr); // Get a plot item from the current plot. IMPLOT_API ImPlotItem* GetItem(const char* label_id); // Gets the current item. IMPLOT_API ImPlotItem* GetCurrentItem(); // Busts the cache for every item for every plot in the current context. IMPLOT_API void BustItemCache(); //----------------------------------------------------------------------------- // [SECTION] Axis Utils //----------------------------------------------------------------------------- // Returns true if any enabled axis is locked from user input. static inline bool AnyAxesInputLocked(ImPlotAxis* axes, int count) { for (int i = 0; i < count; ++i) { if (axes[i].Enabled && axes[i].IsInputLocked()) return true; } return false; } // Returns true if all enabled axes are locked from user input. static inline bool AllAxesInputLocked(ImPlotAxis* axes, int count) { for (int i = 0; i < count; ++i) { if (axes[i].Enabled && !axes[i].IsInputLocked()) return false; } return true; } static inline bool AnyAxesHeld(ImPlotAxis* axes, int count) { for (int i = 0; i < count; ++i) { if (axes[i].Enabled && axes[i].Held) return true; } return false; } static inline bool AnyAxesHovered(ImPlotAxis* axes, int count) { for (int i = 0; i < count; ++i) { if (axes[i].Enabled && axes[i].Hovered) return true; } return false; } // Returns true if the user has requested data to be fit. static inline bool FitThisFrame() { return GImPlot->CurrentPlot->FitThisFrame; } // Extends the current plot's axes so that it encompasses a vertical line at x static inline void FitPointX(double x) { ImPlotPlot& plot = *GetCurrentPlot(); ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; x_axis.ExtendFit(x); } // Extends the current plot's axes so that it encompasses a horizontal line at y static inline void FitPointY(double y) { ImPlotPlot& plot = *GetCurrentPlot(); ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; y_axis.ExtendFit(y); } // Extends the current plot's axes so that it encompasses point p static inline void FitPoint(const ImPlotPoint& p) { ImPlotPlot& plot = *GetCurrentPlot(); ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } // Returns true if two ranges overlap static inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2) { return r1.Min <= r2.Max && r2.Min <= r1.Max; } // Shows an axis's context menu. IMPLOT_API void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed = false); //----------------------------------------------------------------------------- // [SECTION] Legend Utils //----------------------------------------------------------------------------- // Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount. IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0)); // Calculates the bounding box size of a legend _before_ clipping. IMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical); // Clips calculated legend size IMPLOT_API bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad); // Renders legend entries into a bounding box IMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList); // Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window! This is not thoroughly tested nor scrollable!). IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true); // Shows a legend's context menu. IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible); //----------------------------------------------------------------------------- // [SECTION] Label Utils //----------------------------------------------------------------------------- // Create a a string label for a an axis value IMPLOT_API void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round = false); //----------------------------------------------------------------------------- // [SECTION] Styling Utils //----------------------------------------------------------------------------- // Get styling data for next item (call between Begin/EndItem) static inline const ImPlotNextItemData& GetItemData() { return GImPlot->NextItemData; } // Returns true if a color is set to be automatically determined static inline bool IsColorAuto(const ImVec4& col) { return col.w == -1; } // Returns true if a style color is set to be automatically determined static inline bool IsColorAuto(ImPlotCol idx) { return IsColorAuto(GImPlot->Style.Colors[idx]); } // Returns the automatically deduced style color IMPLOT_API ImVec4 GetAutoColor(ImPlotCol idx); // Returns the style color whether it is automatic or custom set static inline ImVec4 GetStyleColorVec4(ImPlotCol idx) { return IsColorAuto(idx) ? GetAutoColor(idx) : GImPlot->Style.Colors[idx]; } static inline ImU32 GetStyleColorU32(ImPlotCol idx) { return ImGui::ColorConvertFloat4ToU32(GetStyleColorVec4(idx)); } // Draws vertical text. The position is the bottom left of the text rect. IMPLOT_API void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char* text_begin, const char* text_end = nullptr); // Draws multiline horizontal text centered. IMPLOT_API void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end = nullptr); // Calculates the size of vertical text static inline ImVec2 CalcTextSizeVertical(const char *text) { ImVec2 sz = ImGui::CalcTextSize(text); return ImVec2(sz.y, sz.x); } // Returns white or black text given background color static inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299f + bg.y * 0.587f + bg.z * 0.114f) > 0.5f ? IM_COL32_BLACK : IM_COL32_WHITE; } static inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); } // Lightens or darkens a color for hover static inline ImU32 CalcHoverColor(ImU32 col) { return ImMixU32(col, CalcTextColor(col), 32); } // Clamps a label position so that it fits a rect defined by Min/Max static inline ImVec2 ClampLabelPos(ImVec2 pos, const ImVec2& size, const ImVec2& Min, const ImVec2& Max) { if (pos.x < Min.x) pos.x = Min.x; if (pos.y < Min.y) pos.y = Min.y; if ((pos.x + size.x) > Max.x) pos.x = Max.x - size.x; if ((pos.y + size.y) > Max.y) pos.y = Max.y - size.y; return pos; } // Returns a color from the Color map given an index >= 0 (modulo will be performed). IMPLOT_API ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap); // Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. IMPLOT_API ImU32 NextColormapColorU32(); // Linearly interpolates a color from the current colormap given t between 0 and 1. IMPLOT_API ImU32 SampleColormapU32(float t, ImPlotColormap cmap); // Render a colormap bar IMPLOT_API void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous); //----------------------------------------------------------------------------- // [SECTION] Math and Misc Utils //----------------------------------------------------------------------------- // Rounds x to powers of 2,5 and 10 for generating axis labels (from Graphics Gems 1 Chapter 11.2) IMPLOT_API double NiceNum(double x, bool round); // Computes order of magnitude of double. static inline int OrderOfMagnitude(double val) { return val == 0 ? 0 : (int)(floor(log10(fabs(val)))); } // Returns the precision required for a order of magnitude. static inline int OrderToPrecision(int order) { return order > 0 ? 0 : 1 - order; } // Returns a floating point precision to use given a value static inline int Precision(double val) { return OrderToPrecision(OrderOfMagnitude(val)); } // Round a value to a given precision static inline double RoundTo(double val, int prec) { double p = pow(10,(double)prec); return floor(val*p+0.5)/p; } // Returns the intersection point of two lines A and B (assumes they are not parallel!) static inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) { float v1 = (a1.x * a2.y - a1.y * a2.x); float v2 = (b1.x * b2.y - b1.y * b2.x); float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)); return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3, (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3); } // Fills a buffer with n samples linear interpolated from vmin to vmax template void FillRange(ImVector& buffer, int n, T vmin, T vmax) { buffer.resize(n); T step = (vmax - vmin) / (n - 1); for (int i = 0; i < n; ++i) { buffer[i] = vmin + i * step; } } // Calculate histogram bin counts and widths template static inline void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) { switch (meth) { case ImPlotBin_Sqrt: bins_out = (int)ceil(sqrt(count)); break; case ImPlotBin_Sturges: bins_out = (int)ceil(1.0 + log2(count)); break; case ImPlotBin_Rice: bins_out = (int)ceil(2 * cbrt(count)); break; case ImPlotBin_Scott: width_out = 3.49 * ImStdDev(values, count) / cbrt(count); bins_out = (int)round(range.Size() / width_out); break; } width_out = range.Size() / bins_out; } //----------------------------------------------------------------------------- // Time Utils //----------------------------------------------------------------------------- // Returns true if year is leap year (366 days long) static inline bool IsLeapYear(int year) { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); } // Returns the number of days in a month, accounting for Feb. leap years. #month is zero indexed. static inline int GetDaysInMonth(int year, int month) { static const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return days[month] + (int)(month == 1 && IsLeapYear(year)); } // Make a UNIX timestamp from a tm struct expressed in UTC time (i.e. GMT timezone). IMPLOT_API ImPlotTime MkGmtTime(struct tm *ptm); // Make a tm struct expressed in UTC time (i.e. GMT timezone) from a UNIX timestamp. IMPLOT_API tm* GetGmtTime(const ImPlotTime& t, tm* ptm); // Make a UNIX timestamp from a tm struct expressed in local time. IMPLOT_API ImPlotTime MkLocTime(struct tm *ptm); // Make a tm struct expressed in local time from a UNIX timestamp. IMPLOT_API tm* GetLocTime(const ImPlotTime& t, tm* ptm); // NB: The following functions only work if there is a current ImPlotContext because the // internal tm struct is owned by the context! They are aware of ImPlotStyle.UseLocalTime. // Make a timestamp from time components. // year[1970-3000], month[0-11], day[1-31], hour[0-23], min[0-59], sec[0-59], us[0,999999] IMPLOT_API ImPlotTime MakeTime(int year, int month = 0, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0); // Get year component from timestamp [1970-3000] IMPLOT_API int GetYear(const ImPlotTime& t); // Adds or subtracts time from a timestamp. #count > 0 to add, < 0 to subtract. IMPLOT_API ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count); // Rounds a timestamp down to nearest unit. IMPLOT_API ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit); // Rounds a timestamp up to the nearest unit. IMPLOT_API ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit); // Rounds a timestamp up or down to the nearest unit. IMPLOT_API ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit); // Combines the date of one timestamp with the time-of-day of another timestamp. IMPLOT_API ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& time_part); // Formats the time part of timestamp t into a buffer according to #fmt IMPLOT_API int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk); // Formats the date part of timestamp t into a buffer according to #fmt IMPLOT_API int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601); // Formats the time and/or date parts of a timestamp t into a buffer according to #fmt IMPLOT_API int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt); // Shows a date picker widget block (year/month/day). // #level = 0 for day, 1 for month, 2 for year. Modified by user interaction. // #t will be set when a day is clicked and the function will return true. // #t1 and #t2 are optional dates to highlight. IMPLOT_API bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1 = nullptr, const ImPlotTime* t2 = nullptr); // Shows a time picker widget block (hour/min/sec). // #t will be set when a new hour, minute, or sec is selected or am/pm is toggled, and the function will return true. IMPLOT_API bool ShowTimePicker(const char* id, ImPlotTime* t); //----------------------------------------------------------------------------- // [SECTION] Transforms //----------------------------------------------------------------------------- static inline double TransformForward_Log10(double v, void*) { v = v <= 0.0 ? DBL_MIN : v; return ImLog10(v); } static inline double TransformInverse_Log10(double v, void*) { return ImPow(10, v); } static inline double TransformForward_SymLog(double v, void*) { return 2.0 * ImAsinh(v / 2.0); } static inline double TransformInverse_SymLog(double v, void*) { return 2.0 * ImSinh(v / 2.0); } static inline double TransformForward_Logit(double v, void*) { v = ImClamp(v, DBL_MIN, 1.0 - DBL_EPSILON); return ImLog10(v / (1 - v)); } static inline double TransformInverse_Logit(double v, void*) { return 1.0 / (1.0 + ImPow(10,-v)); } //----------------------------------------------------------------------------- // [SECTION] Formatters //----------------------------------------------------------------------------- static inline int Formatter_Default(double value, char* buff, int size, void* data) { char* fmt = (char*)data; return ImFormatString(buff, size, fmt, value); } static inline int Formatter_Logit(double value, char* buff, int size, void*) { if (value == 0.5) return ImFormatString(buff,size,"1/2"); else if (value < 0.5) return ImFormatString(buff,size,"%g", value); else return ImFormatString(buff,size,"1 - %g", 1 - value); } struct Formatter_Time_Data { ImPlotTime Time; ImPlotDateTimeSpec Spec; ImPlotFormatter UserFormatter; void* UserFormatterData; }; static inline int Formatter_Time(double, char* buff, int size, void* data) { Formatter_Time_Data* ftd = (Formatter_Time_Data*)data; return FormatDateTime(ftd->Time, buff, size, ftd->Spec); } //------------------------------------------------------------------------------ // [SECTION] Locator //------------------------------------------------------------------------------ void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); } // namespace ImPlot ================================================ FILE: lib/third_party/imgui/implot/source/implot.cpp ================================================ // MIT License // Copyright (c) 2023 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.17 /* API BREAKING CHANGES ==================== Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some 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 implot files. You can read releases logs https://github.com/epezent/implot/releases for more details. - 2023/08/20 (0.17) - ImPlotFlags_NoChild was removed as child windows are no longer needed to capture scroll. You can safely remove this flag if you were using it. - 2023/06/26 (0.15) - Various build fixes related to updates in Dear ImGui internals. - 2022/11/25 (0.15) - Make PlotText honor ImPlotItemFlags_NoFit. - 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter - 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters. If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see unexpected results or crashes without a compiler warning since these three are all default args. We apologize for the inconvenience, but this was a necessary evil. - PlotBarsH has been removed; use PlotBars + ImPlotBarsFlags_Horizontal instead - PlotErrorBarsH has been removed; use PlotErrorBars + ImPlotErrorBarsFlags_Horizontal - PlotHistogram/PlotHistogram2D signatures changed; `cumulative`, `density`, and `outliers` options now specified via ImPlotHistogramFlags - PlotPieChart signature changed; `normalize` option now specified via ImPlotPieChartFlags - PlotText signature changes; `vertical` option now specified via `ImPlotTextFlags_Vertical` - `PlotVLines` and `PlotHLines` replaced with `PlotInfLines` (+ ImPlotInfLinesFlags_Horizontal ) - arguments of ImPlotGetter have been reversed to be consistent with other API callbacks - SetupAxisScale + ImPlotScale have replaced ImPlotAxisFlags_LogScale and ImPlotAxisFlags_Time flags - ImPlotFormatters should now return an int indicating the size written - the signature of ImPlotGetter has been reversed so that void* user_data is the last argument and consistent with other callbacks - 2021/10/19 (0.13) - MAJOR API OVERHAUL! See #168 and #272 - TRIVIAL RENAME: - ImPlotLimits -> ImPlotRect - ImPlotYAxis_ -> ImAxis_ - SetPlotYAxis -> SetAxis - BeginDragDropTarget -> BeginDragDropTargetPlot - BeginDragDropSource -> BeginDragDropSourcePlot - ImPlotFlags_NoMousePos -> ImPlotFlags_NoMouseText - SetNextPlotLimits -> SetNextAxesLimits - SetMouseTextLocation -> SetupMouseText - SIGNATURE MODIFIED: - PixelsToPlot/PlotToPixels -> added optional X-Axis arg - GetPlotMousePos -> added optional X-Axis arg - GetPlotLimits -> added optional X-Axis arg - GetPlotSelection -> added optional X-Axis arg - DragLineX/Y/DragPoint -> now takes int id; removed labels (render with Annotation/Tag instead) - REPLACED: - IsPlotXAxisHovered/IsPlotXYAxisHovered -> IsAxisHovered(ImAxis) - BeginDragDropTargetX/BeginDragDropTargetY -> BeginDragDropTargetAxis(ImAxis) - BeginDragDropSourceX/BeginDragDropSourceY -> BeginDragDropSourceAxis(ImAxis) - ImPlotCol_XAxis, ImPlotCol_YAxis1, etc. -> ImPlotCol_AxisText (push/pop this around SetupAxis to style individual axes) - ImPlotCol_XAxisGrid, ImPlotCol_Y1AxisGrid -> ImPlotCol_AxisGrid (push/pop this around SetupAxis to style individual axes) - SetNextPlotLimitsX/Y -> SetNextAxisLimits(ImAxis) - LinkNextPlotLimits -> SetNextAxisLinks(ImAxis) - FitNextPlotAxes -> SetNextAxisToFit(ImAxis)/SetNextAxesToFit - SetLegendLocation -> SetupLegend - ImPlotFlags_NoHighlight -> ImPlotLegendFlags_NoHighlight - ImPlotOrientation -> ImPlotLegendFlags_Horizontal - Annotate -> Annotation - REMOVED: - GetPlotQuery, SetPlotQuery, IsPlotQueried -> use DragRect - SetNextPlotTicksX, SetNextPlotTicksY -> use SetupAxisTicks - SetNextPlotFormatX, SetNextPlotFormatY -> use SetupAxisFormat - AnnotateClamped -> use Annotation(bool clamp = true) - OBSOLETED: - BeginPlot (original signature) -> use simplified signature + Setup API - 2021/07/30 (0.12) - The offset argument of `PlotXG` functions was been removed. Implement offsetting in your getter callback instead. - 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap. ShowColormapScale was changed to ColormapScale and requires additional arguments. - 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. - 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. - 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved to implot_internal.h due to its immaturity. - 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding - 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0. - 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG) - 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset is simply 0. This more closely matches ImGui's style and makes it easier to enable non-default but commonly used flags (e.g. ImPlotAxisFlags_Time). - 2020/08/28 (0.5) - ImPlotMarker_ can no longer be combined with bitwise OR, |. This features caused unecessary slow-down, and almost no one used it. - 2020/08/25 (0.5) - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation. - 2020/08/17 (0.5) - PlotText was changed so that text is centered horizontally and vertically about the desired point. - 2020/08/16 (0.5) - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file. - 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default. - 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well. - 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`. - 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. - 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2 - 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine` and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate that multiple bars will be plotted. - 2020/05/13 (0.2) - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`. - 2020/05/11 (0.2) - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect` - 2020/05/11 (0.2) - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made: - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`. It should be fairly obvious what was what. - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'. - 2020/05/10 (0.2) - The following function/struct names were changes: - ImPlotRange -> ImPlotLimits - GetPlotRange() -> GetPlotLimits() - SetNextPlotRange -> SetNextPlotLimits - SetNextPlotRangeX -> SetNextPlotLimitsX - SetNextPlotRangeY -> SetNextPlotLimitsY - 2020/05/10 (0.2) - Plot queries are pixel based by default. Query rects that maintain relative plot position have been removed. This was done to support multi-y-axis. */ #define IMGUI_DEFINE_MATH_OPERATORS #include "implot.h" #include "implot_internal.h" #include // Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. #if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All #endif // Support for pre-1.89.7 versions. #if (IMGUI_VERSION_NUM < 18966) #define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap #endif // Visual Studio warnings #ifdef _MSC_VER #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #endif // Global plot context #ifndef GImPlot ImPlotContext* GImPlot = nullptr; #endif //----------------------------------------------------------------------------- // Struct Implementations //----------------------------------------------------------------------------- ImPlotInputMap::ImPlotInputMap() { ImPlot::MapInputDefault(this); } ImPlotStyle::ImPlotStyle() { LineWeight = 1; Marker = ImPlotMarker_None; MarkerSize = 4; MarkerWeight = 1; FillAlpha = 1; ErrorBarSize = 5; ErrorBarWeight = 1.5f; DigitalBitHeight = 8; DigitalBitGap = 4; PlotBorderSize = 1; MinorAlpha = 0.25f; MajorTickLen = ImVec2(10,10); MinorTickLen = ImVec2(5,5); MajorTickSize = ImVec2(1,1); MinorTickSize = ImVec2(1,1); MajorGridSize = ImVec2(1,1); MinorGridSize = ImVec2(1,1); PlotPadding = ImVec2(10,10); LabelPadding = ImVec2(5,5); LegendPadding = ImVec2(10,10); LegendInnerPadding = ImVec2(5,5); LegendSpacing = ImVec2(5,0); MousePosPadding = ImVec2(10,10); AnnotationPadding = ImVec2(2,2); FitPadding = ImVec2(0,0); PlotDefaultSize = ImVec2(400,300); PlotMinSize = ImVec2(200,150); ImPlot::StyleColorsAuto(this); Colormap = ImPlotColormap_Deep; UseLocalTime = false; Use24HourClock = false; UseISO8601 = false; } //----------------------------------------------------------------------------- // Style //----------------------------------------------------------------------------- namespace ImPlot { const char* GetStyleColorName(ImPlotCol col) { static const char* col_names[ImPlotCol_COUNT] = { "Line", "Fill", "MarkerOutline", "MarkerFill", "ErrorBar", "FrameBg", "PlotBg", "PlotBorder", "LegendBg", "LegendBorder", "LegendText", "TitleText", "InlayText", "AxisText", "AxisGrid", "AxisTick", "AxisBg", "AxisBgHovered", "AxisBgActive", "Selection", "Crosshairs" }; return col_names[col]; } const char* GetMarkerName(ImPlotMarker marker) { switch (marker) { case ImPlotMarker_None: return "None"; case ImPlotMarker_Circle: return "Circle"; case ImPlotMarker_Square: return "Square"; case ImPlotMarker_Diamond: return "Diamond"; case ImPlotMarker_Up: return "Up"; case ImPlotMarker_Down: return "Down"; case ImPlotMarker_Left: return "Left"; case ImPlotMarker_Right: return "Right"; case ImPlotMarker_Cross: return "Cross"; case ImPlotMarker_Plus: return "Plus"; case ImPlotMarker_Asterisk: return "Asterisk"; default: return ""; } } ImVec4 GetAutoColor(ImPlotCol idx) { ImVec4 col(0,0,0,1); switch(idx) { case ImPlotCol_Line: return col; // these are plot dependent! case ImPlotCol_Fill: return col; // these are plot dependent! case ImPlotCol_MarkerOutline: return col; // these are plot dependent! case ImPlotCol_MarkerFill: return col; // these are plot dependent! case ImPlotCol_ErrorBar: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_FrameBg: return ImGui::GetStyleColorVec4(ImGuiCol_FrameBg); case ImPlotCol_PlotBg: return ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); case ImPlotCol_PlotBorder: return ImGui::GetStyleColorVec4(ImGuiCol_Border); case ImPlotCol_LegendBg: return ImGui::GetStyleColorVec4(ImGuiCol_PopupBg); case ImPlotCol_LegendBorder: return GetStyleColorVec4(ImPlotCol_PlotBorder); case ImPlotCol_LegendText: return GetStyleColorVec4(ImPlotCol_InlayText); case ImPlotCol_TitleText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_InlayText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_AxisText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_AxisGrid: return GetStyleColorVec4(ImPlotCol_AxisText) * ImVec4(1,1,1,0.25f); case ImPlotCol_AxisTick: return GetStyleColorVec4(ImPlotCol_AxisGrid); case ImPlotCol_AxisBg: return ImVec4(0,0,0,0); case ImPlotCol_AxisBgHovered: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); case ImPlotCol_AxisBgActive: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); case ImPlotCol_Selection: return ImVec4(1,1,0,1); case ImPlotCol_Crosshairs: return GetStyleColorVec4(ImPlotCol_PlotBorder); default: return col; } } struct ImPlotStyleVarInfo { ImGuiDataType Type; ImU32 Count; ImU32 Offset; void* GetVarPtr(ImPlotStyle* style) const { return (void*)((unsigned char*)style + Offset); } }; static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = { { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight { ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize }; static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) { IM_ASSERT(idx >= 0 && idx < ImPlotStyleVar_COUNT); IM_ASSERT(IM_ARRAYSIZE(GPlotStyleVarInfo) == ImPlotStyleVar_COUNT); return &GPlotStyleVarInfo[idx]; } //----------------------------------------------------------------------------- // Generic Helpers //----------------------------------------------------------------------------- void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *text_begin, const char* text_end) { // the code below is based loosely on ImFont::RenderText if (!text_end) text_end = text_begin + strlen(text_begin); ImGuiContext& g = *GImGui; #ifdef IMGUI_HAS_TEXTURES ImFontBaked* font = g.Font->GetFontBaked(g.FontSize); const float scale = g.FontSize / font->Size; #else ImFont* font = g.Font; const float scale = g.FontSize / font->FontSize; #endif // Align to be pixel perfect pos.x = ImFloor(pos.x); pos.y = ImFloor(pos.y); const char* s = text_begin; int chars_exp = (int)(text_end - s); int chars_rnd = 0; const int vtx_count_max = chars_exp * 4; const int idx_count_max = chars_exp * 6; DrawList->PrimReserve(idx_count_max, vtx_count_max); while (s < text_end) { unsigned int c = (unsigned int)*s; if (c < 0x80) { s += 1; } else { s += ImTextCharFromUtf8(&c, s, text_end); if (c == 0) // Malformed UTF-8? break; } const ImFontGlyph * glyph = font->FindGlyph((ImWchar)c); if (glyph == nullptr) { continue; } DrawList->PrimQuadUV(pos + ImVec2(glyph->Y0, -glyph->X0) * scale, pos + ImVec2(glyph->Y0, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X0) * scale, ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V0), ImVec2(glyph->U1, glyph->V1), ImVec2(glyph->U0, glyph->V1), col); pos.y -= glyph->AdvanceX * scale; chars_rnd++; } // Give back unused vertices int chars_skp = chars_exp-chars_rnd; DrawList->PrimUnreserve(chars_skp*6, chars_skp*4); } void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end) { float txt_ht = ImGui::GetTextLineHeight(); const char* title_end = ImGui::FindRenderedTextEnd(text_begin, text_end); ImVec2 text_size; float y = 0; while (const char* tmp = (const char*)memchr(text_begin, '\n', title_end-text_begin)) { text_size = ImGui::CalcTextSize(text_begin,tmp,true); DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,tmp); text_begin = tmp + 1; y += txt_ht; } text_size = ImGui::CalcTextSize(text_begin,title_end,true); DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,title_end); } double NiceNum(double x, bool round) { double f; double nf; int expv = (int)floor(ImLog10(x)); f = x / ImPow(10.0, (double)expv); if (round) if (f < 1.5) nf = 1; else if (f < 3) nf = 2; else if (f < 7) nf = 5; else nf = 10; else if (f <= 1) nf = 1; else if (f <= 2) nf = 2; else if (f <= 5) nf = 5; else nf = 10; return nf * ImPow(10.0, expv); } //----------------------------------------------------------------------------- // Context Utils //----------------------------------------------------------------------------- void SetImGuiContext(ImGuiContext* ctx) { ImGui::SetCurrentContext(ctx); } ImPlotContext* CreateContext() { ImPlotContext* ctx = IM_NEW(ImPlotContext)(); Initialize(ctx); if (GImPlot == nullptr) SetCurrentContext(ctx); return ctx; } void DestroyContext(ImPlotContext* ctx) { if (ctx == nullptr) ctx = GImPlot; if (GImPlot == ctx) SetCurrentContext(nullptr); IM_DELETE(ctx); } ImPlotContext* GetCurrentContext() { return GImPlot; } void SetCurrentContext(ImPlotContext* ctx) { GImPlot = ctx; } #define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual) #define IM_RGB(r,g,b) IM_COL32(r,g,b,255) void Initialize(ImPlotContext* ctx) { ResetCtxForNextPlot(ctx); ResetCtxForNextAlignedPlots(ctx); ResetCtxForNextSubplot(ctx); const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481}; const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 }; const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 }; const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; const ImU32 Twilight[] = {IM_RGB(226,217,226),IM_RGB(166,191,202),IM_RGB(109,144,192),IM_RGB(95,88,176),IM_RGB(83,30,124),IM_RGB(47,20,54),IM_RGB(100,25,75),IM_RGB(159,60,80),IM_RGB(192,117,94),IM_RGB(208,179,158),IM_RGB(226,217,226)}; const ImU32 RdBu[] = {IM_RGB(103,0,31),IM_RGB(178,24,43),IM_RGB(214,96,77),IM_RGB(244,165,130),IM_RGB(253,219,199),IM_RGB(247,247,247),IM_RGB(209,229,240),IM_RGB(146,197,222),IM_RGB(67,147,195),IM_RGB(33,102,172),IM_RGB(5,48,97)}; const ImU32 BrBG[] = {IM_RGB(84,48,5),IM_RGB(140,81,10),IM_RGB(191,129,45),IM_RGB(223,194,125),IM_RGB(246,232,195),IM_RGB(245,245,245),IM_RGB(199,234,229),IM_RGB(128,205,193),IM_RGB(53,151,143),IM_RGB(1,102,94),IM_RGB(0,60,48)}; const ImU32 PiYG[] = {IM_RGB(142,1,82),IM_RGB(197,27,125),IM_RGB(222,119,174),IM_RGB(241,182,218),IM_RGB(253,224,239),IM_RGB(247,247,247),IM_RGB(230,245,208),IM_RGB(184,225,134),IM_RGB(127,188,65),IM_RGB(77,146,33),IM_RGB(39,100,25)}; const ImU32 Spectral[] = {IM_RGB(158,1,66),IM_RGB(213,62,79),IM_RGB(244,109,67),IM_RGB(253,174,97),IM_RGB(254,224,139),IM_RGB(255,255,191),IM_RGB(230,245,152),IM_RGB(171,221,164),IM_RGB(102,194,165),IM_RGB(50,136,189),IM_RGB(94,79,162)}; const ImU32 Greys[] = {IM_COL32_WHITE, IM_COL32_BLACK }; IMPLOT_APPEND_CMAP(Deep, true); IMPLOT_APPEND_CMAP(Dark, true); IMPLOT_APPEND_CMAP(Pastel, true); IMPLOT_APPEND_CMAP(Paired, true); IMPLOT_APPEND_CMAP(Viridis, false); IMPLOT_APPEND_CMAP(Plasma, false); IMPLOT_APPEND_CMAP(Hot, false); IMPLOT_APPEND_CMAP(Cool, false); IMPLOT_APPEND_CMAP(Pink, false); IMPLOT_APPEND_CMAP(Jet, false); IMPLOT_APPEND_CMAP(Twilight, false); IMPLOT_APPEND_CMAP(RdBu, false); IMPLOT_APPEND_CMAP(BrBG, false); IMPLOT_APPEND_CMAP(PiYG, false); IMPLOT_APPEND_CMAP(Spectral, false); IMPLOT_APPEND_CMAP(Greys, false); } void ResetCtxForNextPlot(ImPlotContext* ctx) { // reset the next plot/item data ctx->NextPlotData.Reset(); ctx->NextItemData.Reset(); // reset labels ctx->Annotations.Reset(); ctx->Tags.Reset(); // reset extents/fit ctx->OpenContextThisFrame = false; // reset digital plot items count ctx->DigitalPlotItemCnt = 0; ctx->DigitalPlotOffset = 0; // nullify plot ctx->CurrentPlot = nullptr; ctx->CurrentItem = nullptr; ctx->PreviousItem = nullptr; } void ResetCtxForNextAlignedPlots(ImPlotContext* ctx) { ctx->CurrentAlignmentH = nullptr; ctx->CurrentAlignmentV = nullptr; } void ResetCtxForNextSubplot(ImPlotContext* ctx) { ctx->CurrentSubplot = nullptr; ctx->CurrentAlignmentH = nullptr; ctx->CurrentAlignmentV = nullptr; } //----------------------------------------------------------------------------- // Plot Utils //----------------------------------------------------------------------------- ImPlotPlot* GetPlot(const char* title) { ImGuiWindow* Window = GImGui->CurrentWindow; const ImGuiID ID = Window->GetID(title); return GImPlot->Plots.GetByKey(ID); } ImPlotPlot* GetCurrentPlot() { return GImPlot->CurrentPlot; } void BustPlotCache() { ImPlotContext& gp = *GImPlot; gp.Plots.Clear(); gp.Subplots.Clear(); } //----------------------------------------------------------------------------- // Legend Utils //----------------------------------------------------------------------------- ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation loc, const ImVec2& pad) { ImVec2 pos; if (ImHasFlag(loc, ImPlotLocation_West) && !ImHasFlag(loc, ImPlotLocation_East)) pos.x = outer_rect.Min.x + pad.x; else if (!ImHasFlag(loc, ImPlotLocation_West) && ImHasFlag(loc, ImPlotLocation_East)) pos.x = outer_rect.Max.x - pad.x - inner_size.x; else pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f; // legend reference point y if (ImHasFlag(loc, ImPlotLocation_North) && !ImHasFlag(loc, ImPlotLocation_South)) pos.y = outer_rect.Min.y + pad.y; else if (!ImHasFlag(loc, ImPlotLocation_North) && ImHasFlag(loc, ImPlotLocation_South)) pos.y = outer_rect.Max.y - pad.y - inner_size.y; else pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f; pos.x = IM_ROUND(pos.x); pos.y = IM_ROUND(pos.y); return pos; } ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) { // vars const int nItems = items.GetLegendCount(); const float txt_ht = ImGui::GetTextLineHeight(); const float icon_size = txt_ht; // get label max width float max_label_width = 0; float sum_label_width = 0; for (int i = 0; i < nItems; ++i) { const char* label = items.GetLegendLabel(i); const float label_width = ImGui::CalcTextSize(label, nullptr, true).x; max_label_width = label_width > max_label_width ? label_width : max_label_width; sum_label_width += label_width; } // calc legend size const ImVec2 legend_size = vertical ? ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) : ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht); return legend_size; } bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad) { bool clamped = false; ImRect outer_rect_pad(outer_rect.Min + pad, outer_rect.Max - pad); if (legend_rect.Min.x < outer_rect_pad.Min.x) { legend_rect.Min.x = outer_rect_pad.Min.x; clamped = true; } if (legend_rect.Min.y < outer_rect_pad.Min.y) { legend_rect.Min.y = outer_rect_pad.Min.y; clamped = true; } if (legend_rect.Max.x > outer_rect_pad.Max.x) { legend_rect.Max.x = outer_rect_pad.Max.x; clamped = true; } if (legend_rect.Max.y > outer_rect_pad.Max.y) { legend_rect.Max.y = outer_rect_pad.Max.y; clamped = true; } return clamped; } int LegendSortingComp(const void* _a, const void* _b) { ImPlotItemGroup* items = GImPlot->SortItems; const int a = *(const int*)_a; const int b = *(const int*)_b; const char* label_a = items->GetLegendLabel(a); const char* label_b = items->GetLegendLabel(b); return strcmp(label_a,label_b); } bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList) { // vars const float txt_ht = ImGui::GetTextLineHeight(); const float icon_size = txt_ht; const float icon_shrink = 2; ImU32 col_txt = GetStyleColorU32(ImPlotCol_LegendText); ImU32 col_txt_dis = ImAlphaU32(col_txt, 0.25f); // render each legend item float sum_label_width = 0; bool any_item_hovered = false; const int num_items = items.GetLegendCount(); if (num_items < 1) return hovered; // build render order ImPlotContext& gp = *GImPlot; ImVector& indices = gp.TempInt1; indices.resize(num_items); for (int i = 0; i < num_items; ++i) indices[i] = i; if (ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_Sort) && num_items > 1) { gp.SortItems = &items; qsort(indices.Data, num_items, sizeof(int), LegendSortingComp); } // render for (int i = 0; i < num_items; ++i) { const int idx = indices[i]; ImPlotItem* item = items.GetLegendItem(idx); const char* label = items.GetLegendLabel(idx); const float label_width = ImGui::CalcTextSize(label, nullptr, true).x; const ImVec2 top_left = vertical ? legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) : legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0); sum_label_width += label_width; ImRect icon_bb; icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink); icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink); ImRect label_bb; label_bb.Min = top_left; label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size); ImU32 col_txt_hl; ImU32 col_item = ImAlphaU32(item->Color,1); ImRect button_bb(icon_bb.Min, label_bb.Max); ImGui::KeepAliveID(item->ID); bool item_hov = false; bool item_hld = false; bool item_clk = ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoButtons) ? false : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld); if (item_clk) item->Show = !item->Show; const bool can_hover = (item_hov) && (!ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightItem) || !ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)); if (can_hover) { item->LegendHoverRect.Min = icon_bb.Min; item->LegendHoverRect.Max = label_bb.Max; item->LegendHovered = true; col_txt_hl = ImMixU32(col_txt, col_item, 64); any_item_hovered = true; } else { col_txt_hl = ImGui::GetColorU32(col_txt); } ImU32 col_icon; if (item_hld) col_icon = item->Show ? ImAlphaU32(col_item,0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f); else if (item_hov) col_icon = item->Show ? ImAlphaU32(col_item,0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f); else col_icon = item->Show ? col_item : col_txt_dis; DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, col_icon); const char* text_display_end = ImGui::FindRenderedTextEnd(label, nullptr); if (label != text_display_end) DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl : col_txt_dis, label, text_display_end); } return hovered && !any_item_hovered; } //----------------------------------------------------------------------------- // Locators //----------------------------------------------------------------------------- static const float TICK_FILL_X = 0.8f; static const float TICK_FILL_Y = 1.0f; void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { if (range.Min == range.Max) return; const int nMinor = 10; const int nMajor = ImMax(2, (int)IM_ROUND(pixels / (vertical ? 300.0f : 400.0f))); const double nice_range = NiceNum(range.Size() * 0.99, false); const double interval = NiceNum(nice_range / (nMajor - 1), true); const double graphmin = floor(range.Min / interval) * interval; const double graphmax = ceil(range.Max / interval) * interval; bool first_major_set = false; int first_major_idx = 0; const int idx0 = ticker.TickCount(); // ticker may have user custom ticks ImVec2 total_size(0,0); for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { // is this zero? combat zero formatting issues if (major-interval < 0 && major+interval > 0) major = 0; if (range.Contains(major)) { if (!first_major_set) { first_major_idx = ticker.TickCount(); first_major_set = true; } total_size += ticker.AddTick(major, true, 0, true, formatter, formatter_data).LabelSize; } for (int i = 1; i < nMinor; ++i) { double minor = major + i * interval / nMinor; if (range.Contains(minor)) { total_size += ticker.AddTick(minor, false, 0, true, formatter, formatter_data).LabelSize; } } } // prune if necessary if ((!vertical && total_size.x > pixels*TICK_FILL_X) || (vertical && total_size.y > pixels*TICK_FILL_Y)) { for (int i = first_major_idx-1; i >= idx0; i -= 2) ticker.Ticks[i].ShowLabel = false; for (int i = first_major_idx+1; i < ticker.TickCount(); i += 2) ticker.Ticks[i].ShowLabel = false; } } bool CalcLogarithmicExponents(const ImPlotRange& range, float pix, bool vertical, int& exp_min, int& exp_max, int& exp_step) { if (range.Min * range.Max > 0) { const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); // TODO: magic numbers double log_min = ImLog10(ImAbs(range.Min)); double log_max = ImLog10(ImAbs(range.Max)); double log_a = ImMin(log_min,log_max); double log_b = ImMax(log_min,log_max); exp_step = ImMax(1,(int)(log_b - log_a) / nMajor); exp_min = (int)log_a; exp_max = (int)log_b; if (exp_step != 1) { while(exp_step % 3 != 0) exp_step++; // make step size multiple of three while(exp_min % exp_step != 0) exp_min--; // decrease exp_min until exp_min + N * exp_step will be 0 } return true; } return false; } void AddTicksLogarithmic(const ImPlotRange& range, int exp_min, int exp_max, int exp_step, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { const double sign = ImSign(range.Max); for (int e = exp_min - exp_step; e < (exp_max + exp_step); e += exp_step) { double major1 = sign*ImPow(10, (double)(e)); double major2 = sign*ImPow(10, (double)(e + 1)); double interval = (major2 - major1) / 9; if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) ticker.AddTick(major1, true, 0, true, formatter, data); for (int j = 0; j < exp_step; ++j) { major1 = sign*ImPow(10, (double)(e+j)); major2 = sign*ImPow(10, (double)(e+j+1)); interval = (major2 - major1) / 9; for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) { double minor = major1 + i * interval; if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON)) ticker.AddTick(minor, false, 0, false, formatter, data); } } } } void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { int exp_min, exp_max, exp_step; if (CalcLogarithmicExponents(range, pixels, vertical, exp_min, exp_max, exp_step)) AddTicksLogarithmic(range, exp_min, exp_max, exp_step, ticker, formatter, formatter_data); } float CalcSymLogPixel(double plt, const ImPlotRange& range, float pixels) { double scaleToPixels = pixels / range.Size(); double scaleMin = TransformForward_SymLog(range.Min,nullptr); double scaleMax = TransformForward_SymLog(range.Max,nullptr); double s = TransformForward_SymLog(plt, nullptr); double t = (s - scaleMin) / (scaleMax - scaleMin); plt = range.Min + range.Size() * t; return (float)(0 + scaleToPixels * (plt - range.Min)); } void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { if (range.Min >= -1 && range.Max <= 1) { Locator_Default(ticker, range, pixels, vertical, formatter, formatter_data); } else if (range.Min * range.Max < 0) { // cross zero const float pix_min = 0; const float pix_max = pixels; const float pix_p1 = CalcSymLogPixel(1, range, pixels); const float pix_n1 = CalcSymLogPixel(-1, range, pixels); int exp_min_p, exp_max_p, exp_step_p; int exp_min_n, exp_max_n, exp_step_n; CalcLogarithmicExponents(ImPlotRange(1,range.Max), ImAbs(pix_max-pix_p1),vertical,exp_min_p,exp_max_p,exp_step_p); CalcLogarithmicExponents(ImPlotRange(range.Min,-1),ImAbs(pix_n1-pix_min),vertical,exp_min_n,exp_max_n,exp_step_n); int exp_step = ImMax(exp_step_n, exp_step_p); ticker.AddTick(0,true,0,true,formatter,formatter_data); AddTicksLogarithmic(ImPlotRange(1,range.Max), exp_min_p,exp_max_p,exp_step,ticker,formatter,formatter_data); AddTicksLogarithmic(ImPlotRange(range.Min,-1),exp_min_n,exp_max_n,exp_step,ticker,formatter,formatter_data); } else { Locator_Log10(ticker, range, pixels, vertical, formatter, formatter_data); } } void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { for (int i = 0; i < n; ++i) { if (labels != nullptr) ticker.AddTick(values[i], false, 0, true, labels[i]); else ticker.AddTick(values[i], false, 0, true, formatter, data); } } //----------------------------------------------------------------------------- // Time Ticks and Utils //----------------------------------------------------------------------------- // this may not be thread safe? static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = { 0.000001, 0.001, 1, 60, 3600, 86400, 2629800, 31557600 }; inline ImPlotTimeUnit GetUnitForRange(double range) { static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME}; for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) { if (range <= cutoffs[i]) return (ImPlotTimeUnit)i; } return ImPlotTimeUnit_Yr; } inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) { if (max_divs < divs[0]) return 0; for (int i = 1; i < size; ++i) { if (max_divs < divs[i]) return step[i-1]; } return step[size-1]; } inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) { if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) { static const int step[] = {500,250,200,100,50,25,20,10,5,2,1}; static const int divs[] = {2,4,5,10,20,40,50,100,200,500,1000}; return LowerBoundStep(max_divs, divs, step, 11); } if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) { static const int step[] = {30,15,10,5,1}; static const int divs[] = {2,4,6,12,60}; return LowerBoundStep(max_divs, divs, step, 5); } else if (unit == ImPlotTimeUnit_Hr) { static const int step[] = {12,6,3,2,1}; static const int divs[] = {2,4,8,12,24}; return LowerBoundStep(max_divs, divs, step, 5); } else if (unit == ImPlotTimeUnit_Day) { static const int step[] = {14,7,2,1}; static const int divs[] = {2,4,14,28}; return LowerBoundStep(max_divs, divs, step, 4); } else if (unit == ImPlotTimeUnit_Mo) { static const int step[] = {6,3,2,1}; static const int divs[] = {2,4,6,12}; return LowerBoundStep(max_divs, divs, step, 4); } return 0; } ImPlotTime MkGmtTime(struct tm *ptm) { ImPlotTime t; #ifdef _WIN32 t.S = _mkgmtime(ptm); #else t.S = timegm(ptm); #endif if (t.S < 0) t.S = 0; return t; } tm* GetGmtTime(const ImPlotTime& t, tm* ptm) { #ifdef _WIN32 if (gmtime_s(ptm, &t.S) == 0) return ptm; else return nullptr; #else return gmtime_r(&t.S, ptm); #endif } ImPlotTime MkLocTime(struct tm *ptm) { ImPlotTime t; t.S = mktime(ptm); if (t.S < 0) t.S = 0; return t; } tm* GetLocTime(const ImPlotTime& t, tm* ptm) { #ifdef _WIN32 if (localtime_s(ptm, &t.S) == 0) return ptm; else return nullptr; #else return localtime_r(&t.S, ptm); #endif } inline ImPlotTime MkTime(struct tm *ptm) { if (GetStyle().UseLocalTime) return MkLocTime(ptm); else return MkGmtTime(ptm); } inline tm* GetTime(const ImPlotTime& t, tm* ptm) { if (GetStyle().UseLocalTime) return GetLocTime(t,ptm); else return GetGmtTime(t,ptm); } ImPlotTime MakeTime(int year, int month, int day, int hour, int min, int sec, int us) { tm& Tm = GImPlot->Tm; int yr = year - 1900; if (yr < 0) yr = 0; sec = sec + us / 1000000; us = us % 1000000; Tm.tm_sec = sec; Tm.tm_min = min; Tm.tm_hour = hour; Tm.tm_mday = day; Tm.tm_mon = month; Tm.tm_year = yr; ImPlotTime t = MkTime(&Tm); t.Us = us; return t; } int GetYear(const ImPlotTime& t) { tm& Tm = GImPlot->Tm; GetTime(t, &Tm); return Tm.tm_year + 1900; } ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) { tm& Tm = GImPlot->Tm; ImPlotTime t_out = t; switch(unit) { case ImPlotTimeUnit_Us: t_out.Us += count; break; case ImPlotTimeUnit_Ms: t_out.Us += count * 1000; break; case ImPlotTimeUnit_S: t_out.S += count; break; case ImPlotTimeUnit_Min: t_out.S += count * 60; break; case ImPlotTimeUnit_Hr: t_out.S += count * 3600; break; case ImPlotTimeUnit_Day: t_out.S += count * 86400; break; case ImPlotTimeUnit_Mo: for (int i = 0; i < abs(count); ++i) { GetTime(t_out, &Tm); if (count > 0) t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon); else if (count < 0) t_out.S -= 86400 * GetDaysInMonth(Tm.tm_year + 1900 - (Tm.tm_mon == 0 ? 1 : 0), Tm.tm_mon == 0 ? 11 : Tm.tm_mon - 1); // NOT WORKING } break; case ImPlotTimeUnit_Yr: for (int i = 0; i < abs(count); ++i) { if (count > 0) t_out.S += 86400 * (365 + (int)IsLeapYear(GetYear(t_out))); else if (count < 0) t_out.S -= 86400 * (365 + (int)IsLeapYear(GetYear(t_out) - 1)); // this is incorrect if leap year and we are past Feb 28 } break; default: break; } t_out.RollOver(); return t_out; } ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit) { ImPlotContext& gp = *GImPlot; GetTime(t, &gp.Tm); switch (unit) { case ImPlotTimeUnit_S: return ImPlotTime(t.S, 0); case ImPlotTimeUnit_Ms: return ImPlotTime(t.S, (t.Us / 1000) * 1000); case ImPlotTimeUnit_Us: return t; case ImPlotTimeUnit_Yr: gp.Tm.tm_mon = 0; // fall-through case ImPlotTimeUnit_Mo: gp.Tm.tm_mday = 1; // fall-through case ImPlotTimeUnit_Day: gp.Tm.tm_hour = 0; // fall-through case ImPlotTimeUnit_Hr: gp.Tm.tm_min = 0; // fall-through case ImPlotTimeUnit_Min: gp.Tm.tm_sec = 0; break; default: return t; } return MkTime(&gp.Tm); } ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit) { return AddTime(FloorTime(t, unit), unit, 1); } ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit) { ImPlotTime t1 = FloorTime(t, unit); ImPlotTime t2 = AddTime(t1,unit,1); if (t1.S == t2.S) return t.Us - t1.Us < t2.Us - t.Us ? t1 : t2; return t.S - t1.S < t2.S - t.S ? t1 : t2; } ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& tod_part) { ImPlotContext& gp = *GImPlot; tm& Tm = gp.Tm; GetTime(date_part, &gp.Tm); int y = Tm.tm_year; int m = Tm.tm_mon; int d = Tm.tm_mday; GetTime(tod_part, &gp.Tm); Tm.tm_year = y; Tm.tm_mon = m; Tm.tm_mday = d; ImPlotTime t = MkTime(&Tm); t.Us = tod_part.Us; return t; } // TODO: allow users to define these static const char* MONTH_NAMES[] = {"January","February","March","April","May","June","July","August","September","October","November","December"}; static const char* WD_ABRVS[] = {"Su","Mo","Tu","We","Th","Fr","Sa"}; static const char* MONTH_ABRVS[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk) { tm& Tm = GImPlot->Tm; GetTime(t, &Tm); const int us = t.Us % 1000; const int ms = t.Us / 1000; const int sec = Tm.tm_sec; const int min = Tm.tm_min; if (use_24_hr_clk) { const int hr = Tm.tm_hour; switch(fmt) { case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); case ImPlotTimeFmt_MinSMs: return ImFormatString(buffer, size, ":%02d:%02d.%03d", min, sec, ms); case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%02d:%02d:%02d.%03d", hr, min, sec, ms); case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%02d:%02d:%02d", hr, min, sec); case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%02d:%02d", hr, min); case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%02d:00", hr); default: return 0; } } else { const char* ap = Tm.tm_hour < 12 ? "am" : "pm"; const int hr = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12; switch(fmt) { case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); case ImPlotTimeFmt_MinSMs: return ImFormatString(buffer, size, ":%02d:%02d.%03d", min, sec, ms); case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%d:%02d:%02d.%03d%s", hr, min, sec, ms, ap); case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap); case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%d:%02d%s", hr, min, ap); case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%d%s", hr, ap); default: return 0; } } } int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601) { tm& Tm = GImPlot->Tm; GetTime(t, &Tm); const int day = Tm.tm_mday; const int mon = Tm.tm_mon + 1; const int year = Tm.tm_year + 1900; const int yr = year % 100; if (use_iso_8601) { switch (fmt) { case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "--%02d-%02d", mon, day); case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d-%02d-%02d", year, mon, day); case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%d-%02d", year, mon); case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "--%02d", mon); case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); default: return 0; } } else { switch (fmt) { case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "%d/%d", mon, day); case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d/%d/%02d", mon, day, yr); case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%s %d", MONTH_ABRVS[Tm.tm_mon], year); case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "%s", MONTH_ABRVS[Tm.tm_mon]); case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); default: return 0; } } } int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt) { int written = 0; if (fmt.Date != ImPlotDateFmt_None) written += FormatDate(t, buffer, size, fmt.Date, fmt.UseISO8601); if (fmt.Time != ImPlotTimeFmt_None) { if (fmt.Date != ImPlotDateFmt_None) buffer[written++] = ' '; written += FormatTime(t, &buffer[written], size - written, fmt.Time, fmt.Use24HourClock); } return written; } inline float GetDateTimeWidth(ImPlotDateTimeSpec fmt) { static const ImPlotTime t_max_width = MakeTime(2888, 12, 22, 12, 58, 58, 888888); // best guess at time that maximizes pixel width char buffer[32]; FormatDateTime(t_max_width, buffer, 32, fmt); return ImGui::CalcTextSize(buffer).x; } inline bool TimeLabelSame(const char* l1, const char* l2) { size_t len1 = strlen(l1); size_t len2 = strlen(l2); size_t n = len1 < len2 ? len1 : len2; return strcmp(l1 + len1 - n, l2 + len2 - n) == 0; } static const ImPlotDateTimeSpec TimeFormatLevel0[ImPlotTimeUnit_COUNT] = { ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_S), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Hr), ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Mo, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; static const ImPlotDateTimeSpec TimeFormatLevel1[ImPlotTimeUnit_COUNT] = { ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; static const ImPlotDateTimeSpec TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = { ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; static const ImPlotDateTimeSpec TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = { ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SUs), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_Hr), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_MoYr, ImPlotTimeFmt_None) }; inline ImPlotDateTimeSpec GetDateTimeFmt(const ImPlotDateTimeSpec* ctx, ImPlotTimeUnit idx) { ImPlotStyle& style = GetStyle(); ImPlotDateTimeSpec fmt = ctx[idx]; fmt.UseISO8601 = style.UseISO8601; fmt.Use24HourClock = style.Use24HourClock; return fmt; } void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { IM_ASSERT_USER_ERROR(vertical == false, "Cannot locate Time ticks on vertical axis!"); (void)vertical; // get units for level 0 and level 1 labels const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (pixels / 100)); // level = 0 (top) const ImPlotTimeUnit unit1 = ImClamp(unit0 + 1, 0, ImPlotTimeUnit_COUNT-1); // level = 1 (bottom) // get time format specs const ImPlotDateTimeSpec fmt0 = GetDateTimeFmt(TimeFormatLevel0, unit0); const ImPlotDateTimeSpec fmt1 = GetDateTimeFmt(TimeFormatLevel1, unit1); const ImPlotDateTimeSpec fmtf = GetDateTimeFmt(TimeFormatLevel1First, unit1); // min max times const ImPlotTime t_min = ImPlotTime::FromDouble(range.Min); const ImPlotTime t_max = ImPlotTime::FromDouble(range.Max); // maximum allowable density of labels const float max_density = 0.5f; // book keeping int last_major_offset = -1; // formatter data Formatter_Time_Data ftd; ftd.UserFormatter = formatter; ftd.UserFormatterData = formatter_data; if (unit0 != ImPlotTimeUnit_Yr) { // pixels per major (level 1) division const float pix_per_major_div = pixels / (float)(range.Size() / TimeUnitSpans[unit1]); // nominal pixels taken up by labels const float fmt0_width = GetDateTimeWidth(fmt0); const float fmt1_width = GetDateTimeWidth(fmt1); const float fmtf_width = GetDateTimeWidth(fmtf); // the maximum number of minor (level 0) labels that can fit between major (level 1) divisions const int minor_per_major = (int)(max_density * pix_per_major_div / fmt0_width); // the minor step size (level 0) const int step = GetTimeStep(minor_per_major, unit0); // generate ticks ImPlotTime t1 = FloorTime(ImPlotTime::FromDouble(range.Min), unit1); while (t1 < t_max) { // get next major const ImPlotTime t2 = AddTime(t1, unit1, 1); // add major tick if (t1 >= t_min && t1 <= t_max) { // minor level 0 tick ftd.Time = t1; ftd.Spec = fmt0; ticker.AddTick(t1.ToDouble(), true, 0, true, Formatter_Time, &ftd); // major level 1 tick ftd.Time = t1; ftd.Spec = last_major_offset < 0 ? fmtf : fmt1; ImPlotTick& tick_maj = ticker.AddTick(t1.ToDouble(), true, 1, true, Formatter_Time, &ftd); const char* this_major = ticker.GetText(tick_maj); if (last_major_offset >= 0 && TimeLabelSame(ticker.TextBuffer.Buf.Data + last_major_offset, this_major)) tick_maj.ShowLabel = false; last_major_offset = tick_maj.TextOffset; } // add minor ticks up until next major if (minor_per_major > 1 && (t_min <= t2 && t1 <= t_max)) { ImPlotTime t12 = AddTime(t1, unit0, step); while (t12 < t2) { float px_to_t2 = (float)((t2 - t12).ToDouble()/range.Size()) * pixels; if (t12 >= t_min && t12 <= t_max) { ftd.Time = t12; ftd.Spec = fmt0; ticker.AddTick(t12.ToDouble(), false, 0, px_to_t2 >= fmt0_width, Formatter_Time, &ftd); if (last_major_offset < 0 && px_to_t2 >= fmt0_width && px_to_t2 >= (fmt1_width + fmtf_width) / 2) { ftd.Time = t12; ftd.Spec = fmtf; ImPlotTick& tick_maj = ticker.AddTick(t12.ToDouble(), true, 1, true, Formatter_Time, &ftd); last_major_offset = tick_maj.TextOffset; } } t12 = AddTime(t12, unit0, step); } } t1 = t2; } } else { const ImPlotDateTimeSpec fmty = GetDateTimeFmt(TimeFormatLevel0, ImPlotTimeUnit_Yr); const float label_width = GetDateTimeWidth(fmty); const int max_labels = (int)(max_density * pixels / label_width); const int year_min = GetYear(t_min); const int year_max = GetYear(CeilTime(t_max, ImPlotTimeUnit_Yr)); const double nice_range = NiceNum((year_max - year_min)*0.99,false); const double interval = NiceNum(nice_range / (max_labels - 1), true); const int graphmin = (int)(floor(year_min / interval) * interval); const int graphmax = (int)(ceil(year_max / interval) * interval); const int step = (int)interval <= 0 ? 1 : (int)interval; for (int y = graphmin; y < graphmax; y += step) { ImPlotTime t = MakeTime(y); if (t >= t_min && t <= t_max) { ftd.Time = t; ftd.Spec = fmty; ticker.AddTick(t.ToDouble(), true, 0, true, Formatter_Time, &ftd); } } } } //----------------------------------------------------------------------------- // Context Menu //----------------------------------------------------------------------------- template bool DragFloat(const char*, F*, float, F, F) { return false; } template <> bool DragFloat(const char* label, double* v, float v_speed, double v_min, double v_max) { return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3g", 1); } template <> bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max) { return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3g", 1); } inline void BeginDisabledControls(bool cond) { if (cond) { ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); } } inline void EndDisabledControls(bool cond) { if (cond) { ImGui::PopItemFlag(); ImGui::PopStyleVar(); } } void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool /*time_allowed*/) { ImGui::PushItemWidth(75); bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting(); bool label = axis.HasLabel(); bool grid = axis.HasGridLines(); bool ticks = axis.HasTickMarks(); bool labels = axis.HasTickLabels(); double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. if (axis.Scale == ImPlotScale_Time) { ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min); ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max); BeginDisabledControls(always_locked); ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); EndDisabledControls(always_locked); ImGui::SameLine(); BeginDisabledControls(axis.IsLockedMin() || always_locked); if (ImGui::BeginMenu("Min Time")) { if (ShowTimePicker("mintime", &tmin)) { if (tmin >= tmax) tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); } ImGui::Separator(); if (ShowDatePicker("mindate",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) { tmin = CombineDateTime(axis.PickerTimeMin, tmin); if (tmin >= tmax) tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); } ImGui::EndMenu(); } EndDisabledControls(axis.IsLockedMin() || always_locked); BeginDisabledControls(always_locked); ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); EndDisabledControls(always_locked); ImGui::SameLine(); BeginDisabledControls(axis.IsLockedMax() || always_locked); if (ImGui::BeginMenu("Max Time")) { if (ShowTimePicker("maxtime", &tmax)) { if (tmax <= tmin) tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); } ImGui::Separator(); if (ShowDatePicker("maxdate",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) { tmax = CombineDateTime(axis.PickerTimeMax, tmax); if (tmax <= tmin) tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); } ImGui::EndMenu(); } EndDisabledControls(axis.IsLockedMax() || always_locked); } else { BeginDisabledControls(always_locked); ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); EndDisabledControls(always_locked); ImGui::SameLine(); BeginDisabledControls(axis.IsLockedMin() || always_locked); double temp_min = axis.Range.Min; if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) { axis.SetMin(temp_min,true); if (equal_axis != nullptr) equal_axis->SetAspect(axis.GetAspect()); } EndDisabledControls(axis.IsLockedMin() || always_locked); BeginDisabledControls(always_locked); ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); EndDisabledControls(always_locked); ImGui::SameLine(); BeginDisabledControls(axis.IsLockedMax() || always_locked); double temp_max = axis.Range.Max; if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) { axis.SetMax(temp_max,true); if (equal_axis != nullptr) equal_axis->SetAspect(axis.GetAspect()); } EndDisabledControls(axis.IsLockedMax() || always_locked); } ImGui::Separator(); ImGui::CheckboxFlags("Auto-Fit",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit); // TODO // BeginDisabledControls(axis.IsTime() && time_allowed); // ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); // EndDisabledControls(axis.IsTime() && time_allowed); // if (time_allowed) { // BeginDisabledControls(axis.IsLog() || axis.IsSymLog()); // ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); // EndDisabledControls(axis.IsLog() || axis.IsSymLog()); // } ImGui::Separator(); ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert); ImGui::CheckboxFlags("Opposite",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Opposite); ImGui::Separator(); BeginDisabledControls(axis.LabelOffset == -1); if (ImGui::Checkbox("Label", &label)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel); EndDisabledControls(axis.LabelOffset == -1); if (ImGui::Checkbox("Grid Lines", &grid)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); if (ImGui::Checkbox("Tick Marks", &ticks)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); if (ImGui::Checkbox("Tick Labels", &labels)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); } bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible) { const float s = ImGui::GetFrameHeight(); bool ret = false; if (ImGui::Checkbox("Show",&visible)) ret = true; if (legend.CanGoInside) ImGui::CheckboxFlags("Outside",(unsigned int*)&legend.Flags, ImPlotLegendFlags_Outside); if (ImGui::RadioButton("H", ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) legend.Flags |= ImPlotLegendFlags_Horizontal; ImGui::SameLine(); if (ImGui::RadioButton("V", !ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) legend.Flags &= ~ImPlotLegendFlags_Horizontal; ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2,2)); if (ImGui::Button("NW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthWest; } ImGui::SameLine(); if (ImGui::Button("N", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_North; } ImGui::SameLine(); if (ImGui::Button("NE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthEast; } if (ImGui::Button("W", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_West; } ImGui::SameLine(); if (ImGui::InvisibleButton("C", ImVec2(1.5f*s,s))) { } ImGui::SameLine(); if (ImGui::Button("E", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_East; } if (ImGui::Button("SW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthWest; } ImGui::SameLine(); if (ImGui::Button("S", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_South; } ImGui::SameLine(); if (ImGui::Button("SE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthEast; } ImGui::PopStyleVar(); return ret; } void ShowSubplotsContextMenu(ImPlotSubplot& subplot) { if ((ImGui::BeginMenu("Linking"))) { if (ImGui::MenuItem("Link Rows",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); if (ImGui::MenuItem("Link Cols",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); if (ImGui::MenuItem("Link All X",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); if (ImGui::MenuItem("Link All Y",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); ImGui::EndMenu(); } if ((ImGui::BeginMenu("Settings"))) { BeginDisabledControls(!subplot.HasTitle); if (ImGui::MenuItem("Title",nullptr,subplot.HasTitle && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle); EndDisabledControls(!subplot.HasTitle); if (ImGui::MenuItem("Resizable",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoResize); if (ImGui::MenuItem("Align",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign); if (ImGui::MenuItem("Share Items",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); ImGui::EndMenu(); } } void ShowPlotContextMenu(ImPlotPlot& plot) { ImPlotContext& gp = *GImPlot; const bool owns_legend = gp.CurrentItems == &plot.Items; const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); char buf[16] = {}; for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (!x_axis.Enabled || !x_axis.HasMenus()) continue; ImGui::PushID(i); ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "X-Axis" : "X-Axis %d", i + 1); if (ImGui::BeginMenu(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : buf)) { ShowAxisContextMenu(x_axis, equal ? x_axis.OrthoAxis : nullptr, false); ImGui::EndMenu(); } ImGui::PopID(); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (!y_axis.Enabled || !y_axis.HasMenus()) continue; ImGui::PushID(i); ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); if (ImGui::BeginMenu(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : buf)) { ShowAxisContextMenu(y_axis, equal ? y_axis.OrthoAxis : nullptr, false); ImGui::EndMenu(); } ImGui::PopID(); } ImGui::Separator(); if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoMenus)) { if ((ImGui::BeginMenu("Legend"))) { if (owns_legend) { if (ShowLegendContextMenu(plot.Items.Legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); } else if (gp.CurrentSubplot != nullptr) { if (ShowLegendContextMenu(gp.CurrentSubplot->Items.Legend, !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend))) ImFlipFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend); } ImGui::EndMenu(); } } if ((ImGui::BeginMenu("Settings"))) { if (ImGui::MenuItem("Equal", nullptr, ImHasFlag(plot.Flags, ImPlotFlags_Equal))) ImFlipFlag(plot.Flags, ImPlotFlags_Equal); if (ImGui::MenuItem("Box Select",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect); BeginDisabledControls(plot.TitleOffset == -1); if (ImGui::MenuItem("Title",nullptr,plot.HasTitle())) ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle); EndDisabledControls(plot.TitleOffset == -1); if (ImGui::MenuItem("Mouse Position",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText))) ImFlipFlag(plot.Flags, ImPlotFlags_NoMouseText); if (ImGui::MenuItem("Crosshairs",nullptr,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); ImGui::EndMenu(); } if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoMenus)) { ImGui::Separator(); if ((ImGui::BeginMenu("Subplots"))) { ShowSubplotsContextMenu(*gp.CurrentSubplot); ImGui::EndMenu(); } } } //----------------------------------------------------------------------------- // Axis Utils //----------------------------------------------------------------------------- static inline int AxisPrecision(const ImPlotAxis& axis) { const double range = axis.Ticker.TickCount() > 1 ? (axis.Ticker.Ticks[1].PlotPos - axis.Ticker.Ticks[0].PlotPos) : axis.Range.Size(); return Precision(range); } static inline double RoundAxisValue(const ImPlotAxis& axis, double value) { return RoundTo(value, AxisPrecision(axis)); } void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round) { ImPlotContext& gp = *GImPlot; // TODO: We shouldn't explicitly check that the axis is Time here. Ideally, // Formatter_Time would handle the formatting for us, but the code below // needs additional arguments which are not currently available in ImPlotFormatter if (axis.Locator == Locator_Time) { ImPlotTimeUnit unit = axis.Vertical ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)) // TODO: magic value! : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)); // TODO: magic value! FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); } else { if (round) value = RoundAxisValue(axis, value); axis.Formatter(value, buff, size, axis.FormatterData); } } void UpdateAxisColors(ImPlotAxis& axis) { const ImVec4 col_grid = GetStyleColorVec4(ImPlotCol_AxisGrid); axis.ColorMaj = ImGui::GetColorU32(col_grid); axis.ColorMin = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha)); axis.ColorTick = GetStyleColorU32(ImPlotCol_AxisTick); axis.ColorTxt = GetStyleColorU32(ImPlotCol_AxisText); axis.ColorBg = GetStyleColorU32(ImPlotCol_AxisBg); axis.ColorHov = GetStyleColorU32(ImPlotCol_AxisBgHovered); axis.ColorAct = GetStyleColorU32(ImPlotCol_AxisBgActive); // axis.ColorHiLi = IM_COL32_BLACK_TRANS; } void PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignmentData* align) { ImPlotContext& gp = *GImPlot; const float T = ImGui::GetTextLineHeight(); const float P = gp.Style.LabelPadding.y; const float K = gp.Style.MinorTickLen.x; int count_T = 0; int count_B = 0; float last_T = plot.AxesRect.Min.y; float last_B = plot.AxesRect.Max.y; for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { // FYI: can iterate forward ImPlotAxis& axis = plot.XAxis(i); if (!axis.Enabled) continue; const bool label = axis.HasLabel(); const bool ticks = axis.HasTickLabels(); const bool opp = axis.IsOpposite(); const bool time = axis.Scale == ImPlotScale_Time; if (opp) { if (count_T++ > 0) pad_T += K + P; if (label) pad_T += T + P; if (ticks) pad_T += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); axis.Datum1 = plot.CanvasRect.Min.y + pad_T; axis.Datum2 = last_T; last_T = axis.Datum1; } else { if (count_B++ > 0) pad_B += K + P; if (label) pad_B += T + P; if (ticks) pad_B += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); axis.Datum1 = plot.CanvasRect.Max.y - pad_B; axis.Datum2 = last_B; last_B = axis.Datum1; } } if (align) { count_T = count_B = 0; float delta_T, delta_B; align->Update(pad_T,pad_B,delta_T,delta_B); for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { ImPlotAxis& axis = plot.XAxis(i); if (!axis.Enabled) continue; if (axis.IsOpposite()) { axis.Datum1 += delta_T; axis.Datum2 += count_T++ > 1 ? delta_T : 0; } else { axis.Datum1 -= delta_B; axis.Datum2 -= count_B++ > 1 ? delta_B : 0; } } } } void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignmentData* align) { // [ pad_L ] [ pad_R ] // .................CanvasRect................ // :TPWPK.PTPWP _____PlotRect____ PWPTP.KPWPT: // :A # |- A # |- -| # A -| # A: // :X | X | | X | x: // :I # |- I # |- -| # I -| # I: // :S | S | | S | S: // :3 # |- 0 # |-_______________-| # 1 -| # 2: // :.........................................: // // T = text height // P = label padding // K = minor tick length // W = label width ImPlotContext& gp = *GImPlot; const float T = ImGui::GetTextLineHeight(); const float P = gp.Style.LabelPadding.x; const float K = gp.Style.MinorTickLen.y; int count_L = 0; int count_R = 0; float last_L = plot.AxesRect.Min.x; float last_R = plot.AxesRect.Max.x; for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { // FYI: can iterate forward ImPlotAxis& axis = plot.YAxis(i); if (!axis.Enabled) continue; const bool label = axis.HasLabel(); const bool ticks = axis.HasTickLabels(); const bool opp = axis.IsOpposite(); if (opp) { if (count_R++ > 0) pad_R += K + P; if (label) pad_R += T + P; if (ticks) pad_R += axis.Ticker.MaxSize.x + P; axis.Datum1 = plot.CanvasRect.Max.x - pad_R; axis.Datum2 = last_R; last_R = axis.Datum1; } else { if (count_L++ > 0) pad_L += K + P; if (label) pad_L += T + P; if (ticks) pad_L += axis.Ticker.MaxSize.x + P; axis.Datum1 = plot.CanvasRect.Min.x + pad_L; axis.Datum2 = last_L; last_L = axis.Datum1; } } plot.PlotRect.Min.x = plot.CanvasRect.Min.x + pad_L; plot.PlotRect.Max.x = plot.CanvasRect.Max.x - pad_R; if (align) { count_L = count_R = 0; float delta_L, delta_R; align->Update(pad_L,pad_R,delta_L,delta_R); for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { ImPlotAxis& axis = plot.YAxis(i); if (!axis.Enabled) continue; if (axis.IsOpposite()) { axis.Datum1 -= delta_R; axis.Datum2 -= count_R++ > 1 ? delta_R : 0; } else { axis.Datum1 += delta_L; axis.Datum2 += count_L++ > 1 ? delta_L : 0; } } } } //----------------------------------------------------------------------------- // RENDERING //----------------------------------------------------------------------------- static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { const float density = ticker.TickCount() / rect.GetWidth(); ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); col_min = ImGui::ColorConvertFloat4ToU32(col_min4); for (int t = 0; t < ticker.TickCount(); t++) { const ImPlotTick& xt = ticker.Ticks[t]; if (xt.PixelPos < rect.Min.x || xt.PixelPos > rect.Max.x) continue; if (xt.Level == 0) { if (xt.Major) DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_maj, size_maj); else if (density < 0.2f) DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_min, size_min); } } } static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { const float density = ticker.TickCount() / rect.GetHeight(); ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); col_min = ImGui::ColorConvertFloat4ToU32(col_min4); for (int t = 0; t < ticker.TickCount(); t++) { const ImPlotTick& yt = ticker.Ticks[t]; if (yt.PixelPos < rect.Min.y || yt.PixelPos > rect.Max.y) continue; if (yt.Major) DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_maj, size_maj); else if (density < 0.2f) DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_min, size_min); } } static inline void RenderSelectionRect(ImDrawList& DrawList, const ImVec2& p_min, const ImVec2& p_max, const ImVec4& col) { const ImU32 col_bg = ImGui::GetColorU32(col * ImVec4(1,1,1,0.25f)); const ImU32 col_bd = ImGui::GetColorU32(col); DrawList.AddRectFilled(p_min, p_max, col_bg); DrawList.AddRect(p_min, p_max, col_bd); } //----------------------------------------------------------------------------- // Input Handling //----------------------------------------------------------------------------- static const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f; static const float BOX_SELECT_DRAG_THRESHOLD = 4.0f; bool UpdateInput(ImPlotPlot& plot) { bool changed = false; ImPlotContext& gp = *GImPlot; ImGuiIO& IO = ImGui::GetIO(); // BUTTON STATE ----------------------------------------------------------- const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle; const ImGuiButtonFlags axis_button_flags = ImGuiButtonFlags_FlattenChildren | plot_button_flags; const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags); #if (IMGUI_VERSION_NUM < 18966) ImGui::SetItemAllowOverlap(); // Handled by ButtonBehavior() #endif if (plot_clicked) { if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) { plot.Selecting = true; plot.SelectStart = IO.MousePos; plot.SelectRect = ImRect(0,0,0,0); } if (IO.MouseDoubleClicked[gp.InputMap.Fit]) { plot.FitThisFrame = true; for (int i = 0; i < ImAxis_COUNT; ++i) plot.Axes[i].FitThisFrame = true; } } const bool can_pan = IO.MouseDown[gp.InputMap.Pan] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod); plot.Held = plot.Held && can_pan; bool x_click[IMPLOT_NUM_X_AXES] = {false}; bool x_held[IMPLOT_NUM_X_AXES] = {false}; bool x_hov[IMPLOT_NUM_X_AXES] = {false}; bool y_click[IMPLOT_NUM_Y_AXES] = {false}; bool y_held[IMPLOT_NUM_Y_AXES] = {false}; bool y_hov[IMPLOT_NUM_Y_AXES] = {false}; for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImPlotAxis& xax = plot.XAxis(i); if (xax.Enabled) { ImGui::KeepAliveID(xax.ID); x_click[i] = ImGui::ButtonBehavior(xax.HoverRect,xax.ID,&xax.Hovered,&xax.Held,axis_button_flags); if (x_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) plot.FitThisFrame = xax.FitThisFrame = true; xax.Held = xax.Held && can_pan; x_hov[i] = xax.Hovered || plot.Hovered; x_held[i] = xax.Held || plot.Held; } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImPlotAxis& yax = plot.YAxis(i); if (yax.Enabled) { ImGui::KeepAliveID(yax.ID); y_click[i] = ImGui::ButtonBehavior(yax.HoverRect,yax.ID,&yax.Hovered,&yax.Held,axis_button_flags); if (y_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) plot.FitThisFrame = yax.FitThisFrame = true; yax.Held = yax.Held && can_pan; y_hov[i] = yax.Hovered || plot.Hovered; y_held[i] = yax.Held || plot.Held; } } // cancel due to DND activity if (GImGui->DragDropActive || (IO.KeyMods == gp.InputMap.OverrideMod && gp.InputMap.OverrideMod != 0)) return false; // STATE ------------------------------------------------------------------- const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); const bool any_x_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool any_y_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); const bool any_hov = any_x_hov || any_y_hov; const bool any_held = any_x_held || any_y_held; const ImVec2 select_drag = ImGui::GetMouseDragDelta(gp.InputMap.Select); const ImVec2 pan_drag = ImGui::GetMouseDragDelta(gp.InputMap.Pan); const float select_drag_sq = ImLengthSqr(select_drag); const float pan_drag_sq = ImLengthSqr(pan_drag); const bool selecting = plot.Selecting && select_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; const bool panning = any_held && pan_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; // CONTEXT MENU ----------------------------------------------------------- if (IO.MouseReleased[gp.InputMap.Menu] && !plot.ContextLocked) gp.OpenContextThisFrame = true; if (selecting || panning) plot.ContextLocked = true; else if (!(IO.MouseDown[gp.InputMap.Menu] || IO.MouseReleased[gp.InputMap.Menu])) plot.ContextLocked = false; // DRAG INPUT ------------------------------------------------------------- if (any_held && !plot.Selecting) { int drag_direction = 0; for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_held[i] && !x_axis.IsInputLocked()) { drag_direction |= (1 << 1); bool increasing = x_axis.IsInverted() ? IO.MouseDelta.x > 0 : IO.MouseDelta.x < 0; if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) { const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x); const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x); x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); if (axis_equal && x_axis.OrthoAxis != nullptr) x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); changed = true; } } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_held[i] && !y_axis.IsInputLocked()) { drag_direction |= (1 << 2); bool increasing = y_axis.IsInverted() ? IO.MouseDelta.y < 0 : IO.MouseDelta.y > 0; if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) { const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y); const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y); y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); if (axis_equal && y_axis.OrthoAxis != nullptr) y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); changed = true; } } } if (IO.MouseDragMaxDistanceSqr[gp.InputMap.Pan] > MOUSE_CURSOR_DRAG_THRESHOLD) { switch (drag_direction) { case 0 : ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); break; case (1 << 1) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); break; case (1 << 2) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); break; default : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); break; } } } // SCROLL INPUT ----------------------------------------------------------- if (any_hov && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) { float zoom_rate = gp.InputMap.ZoomRate; if (IO.MouseWheel == 0.0f) zoom_rate = 0; else if (IO.MouseWheel > 0) zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); ImVec2 rect_size = plot.PlotRect.GetSize(); float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f); float ty = ImRemap(IO.MousePos.y, plot.PlotRect.Min.y, plot.PlotRect.Max.y, 0.0f, 1.0f); for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr; const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked(); if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) { ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID); if (zoom_rate != 0.0f) { float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction); const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction); x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); if (axis_equal && x_axis.OrthoAxis != nullptr) x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); changed = true; } } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr; const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked(); if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) { ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID); if (zoom_rate != 0.0f) { float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction); const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction); y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); if (axis_equal && y_axis.OrthoAxis != nullptr) y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); changed = true; } } } } // BOX-SELECTION ---------------------------------------------------------- if (plot.Selecting) { const ImVec2 d = plot.SelectStart - IO.MousePos; const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImFabs(d.x) > 2; const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod) && ImFabs(d.y) > 2; // confirm if (IO.MouseReleased[gp.InputMap.Select]) { for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (!x_axis.IsInputLocked() && x_can_change) { const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x); const double p2 = x_axis.PixelsToPlot(IO.MousePos.x); x_axis.SetMin(ImMin(p1, p2)); x_axis.SetMax(ImMax(p1, p2)); changed = true; } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (!y_axis.IsInputLocked() && y_can_change) { const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y); const double p2 = y_axis.PixelsToPlot(IO.MousePos.y); y_axis.SetMin(ImMin(p1, p2)); y_axis.SetMax(ImMax(p1, p2)); changed = true; } } if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod))) gp.OpenContextThisFrame = false; plot.Selected = plot.Selecting = false; } // cancel else if (IO.MouseReleased[gp.InputMap.SelectCancel]) { plot.Selected = plot.Selecting = false; gp.OpenContextThisFrame = false; } else if (ImLengthSqr(d) > BOX_SELECT_DRAG_THRESHOLD) { // bad selection if (plot.IsInputLocked()) { ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); gp.OpenContextThisFrame = false; plot.Selected = false; } else { // TODO: Handle only min or max locked cases const bool full_width = ImHasFlag(IO.KeyMods, gp.InputMap.SelectHorzMod) || AllAxesInputLocked(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool full_height = ImHasFlag(IO.KeyMods, gp.InputMap.SelectVertMod) || AllAxesInputLocked(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); plot.SelectRect.Min.x = full_width ? plot.PlotRect.Min.x : ImMin(plot.SelectStart.x, IO.MousePos.x); plot.SelectRect.Max.x = full_width ? plot.PlotRect.Max.x : ImMax(plot.SelectStart.x, IO.MousePos.x); plot.SelectRect.Min.y = full_height ? plot.PlotRect.Min.y : ImMin(plot.SelectStart.y, IO.MousePos.y); plot.SelectRect.Max.y = full_height ? plot.PlotRect.Max.y : ImMax(plot.SelectStart.y, IO.MousePos.y); plot.SelectRect.Min -= plot.PlotRect.Min; plot.SelectRect.Max -= plot.PlotRect.Min; plot.Selected = true; } } else { plot.Selected = false; } } return changed; } //----------------------------------------------------------------------------- // Next Plot Data (Legacy) //----------------------------------------------------------------------------- void ApplyNextPlotData(ImAxis idx) { ImPlotContext& gp = *GImPlot; ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; if (!axis.Enabled) return; double* npd_lmin = gp.NextPlotData.LinkedMin[idx]; double* npd_lmax = gp.NextPlotData.LinkedMax[idx]; bool npd_rngh = gp.NextPlotData.HasRange[idx]; ImPlotCond npd_rngc = gp.NextPlotData.RangeCond[idx]; ImPlotRange npd_rngv = gp.NextPlotData.Range[idx]; axis.LinkedMin = npd_lmin; axis.LinkedMax = npd_lmax; axis.PullLinks(); if (npd_rngh) { if (!plot.Initialized || npd_rngc == ImPlotCond_Always) axis.SetRange(npd_rngv); } axis.HasRange = npd_rngh; axis.RangeCond = npd_rngc; } //----------------------------------------------------------------------------- // Setup //----------------------------------------------------------------------------- void SetupAxis(ImAxis idx, const char* label, ImPlotAxisFlags flags) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); // get plot and axis ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; // set ID axis.ID = plot.ID + idx + 1; // check and set flags if (plot.JustCreated || flags != axis.PreviousFlags) axis.Flags = flags; axis.PreviousFlags = flags; // enable axis axis.Enabled = true; // set label plot.SetAxisLabel(axis,label); // cache colors UpdateAxisColors(axis); } void SetupAxisLimits(ImAxis idx, double min_lim, double max_lim, ImPlotCond cond) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); // get plot and axis ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); if (!plot.Initialized || cond == ImPlotCond_Always) axis.SetRange(min_lim, max_lim); axis.HasRange = true; axis.RangeCond = cond; } void SetupAxisFormat(ImAxis idx, const char* fmt) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.HasFormatSpec = fmt != nullptr; if (fmt != nullptr) ImStrncpy(axis.FormatSpec,fmt,sizeof(axis.FormatSpec)); } void SetupAxisLinks(ImAxis idx, double* min_lnk, double* max_lnk) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.LinkedMin = min_lnk; axis.LinkedMax = max_lnk; axis.PullLinks(); } void SetupAxisFormat(ImAxis idx, ImPlotFormatter formatter, void* data) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.Formatter = formatter; axis.FormatterData = data; } void SetupAxisTicks(ImAxis idx, const double* values, int n_ticks, const char* const labels[], bool show_default) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.ShowDefaultTicks = show_default; AddTicksCustom(values, labels, n_ticks, axis.Ticker, axis.Formatter ? axis.Formatter : Formatter_Default, (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); } void SetupAxisTicks(ImAxis idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool show_default) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); n_ticks = n_ticks < 2 ? 2 : n_ticks; FillRange(gp.TempDouble1, n_ticks, v_min, v_max); SetupAxisTicks(idx, gp.TempDouble1.Data, n_ticks, labels, show_default); } void SetupAxisScale(ImAxis idx, ImPlotScale scale) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.Scale = scale; switch (scale) { case ImPlotScale_Time: axis.TransformForward = nullptr; axis.TransformInverse = nullptr; axis.TransformData = nullptr; axis.Locator = Locator_Time; axis.ConstraintRange = ImPlotRange(IMPLOT_MIN_TIME, IMPLOT_MAX_TIME); axis.Ticker.Levels = 2; break; case ImPlotScale_Log10: axis.TransformForward = TransformForward_Log10; axis.TransformInverse = TransformInverse_Log10; axis.TransformData = nullptr; axis.Locator = Locator_Log10; axis.ConstraintRange = ImPlotRange(DBL_MIN, INFINITY); break; case ImPlotScale_SymLog: axis.TransformForward = TransformForward_SymLog; axis.TransformInverse = TransformInverse_SymLog; axis.TransformData = nullptr; axis.Locator = Locator_SymLog; axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); break; default: axis.TransformForward = nullptr; axis.TransformInverse = nullptr; axis.TransformData = nullptr; axis.Locator = nullptr; axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); break; } } void SetupAxisScale(ImAxis idx, ImPlotTransform fwd, ImPlotTransform inv, void* data) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.Scale = IMPLOT_AUTO; axis.TransformForward = fwd; axis.TransformInverse = inv; axis.TransformData = data; } void SetupAxisLimitsConstraints(ImAxis idx, double v_min, double v_max) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.ConstraintRange.Min = v_min; axis.ConstraintRange.Max = v_max; } void SetupAxisZoomConstraints(ImAxis idx, double z_min, double z_max) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.ConstraintZoom.Min = z_min; axis.ConstraintZoom.Max = z_max; } void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags) { SetupAxis(ImAxis_X1, x_label, x_flags); SetupAxis(ImAxis_Y1, y_label, y_flags); } void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { SetupAxisLimits(ImAxis_X1, x_min, x_max, cond); SetupAxisLimits(ImAxis_Y1, y_min, y_max, cond); } void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR((gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked) || (gp.CurrentSubplot != nullptr && gp.CurrentPlot == nullptr), "Setup needs to be called after BeginPlot or BeginSubplots and before any setup locking functions (e.g. PlotX)!"); if (gp.CurrentItems) { ImPlotLegend& legend = gp.CurrentItems->Legend; // check and set location if (location != legend.PreviousLocation) legend.Location = location; legend.PreviousLocation = location; // check and set flags if (flags != legend.PreviousFlags) legend.Flags = flags; legend.PreviousFlags = flags; } } void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); gp.CurrentPlot->MouseTextLocation = location; gp.CurrentPlot->MouseTextFlags = flags; } //----------------------------------------------------------------------------- // SetNext //----------------------------------------------------------------------------- void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisLimits() needs to be called before BeginPlot()!"); IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. gp.NextPlotData.HasRange[axis] = true; gp.NextPlotData.RangeCond[axis] = cond; gp.NextPlotData.Range[axis].Min = v_min; gp.NextPlotData.Range[axis].Max = v_max; } void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisLinks() needs to be called before BeginPlot()!"); gp.NextPlotData.LinkedMin[axis] = link_min; gp.NextPlotData.LinkedMax[axis] = link_max; } void SetNextAxisToFit(ImAxis axis) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisToFit() needs to be called before BeginPlot()!"); gp.NextPlotData.Fit[axis] = true; } void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { SetNextAxisLimits(ImAxis_X1, x_min, x_max, cond); SetNextAxisLimits(ImAxis_Y1, y_min, y_max, cond); } void SetNextAxesToFit() { for (int i = 0; i < ImAxis_COUNT; ++i) SetNextAxisToFit(i); } //----------------------------------------------------------------------------- // BeginPlot //----------------------------------------------------------------------------- bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "Mismatched BeginPlot()/EndPlot()!"); // FRONT MATTER ----------------------------------------------------------- if (gp.CurrentSubplot != nullptr) ImGui::PushID(gp.CurrentSubplot->CurrentIdx); // get globals ImGuiContext &G = *GImGui; ImGuiWindow* Window = G.CurrentWindow; // skip if needed if (Window->SkipItems && !gp.CurrentSubplot) { ResetCtxForNextPlot(GImPlot); return false; } // ID and age (TODO: keep track of plot age in frames) const ImGuiID ID = Window->GetID(title_id); const bool just_created = gp.Plots.GetByKey(ID) == nullptr; gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID); ImPlotPlot &plot = *gp.CurrentPlot; plot.ID = ID; plot.Items.ID = ID - 1; plot.JustCreated = just_created; plot.SetupLocked = false; // check flags if (plot.JustCreated) plot.Flags = flags; else if (flags != plot.PreviousFlags) plot.Flags = flags; plot.PreviousFlags = flags; // setup default axes if (plot.JustCreated) { SetupAxis(ImAxis_X1); SetupAxis(ImAxis_Y1); } // reset axes for (int i = 0; i < ImAxis_COUNT; ++i) { plot.Axes[i].Reset(); UpdateAxisColors(plot.Axes[i]); } // ensure first axes enabled plot.Axes[ImAxis_X1].Enabled = true; plot.Axes[ImAxis_Y1].Enabled = true; // set initial axes plot.CurrentX = ImAxis_X1; plot.CurrentY = ImAxis_Y1; // process next plot data (legacy) for (int i = 0; i < ImAxis_COUNT; ++i) ApplyNextPlotData(i); // clear text buffers plot.ClearTextBuffer(); plot.SetTitle(title_id); // set frame size ImVec2 frame_size; if (gp.CurrentSubplot != nullptr) frame_size = gp.CurrentSubplot->CellSize; else frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); if (frame_size.x < gp.Style.PlotMinSize.x && (size.x < 0.0f || gp.CurrentSubplot != nullptr)) frame_size.x = gp.Style.PlotMinSize.x; if (frame_size.y < gp.Style.PlotMinSize.y && (size.y < 0.0f || gp.CurrentSubplot != nullptr)) frame_size.y = gp.Style.PlotMinSize.y; plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(plot.FrameRect); if (!ImGui::ItemAdd(plot.FrameRect, plot.ID, &plot.FrameRect) && !gp.CurrentSubplot) { ResetCtxForNextPlot(GImPlot); return false; } // setup items (or dont) if (gp.CurrentItems == nullptr) gp.CurrentItems = &plot.Items; return true; } //----------------------------------------------------------------------------- // SetupFinish //----------------------------------------------------------------------------- void SetupFinish() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetupFinish needs to be called after BeginPlot!"); ImGuiContext& G = *GImGui; ImDrawList& DrawList = *G.CurrentWindow->DrawList; const ImGuiStyle& Style = G.Style; ImPlotPlot &plot = *gp.CurrentPlot; // lock setup plot.SetupLocked = true; // finalize axes and set default formatter/locator for (int i = 0; i < ImAxis_COUNT; ++i) { ImPlotAxis& axis = plot.Axes[i]; if (axis.Enabled) { axis.Constrain(); if (!plot.Initialized && axis.CanInitFit()) plot.FitThisFrame = axis.FitThisFrame = true; } if (axis.Formatter == nullptr) { axis.Formatter = Formatter_Default; if (axis.HasFormatSpec) axis.FormatterData = axis.FormatSpec; else axis.FormatterData = (void*)IMPLOT_LABEL_FORMAT; } if (axis.Locator == nullptr) { axis.Locator = Locator_Default; } } // setup nullptr orthogonal axes const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); for (int ix = ImAxis_X1, iy = ImAxis_Y1; ix < ImAxis_Y1 || iy < ImAxis_COUNT; ++ix, ++iy) { ImPlotAxis& x_axis = plot.Axes[ix]; ImPlotAxis& y_axis = plot.Axes[iy]; if (x_axis.Enabled && y_axis.Enabled) { if (x_axis.OrthoAxis == nullptr) x_axis.OrthoAxis = &y_axis; if (y_axis.OrthoAxis == nullptr) y_axis.OrthoAxis = &x_axis; } else if (x_axis.Enabled) { if (x_axis.OrthoAxis == nullptr && !axis_equal) x_axis.OrthoAxis = &plot.Axes[ImAxis_Y1]; } else if (y_axis.Enabled) { if (y_axis.OrthoAxis == nullptr && !axis_equal) y_axis.OrthoAxis = &plot.Axes[ImAxis_X1]; } } // canvas/axes bb plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding); plot.AxesRect = plot.FrameRect; // outside legend adjustments if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0 && ImHasFlag(plot.Items.Legend.Flags, ImPlotLegendFlags_Outside)) { ImPlotLegend& legend = plot.Items.Legend; const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); if ((west && !horz) || (west && horz && !north && !south)) { plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); plot.AxesRect.Min.x += (legend_size.x + gp.Style.PlotPadding.x); } if ((east && !horz) || (east && horz && !north && !south)) { plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); plot.AxesRect.Max.x -= (legend_size.x + gp.Style.PlotPadding.x); } if ((north && horz) || (north && !horz && !west && !east)) { plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); plot.AxesRect.Min.y += (legend_size.y + gp.Style.PlotPadding.y); } if ((south && horz) || (south && !horz && !west && !east)) { plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); plot.AxesRect.Max.y -= (legend_size.y + gp.Style.PlotPadding.y); } } // plot bb float pad_top = 0, pad_bot = 0, pad_left = 0, pad_right = 0; // (0) calc top padding form title ImVec2 title_size(0.0f, 0.0f); if (plot.HasTitle()) title_size = ImGui::CalcTextSize(plot.GetTitle(), nullptr, true); if (title_size.x > 0) { pad_top += title_size.y + gp.Style.LabelPadding.y; plot.AxesRect.Min.y += gp.Style.PlotPadding.y + pad_top; } // (1) calc addition top padding and bot padding PadAndDatumAxesX(plot,pad_top,pad_bot,gp.CurrentAlignmentH); const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; // (2) get y tick labels (needed for left/right pad) for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& axis = plot.YAxis(i); if (axis.WillRender() && axis.ShowDefaultTicks && plot_height > 0) { axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData); } } // (3) calc left/right pad PadAndDatumAxesY(plot,pad_left,pad_right,gp.CurrentAlignmentV); const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right; // (4) get x ticks for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& axis = plot.XAxis(i); if (axis.WillRender() && axis.ShowDefaultTicks && plot_width > 0) { axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData); } } // (5) calc plot bb plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot)); // HOVER------------------------------------------------------------ // axes hover rect, pixel ranges for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImPlotAxis& xax = plot.XAxis(i); xax.HoverRect = ImRect(ImVec2(plot.PlotRect.Min.x, ImMin(xax.Datum1,xax.Datum2)), ImVec2(plot.PlotRect.Max.x, ImMax(xax.Datum1,xax.Datum2))); xax.PixelMin = xax.IsInverted() ? plot.PlotRect.Max.x : plot.PlotRect.Min.x; xax.PixelMax = xax.IsInverted() ? plot.PlotRect.Min.x : plot.PlotRect.Max.x; xax.UpdateTransformCache(); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImPlotAxis& yax = plot.YAxis(i); yax.HoverRect = ImRect(ImVec2(ImMin(yax.Datum1,yax.Datum2),plot.PlotRect.Min.y), ImVec2(ImMax(yax.Datum1,yax.Datum2),plot.PlotRect.Max.y)); yax.PixelMin = yax.IsInverted() ? plot.PlotRect.Min.y : plot.PlotRect.Max.y; yax.PixelMax = yax.IsInverted() ? plot.PlotRect.Max.y : plot.PlotRect.Min.y; yax.UpdateTransformCache(); } // Equal axis constraint. Must happen after we set Pixels // constrain equal axes for primary x and y if not approximately equal // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case if (axis_equal) { for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.OrthoAxis == nullptr) continue; double xar = x_axis.GetAspect(); double yar = x_axis.OrthoAxis->GetAspect(); // edge case: user has set x range this frame, so fit y to x so that we honor their request for x range // NB: because of feedback across several frames, the user's x request may not be perfectly honored if (x_axis.HasRange) x_axis.OrthoAxis->SetAspect(xar); else if (!ImAlmostEqual(xar,yar) && !x_axis.OrthoAxis->IsInputLocked()) x_axis.SetAspect(yar); } } // INPUT ------------------------------------------------------------------ if (!ImHasFlag(plot.Flags, ImPlotFlags_NoInputs)) UpdateInput(plot); // fit from FitNextPlotAxes or auto fit for (int i = 0; i < ImAxis_COUNT; ++i) { if (gp.NextPlotData.Fit[i] || plot.Axes[i].IsAutoFitting()) { plot.FitThisFrame = true; plot.Axes[i].FitThisFrame = true; } } // RENDER ----------------------------------------------------------------- const float txt_height = ImGui::GetTextLineHeight(); // render frame if (!ImHasFlag(plot.Flags, ImPlotFlags_NoFrame)) ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); // grid bg DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg)); // transform ticks for (int i = 0; i < ImAxis_COUNT; i++) { ImPlotAxis& axis = plot.Axes[i]; if (axis.WillRender()) { for (int t = 0; t < axis.Ticker.TickCount(); t++) { ImPlotTick& tk = axis.Ticker.Ticks[t]; tk.PixelPos = IM_ROUND(axis.PlotToPixels(tk.PlotPos)); } } } // render grid (background) for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.Enabled && x_axis.HasGridLines() && !x_axis.IsForeground()) RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_axis.Enabled && y_axis.HasGridLines() && !y_axis.IsForeground()) RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); } // render x axis button, label, tick labels for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& ax = plot.XAxis(i); if (!ax.Enabled) continue; if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight)) DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); ax.ColorHiLi = IM_COL32_BLACK_TRANS; } else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); } const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); if (ax.HasLabel()) { const char* label = plot.GetAxisLabel(ax); const ImVec2 label_size = ImGui::CalcTextSize(label); const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.y + gp.Style.LabelPadding.y : 0.0f) + (tkr.Levels - 1) * (txt_height + gp.Style.LabelPadding.y) + gp.Style.LabelPadding.y; const ImVec2 label_pos(plot.PlotRect.GetCenter().x - label_size.x * 0.5f, opp ? ax.Datum1 - label_offset - label_size.y : ax.Datum1 + label_offset); DrawList.AddText(label_pos, ax.ColorTxt, label); } if (ax.HasTickLabels()) { for (int j = 0; j < tkr.TickCount(); ++j) { const ImPlotTick& tk = tkr.Ticks[j]; const float datum = ax.Datum1 + (opp ? (-gp.Style.LabelPadding.y -txt_height -tk.Level * (txt_height + gp.Style.LabelPadding.y)) : gp.Style.LabelPadding.y + tk.Level * (txt_height + gp.Style.LabelPadding.y)); if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.x - 1 && tk.PixelPos <= plot.PlotRect.Max.x + 1) { ImVec2 start(tk.PixelPos - 0.5f * tk.LabelSize.x, datum); DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); } } } } // render y axis button, label, tick labels for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& ax = plot.YAxis(i); if (!ax.Enabled) continue; if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight)) DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); ax.ColorHiLi = IM_COL32_BLACK_TRANS; } else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); } const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); if (ax.HasLabel()) { const char* label = plot.GetAxisLabel(ax); const ImVec2 label_size = CalcTextSizeVertical(label); const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.x + gp.Style.LabelPadding.x : 0.0f) + gp.Style.LabelPadding.x; const ImVec2 label_pos(opp ? ax.Datum1 + label_offset : ax.Datum1 - label_offset - label_size.x, plot.PlotRect.GetCenter().y + label_size.y * 0.5f); AddTextVertical(&DrawList, label_pos, ax.ColorTxt, label); } if (ax.HasTickLabels()) { for (int j = 0; j < tkr.TickCount(); ++j) { const ImPlotTick& tk = tkr.Ticks[j]; const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x)); if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) { ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y); DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); } } } } // clear legend (TODO: put elsewhere) plot.Items.Legend.Reset(); // push ID to set item hashes (NB: !!!THIS PROBABLY NEEDS TO BE IN BEGIN PLOT!!!!) ImGui::PushOverrideID(gp.CurrentItems->ID); } //----------------------------------------------------------------------------- // EndPlot() //----------------------------------------------------------------------------- void EndPlot() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Mismatched BeginPlot()/EndPlot()!"); SetupLock(); ImGuiContext &G = *GImGui; ImPlotPlot &plot = *gp.CurrentPlot; ImGuiWindow * Window = G.CurrentWindow; ImDrawList & DrawList = *Window->DrawList; const ImGuiIO & IO = ImGui::GetIO(); // FINAL RENDER ----------------------------------------------------------- const bool render_border = gp.Style.PlotBorderSize > 0 && GetStyleColorVec4(ImPlotCol_PlotBorder).w > 0; const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); // render grid (foreground) for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.Enabled && x_axis.HasGridLines() && x_axis.IsForeground()) RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_axis.Enabled && y_axis.HasGridLines() && y_axis.IsForeground()) RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); } // render title if (plot.HasTitle()) { ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); AddTextCentered(&DrawList,ImVec2(plot.PlotRect.GetCenter().x, plot.CanvasRect.Min.y),col,plot.GetTitle()); } // render x ticks int count_B = 0, count_T = 0; for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { const ImPlotAxis& ax = plot.XAxis(i); if (!ax.Enabled) continue; const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); const bool aux = ((opp && count_T > 0)||(!opp && count_B > 0)); if (ax.HasTickMarks()) { const float direction = opp ? 1.0f : -1.0f; for (int j = 0; j < tkr.TickCount(); ++j) { const ImPlotTick& tk = tkr.Ticks[j]; if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.x || tk.PixelPos > plot.PlotRect.Max.x) continue; const ImVec2 start(tk.PixelPos, ax.Datum1); const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x; const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x; DrawList.AddLine(start, start + ImVec2(0,direction*len), ax.ColorTick, thk); } if (aux || !render_border) DrawList.AddLine(ImVec2(plot.PlotRect.Min.x,ax.Datum1), ImVec2(plot.PlotRect.Max.x,ax.Datum1), ax.ColorTick, gp.Style.MinorTickSize.x); } count_B += !opp; count_T += opp; } // render y ticks int count_L = 0, count_R = 0; for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { const ImPlotAxis& ax = plot.YAxis(i); if (!ax.Enabled) continue; const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); const bool aux = ((opp && count_R > 0)||(!opp && count_L > 0)); if (ax.HasTickMarks()) { const float direction = opp ? -1.0f : 1.0f; for (int j = 0; j < tkr.TickCount(); ++j) { const ImPlotTick& tk = tkr.Ticks[j]; if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.y || tk.PixelPos > plot.PlotRect.Max.y) continue; const ImVec2 start(ax.Datum1, tk.PixelPos); const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; DrawList.AddLine(start, start + ImVec2(direction*len,0), ax.ColorTick, thk); } if (aux || !render_border) DrawList.AddLine(ImVec2(ax.Datum1, plot.PlotRect.Min.y), ImVec2(ax.Datum1, plot.PlotRect.Max.y), ax.ColorTick, gp.Style.MinorTickSize.y); } count_L += !opp; count_R += opp; } ImGui::PopClipRect(); // render annotations PushPlotClipRect(); for (int i = 0; i < gp.Annotations.Size; ++i) { const char* txt = gp.Annotations.GetText(i); ImPlotAnnotation& an = gp.Annotations.Annotations[i]; const ImVec2 txt_size = ImGui::CalcTextSize(txt); const ImVec2 size = txt_size + gp.Style.AnnotationPadding * 2; ImVec2 pos = an.Pos; if (an.Offset.x == 0) pos.x -= size.x / 2; else if (an.Offset.x > 0) pos.x += an.Offset.x; else pos.x -= size.x - an.Offset.x; if (an.Offset.y == 0) pos.y -= size.y / 2; else if (an.Offset.y > 0) pos.y += an.Offset.y; else pos.y -= size.y - an.Offset.y; if (an.Clamp) pos = ClampLabelPos(pos, size, plot.PlotRect.Min, plot.PlotRect.Max); ImRect rect(pos,pos+size); if (an.Offset.x != 0 || an.Offset.y != 0) { ImVec2 corners[4] = {rect.GetTL(), rect.GetTR(), rect.GetBR(), rect.GetBL()}; int min_corner = 0; float min_len = FLT_MAX; for (int c = 0; c < 4; ++c) { float len = ImLengthSqr(an.Pos - corners[c]); if (len < min_len) { min_corner = c; min_len = len; } } DrawList.AddLine(an.Pos, corners[min_corner], an.ColorBg); } DrawList.AddRectFilled(rect.Min, rect.Max, an.ColorBg); DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt); } // render selection if (plot.Selected) RenderSelectionRect(DrawList, plot.SelectRect.Min + plot.PlotRect.Min, plot.SelectRect.Max + plot.PlotRect.Min, GetStyleColorVec4(ImPlotCol_Selection)); // render crosshairs if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.Hovered && !(any_x_held || any_y_held) && !plot.Selecting && !plot.Items.Legend.Hovered) { ImGui::SetMouseCursor(ImGuiMouseCursor_None); ImVec2 xy = IO.MousePos; ImVec2 h1(plot.PlotRect.Min.x, xy.y); ImVec2 h2(xy.x - 5, xy.y); ImVec2 h3(xy.x + 5, xy.y); ImVec2 h4(plot.PlotRect.Max.x, xy.y); ImVec2 v1(xy.x, plot.PlotRect.Min.y); ImVec2 v2(xy.x, xy.y - 5); ImVec2 v3(xy.x, xy.y + 5); ImVec2 v4(xy.x, plot.PlotRect.Max.y); ImU32 col = GetStyleColorU32(ImPlotCol_Crosshairs); DrawList.AddLine(h1, h2, col); DrawList.AddLine(h3, h4, col); DrawList.AddLine(v1, v2, col); DrawList.AddLine(v3, v4, col); } // render mouse pos if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText) && (plot.Hovered || ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_ShowAlways))) { const bool no_aux = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoAuxAxes); const bool no_fmt = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoFormat); ImGuiTextBuffer& builder = gp.MousePosStringBuilder; builder.Buf.shrink(0); char buff[IMPLOT_LABEL_MAX_SIZE]; const int num_x = no_aux ? 1 : IMPLOT_NUM_X_AXES; for (int i = 0; i < num_x; ++i) { ImPlotAxis& x_axis = plot.XAxis(i); if (!x_axis.Enabled) continue; if (i > 0) builder.append(", ("); double v = x_axis.PixelsToPlot(IO.MousePos.x); if (no_fmt) Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); else LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); builder.append(buff); if (i > 0) builder.append(")"); } builder.append(", "); const int num_y = no_aux ? 1 : IMPLOT_NUM_Y_AXES; for (int i = 0; i < num_y; ++i) { ImPlotAxis& y_axis = plot.YAxis(i); if (!y_axis.Enabled) continue; if (i > 0) builder.append(", ("); double v = y_axis.PixelsToPlot(IO.MousePos.y); if (no_fmt) Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); else LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); builder.append(buff); if (i > 0) builder.append(")"); } if (!builder.empty()) { const ImVec2 size = ImGui::CalcTextSize(builder.c_str()); const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MouseTextLocation, gp.Style.MousePosPadding); DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), builder.c_str()); } } PopPlotClipRect(); // axis side switch if (!plot.Held) { ImVec2 mouse_pos = ImGui::GetIO().MousePos; ImRect trigger_rect = plot.PlotRect; trigger_rect.Expand(-10); for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImPlotAxis& x_axis = plot.XAxis(i); if (ImHasFlag(x_axis.Flags, ImPlotAxisFlags_NoSideSwitch)) continue; if (x_axis.Held && plot.PlotRect.Contains(mouse_pos)) { const bool opp = ImHasFlag(x_axis.Flags, ImPlotAxisFlags_Opposite); if (!opp) { ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, plot.PlotRect.Max.x + 5, plot.PlotRect.Min.y + 5); if (mouse_pos.y < plot.PlotRect.Max.y - 10) DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); if (rect.Contains(mouse_pos)) x_axis.Flags |= ImPlotAxisFlags_Opposite; } else { ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Max.y - 5, plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); if (mouse_pos.y > plot.PlotRect.Min.y + 10) DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); if (rect.Contains(mouse_pos)) x_axis.Flags &= ~ImPlotAxisFlags_Opposite; } } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImPlotAxis& y_axis = plot.YAxis(i); if (ImHasFlag(y_axis.Flags, ImPlotAxisFlags_NoSideSwitch)) continue; if (y_axis.Held && plot.PlotRect.Contains(mouse_pos)) { const bool opp = ImHasFlag(y_axis.Flags, ImPlotAxisFlags_Opposite); if (!opp) { ImRect rect(plot.PlotRect.Max.x - 5, plot.PlotRect.Min.y - 5, plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); if (mouse_pos.x > plot.PlotRect.Min.x + 10) DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); if (rect.Contains(mouse_pos)) y_axis.Flags |= ImPlotAxisFlags_Opposite; } else { ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, plot.PlotRect.Min.x + 5, plot.PlotRect.Max.y + 5); if (mouse_pos.x < plot.PlotRect.Max.x - 10) DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); if (rect.Contains(mouse_pos)) y_axis.Flags &= ~ImPlotAxisFlags_Opposite; } } } } // reset legend hovers plot.Items.Legend.Hovered = false; for (int i = 0; i < plot.Items.GetItemCount(); ++i) plot.Items.GetItemByIndex(i)->LegendHovered = false; // render legend if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0) { ImPlotLegend& legend = plot.Items.Legend; const bool legend_out = ImHasFlag(legend.Flags, ImPlotLegendFlags_Outside); const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); const ImVec2 legend_pos = GetLocationPos(legend_out ? plot.FrameRect : plot.PlotRect, legend_size, legend.Location, legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding); legend.Rect = ImRect(legend_pos, legend_pos + legend_size); legend.RectClamped = legend.Rect; const bool legend_scrollable = ClampLegendRect(legend.RectClamped, legend_out ? plot.FrameRect : plot.PlotRect, legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding ); const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle | ImGuiButtonFlags_FlattenChildren; ImGui::KeepAliveID(plot.Items.ID); ImGui::ButtonBehavior(legend.RectClamped, plot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags); legend.Hovered = legend.Hovered || (ImGui::IsWindowHovered() && legend.RectClamped.Contains(IO.MousePos)); if (legend_scrollable) { if (legend.Hovered) { ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.Items.ID); if (IO.MouseWheel != 0.0f) { ImVec2 max_step = legend.Rect.GetSize() * 0.67f; #if IMGUI_VERSION_NUM < 19172 float font_size = ImGui::GetCurrentWindow()->CalcFontSize(); #else float font_size = ImGui::GetCurrentWindow()->FontRefSize; #endif float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x)); legend.Scroll.x += scroll_step * IO.MouseWheel; legend.Scroll.y += scroll_step * IO.MouseWheel; } } const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize(); legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f); legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f); const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y); ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset; legend.Rect.Min += legend_offset; legend.Rect.Max += legend_offset; } else { legend.Scroll = ImVec2(0,0); } const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true); DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg); bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus); DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd); ImGui::PopClipRect(); // main ctx menu if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus)) ImGui::OpenPopup("##LegendContext"); if (ImGui::BeginPopup("##LegendContext")) { ImGui::Text("Legend"); ImGui::Separator(); if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); ImGui::EndPopup(); } } else { plot.Items.Legend.Rect = ImRect(); } // render border if (render_border) DrawList.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawFlags_RoundCornersAll, gp.Style.PlotBorderSize); // render tags for (int i = 0; i < gp.Tags.Size; ++i) { ImPlotTag& tag = gp.Tags.Tags[i]; ImPlotAxis& axis = plot.Axes[tag.Axis]; if (!axis.Enabled || !axis.Range.Contains(tag.Value)) continue; const char* txt = gp.Tags.GetText(i); ImVec2 text_size = ImGui::CalcTextSize(txt); ImVec2 size = text_size + gp.Style.AnnotationPadding * 2; ImVec2 pos; axis.Ticker.OverrideSizeLate(size); float pix = IM_ROUND(axis.PlotToPixels(tag.Value)); if (axis.Vertical) { if (axis.IsOpposite()) { pos = ImVec2(axis.Datum1 + gp.Style.LabelPadding.x, pix - size.y * 0.5f); DrawList.AddTriangleFilled(ImVec2(axis.Datum1,pix), pos, pos + ImVec2(0,size.y), tag.ColorBg); } else { pos = ImVec2(axis.Datum1 - size.x - gp.Style.LabelPadding.x, pix - size.y * 0.5f); DrawList.AddTriangleFilled(pos + ImVec2(size.x,0), ImVec2(axis.Datum1,pix), pos+size, tag.ColorBg); } } else { if (axis.IsOpposite()) { pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 - size.y - gp.Style.LabelPadding.y ); DrawList.AddTriangleFilled(pos + ImVec2(0,size.y), pos + size, ImVec2(pix,axis.Datum1), tag.ColorBg); } else { pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 + gp.Style.LabelPadding.y); DrawList.AddTriangleFilled(pos, ImVec2(pix,axis.Datum1), pos + ImVec2(size.x, 0), tag.ColorBg); } } DrawList.AddRectFilled(pos,pos+size,tag.ColorBg); DrawList.AddText(pos+gp.Style.AnnotationPadding,tag.ColorFg,txt); } // FIT DATA -------------------------------------------------------------- const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); if (plot.FitThisFrame) { for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.FitThisFrame) { x_axis.ApplyFit(gp.Style.FitPadding.x); if (axis_equal && x_axis.OrthoAxis != nullptr) { double aspect = x_axis.GetAspect(); ImPlotAxis& y_axis = *x_axis.OrthoAxis; if (y_axis.FitThisFrame) { y_axis.ApplyFit(gp.Style.FitPadding.y); y_axis.FitThisFrame = false; aspect = ImMax(aspect, y_axis.GetAspect()); } x_axis.SetAspect(aspect); y_axis.SetAspect(aspect); } } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_axis.FitThisFrame) { y_axis.ApplyFit(gp.Style.FitPadding.y); if (axis_equal && y_axis.OrthoAxis != nullptr) { double aspect = y_axis.GetAspect(); ImPlotAxis& x_axis = *y_axis.OrthoAxis; if (x_axis.FitThisFrame) { x_axis.ApplyFit(gp.Style.FitPadding.x); x_axis.FitThisFrame = false; aspect = ImMax(x_axis.GetAspect(), aspect); } x_axis.SetAspect(aspect); y_axis.SetAspect(aspect); } } } plot.FitThisFrame = false; } // CONTEXT MENUS ----------------------------------------------------------- ImGui::PushOverrideID(plot.ID); const bool can_ctx = gp.OpenContextThisFrame && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && !plot.Items.Legend.Hovered; // main ctx menu if (can_ctx && plot.Hovered) ImGui::OpenPopup("##PlotContext"); if (ImGui::BeginPopup("##PlotContext")) { ShowPlotContextMenu(plot); ImGui::EndPopup(); } // axes ctx menus for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImGui::PushID(i); ImPlotAxis& x_axis = plot.XAxis(i); if (can_ctx && x_axis.Hovered && x_axis.HasMenus()) ImGui::OpenPopup("##XContext"); if (ImGui::BeginPopup("##XContext")) { ImGui::Text(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : i == 0 ? "X-Axis" : "X-Axis %d", i + 1); ImGui::Separator(); ShowAxisContextMenu(x_axis, axis_equal ? x_axis.OrthoAxis : nullptr, true); ImGui::EndPopup(); } ImGui::PopID(); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImGui::PushID(i); ImPlotAxis& y_axis = plot.YAxis(i); if (can_ctx && y_axis.Hovered && y_axis.HasMenus()) ImGui::OpenPopup("##YContext"); if (ImGui::BeginPopup("##YContext")) { ImGui::Text(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); ImGui::Separator(); ShowAxisContextMenu(y_axis, axis_equal ? y_axis.OrthoAxis : nullptr, false); ImGui::EndPopup(); } ImGui::PopID(); } ImGui::PopID(); // LINKED AXES ------------------------------------------------------------ for (int i = 0; i < ImAxis_COUNT; ++i) plot.Axes[i].PushLinks(); // CLEANUP ---------------------------------------------------------------- // remove items if (gp.CurrentItems == &plot.Items) gp.CurrentItems = nullptr; // reset the plot items for the next frame for (int i = 0; i < plot.Items.GetItemCount(); ++i) { plot.Items.GetItemByIndex(i)->SeenThisFrame = false; } // mark the plot as initialized, i.e. having made it through one frame completely plot.Initialized = true; // Pop ImGui::PushID at the end of BeginPlot ImGui::PopID(); // Reset context for next plot ResetCtxForNextPlot(GImPlot); // setup next subplot if (gp.CurrentSubplot != nullptr) { ImGui::PopID(); SubplotNextCell(); } } //----------------------------------------------------------------------------- // BEGIN/END SUBPLOT //----------------------------------------------------------------------------- static const float SUBPLOT_BORDER_SIZE = 1.0f; static const float SUBPLOT_SPLITTER_HALF_THICKNESS = 4.0f; static const float SUBPLOT_SPLITTER_FEEDBACK_TIMER = 0.06f; void SubplotSetCell(int row, int col) { ImPlotContext& gp = *GImPlot; ImPlotSubplot& subplot = *gp.CurrentSubplot; if (row >= subplot.Rows || col >= subplot.Cols) return; float xoff = 0; float yoff = 0; for (int c = 0; c < col; ++c) xoff += subplot.ColRatios[c]; for (int r = 0; r < row; ++r) yoff += subplot.RowRatios[r]; const ImVec2 grid_size = subplot.GridRect.GetSize(); ImVec2 cpos = subplot.GridRect.Min + ImVec2(xoff*grid_size.x,yoff*grid_size.y); cpos.x = IM_ROUND(cpos.x); cpos.y = IM_ROUND(cpos.y); ImGui::GetCurrentWindow()->DC.CursorPos = cpos; // set cell size subplot.CellSize.x = IM_ROUND(subplot.GridRect.GetWidth() * subplot.ColRatios[col]); subplot.CellSize.y = IM_ROUND(subplot.GridRect.GetHeight() * subplot.RowRatios[row]); // setup links const bool lx = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); const bool ly = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); const bool lr = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); const bool lc = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); SetNextAxisLinks(ImAxis_X1, lx ? &subplot.ColLinkData[0].Min : lc ? &subplot.ColLinkData[col].Min : nullptr, lx ? &subplot.ColLinkData[0].Max : lc ? &subplot.ColLinkData[col].Max : nullptr); SetNextAxisLinks(ImAxis_Y1, ly ? &subplot.RowLinkData[0].Min : lr ? &subplot.RowLinkData[row].Min : nullptr, ly ? &subplot.RowLinkData[0].Max : lr ? &subplot.RowLinkData[row].Max : nullptr); // setup alignment if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)) { gp.CurrentAlignmentH = &subplot.RowAlignmentData[row]; gp.CurrentAlignmentV = &subplot.ColAlignmentData[col]; } // set idx if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) subplot.CurrentIdx = col * subplot.Rows + row; else subplot.CurrentIdx = row * subplot.Cols + col; } void SubplotSetCell(int idx) { ImPlotContext& gp = *GImPlot; ImPlotSubplot& subplot = *gp.CurrentSubplot; if (idx >= subplot.Rows * subplot.Cols) return; int row = 0, col = 0; if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) { row = idx % subplot.Rows; col = idx / subplot.Rows; } else { row = idx / subplot.Cols; col = idx % subplot.Cols; } return SubplotSetCell(row, col); } void SubplotNextCell() { ImPlotContext& gp = *GImPlot; ImPlotSubplot& subplot = *gp.CurrentSubplot; SubplotSetCell(++subplot.CurrentIdx); } bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags, float* row_sizes, float* col_sizes) { IM_ASSERT_USER_ERROR(rows > 0 && cols > 0, "Invalid sizing arguments!"); IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentSubplot == nullptr, "Mismatched BeginSubplots()/EndSubplots()!"); ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return false; const ImGuiID ID = Window->GetID(title); bool just_created = gp.Subplots.GetByKey(ID) == nullptr; gp.CurrentSubplot = gp.Subplots.GetOrAddByKey(ID); ImPlotSubplot& subplot = *gp.CurrentSubplot; subplot.ID = ID; subplot.Items.ID = ID - 1; subplot.HasTitle = ImGui::FindRenderedTextEnd(title, nullptr) != title; // push ID ImGui::PushID(ID); if (just_created) subplot.Flags = flags; else if (flags != subplot.PreviousFlags) subplot.Flags = flags; subplot.PreviousFlags = flags; // check for change in rows and cols if (subplot.Rows != rows || subplot.Cols != cols) { subplot.RowAlignmentData.resize(rows); subplot.RowLinkData.resize(rows); subplot.RowRatios.resize(rows); for (int r = 0; r < rows; ++r) { subplot.RowAlignmentData[r].Reset(); subplot.RowLinkData[r] = ImPlotRange(0,1); subplot.RowRatios[r] = 1.0f / rows; } subplot.ColAlignmentData.resize(cols); subplot.ColLinkData.resize(cols); subplot.ColRatios.resize(cols); for (int c = 0; c < cols; ++c) { subplot.ColAlignmentData[c].Reset(); subplot.ColLinkData[c] = ImPlotRange(0,1); subplot.ColRatios[c] = 1.0f / cols; } } // check incoming size requests float row_sum = 0, col_sum = 0; if (row_sizes != nullptr) { row_sum = ImSum(row_sizes, rows); for (int r = 0; r < rows; ++r) subplot.RowRatios[r] = row_sizes[r] / row_sum; } if (col_sizes != nullptr) { col_sum = ImSum(col_sizes, cols); for (int c = 0; c < cols; ++c) subplot.ColRatios[c] = col_sizes[c] / col_sum; } subplot.Rows = rows; subplot.Cols = cols; // calc plot frame sizes ImVec2 title_size(0.0f, 0.0f); if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle)) title_size = ImGui::CalcTextSize(title, nullptr, true); const float pad_top = title_size.x > 0.0f ? title_size.y + gp.Style.LabelPadding.y : 0; const ImVec2 half_pad = gp.Style.PlotPadding/2; const ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top); subplot.GridRect.Max = subplot.FrameRect.Max - half_pad; subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows|ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); // outside legend adjustments (TODO: make function) const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); if (share_items) gp.CurrentItems = &subplot.Items; if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { ImPlotLegend& legend = subplot.Items.Legend; const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); if ((west && !horz) || (west && horz && !north && !south)) subplot.GridRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); if ((east && !horz) || (east && horz && !north && !south)) subplot.GridRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); if ((north && horz) || (north && !horz && !west && !east)) subplot.GridRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); if ((south && horz) || (south && !horz && !west && !east)) subplot.GridRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); } // render single background frame ImGui::RenderFrame(subplot.FrameRect.Min, subplot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, ImGui::GetStyle().FrameRounding); // render title if (title_size.x > 0.0f && !ImHasFlag(subplot.Flags, ImPlotFlags_NoTitle)) { const ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); AddTextCentered(ImGui::GetWindowDrawList(),ImVec2(subplot.GridRect.GetCenter().x, subplot.GridRect.Min.y - pad_top + half_pad.y),col,title); } // render splitters if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)) { ImDrawList& DrawList = *ImGui::GetWindowDrawList(); const ImU32 hov_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorHovered]); const ImU32 act_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); float xpos = subplot.GridRect.Min.x; float ypos = subplot.GridRect.Min.y; int separator = 1; // bool pass = false; for (int r = 0; r < subplot.Rows-1; ++r) { ypos += subplot.RowRatios[r] * subplot.GridRect.GetHeight(); const ImGuiID sep_id = subplot.ID + separator; ImGui::KeepAliveID(sep_id); const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS); bool sep_hov = false, sep_hld = false; const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2; subplot.RowRatios[r] = subplot.RowRatios[r+1] = p; } if (sep_clk) { subplot.TempSizes[0] = subplot.RowRatios[r]; subplot.TempSizes[1] = subplot.RowRatios[r+1]; } if (sep_hld) { float dp = ImGui::GetMouseDragDelta(0).y / subplot.GridRect.GetHeight(); if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { subplot.RowRatios[r] = subplot.TempSizes[0] + dp; subplot.RowRatios[r+1] = subplot.TempSizes[1] - dp; } } DrawList.AddLine(ImVec2(IM_ROUND(subplot.GridRect.Min.x),IM_ROUND(ypos)), ImVec2(IM_ROUND(subplot.GridRect.Max.x),IM_ROUND(ypos)), sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); } separator++; } for (int c = 0; c < subplot.Cols-1; ++c) { xpos += subplot.ColRatios[c] * subplot.GridRect.GetWidth(); const ImGuiID sep_id = subplot.ID + separator; ImGui::KeepAliveID(sep_id); const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y); bool sep_hov = false, sep_hld = false; const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2; subplot.ColRatios[c] = subplot.ColRatios[c+1] = p; } if (sep_clk) { subplot.TempSizes[0] = subplot.ColRatios[c]; subplot.TempSizes[1] = subplot.ColRatios[c+1]; } if (sep_hld) { float dp = ImGui::GetMouseDragDelta(0).x / subplot.GridRect.GetWidth(); if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { subplot.ColRatios[c] = subplot.TempSizes[0] + dp; subplot.ColRatios[c+1] = subplot.TempSizes[1] - dp; } } DrawList.AddLine(ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Min.y)), ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Max.y)), sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); } separator++; } } // set outgoing sizes if (row_sizes != nullptr) { for (int r = 0; r < rows; ++r) row_sizes[r] = subplot.RowRatios[r] * row_sum; } if (col_sizes != nullptr) { for (int c = 0; c < cols; ++c) col_sizes[c] = subplot.ColRatios[c] * col_sum; } // push styling PushStyleColor(ImPlotCol_FrameBg, IM_COL32_BLACK_TRANS); PushStyleVar(ImPlotStyleVar_PlotPadding, half_pad); PushStyleVar(ImPlotStyleVar_PlotMinSize, ImVec2(0,0)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize,0); // set initial cursor pos Window->DC.CursorPos = subplot.GridRect.Min; // begin alignments for (int r = 0; r < subplot.Rows; ++r) subplot.RowAlignmentData[r].Begin(); for (int c = 0; c < subplot.Cols; ++c) subplot.ColAlignmentData[c].Begin(); // clear legend data subplot.Items.Legend.Reset(); // Setup first subplot SubplotSetCell(0,0); return true; } void EndSubplots() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!"); ImPlotSubplot& subplot = *gp.CurrentSubplot; const ImGuiIO& IO = ImGui::GetIO(); // set alignments for (int r = 0; r < subplot.Rows; ++r) subplot.RowAlignmentData[r].End(); for (int c = 0; c < subplot.Cols; ++c) subplot.ColAlignmentData[c].End(); // pop styling PopStyleColor(); PopStyleVar(); PopStyleVar(); ImGui::PopStyleVar(); // legend subplot.Items.Legend.Hovered = false; for (int i = 0; i < subplot.Items.GetItemCount(); ++i) subplot.Items.GetItemByIndex(i)->LegendHovered = false; // render legend const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); ImDrawList& DrawList = *ImGui::GetWindowDrawList(); if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { ImPlotLegend& legend = subplot.Items.Legend; const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, legend.Location, gp.Style.PlotPadding); legend.Rect = ImRect(legend_pos, legend_pos + legend_size); legend.RectClamped = legend.Rect; const bool legend_scrollable = ClampLegendRect(legend.RectClamped,subplot.FrameRect, gp.Style.PlotPadding); const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle | ImGuiButtonFlags_FlattenChildren; ImGui::KeepAliveID(subplot.Items.ID); ImGui::ButtonBehavior(legend.RectClamped, subplot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags); legend.Hovered = legend.Hovered || (subplot.FrameHovered && legend.RectClamped.Contains(ImGui::GetIO().MousePos)); if (legend_scrollable) { if (legend.Hovered) { ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, subplot.Items.ID); if (IO.MouseWheel != 0.0f) { ImVec2 max_step = legend.Rect.GetSize() * 0.67f; #if IMGUI_VERSION_NUM < 19172 float font_size = ImGui::GetCurrentWindow()->CalcFontSize(); #else float font_size = ImGui::GetCurrentWindow()->FontRefSize; #endif float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x)); legend.Scroll.x += scroll_step * IO.MouseWheel; legend.Scroll.y += scroll_step * IO.MouseWheel; } } const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize(); legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f); legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f); const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y); ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset; legend.Rect.Min += legend_offset; legend.Rect.Max += legend_offset; } else { legend.Scroll = ImVec2(0,0); } const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true); DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg); bool legend_contextable = ShowLegendEntries(subplot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus); DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd); ImGui::PopClipRect(); if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu]) ImGui::OpenPopup("##LegendContext"); if (ImGui::BeginPopup("##LegendContext")) { ImGui::Text("Legend"); ImGui::Separator(); if (ShowLegendContextMenu(legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend))) ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend); ImGui::EndPopup(); } } else { subplot.Items.Legend.Rect = ImRect(); } // remove items if (gp.CurrentItems == &subplot.Items) gp.CurrentItems = nullptr; // reset the plot items for the next frame (TODO: put this elswhere) for (int i = 0; i < subplot.Items.GetItemCount(); ++i) { subplot.Items.GetItemByIndex(i)->SeenThisFrame = false; } // pop id ImGui::PopID(); // set DC back correctly GImGui->CurrentWindow->DC.CursorPos = subplot.FrameRect.Min; ImGui::Dummy(subplot.FrameRect.GetSize()); ResetCtxForNextSubplot(GImPlot); } //----------------------------------------------------------------------------- // [SECTION] Plot Utils //----------------------------------------------------------------------------- void SetAxis(ImAxis axis) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetAxis() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(axis >= ImAxis_X1 && axis < ImAxis_COUNT, "Axis index out of bounds!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[axis].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); SetupLock(); if (axis < ImAxis_Y1) gp.CurrentPlot->CurrentX = axis; else gp.CurrentPlot->CurrentY = axis; } void SetAxes(ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetAxes() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1, "X-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT, "Y-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[x_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[y_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); SetupLock(); gp.CurrentPlot->CurrentX = x_idx; gp.CurrentPlot->CurrentY = y_idx; } ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; return ImPlotPoint( x_axis.PixelsToPlot(x), y_axis.PixelsToPlot(y) ); } ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_idx, ImAxis y_idx) { return PixelsToPlot(pix.x, pix.y, x_idx, y_idx); } ImVec2 PlotToPixels(double x, double y, ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; return ImVec2( x_axis.PlotToPixels(x), y_axis.PlotToPixels(y) ); } ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_idx, ImAxis y_idx) { return PlotToPixels(plt.x, plt.y, x_idx, y_idx); } ImVec2 GetPlotPos() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->PlotRect.Min; } ImVec2 GetPlotSize() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->PlotRect.GetSize(); } ImPlotPoint GetPlotMousePos(ImAxis x_idx, ImAxis y_idx) { IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return PixelsToPlot(ImGui::GetMousePos(), x_idx, y_idx); } ImPlotRect GetPlotLimits(ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; ImPlotRect limits; limits.X = x_axis.Range; limits.Y = y_axis.Range; return limits; } bool IsPlotHovered() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->Hovered; } bool IsAxisHovered(ImAxis axis) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->Axes[axis].Hovered; } bool IsSubplotsHovered() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "IsSubplotsHovered() needs to be called between BeginSubplots() and EndSubplots()!"); return gp.CurrentSubplot->FrameHovered; } bool IsPlotSelected() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotSelected() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->Selected; } ImPlotRect GetPlotSelection(ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; if (!plot.Selected) return ImPlotRect(0,0,0,0); ImPlotPoint p1 = PixelsToPlot(plot.SelectRect.Min + plot.PlotRect.Min, x_idx, y_idx); ImPlotPoint p2 = PixelsToPlot(plot.SelectRect.Max + plot.PlotRect.Min, x_idx, y_idx); ImPlotRect result; result.X.Min = ImMin(p1.x, p2.x); result.X.Max = ImMax(p1.x, p2.x); result.Y.Min = ImMin(p1.y, p2.y); result.Y.Max = ImMax(p1.y, p2.y); return result; } void CancelPlotSelection() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "CancelPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; if (plot.Selected) plot.Selected = plot.Selecting = false; } void HideNextItem(bool hidden, ImPlotCond cond) { ImPlotContext& gp = *GImPlot; gp.NextItemData.HasHidden = true; gp.NextItemData.Hidden = hidden; gp.NextItemData.HiddenCond = cond; } //----------------------------------------------------------------------------- // [SECTION] Plot Tools //----------------------------------------------------------------------------- void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, bool round) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); char x_buff[IMPLOT_LABEL_MAX_SIZE]; char y_buff[IMPLOT_LABEL_MAX_SIZE]; ImPlotAxis& x_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX]; ImPlotAxis& y_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY]; LabelAxisValue(x_axis, x, x_buff, sizeof(x_buff), round); LabelAxisValue(y_axis, y, y_buff, sizeof(y_buff), round); Annotation(x,y,col,offset,clamp,"%s, %s",x_buff,y_buff); } void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); ImVec2 pos = PlotToPixels(x,y,IMPLOT_AUTO,IMPLOT_AUTO); ImU32 bg = ImGui::GetColorU32(col); ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_InlayText) : CalcTextColor(col); gp.Annotations.AppendV(pos, offset, bg, fg, clamp, fmt, args); } void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, ...) { va_list args; va_start(args, fmt); AnnotationV(x,y,col,offset,clamp,fmt,args); va_end(args); } void TagV(ImAxis axis, double v, const ImVec4& col, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; SetupLock(); ImU32 bg = ImGui::GetColorU32(col); ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_AxisText) : CalcTextColor(col); gp.Tags.AppendV(axis,v,bg,fg,fmt,args); } void Tag(ImAxis axis, double v, const ImVec4& col, const char* fmt, ...) { va_list args; va_start(args, fmt); TagV(axis,v,col,fmt,args); va_end(args); } void Tag(ImAxis axis, double v, const ImVec4& color, bool round) { ImPlotContext& gp = *GImPlot; SetupLock(); char buff[IMPLOT_LABEL_MAX_SIZE]; ImPlotAxis& ax = gp.CurrentPlot->Axes[axis]; LabelAxisValue(ax, v, buff, sizeof(buff), round); Tag(axis,v,color,"%s",buff); } IMPLOT_API void TagX(double x, const ImVec4& color, bool round) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); Tag(gp.CurrentPlot->CurrentX, x, color, round); } IMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); va_list args; va_start(args, fmt); TagV(gp.CurrentPlot->CurrentX,x,color,fmt,args); va_end(args); } IMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); TagV(gp.CurrentPlot->CurrentX, x, color, fmt, args); } IMPLOT_API void TagY(double y, const ImVec4& color, bool round) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); Tag(gp.CurrentPlot->CurrentY, y, color, round); } IMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); va_list args; va_start(args, fmt); TagV(gp.CurrentPlot->CurrentY,y,color,fmt,args); va_end(args); } IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); TagV(gp.CurrentPlot->CurrentY, y, color, fmt, args); } static const float DRAG_GRAB_HALF_SIZE = 4.0f; bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { ImGui::PushID("#IMPLOT_DRAG_POINT"); IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { FitPoint(ImPlotPoint(*x,*y)); } const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, radius); const ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; const ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); ImVec2 pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); ImRect rect(pos.x-grab_half_size,pos.y-grab_half_size,pos.x+grab_half_size,pos.y+grab_half_size); bool hovered = false, held = false; ImGui::KeepAliveID(id); if (input) { bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held); if (out_clicked) *out_clicked = clicked; if (out_hovered) *out_hovered = hovered; if (out_held) *out_held = held; } bool modified = false; if (held && ImGui::IsMouseDragging(0)) { *x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; *y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; modified = true; } PushPlotClipRect(); ImDrawList& DrawList = *GetPlotDrawList(); if ((hovered || held) && show_curs) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); if (modified && no_delay) pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); DrawList.AddCircleFilled(pos, radius, col32); PopPlotClipRect(); ImGui::PopID(); return modified; } bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { // ImGui::PushID("#IMPLOT_DRAG_LINE_X"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { FitPointX(*value); } const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); float yt = gp.CurrentPlot->PlotRect.Min.y; float yb = gp.CurrentPlot->PlotRect.Max.y; float x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); ImRect rect(x-grab_half_size,yt,x+grab_half_size,yb); bool hovered = false, held = false; ImGui::KeepAliveID(id); if (input) { bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held); if (out_clicked) *out_clicked = clicked; if (out_hovered) *out_hovered = hovered; if (out_held) *out_held = held; } if ((hovered || held) && show_curs) ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); float len = gp.Style.MajorTickLen.x; ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); bool modified = false; if (held && ImGui::IsMouseDragging(0)) { *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; modified = true; } PushPlotClipRect(); ImDrawList& DrawList = *GetPlotDrawList(); if (modified && no_delay) x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness); DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness); PopPlotClipRect(); // ImGui::PopID(); return modified; } bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { ImGui::PushID("#IMPLOT_DRAG_LINE_Y"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineY() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { FitPointY(*value); } const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); float xl = gp.CurrentPlot->PlotRect.Min.x; float xr = gp.CurrentPlot->PlotRect.Max.x; float y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); ImRect rect(xl,y-grab_half_size,xr,y+grab_half_size); bool hovered = false, held = false; ImGui::KeepAliveID(id); if (input) { bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held); if (out_clicked) *out_clicked = clicked; if (out_hovered) *out_hovered = hovered; if (out_held) *out_held = held; } if ((hovered || held) && show_curs) ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); float len = gp.Style.MajorTickLen.y; ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); bool modified = false; if (held && ImGui::IsMouseDragging(0)) { *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; modified = true; } PushPlotClipRect(); ImDrawList& DrawList = *GetPlotDrawList(); if (modified && no_delay) y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness); PopPlotClipRect(); ImGui::PopID(); return modified; } bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { ImGui::PushID("#IMPLOT_DRAG_RECT"); IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { FitPoint(ImPlotPoint(*x_min,*y_min)); FitPoint(ImPlotPoint(*x_max,*y_max)); } const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); bool h[] = {true,false,true,false}; double* x[] = {x_min,x_max,x_max,x_min}; double* y[] = {y_min,y_min,y_max,y_max}; ImVec2 p[4]; for (int i = 0; i < 4; ++i) p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); ImVec2 pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); ImRect rect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); ImRect rect_grab = rect; rect_grab.Expand(DRAG_GRAB_HALF_SIZE); ImGuiMouseCursor cur[4]; if (show_curs) { cur[0] = (rect.Min.x == p[0].x && rect.Min.y == p[0].y) || (rect.Max.x == p[0].x && rect.Max.y == p[0].y) ? ImGuiMouseCursor_ResizeNWSE : ImGuiMouseCursor_ResizeNESW; cur[1] = cur[0] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; cur[2] = cur[1] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; cur[3] = cur[2] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; } ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); color.w *= 0.25f; ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); bool modified = false; bool clicked = false, hovered = false, held = false; ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); ImGui::KeepAliveID(id); if (input) { // middle point clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held); if (out_clicked) *out_clicked = clicked; if (out_hovered) *out_hovered = hovered; if (out_held) *out_held = held; } if ((hovered || held) && show_curs) ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); if (held && ImGui::IsMouseDragging(0)) { for (int i = 0; i < 4; ++i) { ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); *y[i] = pp.y; *x[i] = pp.x; } modified = true; } for (int i = 0; i < 4; ++i) { // points b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE); ImGuiID p_id = id + i + 1; ImGui::KeepAliveID(p_id); if (input) { clicked = ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held); if (out_clicked) *out_clicked = *out_clicked || clicked; if (out_hovered) *out_hovered = *out_hovered || hovered; if (out_held) *out_held = *out_held || held; } if ((hovered || held) && show_curs) ImGui::SetMouseCursor(cur[i]); if (held && ImGui::IsMouseDragging(0)) { *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; modified = true; } // edges ImVec2 e_min = ImMin(p[i],p[(i+1)%4]); ImVec2 e_max = ImMax(p[i],p[(i+1)%4]); b_rect = h[i] ? ImRect(e_min.x + DRAG_GRAB_HALF_SIZE, e_min.y - DRAG_GRAB_HALF_SIZE, e_max.x - DRAG_GRAB_HALF_SIZE, e_max.y + DRAG_GRAB_HALF_SIZE) : ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE); ImGuiID e_id = id + i + 5; ImGui::KeepAliveID(e_id); if (input) { clicked = ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held); if (out_clicked) *out_clicked = *out_clicked || clicked; if (out_hovered) *out_hovered = *out_hovered || hovered; if (out_held) *out_held = *out_held || held; } if ((hovered || held) && show_curs) h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); if (held && ImGui::IsMouseDragging(0)) { if (h[i]) *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; else *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; modified = true; } if (hovered && ImGui::IsMouseDoubleClicked(0)) { ImPlotRect b = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO); if (h[i]) *y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max; else *x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max; modified = true; } } const bool mouse_inside = rect_grab.Contains(ImGui::GetMousePos()); const bool mouse_clicked = ImGui::IsMouseClicked(0); const bool mouse_down = ImGui::IsMouseDown(0); if (input && mouse_inside) { if (out_clicked) *out_clicked = *out_clicked || mouse_clicked; if (out_hovered) *out_hovered = true; if (out_held) *out_held = *out_held || mouse_down; } PushPlotClipRect(); ImDrawList& DrawList = *GetPlotDrawList(); if (modified && no_delay) { for (int i = 0; i < 4; ++i) p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); rect = ImRect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); } DrawList.AddRectFilled(rect.Min, rect.Max, col32_a); DrawList.AddRect(rect.Min, rect.Max, col32); if (input && (modified || mouse_inside)) { DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32); for (int i = 0; i < 4; ++i) DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32); } PopPlotClipRect(); ImGui::PopID(); return modified; } bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags, out_clicked, out_hovered, out_held); } //----------------------------------------------------------------------------- // [SECTION] Legend Utils and Tools //----------------------------------------------------------------------------- bool IsLegendEntryHovered(const char* label_id) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "IsPlotItemHighlight() needs to be called within an itemized context!"); SetupLock(); ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); ImPlotItem* item = gp.CurrentItems->GetItem(id); return item && item->LegendHovered; } bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "BeginLegendPopup() needs to be called within an itemized context!"); SetupLock(); ImGuiWindow* window = GImGui->CurrentWindow; if (window->SkipItems) return false; ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); if (ImGui::IsMouseReleased(mouse_button)) { ImPlotItem* item = gp.CurrentItems->GetItem(id); if (item && item->LegendHovered) ImGui::OpenPopupEx(id); } return ImGui::BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } void EndLegendPopup() { SetupLock(); ImGui::EndPopup(); } void ShowAltLegend(const char* title_id, bool vertical, const ImVec2 size, bool interactable) { ImPlotContext& gp = *GImPlot; ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return; ImDrawList &DrawList = *Window->DrawList; ImPlotPlot* plot = GetPlot(title_id); ImVec2 legend_size; ImVec2 default_size = gp.Style.LegendPadding * 2; if (plot != nullptr) { legend_size = CalcLegendSize(plot->Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical); default_size = legend_size + gp.Style.LegendPadding * 2; } ImVec2 frame_size = ImGui::CalcItemSize(size, default_size.x, default_size.y); ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(bb_frame); if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame)) return; ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); DrawList.PushClipRect(bb_frame.Min, bb_frame.Max, true); if (plot != nullptr) { const ImVec2 legend_pos = GetLocationPos(bb_frame, legend_size, 0, gp.Style.LegendPadding); const ImRect legend_bb(legend_pos, legend_pos + legend_size); interactable = interactable && bb_frame.Contains(ImGui::GetIO().MousePos); // render legend box ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg); DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd); // render entries ShowLegendEntries(plot->Items, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical, DrawList); } DrawList.PopClipRect(); } //----------------------------------------------------------------------------- // [SECTION] Drag and Drop Utils //----------------------------------------------------------------------------- bool BeginDragDropTargetPlot() { SetupLock(); ImPlotContext& gp = *GImPlot; ImRect rect = gp.CurrentPlot->PlotRect; return ImGui::BeginDragDropTargetCustom(rect, gp.CurrentPlot->ID); } bool BeginDragDropTargetAxis(ImAxis axis) { SetupLock(); ImPlotPlot& plot = *GImPlot->CurrentPlot; ImPlotAxis& ax = plot.Axes[axis]; ImRect rect = ax.HoverRect; rect.Expand(-3.5f); return ImGui::BeginDragDropTargetCustom(rect, ax.ID); } bool BeginDragDropTargetLegend() { SetupLock(); ImPlotItemGroup& items = *GImPlot->CurrentItems; ImRect rect = items.Legend.RectClamped; return ImGui::BeginDragDropTargetCustom(rect, items.ID); } void EndDragDropTarget() { SetupLock(); ImGui::EndDragDropTarget(); } bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags) { SetupLock(); ImPlotContext& gp = *GImPlot; ImPlotPlot* plot = gp.CurrentPlot; if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == plot->ID) return ImGui::ItemAdd(plot->PlotRect, plot->ID) && ImGui::BeginDragDropSource(flags); return false; } bool BeginDragDropSourceAxis(ImAxis idx, ImGuiDragDropFlags flags) { SetupLock(); ImPlotContext& gp = *GImPlot; ImPlotAxis& axis = gp.CurrentPlot->Axes[idx]; if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == axis.ID) return ImGui::ItemAdd(axis.HoverRect, axis.ID) && ImGui::BeginDragDropSource(flags); return false; } bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) { SetupLock(); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "BeginDragDropSourceItem() needs to be called within an itemized context!"); ImGuiID item_id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); ImPlotItem* item = gp.CurrentItems->GetItem(item_id); if (item != nullptr) { return ImGui::ItemAdd(item->LegendHoverRect, item->ID) && ImGui::BeginDragDropSource(flags); } return false; } void EndDragDropSource() { SetupLock(); ImGui::EndDragDropSource(); } //----------------------------------------------------------------------------- // [SECTION] Aligned Plots //----------------------------------------------------------------------------- bool BeginAlignedPlots(const char* group_id, bool vertical) { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH == nullptr && gp.CurrentAlignmentV == nullptr, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return false; const ImGuiID ID = Window->GetID(group_id); ImPlotAlignmentData* alignment = gp.AlignmentData.GetOrAddByKey(ID); if (vertical) gp.CurrentAlignmentV = alignment; else gp.CurrentAlignmentH = alignment; if (alignment->Vertical != vertical) alignment->Reset(); alignment->Vertical = vertical; alignment->Begin(); return true; } void EndAlignedPlots() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH != nullptr || gp.CurrentAlignmentV != nullptr, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); ImPlotAlignmentData* alignment = gp.CurrentAlignmentH != nullptr ? gp.CurrentAlignmentH : (gp.CurrentAlignmentV != nullptr ? gp.CurrentAlignmentV : nullptr); if (alignment) alignment->End(); ResetCtxForNextAlignedPlots(GImPlot); } //----------------------------------------------------------------------------- // [SECTION] Plot and Item Styling //----------------------------------------------------------------------------- ImPlotStyle& GetStyle() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; return gp.Style; } void PushStyleColor(ImPlotCol idx, ImU32 col) { ImPlotContext& gp = *GImPlot; ImGuiColorMod backup; backup.Col = (ImGuiCol)idx; backup.BackupValue = gp.Style.Colors[idx]; gp.ColorModifiers.push_back(backup); gp.Style.Colors[idx] = ImGui::ColorConvertU32ToFloat4(col); } void PushStyleColor(ImPlotCol idx, const ImVec4& col) { ImPlotContext& gp = *GImPlot; ImGuiColorMod backup; backup.Col = (ImGuiCol)idx; backup.BackupValue = gp.Style.Colors[idx]; gp.ColorModifiers.push_back(backup); gp.Style.Colors[idx] = col; } void PopStyleColor(int count) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(count <= gp.ColorModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { ImGuiColorMod& backup = gp.ColorModifiers.back(); gp.Style.Colors[backup.Col] = backup.BackupValue; gp.ColorModifiers.pop_back(); count--; } } void PushStyleVar(ImPlotStyleVar idx, float val) { ImPlotContext& gp = *GImPlot; const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { float* pvar = (float*)var_info->GetVarPtr(&gp.Style); gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); *pvar = val; return; } IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); } void PushStyleVar(ImPlotStyleVar idx, int val) { ImPlotContext& gp = *GImPlot; const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); if (var_info->Type == ImGuiDataType_S32 && var_info->Count == 1) { int* pvar = (int*)var_info->GetVarPtr(&gp.Style); gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); *pvar = val; return; } else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { float* pvar = (float*)var_info->GetVarPtr(&gp.Style); gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); *pvar = (float)val; return; } IM_ASSERT(0 && "Called PushStyleVar() int variant but variable is not a int!"); } void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val) { ImPlotContext& gp = *GImPlot; const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) { ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&gp.Style); gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); *pvar = val; return; } IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); } void PopStyleVar(int count) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(count <= gp.StyleModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { ImGuiStyleMod& backup = gp.StyleModifiers.back(); const ImPlotStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx); void* data = info->GetVarPtr(&gp.Style); if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } else if (info->Type == ImGuiDataType_S32 && info->Count == 1) { ((int*)data)[0] = backup.BackupInt[0]; } gp.StyleModifiers.pop_back(); count--; } } //------------------------------------------------------------------------------ // [Section] Colormaps //------------------------------------------------------------------------------ ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already been used!"); ImVector buffer; buffer.resize(size); for (int i = 0; i < size; ++i) buffer[i] = ImGui::ColorConvertFloat4ToU32(colormap[i]); return gp.ColormapData.Append(name, buffer.Data, size, qual); } ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size, bool qual) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already be used!"); return gp.ColormapData.Append(name, colormap, size, qual); } int GetColormapCount() { ImPlotContext& gp = *GImPlot; return gp.ColormapData.Count; } const char* GetColormapName(ImPlotColormap colormap) { ImPlotContext& gp = *GImPlot; return gp.ColormapData.GetName(colormap); } ImPlotColormap GetColormapIndex(const char* name) { ImPlotContext& gp = *GImPlot; return gp.ColormapData.GetIndex(name); } void PushColormap(ImPlotColormap colormap) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, "The colormap index is invalid!"); gp.ColormapModifiers.push_back(gp.Style.Colormap); gp.Style.Colormap = colormap; } void PushColormap(const char* name) { ImPlotContext& gp = *GImPlot; ImPlotColormap idx = gp.ColormapData.GetIndex(name); IM_ASSERT_USER_ERROR(idx != -1, "The colormap name is invalid!"); PushColormap(idx); } void PopColormap(int count) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(count <= gp.ColormapModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { const ImPlotColormap& backup = gp.ColormapModifiers.back(); gp.Style.Colormap = backup; gp.ColormapModifiers.pop_back(); count--; } } ImU32 NextColormapColorU32() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); int idx = gp.CurrentItems->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); gp.CurrentItems->ColormapIdx++; return col; } ImVec4 NextColormapColor() { return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); } int GetColormapSize(ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); return gp.ColormapData.GetKeyCount(cmap); } ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); idx = idx % gp.ColormapData.GetKeyCount(cmap); return gp.ColormapData.GetKeyColor(cmap, idx); } ImVec4 GetColormapColor(int idx, ImPlotColormap cmap) { return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx,cmap)); } ImU32 SampleColormapU32(float t, ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); return gp.ColormapData.LerpTable(cmap, t); } ImVec4 SampleColormap(float t, ImPlotColormap cmap) { return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t,cmap)); } void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) { const int n = continuous ? size - 1 : size; ImU32 col1, col2; if (vert) { const float step = bounds.GetHeight() / n; ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step); for (int i = 0; i < n; ++i) { if (reversed) { col1 = colors[size-i-1]; col2 = continuous ? colors[size-i-2] : col1; } else { col1 = colors[i]; col2 = continuous ? colors[i+1] : col1; } DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); rect.TranslateY(step); } } else { const float step = bounds.GetWidth() / n; ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y); for (int i = 0; i < n; ++i) { if (reversed) { col1 = colors[size-i-1]; col2 = continuous ? colors[size-i-2] : col1; } else { col1 = colors[i]; col2 = continuous ? colors[i+1] : col1; } DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1); rect.TranslateX(step); } } } void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, const char* format, ImPlotColormapScaleFlags flags, ImPlotColormap cmap) { ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return; const ImGuiID ID = Window->GetID(label); ImVec2 label_size(0,0); if (!ImHasFlag(flags, ImPlotColormapScaleFlags_NoLabel)) { label_size = ImGui::CalcTextSize(label,nullptr,true); } ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); ImVec2 frame_size = ImGui::CalcItemSize(size, 0, gp.Style.PlotDefaultSize.y); if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f) frame_size.y = gp.Style.PlotMinSize.y; ImPlotRange range(ImMin(scale_min,scale_max), ImMax(scale_min,scale_max)); gp.CTicker.Reset(); Locator_Default(gp.CTicker, range, frame_size.y, true, Formatter_Default, (void*)format); const bool rend_label = label_size.x > 0; const float txt_off = gp.Style.LabelPadding.x; const float pad = txt_off + gp.CTicker.MaxSize.x + (rend_label ? txt_off + label_size.y : 0); float bar_w = 20; if (frame_size.x == 0) frame_size.x = bar_w + pad + 2 * gp.Style.PlotPadding.x; else { bar_w = frame_size.x - (pad + 2 * gp.Style.PlotPadding.x); if (bar_w < gp.Style.MajorTickLen.y) bar_w = gp.Style.MajorTickLen.y; } ImDrawList &DrawList = *Window->DrawList; ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(bb_frame); if (!ImGui::ItemAdd(bb_frame, ID, &bb_frame)) return; ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); const bool opposite = ImHasFlag(flags, ImPlotColormapScaleFlags_Opposite); const bool inverted = ImHasFlag(flags, ImPlotColormapScaleFlags_Invert); const bool reversed = scale_min > scale_max; float bb_grad_shift = opposite ? pad : 0; ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding + ImVec2(bb_grad_shift, 0), bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x + bb_grad_shift, frame_size.y - gp.Style.PlotPadding.y)); ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text); const bool invert_scale = inverted ? (reversed ? false : true) : (reversed ? true : false); const float y_min = invert_scale ? bb_grad.Max.y : bb_grad.Min.y; const float y_max = invert_scale ? bb_grad.Min.y : bb_grad.Max.y; RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, !inverted, !gp.ColormapData.IsQual(cmap)); for (int i = 0; i < gp.CTicker.TickCount(); ++i) { const double y_pos_plt = gp.CTicker.Ticks[i].PlotPos; const float y_pos = ImRemap((float)y_pos_plt, (float)range.Max, (float)range.Min, y_min, y_max); const float tick_width = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; const float tick_thick = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; const float tick_t = (float)((y_pos_plt - scale_min) / (scale_max - scale_min)); const ImU32 tick_col = CalcTextColor(gp.ColormapData.LerpTable(cmap,tick_t)); if (y_pos < bb_grad.Max.y - 2 && y_pos > bb_grad.Min.y + 2) { DrawList.AddLine(opposite ? ImVec2(bb_grad.Min.x+1, y_pos) : ImVec2(bb_grad.Max.x-1, y_pos), opposite ? ImVec2(bb_grad.Min.x + tick_width, y_pos) : ImVec2(bb_grad.Max.x - tick_width, y_pos), tick_col, tick_thick); } const float txt_x = opposite ? bb_grad.Min.x - txt_off - gp.CTicker.Ticks[i].LabelSize.x : bb_grad.Max.x + txt_off; const float txt_y = y_pos - gp.CTicker.Ticks[i].LabelSize.y * 0.5f; DrawList.AddText(ImVec2(txt_x, txt_y), col_text, gp.CTicker.GetText(i)); } if (rend_label) { const float pos_x = opposite ? bb_frame.Min.x + gp.Style.PlotPadding.x : bb_grad.Max.x + 2 * txt_off + gp.CTicker.MaxSize.x; const float pos_y = bb_grad.GetCenter().y + label_size.x * 0.5f; const char* label_end = ImGui::FindRenderedTextEnd(label); AddTextVertical(&DrawList,ImVec2(pos_x,pos_y),col_text,label,label_end); } DrawList.AddRect(bb_grad.Min, bb_grad.Max, GetStyleColorU32(ImPlotCol_PlotBorder)); ImGui::PopClipRect(); } bool ColormapSlider(const char* label, float* t, ImVec4* out, const char* format, ImPlotColormap cmap) { *t = ImClamp(*t,0.0f,1.0f); ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return false; ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); const ImU32* keys = gp.ColormapData.GetKeys(cmap); const int count = gp.ColormapData.GetKeyCount(cmap); const bool qual = gp.ColormapData.IsQual(cmap); const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; const float w = ImGui::CalcItemWidth(); const float h = ImGui::GetFrameHeight(); const ImRect rect = ImRect(pos.x,pos.y,pos.x+w,pos.y+h); RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); const ImU32 grab = CalcTextColor(gp.ColormapData.LerpTable(cmap,*t)); // const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,0.5f)); ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,ImVec4(1,1,1,0.1f)); ImGui::PushStyleColor(ImGuiCol_SliderGrab,grab); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, grab); ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,2); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); const bool changed = ImGui::SliderFloat(label,t,0,1,format); ImGui::PopStyleColor(5); ImGui::PopStyleVar(2); if (out != nullptr) *out = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.LerpTable(cmap,*t)); return changed; } bool ColormapButton(const char* label, const ImVec2& size_arg, ImPlotColormap cmap) { ImGuiContext &G = *GImGui; const ImGuiStyle& style = G.Style; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return false; ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); const ImU32* keys = gp.ColormapData.GetKeys(cmap); const int count = gp.ColormapData.GetKeyCount(cmap); const bool qual = gp.ColormapData.IsQual(cmap); const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true); ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); const ImRect rect = ImRect(pos.x,pos.y,pos.x+size.x,pos.y+size.y); RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,G.Style.ButtonTextAlign.x)); ImGui::PushStyleColor(ImGuiCol_Button,IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ImVec4(1,1,1,0.1f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive,ImVec4(1,1,1,0.2f)); ImGui::PushStyleColor(ImGuiCol_Text,text); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); const bool pressed = ImGui::Button(label,size); ImGui::PopStyleColor(4); ImGui::PopStyleVar(1); return pressed; } //----------------------------------------------------------------------------- // [Section] Miscellaneous //----------------------------------------------------------------------------- ImPlotInputMap& GetInputMap() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; return gp.InputMap; } void MapInputDefault(ImPlotInputMap* dst) { ImPlotInputMap& map = dst ? *dst : GetInputMap(); map.Pan = ImGuiMouseButton_Left; map.PanMod = ImGuiMod_None; map.Fit = ImGuiMouseButton_Left; map.Menu = ImGuiMouseButton_Right; map.Select = ImGuiMouseButton_Right; map.SelectMod = ImGuiMod_None; map.SelectCancel = ImGuiMouseButton_Left; map.SelectHorzMod = ImGuiMod_Alt; map.SelectVertMod = ImGuiMod_Shift; map.OverrideMod = ImGuiMod_Ctrl; map.ZoomMod = ImGuiMod_None; map.ZoomRate = 0.1f; } void MapInputReverse(ImPlotInputMap* dst) { ImPlotInputMap& map = dst ? *dst : GetInputMap(); map.Pan = ImGuiMouseButton_Right; map.PanMod = ImGuiMod_None; map.Fit = ImGuiMouseButton_Left; map.Menu = ImGuiMouseButton_Right; map.Select = ImGuiMouseButton_Left; map.SelectMod = ImGuiMod_None; map.SelectCancel = ImGuiMouseButton_Right; map.SelectHorzMod = ImGuiMod_Alt; map.SelectVertMod = ImGuiMod_Shift; map.OverrideMod = ImGuiMod_Ctrl; map.ZoomMod = ImGuiMod_None; map.ZoomRate = 0.1f; } //----------------------------------------------------------------------------- // [Section] Miscellaneous //----------------------------------------------------------------------------- void ItemIcon(const ImVec4& col) { ItemIcon(ImGui::ColorConvertFloat4ToU32(col)); } void ItemIcon(ImU32 col) { const float txt_size = ImGui::GetTextLineHeight(); ImVec2 size(txt_size-4,txt_size); ImGuiWindow* window = ImGui::GetCurrentWindow(); ImVec2 pos = window->DC.CursorPos; ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col); ImGui::Dummy(size); } void ColormapIcon(ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; const float txt_size = ImGui::GetTextLineHeight(); ImVec2 size(txt_size-4,txt_size); ImGuiWindow* window = ImGui::GetCurrentWindow(); ImVec2 pos = window->DC.CursorPos; ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2)); ImDrawList& DrawList = *ImGui::GetWindowDrawList(); RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap)); ImGui::Dummy(size); } ImDrawList* GetPlotDrawList() { return ImGui::GetWindowDrawList(); } void PushPlotClipRect(float expand) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); ImRect rect = gp.CurrentPlot->PlotRect; rect.Expand(expand); ImGui::PushClipRect(rect.Min, rect.Max, true); } void PopPlotClipRect() { SetupLock(); ImGui::PopClipRect(); } static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } } bool ShowStyleSelector(const char* label) { static int style_idx = -1; if (ImGui::Combo(label, &style_idx, "Auto\0Classic\0Dark\0Light\0")) { switch (style_idx) { case 0: StyleColorsAuto(); break; case 1: StyleColorsClassic(); break; case 2: StyleColorsDark(); break; case 3: StyleColorsLight(); break; } return true; } return false; } bool ShowColormapSelector(const char* label) { ImPlotContext& gp = *GImPlot; bool set = false; if (ImGui::BeginCombo(label, gp.ColormapData.GetName(gp.Style.Colormap))) { for (int i = 0; i < gp.ColormapData.Count; ++i) { const char* name = gp.ColormapData.GetName(i); if (ImGui::Selectable(name, gp.Style.Colormap == i)) { gp.Style.Colormap = i; ImPlot::BustItemCache(); set = true; } } ImGui::EndCombo(); } return set; } bool ShowInputMapSelector(const char* label) { static int map_idx = -1; if (ImGui::Combo(label, &map_idx, "Default\0Reversed\0")) { switch (map_idx) { case 0: MapInputDefault(); break; case 1: MapInputReverse(); break; } return true; } return false; } void ShowStyleEditor(ImPlotStyle* ref) { ImPlotContext& gp = *GImPlot; ImPlotStyle& style = GetStyle(); static ImPlotStyle ref_saved_style; // Default to using internal storage as reference static bool init = true; if (init && ref == nullptr) ref_saved_style = style; init = false; if (ref == nullptr) ref = &ref_saved_style; if (ImPlot::ShowStyleSelector("Colors##Selector")) ref_saved_style = style; // Save/Revert button if (ImGui::Button("Save Ref")) *ref = ref_saved_style = style; ImGui::SameLine(); if (ImGui::Button("Revert Ref")) style = *ref; ImGui::SameLine(); HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. " "Use \"Export\" below to save them somewhere."); if (ImGui::BeginTabBar("##StyleEditor")) { if (ImGui::BeginTabItem("Variables")) { ImGui::Text("Item Styling"); ImGui::SliderFloat("LineWeight", &style.LineWeight, 0.0f, 5.0f, "%.1f"); ImGui::SliderFloat("MarkerSize", &style.MarkerSize, 2.0f, 10.0f, "%.1f"); ImGui::SliderFloat("MarkerWeight", &style.MarkerWeight, 0.0f, 5.0f, "%.1f"); ImGui::SliderFloat("FillAlpha", &style.FillAlpha, 0.0f, 1.0f, "%.2f"); ImGui::SliderFloat("ErrorBarSize", &style.ErrorBarSize, 0.0f, 10.0f, "%.1f"); ImGui::SliderFloat("ErrorBarWeight", &style.ErrorBarWeight, 0.0f, 5.0f, "%.1f"); ImGui::SliderFloat("DigitalBitHeight", &style.DigitalBitHeight, 0.0f, 20.0f, "%.1f"); ImGui::SliderFloat("DigitalBitGap", &style.DigitalBitGap, 0.0f, 20.0f, "%.1f"); ImGui::Text("Plot Styling"); ImGui::SliderFloat("PlotBorderSize", &style.PlotBorderSize, 0.0f, 2.0f, "%.0f"); ImGui::SliderFloat("MinorAlpha", &style.MinorAlpha, 0.0f, 1.0f, "%.2f"); ImGui::SliderFloat2("MajorTickLen", (float*)&style.MajorTickLen, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("MinorTickLen", (float*)&style.MinorTickLen, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("MajorTickSize", (float*)&style.MajorTickSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MinorTickSize", (float*)&style.MinorTickSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MajorGridSize", (float*)&style.MajorGridSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MinorGridSize", (float*)&style.MinorGridSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("PlotDefaultSize", (float*)&style.PlotDefaultSize, 0.0f, 1000, "%.0f"); ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f"); ImGui::Text("Plot Padding"); ImGui::SliderFloat2("PlotPadding", (float*)&style.PlotPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LabelPadding", (float*)&style.LabelPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LegendPadding", (float*)&style.LegendPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LegendInnerPadding", (float*)&style.LegendInnerPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat2("LegendSpacing", (float*)&style.LegendSpacing, 0.0f, 5.0f, "%.0f"); ImGui::SliderFloat2("MousePosPadding", (float*)&style.MousePosPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("AnnotationPadding", (float*)&style.AnnotationPadding, 0.0f, 5.0f, "%.0f"); ImGui::SliderFloat2("FitPadding", (float*)&style.FitPadding, 0, 0.2f, "%.2f"); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Colors")) { static int output_dest = 0; static bool output_only_modified = false; if (ImGui::Button("Export", ImVec2(75,0))) { if (output_dest == 0) ImGui::LogToClipboard(); else ImGui::LogToTTY(); ImGui::LogText("ImVec4* colors = ImPlot::GetStyle().Colors;\n"); for (int i = 0; i < ImPlotCol_COUNT; i++) { const ImVec4& col = style.Colors[i]; const char* name = ImPlot::GetStyleColorName(i); if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) { if (IsColorAuto(i)) ImGui::LogText("colors[ImPlotCol_%s]%*s= IMPLOT_AUTO_COL;\n",name,14 - (int)strlen(name), ""); else ImGui::LogText("colors[ImPlotCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n", name, 14 - (int)strlen(name), "", col.x, col.y, col.z, col.w); } } ImGui::LogFinish(); } ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); static ImGuiTextFilter filter; filter.Draw("Filter colors", ImGui::GetFontSize() * 16); static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; #if IMGUI_VERSION_NUM < 19173 if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); #else if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } ImGui::SameLine(); if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); #endif HelpMarker( "In the color list:\n" "Left-click on colored square to open color picker,\n" "Right-click to open edit options menu."); ImGui::Separator(); ImGui::PushItemWidth(-160); for (int i = 0; i < ImPlotCol_COUNT; i++) { const char* name = ImPlot::GetStyleColorName(i); if (!filter.PassFilter(name)) continue; ImGui::PushID(i); ImVec4 temp = GetStyleColorVec4(i); const bool is_auto = IsColorAuto(i); if (!is_auto) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); if (ImGui::Button("Auto")) { if (is_auto) style.Colors[i] = temp; else style.Colors[i] = IMPLOT_AUTO_COL; BustItemCache(); } if (!is_auto) ImGui::PopStyleVar(); ImGui::SameLine(); if (ImGui::ColorEdit4(name, &temp.x, ImGuiColorEditFlags_NoInputs | alpha_flags)) { style.Colors[i] = temp; BustItemCache(); } if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) { ImGui::SameLine(175); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; } ImGui::SameLine(); if (ImGui::Button("Revert")) { style.Colors[i] = ref->Colors[i]; BustItemCache(); } } ImGui::PopID(); } ImGui::PopItemWidth(); ImGui::Separator(); ImGui::Text("Colors that are set to Auto (i.e. IMPLOT_AUTO_COL) will\n" "be automatically deduced from your ImGui style or the\n" "current ImPlot Colormap. If you want to style individual\n" "plot items, use Push/PopStyleColor around its function."); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Colormaps")) { static int output_dest = 0; if (ImGui::Button("Export", ImVec2(75,0))) { if (output_dest == 0) ImGui::LogToClipboard(); else ImGui::LogToTTY(); int size = GetColormapSize(); const char* name = GetColormapName(gp.Style.Colormap); ImGui::LogText("static const ImU32 %s_Data[%d] = {\n", name, size); for (int i = 0; i < size; ++i) { ImU32 col = GetColormapColorU32(i,gp.Style.Colormap); ImGui::LogText(" %u%s\n", col, i == size - 1 ? "" : ","); } ImGui::LogText("};\nImPlotColormap %s = ImPlot::AddColormap(\"%s\", %s_Data, %d);", name, name, name, size); ImGui::LogFinish(); } ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::SameLine(); static bool edit = false; ImGui::Checkbox("Edit Mode",&edit); // built-in/added ImGui::Separator(); for (int i = 0; i < gp.ColormapData.Count; ++i) { ImGui::PushID(i); int size = gp.ColormapData.GetKeyCount(i); bool selected = i == gp.Style.Colormap; const char* name = GetColormapName(i); if (!selected) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); if (ImGui::Button(name, ImVec2(100,0))) { gp.Style.Colormap = i; BustItemCache(); } if (!selected) ImGui::PopStyleVar(); ImGui::SameLine(); ImGui::BeginGroup(); if (edit) { for (int c = 0; c < size; ++c) { ImGui::PushID(c); ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c)); if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) { ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4); gp.ColormapData.SetKeyColor(i,c,col32); BustItemCache(); } if ((c + 1) % 12 != 0 && c != size -1) ImGui::SameLine(); ImGui::PopID(); } } else { if (ImPlot::ColormapButton("##",ImVec2(-1,0),i)) edit = true; } ImGui::EndGroup(); ImGui::PopID(); } static ImVector custom; if (custom.Size == 0) { custom.push_back(ImVec4(1,0,0,1)); custom.push_back(ImVec4(0,1,0,1)); custom.push_back(ImVec4(0,0,1,1)); } ImGui::Separator(); ImGui::BeginGroup(); static char name[16] = "MyColormap"; if (ImGui::Button("+", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0))) custom.push_back(ImVec4(0,0,0,1)); ImGui::SameLine(); if (ImGui::Button("-", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) custom.pop_back(); ImGui::SetNextItemWidth(100); ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank); static bool qual = true; ImGui::Checkbox("Qualitative",&qual); if (ImGui::Button("Add", ImVec2(100, 0)) && gp.ColormapData.GetIndex(name)==-1) AddColormap(name,custom.Data,custom.Size,qual); ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); for (int c = 0; c < custom.Size; ++c) { ImGui::PushID(c); if (ImGui::ColorEdit4("##Col1", &custom[c].x, ImGuiColorEditFlags_NoInputs)) { } if ((c + 1) % 12 != 0) ImGui::SameLine(); ImGui::PopID(); } ImGui::EndGroup(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } } void ShowUserGuide() { ImGui::BulletText("Left-click drag within the plot area to pan X and Y axes."); ImGui::Indent(); ImGui::BulletText("Left-click drag on axis labels to pan an individual axis."); ImGui::Unindent(); ImGui::BulletText("Scroll in the plot area to zoom both X any Y axes."); ImGui::Indent(); ImGui::BulletText("Scroll on axis labels to zoom an individual axis."); ImGui::Unindent(); ImGui::BulletText("Right-click drag to box select data."); ImGui::Indent(); ImGui::BulletText("Hold Alt to expand box selection horizontally."); ImGui::BulletText("Hold Shift to expand box selection vertically."); ImGui::BulletText("Left-click while box selecting to cancel the selection."); ImGui::Unindent(); ImGui::BulletText("Double left-click to fit all visible data."); ImGui::Indent(); ImGui::BulletText("Double left-click axis labels to fit the individual axis."); ImGui::Unindent(); ImGui::BulletText("Right-click open the full plot context menu."); ImGui::Indent(); ImGui::BulletText("Right-click axis labels to open an individual axis context menu."); ImGui::Unindent(); ImGui::BulletText("Click legend label icons to show/hide plot items."); } void ShowTicksMetrics(const ImPlotTicker& ticker) { ImGui::BulletText("Size: %d", ticker.TickCount()); ImGui::BulletText("MaxSize: [%f,%f]", ticker.MaxSize.x, ticker.MaxSize.y); } void ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) { ImGui::BulletText("Label: %s", axis.LabelOffset == -1 ? "[none]" : plot.GetAxisLabel(axis)); ImGui::BulletText("Flags: 0x%08X", axis.Flags); ImGui::BulletText("Range: [%f,%f]",axis.Range.Min, axis.Range.Max); ImGui::BulletText("Pixels: %f", axis.PixelSize()); ImGui::BulletText("Aspect: %f", axis.GetAspect()); ImGui::BulletText(axis.OrthoAxis == nullptr ? "OrtherAxis: NULL" : "OrthoAxis: 0x%08X", axis.OrthoAxis->ID); ImGui::BulletText("LinkedMin: %p", (void*)axis.LinkedMin); ImGui::BulletText("LinkedMax: %p", (void*)axis.LinkedMax); ImGui::BulletText("HasRange: %s", axis.HasRange ? "true" : "false"); ImGui::BulletText("Hovered: %s", axis.Hovered ? "true" : "false"); ImGui::BulletText("Held: %s", axis.Held ? "true" : "false"); if (ImGui::TreeNode("Transform")) { ImGui::BulletText("PixelMin: %f", axis.PixelMin); ImGui::BulletText("PixelMax: %f", axis.PixelMax); ImGui::BulletText("ScaleToPixel: %f", axis.ScaleToPixel); ImGui::BulletText("ScaleMax: %f", axis.ScaleMax); ImGui::TreePop(); } if (ImGui::TreeNode("Ticks")) { ShowTicksMetrics(axis.Ticker); ImGui::TreePop(); } } void ShowMetricsWindow(bool* p_popen) { static bool show_plot_rects = false; static bool show_axes_rects = false; static bool show_axis_rects = false; static bool show_canvas_rects = false; static bool show_frame_rects = false; static bool show_subplot_frame_rects = false; static bool show_subplot_grid_rects = false; static bool show_legend_rects = false; ImDrawList& fg = *ImGui::GetForegroundDrawList(); ImPlotContext& gp = *GImPlot; // ImGuiContext& g = *GImGui; ImGuiIO& io = ImGui::GetIO(); ImGui::Begin("ImPlot Metrics", p_popen); ImGui::Text("ImPlot " IMPLOT_VERSION); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Text("Mouse Position: [%.0f,%.0f]", io.MousePos.x, io.MousePos.y); ImGui::Separator(); if (ImGui::TreeNode("Tools")) { if (ImGui::Button("Bust Plot Cache")) BustPlotCache(); ImGui::SameLine(); if (ImGui::Button("Bust Item Cache")) BustItemCache(); ImGui::Checkbox("Show Frame Rects", &show_frame_rects); ImGui::Checkbox("Show Canvas Rects",&show_canvas_rects); ImGui::Checkbox("Show Plot Rects", &show_plot_rects); ImGui::Checkbox("Show Axes Rects", &show_axes_rects); ImGui::Checkbox("Show Axis Rects", &show_axis_rects); ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects); ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects); ImGui::Checkbox("Show Legend Rects", &show_legend_rects); ImGui::TreePop(); } const int n_plots = gp.Plots.GetBufSize(); const int n_subplots = gp.Subplots.GetBufSize(); // render rects for (int p = 0; p < n_plots; ++p) { ImPlotPlot* plot = gp.Plots.GetByIndex(p); if (show_frame_rects) fg.AddRect(plot->FrameRect.Min, plot->FrameRect.Max, IM_COL32(255,0,255,255)); if (show_canvas_rects) fg.AddRect(plot->CanvasRect.Min, plot->CanvasRect.Max, IM_COL32(0,255,255,255)); if (show_plot_rects) fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255)); if (show_axes_rects) fg.AddRect(plot->AxesRect.Min, plot->AxesRect.Max, IM_COL32(0,255,128,255)); if (show_axis_rects) { for (int i = 0; i < ImAxis_COUNT; ++i) { if (plot->Axes[i].Enabled) fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255)); } } if (show_legend_rects && plot->Items.GetLegendCount() > 0) { fg.AddRect(plot->Items.Legend.Rect.Min, plot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255)); fg.AddRect(plot->Items.Legend.RectClamped.Min, plot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255)); } } for (int p = 0; p < n_subplots; ++p) { ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p); if (show_subplot_frame_rects) fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255)); if (show_subplot_grid_rects) fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255)); if (show_legend_rects && subplot->Items.GetLegendCount() > 0) { fg.AddRect(subplot->Items.Legend.Rect.Min, subplot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255)); fg.AddRect(subplot->Items.Legend.RectClamped.Min, subplot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255)); } } if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { for (int p = 0; p < n_plots; ++p) { // plot ImPlotPlot& plot = *gp.Plots.GetByIndex(p); ImGui::PushID(p); if (ImGui::TreeNode("Plot", "Plot [0x%08X]", plot.ID)) { int n_items = plot.Items.GetItemCount(); if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { for (int i = 0; i < n_items; ++i) { ImPlotItem* item = plot.Items.GetItemByIndex(i); ImGui::PushID(i); if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); ImGui::Bullet(); ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) item->Color = ImGui::ColorConvertFloat4ToU32(temp); ImGui::BulletText("NameOffset: %d",item->NameOffset); ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } char buff[16]; for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImFormatString(buff,16,"X-Axis %d", i+1); if (plot.XAxis(i).Enabled && ImGui::TreeNode(buff, "X-Axis %d [0x%08X]", i+1, plot.XAxis(i).ID)) { ShowAxisMetrics(plot, plot.XAxis(i)); ImGui::TreePop(); } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImFormatString(buff,16,"Y-Axis %d", i+1); if (plot.YAxis(i).Enabled && ImGui::TreeNode(buff, "Y-Axis %d [0x%08X]", i+1, plot.YAxis(i).ID)) { ShowAxisMetrics(plot, plot.YAxis(i)); ImGui::TreePop(); } } ImGui::BulletText("Title: %s", plot.HasTitle() ? plot.GetTitle() : "none"); ImGui::BulletText("Flags: 0x%08X", plot.Flags); ImGui::BulletText("Initialized: %s", plot.Initialized ? "true" : "false"); ImGui::BulletText("Selecting: %s", plot.Selecting ? "true" : "false"); ImGui::BulletText("Selected: %s", plot.Selected ? "true" : "false"); ImGui::BulletText("Hovered: %s", plot.Hovered ? "true" : "false"); ImGui::BulletText("Held: %s", plot.Held ? "true" : "false"); ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); ImGui::BulletText("ContextLocked: %s", plot.ContextLocked ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } if (ImGui::TreeNode("Subplots","Subplots (%d)", n_subplots)) { for (int p = 0; p < n_subplots; ++p) { // plot ImPlotSubplot& plot = *gp.Subplots.GetByIndex(p); ImGui::PushID(p); if (ImGui::TreeNode("Subplot", "Subplot [0x%08X]", plot.ID)) { int n_items = plot.Items.GetItemCount(); if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { for (int i = 0; i < n_items; ++i) { ImPlotItem* item = plot.Items.GetItemByIndex(i); ImGui::PushID(i); if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); ImGui::Bullet(); ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) item->Color = ImGui::ColorConvertFloat4ToU32(temp); ImGui::BulletText("NameOffset: %d",item->NameOffset); ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } ImGui::BulletText("Flags: 0x%08X", plot.Flags); ImGui::BulletText("FrameHovered: %s", plot.FrameHovered ? "true" : "false"); ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } if (ImGui::TreeNode("Colormaps")) { ImGui::BulletText("Colormaps: %d", gp.ColormapData.Count); ImGui::BulletText("Memory: %d bytes", gp.ColormapData.Tables.Size * 4); if (ImGui::TreeNode("Data")) { for (int m = 0; m < gp.ColormapData.Count; ++m) { if (ImGui::TreeNode(gp.ColormapData.GetName(m))) { int count = gp.ColormapData.GetKeyCount(m); int size = gp.ColormapData.GetTableSize(m); bool qual = gp.ColormapData.IsQual(m); ImGui::BulletText("Qualitative: %s", qual ? "true" : "false"); ImGui::BulletText("Key Count: %d", count); ImGui::BulletText("Table Size: %d", size); ImGui::Indent(); static float t = 0.5; ImVec4 samp; float wid = 32 * 10 - ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.x; ImGui::SetNextItemWidth(wid); ImPlot::ColormapSlider("##Sample",&t,&samp,"%.3f",m); ImGui::SameLine(); ImGui::ColorButton("Sampler",samp); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); for (int c = 0; c < size; ++c) { ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetTableColor(m,c)); ImGui::PushID(m*1000+c); ImGui::ColorButton("",col,0,ImVec2(10,10)); ImGui::PopID(); if ((c + 1) % 32 != 0 && c != size - 1) ImGui::SameLine(); } ImGui::PopStyleVar(); ImGui::PopStyleColor(); ImGui::Unindent(); ImGui::TreePop(); } } ImGui::TreePop(); } ImGui::TreePop(); } ImGui::End(); } bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1, const ImPlotTime* t2) { ImGui::PushID(id); ImGui::BeginGroup(); ImGuiStyle& style = ImGui::GetStyle(); ImVec4 col_txt = style.Colors[ImGuiCol_Text]; ImVec4 col_dis = style.Colors[ImGuiCol_TextDisabled]; ImVec4 col_btn = style.Colors[ImGuiCol_Button]; ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); const float ht = ImGui::GetFrameHeight(); ImVec2 cell_size(ht*1.25f,ht); char buff[32]; bool clk = false; tm& Tm = GImPlot->Tm; const int min_yr = 1970; const int max_yr = 2999; // t1 parts int t1_mo = 0; int t1_md = 0; int t1_yr = 0; if (t1 != nullptr) { GetTime(*t1,&Tm); t1_mo = Tm.tm_mon; t1_md = Tm.tm_mday; t1_yr = Tm.tm_year + 1900; } // t2 parts int t2_mo = 0; int t2_md = 0; int t2_yr = 0; if (t2 != nullptr) { GetTime(*t2,&Tm); t2_mo = Tm.tm_mon; t2_md = Tm.tm_mday; t2_yr = Tm.tm_year + 1900; } // day widget if (*level == 0) { *t = FloorTime(*t, ImPlotTimeUnit_Day); GetTime(*t, &Tm); const int this_year = Tm.tm_year + 1900; const int last_year = this_year - 1; const int next_year = this_year + 1; const int this_mon = Tm.tm_mon; const int last_mon = this_mon == 0 ? 11 : this_mon - 1; const int next_mon = this_mon == 11 ? 0 : this_mon + 1; const int days_this_mo = GetDaysInMonth(this_year, this_mon); const int days_last_mo = GetDaysInMonth(this_mon == 0 ? last_year : this_year, last_mon); ImPlotTime t_first_mo = FloorTime(*t,ImPlotTimeUnit_Mo); GetTime(t_first_mo,&Tm); const int first_wd = Tm.tm_wday; // month year ImFormatString(buff, 32, "%s %d", MONTH_NAMES[this_mon], this_year); if (ImGui::Button(buff)) *level = 1; ImGui::SameLine(5*cell_size.x); BeginDisabledControls(this_year <= min_yr && this_mon == 0); if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) *t = AddTime(*t, ImPlotTimeUnit_Mo, -1); EndDisabledControls(this_year <= min_yr && this_mon == 0); ImGui::SameLine(); BeginDisabledControls(this_year >= max_yr && this_mon == 11); if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) *t = AddTime(*t, ImPlotTimeUnit_Mo, 1); EndDisabledControls(this_year >= max_yr && this_mon == 11); // render weekday abbreviations ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); for (int i = 0; i < 7; ++i) { ImGui::Button(WD_ABRVS[i],cell_size); if (i != 6) { ImGui::SameLine(); } } ImGui::PopItemFlag(); // 0 = last mo, 1 = this mo, 2 = next mo int mo = first_wd > 0 ? 0 : 1; int day = mo == 1 ? 1 : days_last_mo - first_wd + 1; for (int i = 0; i < 6; ++i) { for (int j = 0; j < 7; ++j) { if (mo == 0 && day > days_last_mo) { mo = 1; day = 1; } else if (mo == 1 && day > days_this_mo) { mo = 2; day = 1; } const int now_yr = (mo == 0 && this_mon == 0) ? last_year : ((mo == 2 && this_mon == 11) ? next_year : this_year); const int now_mo = mo == 0 ? last_mon : (mo == 1 ? this_mon : next_mon); const int now_md = day; const bool off_mo = mo == 0 || mo == 2; const bool t1_or_t2 = (t1 != nullptr && t1_mo == now_mo && t1_yr == now_yr && t1_md == now_md) || (t2 != nullptr && t2_mo == now_mo && t2_yr == now_yr && t2_md == now_md); if (off_mo) ImGui::PushStyleColor(ImGuiCol_Text, col_dis); if (t1_or_t2) { ImGui::PushStyleColor(ImGuiCol_Button, col_btn); ImGui::PushStyleColor(ImGuiCol_Text, col_txt); } ImGui::PushID(i*7+j); ImFormatString(buff,32,"%d",day); if (now_yr == min_yr-1 || now_yr == max_yr+1) { ImGui::Dummy(cell_size); } else if (ImGui::Button(buff,cell_size) && !clk) { *t = MakeTime(now_yr, now_mo, now_md); clk = true; } ImGui::PopID(); if (t1_or_t2) ImGui::PopStyleColor(2); if (off_mo) ImGui::PopStyleColor(); if (j != 6) ImGui::SameLine(); day++; } } } // month widget else if (*level == 1) { *t = FloorTime(*t, ImPlotTimeUnit_Mo); GetTime(*t, &Tm); int this_yr = Tm.tm_year + 1900; ImFormatString(buff, 32, "%d", this_yr); if (ImGui::Button(buff)) *level = 2; BeginDisabledControls(this_yr <= min_yr); ImGui::SameLine(5*cell_size.x); if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) *t = AddTime(*t, ImPlotTimeUnit_Yr, -1); EndDisabledControls(this_yr <= min_yr); ImGui::SameLine(); BeginDisabledControls(this_yr >= max_yr); if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) *t = AddTime(*t, ImPlotTimeUnit_Yr, 1); EndDisabledControls(this_yr >= max_yr); // ImGui::Dummy(cell_size); cell_size.x *= 7.0f/4.0f; cell_size.y *= 7.0f/3.0f; int mo = 0; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { const bool t1_or_t2 = (t1 != nullptr && t1_yr == this_yr && t1_mo == mo) || (t2 != nullptr && t2_yr == this_yr && t2_mo == mo); if (t1_or_t2) ImGui::PushStyleColor(ImGuiCol_Button, col_btn); if (ImGui::Button(MONTH_ABRVS[mo],cell_size) && !clk) { *t = MakeTime(this_yr, mo); *level = 0; } if (t1_or_t2) ImGui::PopStyleColor(); if (j != 3) ImGui::SameLine(); mo++; } } } else if (*level == 2) { *t = FloorTime(*t, ImPlotTimeUnit_Yr); int this_yr = GetYear(*t); int yr = this_yr - this_yr % 20; ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); ImFormatString(buff,32,"%d-%d",yr,yr+19); ImGui::Button(buff); ImGui::PopItemFlag(); ImGui::SameLine(5*cell_size.x); BeginDisabledControls(yr <= min_yr); if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) *t = MakeTime(yr-20); EndDisabledControls(yr <= min_yr); ImGui::SameLine(); BeginDisabledControls(yr + 20 >= max_yr); if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) *t = MakeTime(yr+20); EndDisabledControls(yr+ 20 >= max_yr); // ImGui::Dummy(cell_size); cell_size.x *= 7.0f/4.0f; cell_size.y *= 7.0f/5.0f; for (int i = 0; i < 5; ++i) { for (int j = 0; j < 4; ++j) { const bool t1_or_t2 = (t1 != nullptr && t1_yr == yr) || (t2 != nullptr && t2_yr == yr); if (t1_or_t2) ImGui::PushStyleColor(ImGuiCol_Button, col_btn); ImFormatString(buff,32,"%d",yr); if (yr<1970||yr>3000) { ImGui::Dummy(cell_size); } else if (ImGui::Button(buff,cell_size)) { *t = MakeTime(yr); *level = 1; } if (t1_or_t2) ImGui::PopStyleColor(); if (j != 3) ImGui::SameLine(); yr++; } } } ImGui::PopStyleVar(); ImGui::PopStyleColor(); ImGui::EndGroup(); ImGui::PopID(); return clk; } bool ShowTimePicker(const char* id, ImPlotTime* t) { ImPlotContext& gp = *GImPlot; ImGui::PushID(id); tm& Tm = gp.Tm; GetTime(*t,&Tm); static const char* nums[] = { "00","01","02","03","04","05","06","07","08","09", "10","11","12","13","14","15","16","17","18","19", "20","21","22","23","24","25","26","27","28","29", "30","31","32","33","34","35","36","37","38","39", "40","41","42","43","44","45","46","47","48","49", "50","51","52","53","54","55","56","57","58","59"}; static const char* am_pm[] = {"am","pm"}; bool hour24 = gp.Style.Use24HourClock; int hr = hour24 ? Tm.tm_hour : ((Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12); int min = Tm.tm_min; int sec = Tm.tm_sec; int ap = Tm.tm_hour < 12 ? 0 : 1; bool changed = false; ImVec2 spacing = ImGui::GetStyle().ItemSpacing; spacing.x = 0; float width = ImGui::CalcTextSize("888").x; float height = ImGui::GetFrameHeight(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing); ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,2.0f); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered)); ImGui::SetNextItemWidth(width); if (ImGui::BeginCombo("##hr",nums[hr],ImGuiComboFlags_NoArrowButton)) { const int ia = hour24 ? 0 : 1; const int ib = hour24 ? 24 : 13; for (int i = ia; i < ib; ++i) { if (ImGui::Selectable(nums[i],i==hr)) { hr = i; changed = true; } } ImGui::EndCombo(); } ImGui::SameLine(); ImGui::Text(":"); ImGui::SameLine(); ImGui::SetNextItemWidth(width); if (ImGui::BeginCombo("##min",nums[min],ImGuiComboFlags_NoArrowButton)) { for (int i = 0; i < 60; ++i) { if (ImGui::Selectable(nums[i],i==min)) { min = i; changed = true; } } ImGui::EndCombo(); } ImGui::SameLine(); ImGui::Text(":"); ImGui::SameLine(); ImGui::SetNextItemWidth(width); if (ImGui::BeginCombo("##sec",nums[sec],ImGuiComboFlags_NoArrowButton)) { for (int i = 0; i < 60; ++i) { if (ImGui::Selectable(nums[i],i==sec)) { sec = i; changed = true; } } ImGui::EndCombo(); } if (!hour24) { ImGui::SameLine(); if (ImGui::Button(am_pm[ap],ImVec2(0,height))) { ap = 1 - ap; changed = true; } } ImGui::PopStyleColor(3); ImGui::PopStyleVar(2); ImGui::PopID(); if (changed) { if (!hour24) hr = hr % 12 + ap * 12; Tm.tm_hour = hr; Tm.tm_min = min; Tm.tm_sec = sec; *t = MkTime(&Tm); } return changed; } void StyleColorsAuto(ImPlotStyle* dst) { ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImVec4* colors = style->Colors; style->MinorAlpha = 0.25f; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; colors[ImPlotCol_FrameBg] = IMPLOT_AUTO_COL; colors[ImPlotCol_PlotBg] = IMPLOT_AUTO_COL; colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; colors[ImPlotCol_LegendBg] = IMPLOT_AUTO_COL; colors[ImPlotCol_LegendBorder] = IMPLOT_AUTO_COL; colors[ImPlotCol_LegendText] = IMPLOT_AUTO_COL; colors[ImPlotCol_TitleText] = IMPLOT_AUTO_COL; colors[ImPlotCol_InlayText] = IMPLOT_AUTO_COL; colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisText] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisGrid] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; colors[ImPlotCol_Selection] = IMPLOT_AUTO_COL; colors[ImPlotCol_Crosshairs] = IMPLOT_AUTO_COL; } void StyleColorsClassic(ImPlotStyle* dst) { ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImVec4* colors = style->Colors; style->MinorAlpha = 0.5f; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f); colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f); colors[ImPlotCol_PlotBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); colors[ImPlotCol_LegendBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f); colors[ImPlotCol_LegendBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); colors[ImPlotCol_LegendText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_AxisText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_AxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(0.97f, 0.97f, 0.39f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.50f, 0.50f, 0.50f, 0.75f); } void StyleColorsDark(ImPlotStyle* dst) { ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImVec4* colors = style->Colors; style->MinorAlpha = 0.25f; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); colors[ImPlotCol_PlotBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); colors[ImPlotCol_LegendBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); colors[ImPlotCol_LegendBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); colors[ImPlotCol_LegendText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_AxisText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f); } void StyleColorsLight(ImPlotStyle* dst) { ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImVec4* colors = style->Colors; style->MinorAlpha = 1.0f; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_PlotBg] = ImVec4(0.42f, 0.57f, 1.00f, 0.13f); colors[ImPlotCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImPlotCol_LegendBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f); colors[ImPlotCol_LegendBorder] = ImVec4(0.82f, 0.82f, 0.82f, 0.80f); colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_AxisTick] = ImVec4(0.00f, 0.00f, 0.00f, 0.25f); colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(0.82f, 0.64f, 0.03f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); } //----------------------------------------------------------------------------- // [SECTION] Obsolete Functions/Types //----------------------------------------------------------------------------- #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS bool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size, ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags, const char* y2_label, const char* y3_label) { if (!BeginPlot(title, size, flags)) return false; SetupAxis(ImAxis_X1, x_label, x_flags); SetupAxis(ImAxis_Y1, y1_label, y1_flags); if (ImHasFlag(flags, ImPlotFlags_YAxis2)) SetupAxis(ImAxis_Y2, y2_label, y2_flags); if (ImHasFlag(flags, ImPlotFlags_YAxis3)) SetupAxis(ImAxis_Y3, y3_label, y3_flags); return true; } #endif } // namespace ImPlot ================================================ FILE: lib/third_party/imgui/implot/source/implot_demo.cpp ================================================ // MIT License // Copyright (c) 2023 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.17 // We define this so that the demo does not accidentally use deprecated API #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS #define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS #endif #include "implot.h" #include #include #include #include #ifdef _MSC_VER #define sprintf sprintf_s #endif #ifndef PI #define PI 3.14159265358979323846 #endif #define CHECKBOX_FLAG(flags, flag) ImGui::CheckboxFlags(#flag, (unsigned int*)&flags, flag) // Encapsulates examples for customizing ImPlot. namespace MyImPlot { // Example for Custom Data and Getters section. struct Vector2f { Vector2f(float _x, float _y) { x = _x; y = _y; } float x, y; }; // Example for Custom Data and Getters section. struct WaveData { double X, Amp, Freq, Offset; WaveData(double x, double amp, double freq, double offset) { X = x; Amp = amp; Freq = freq; Offset = offset; } }; ImPlotPoint SineWave(int idx, void* wave_data); ImPlotPoint SawWave(int idx, void* wave_data); ImPlotPoint Spiral(int idx, void* wave_data); // Example for Tables section. void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size); // Example for Custom Plotters and Tooltips section. void PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip = true, float width_percent = 0.25f, ImVec4 bullCol = ImVec4(0,1,0,1), ImVec4 bearCol = ImVec4(1,0,0,1)); // Example for Custom Styles section. void StyleSeaborn(); } // namespace MyImPlot namespace ImPlot { template inline T RandomRange(T min, T max) { T scale = rand() / (T) RAND_MAX; return min + scale * ( max - min ); } ImVec4 RandomColor() { ImVec4 col; col.x = RandomRange(0.0f,1.0f); col.y = RandomRange(0.0f,1.0f); col.z = RandomRange(0.0f,1.0f); col.w = 1.0f; return col; } double RandomGauss() { static double V1, V2, S; static int phase = 0; double X; if(phase == 0) { do { double U1 = (double)rand() / RAND_MAX; double U2 = (double)rand() / RAND_MAX; V1 = 2 * U1 - 1; V2 = 2 * U2 - 1; S = V1 * V1 + V2 * V2; } while(S >= 1 || S == 0); X = V1 * sqrt(-2 * log(S) / S); } else X = V2 * sqrt(-2 * log(S) / S); phase = 1 - phase; return X; } template struct NormalDistribution { NormalDistribution(double mean, double sd) { for (int i = 0; i < N; ++i) Data[i] = RandomGauss()*sd + mean; } double Data[N]; }; // utility structure for realtime plot struct ScrollingBuffer { int MaxSize; int Offset; ImVector Data; ScrollingBuffer(int max_size = 2000) { MaxSize = max_size; Offset = 0; Data.reserve(MaxSize); } void AddPoint(float x, float y) { if (Data.size() < MaxSize) Data.push_back(ImVec2(x,y)); else { Data[Offset] = ImVec2(x,y); Offset = (Offset + 1) % MaxSize; } } void Erase() { if (Data.size() > 0) { Data.shrink(0); Offset = 0; } } }; // utility structure for realtime plot struct RollingBuffer { float Span; ImVector Data; RollingBuffer() { Span = 10.0f; Data.reserve(2000); } void AddPoint(float x, float y) { float xmod = fmodf(x, Span); if (!Data.empty() && xmod < Data.back().x) Data.shrink(0); Data.push_back(ImVec2(xmod, y)); } }; // Huge data used by Time Formatting example (~500 MB allocation!) struct HugeTimeData { HugeTimeData(double min) { Ts = new double[Size]; Ys = new double[Size]; for (int i = 0; i < Size; ++i) { Ts[i] = min + i; Ys[i] = GetY(Ts[i]); } } ~HugeTimeData() { delete[] Ts; delete[] Ys; } static double GetY(double t) { return 0.5 + 0.25 * sin(t/86400/12) + 0.005 * sin(t/3600); } double* Ts; double* Ys; static const int Size = 60*60*24*366; }; //----------------------------------------------------------------------------- // [SECTION] Demo Functions //----------------------------------------------------------------------------- void Demo_Help() { ImGui::Text("ABOUT THIS DEMO:"); ImGui::BulletText("Sections below are demonstrating many aspects of the library."); ImGui::BulletText("The \"Tools\" menu above gives access to: Style Editors (ImPlot/ImGui)\n" "and Metrics (general purpose Dear ImGui debugging tool)."); ImGui::Separator(); ImGui::Text("PROGRAMMER GUIDE:"); ImGui::BulletText("See the ShowDemoWindow() code in implot_demo.cpp. <- you are here!"); ImGui::BulletText("If you see visual artifacts, do one of the following:"); ImGui::Indent(); ImGui::BulletText("Handle ImGuiBackendFlags_RendererHasVtxOffset for 16-bit indices in your backend."); ImGui::BulletText("Or, enable 32-bit indices in imconfig.h."); ImGui::BulletText("Your current configuration is:"); ImGui::Indent(); ImGui::BulletText("ImDrawIdx: %d-bit", (int)(sizeof(ImDrawIdx) * 8)); ImGui::BulletText("ImGuiBackendFlags_RendererHasVtxOffset: %s", (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ? "True" : "False"); ImGui::Unindent(); ImGui::Unindent(); ImGui::Separator(); ImGui::Text("USER GUIDE:"); ShowUserGuide(); } //----------------------------------------------------------------------------- void ButtonSelector(const char* label, ImGuiMouseButton* b) { ImGui::PushID(label); if (ImGui::RadioButton("LMB",*b == ImGuiMouseButton_Left)) *b = ImGuiMouseButton_Left; ImGui::SameLine(); if (ImGui::RadioButton("RMB",*b == ImGuiMouseButton_Right)) *b = ImGuiMouseButton_Right; ImGui::SameLine(); if (ImGui::RadioButton("MMB",*b == ImGuiMouseButton_Middle)) *b = ImGuiMouseButton_Middle; ImGui::PopID(); } void ModSelector(const char* label, int* k) { ImGui::PushID(label); ImGui::CheckboxFlags("Ctrl", (unsigned int*)k, ImGuiMod_Ctrl); ImGui::SameLine(); ImGui::CheckboxFlags("Shift", (unsigned int*)k, ImGuiMod_Shift); ImGui::SameLine(); ImGui::CheckboxFlags("Alt", (unsigned int*)k, ImGuiMod_Alt); ImGui::SameLine(); ImGui::CheckboxFlags("Super", (unsigned int*)k, ImGuiMod_Super); ImGui::PopID(); } void InputMapping(const char* label, ImGuiMouseButton* b, int* k) { ImGui::LabelText("##","%s",label); if (b != nullptr) { ImGui::SameLine(100); ButtonSelector(label,b); } if (k != nullptr) { ImGui::SameLine(300); ModSelector(label,k); } } void ShowInputMapping() { ImPlotInputMap& map = ImPlot::GetInputMap(); InputMapping("Pan",&map.Pan,&map.PanMod); InputMapping("Fit",&map.Fit,nullptr); InputMapping("Select",&map.Select,&map.SelectMod); InputMapping("SelectHorzMod",nullptr,&map.SelectHorzMod); InputMapping("SelectVertMod",nullptr,&map.SelectVertMod); InputMapping("SelectCancel",&map.SelectCancel,nullptr); InputMapping("Menu",&map.Menu,nullptr); InputMapping("OverrideMod",nullptr,&map.OverrideMod); InputMapping("ZoomMod",nullptr,&map.ZoomMod); ImGui::SliderFloat("ZoomRate",&map.ZoomRate,-1,1); } void Demo_Config() { ImGui::ShowFontSelector("Font"); ImGui::ShowStyleSelector("ImGui Style"); ImPlot::ShowStyleSelector("ImPlot Style"); ImPlot::ShowColormapSelector("ImPlot Colormap"); ImPlot::ShowInputMapSelector("Input Map"); ImGui::Separator(); ImGui::Checkbox("Use Local Time", &ImPlot::GetStyle().UseLocalTime); ImGui::Checkbox("Use ISO 8601", &ImPlot::GetStyle().UseISO8601); ImGui::Checkbox("Use 24 Hour Clock", &ImPlot::GetStyle().Use24HourClock); ImGui::Separator(); if (ImPlot::BeginPlot("Preview")) { static double now = (double)time(nullptr); ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); ImPlot::SetupAxisLimits(ImAxis_X1, now, now + 24*3600); for (int i = 0; i < 10; ++i) { double x[2] = {now, now + 24*3600}; double y[2] = {0,i/9.0}; ImGui::PushID(i); ImPlot::PlotLine("##Line",x,y,2); ImGui::PopID(); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_LinePlots() { static float xs1[1001], ys1[1001]; for (int i = 0; i < 1001; ++i) { xs1[i] = i * 0.001f; ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); } static double xs2[20], ys2[20]; for (int i = 0; i < 20; ++i) { xs2[i] = i * 1/19.0f; ys2[i] = xs2[i] * xs2[i]; } if (ImPlot::BeginPlot("Line Plots")) { ImPlot::SetupAxes("x","y"); ImPlot::PlotLine("f(x)", xs1, ys1, 1001); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::PlotLine("g(x)", xs2, ys2, 20,ImPlotLineFlags_Segments); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_FilledLinePlots() { static double xs1[101], ys1[101], ys2[101], ys3[101]; srand(0); for (int i = 0; i < 101; ++i) { xs1[i] = (float)i; ys1[i] = RandomRange(400.0,450.0); ys2[i] = RandomRange(275.0,350.0); ys3[i] = RandomRange(150.0,225.0); } static bool show_lines = true; static bool show_fills = true; static float fill_ref = 0; static int shade_mode = 0; static ImPlotShadedFlags flags = 0; ImGui::Checkbox("Lines",&show_lines); ImGui::SameLine(); ImGui::Checkbox("Fills",&show_fills); if (show_fills) { ImGui::SameLine(); if (ImGui::RadioButton("To -INF",shade_mode == 0)) shade_mode = 0; ImGui::SameLine(); if (ImGui::RadioButton("To +INF",shade_mode == 1)) shade_mode = 1; ImGui::SameLine(); if (ImGui::RadioButton("To Ref",shade_mode == 2)) shade_mode = 2; if (shade_mode == 2) { ImGui::SameLine(); ImGui::SetNextItemWidth(100); ImGui::DragFloat("##Ref",&fill_ref, 1, -100, 500); } } if (ImPlot::BeginPlot("Stock Prices")) { ImPlot::SetupAxes("Days","Price"); ImPlot::SetupAxesLimits(0,100,0,500); if (show_fills) { ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::PlotShaded("Stock 1", xs1, ys1, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); ImPlot::PlotShaded("Stock 2", xs1, ys2, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); ImPlot::PlotShaded("Stock 3", xs1, ys3, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); ImPlot::PopStyleVar(); } if (show_lines) { ImPlot::PlotLine("Stock 1", xs1, ys1, 101); ImPlot::PlotLine("Stock 2", xs1, ys2, 101); ImPlot::PlotLine("Stock 3", xs1, ys3, 101); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_ShadedPlots() { static float xs[1001], ys[1001], ys1[1001], ys2[1001], ys3[1001], ys4[1001]; srand(0); for (int i = 0; i < 1001; ++i) { xs[i] = i * 0.001f; ys[i] = 0.25f + 0.25f * sinf(25 * xs[i]) * sinf(5 * xs[i]) + RandomRange(-0.01f, 0.01f); ys1[i] = ys[i] + RandomRange(0.1f, 0.12f); ys2[i] = ys[i] - RandomRange(0.1f, 0.12f); ys3[i] = 0.75f + 0.2f * sinf(25 * xs[i]); ys4[i] = 0.75f + 0.1f * cosf(25 * xs[i]); } static float alpha = 0.25f; ImGui::DragFloat("Alpha",&alpha,0.01f,0,1); if (ImPlot::BeginPlot("Shaded Plots")) { ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); ImPlot::PlotShaded("Uncertain Data",xs,ys1,ys2,1001); ImPlot::PlotLine("Uncertain Data", xs, ys, 1001); ImPlot::PlotShaded("Overlapping",xs,ys3,ys4,1001); ImPlot::PlotLine("Overlapping",xs,ys3,1001); ImPlot::PlotLine("Overlapping",xs,ys4,1001); ImPlot::PopStyleVar(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_ScatterPlots() { srand(0); static float xs1[100], ys1[100]; for (int i = 0; i < 100; ++i) { xs1[i] = i * 0.01f; ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); } static float xs2[50], ys2[50]; for (int i = 0; i < 50; i++) { xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX); ys2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX); } if (ImPlot::BeginPlot("Scatter Plot")) { ImPlot::PlotScatter("Data 1", xs1, ys1, 100); ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImPlot::GetColormapColor(1), IMPLOT_AUTO, ImPlot::GetColormapColor(1)); ImPlot::PlotScatter("Data 2", xs2, ys2, 50); ImPlot::PopStyleVar(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_StairstepPlots() { static float ys1[21], ys2[21]; for (int i = 0; i < 21; ++i) { ys1[i] = 0.75f + 0.2f * sinf(10 * i * 0.05f); ys2[i] = 0.25f + 0.2f * sinf(10 * i * 0.05f); } static ImPlotStairsFlags flags = 0; CHECKBOX_FLAG(flags, ImPlotStairsFlags_Shaded); if (ImPlot::BeginPlot("Stairstep Plot")) { ImPlot::SetupAxes("x","f(x)"); ImPlot::SetupAxesLimits(0,1,0,1); ImPlot::PushStyleColor(ImPlotCol_Line, ImVec4(0.5f,0.5f,0.5f,1.0f)); ImPlot::PlotLine("##1",ys1,21,0.05f); ImPlot::PlotLine("##2",ys2,21,0.05f); ImPlot::PopStyleColor(); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.25f); ImPlot::PlotStairs("Post Step (default)", ys1, 21, 0.05f, 0, flags); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.25f); ImPlot::PlotStairs("Pre Step", ys2, 21, 0.05f, 0, flags|ImPlotStairsFlags_PreStep); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_BarPlots() { static ImS8 data[10] = {1,2,3,4,5,6,7,8,9,10}; if (ImPlot::BeginPlot("Bar Plot")) { ImPlot::PlotBars("Vertical",data,10,0.7,1); ImPlot::PlotBars("Horizontal",data,10,0.4,1,ImPlotBarsFlags_Horizontal); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_BarGroups() { static ImS8 data[30] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90, // midterm 80, 62, 56, 99, 55, 78, 88, 78, 90, 100, // final 80, 69, 52, 92, 72, 78, 75, 76, 89, 95}; // course static const char* ilabels[] = {"Midterm Exam","Final Exam","Course Grade"}; static const char* glabels[] = {"S1","S2","S3","S4","S5","S6","S7","S8","S9","S10"}; static const double positions[] = {0,1,2,3,4,5,6,7,8,9}; static int items = 3; static int groups = 10; static float size = 0.67f; static ImPlotBarGroupsFlags flags = 0; static bool horz = false; ImGui::CheckboxFlags("Stacked", (unsigned int*)&flags, ImPlotBarGroupsFlags_Stacked); ImGui::SameLine(); ImGui::Checkbox("Horizontal",&horz); ImGui::SliderInt("Items",&items,1,3); ImGui::SliderFloat("Size",&size,0,1); if (ImPlot::BeginPlot("Bar Group")) { ImPlot::SetupLegend(ImPlotLocation_East, ImPlotLegendFlags_Outside); if (horz) { ImPlot::SetupAxes("Score","Student",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); ImPlot::SetupAxisTicks(ImAxis_Y1,positions, groups, glabels); ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags|ImPlotBarGroupsFlags_Horizontal); } else { ImPlot::SetupAxes("Student","Score",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); ImPlot::SetupAxisTicks(ImAxis_X1,positions, groups, glabels); ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_BarStacks() { static ImPlotColormap Liars = -1; if (Liars == -1) { static const ImU32 Liars_Data[6] = { 4282515870, 4282609140, 4287357182, 4294630301, 4294945280, 4294921472 }; Liars = ImPlot::AddColormap("Liars", Liars_Data, 6); } static bool diverging = true; ImGui::Checkbox("Diverging",&diverging); static const char* politicians[] = {"Trump","Bachman","Cruz","Gingrich","Palin","Santorum","Walker","Perry","Ryan","McCain","Rubio","Romney","Rand Paul","Christie","Biden","Kasich","Sanders","J Bush","H Clinton","Obama"}; static int data_reg[] = {18,26,7,14,10,8,6,11,4,4,3,8,6,8,6,5,0,3,1,2, // Pants on Fire 43,36,30,21,30,27,25,17,11,22,15,16,16,17,12,12,14,6,13,12, // False 16,13,28,22,15,21,15,18,30,17,24,18,13,10,14,15,17,22,14,12, // Mostly False 17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True 5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26, // Mostly True 1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21}; // True static const char* labels_reg[] = {"Pants on Fire","False","Mostly False","Half True","Mostly True","True"}; static int data_div[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Pants on Fire (dummy, to order legend logically) 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // False (dummy, to order legend logically) 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Mostly False (dummy, to order legend logically) -16,-13,-28,-22,-15,-21,-15,-18,-30,-17,-24,-18,-13,-10,-14,-15,-17,-22,-14,-12, // Mostly False -43,-36,-30,-21,-30,-27,-25,-17,-11,-22,-15,-16,-16,-17,-12,-12,-14,-6,-13,-12, // False -18,-26,-7,-14,-10,-8,-6,-11,-4,-4,-3,-8,-6,-8,-6,-5,0,-3,-1,-2, // Pants on Fire 17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True 5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26, // Mostly True 1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21}; // True static const char* labels_div[] = {"Pants on Fire","False","Mostly False","Mostly False","False","Pants on Fire","Half True","Mostly True","True"}; ImPlot::PushColormap(Liars); if (ImPlot::BeginPlot("PolitiFact: Who Lies More?",ImVec2(-1,400),ImPlotFlags_NoMouseText)) { ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Outside|ImPlotLegendFlags_Horizontal); ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Invert); ImPlot::SetupAxisTicks(ImAxis_Y1,0,19,20,politicians,false); if (diverging) ImPlot::PlotBarGroups(labels_div,data_div,9,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal); else ImPlot::PlotBarGroups(labels_reg,data_reg,6,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal); ImPlot::EndPlot(); } ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_ErrorBars() { static float xs[5] = {1,2,3,4,5}; static float bar[5] = {1,2,5,3,4}; static float lin1[5] = {8,8,9,7,8}; static float lin2[5] = {6,7,6,9,6}; static float err1[5] = {0.2f, 0.4f, 0.2f, 0.6f, 0.4f}; static float err2[5] = {0.4f, 0.2f, 0.4f, 0.8f, 0.6f}; static float err3[5] = {0.09f, 0.14f, 0.09f, 0.12f, 0.16f}; static float err4[5] = {0.02f, 0.08f, 0.15f, 0.05f, 0.2f}; if (ImPlot::BeginPlot("##ErrorBars")) { ImPlot::SetupAxesLimits(0, 6, 0, 10); ImPlot::PlotBars("Bar", xs, bar, 5, 0.5f); ImPlot::PlotErrorBars("Bar", xs, bar, err1, 5); ImPlot::SetNextErrorBarStyle(ImPlot::GetColormapColor(1), 0); ImPlot::PlotErrorBars("Line", xs, lin1, err1, err2, 5); ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); ImPlot::PlotLine("Line", xs, lin1, 5); ImPlot::PushStyleColor(ImPlotCol_ErrorBar, ImPlot::GetColormapColor(2)); ImPlot::PlotErrorBars("Scatter", xs, lin2, err2, 5); ImPlot::PlotErrorBars("Scatter", xs, lin2, err3, err4, 5, ImPlotErrorBarsFlags_Horizontal); ImPlot::PopStyleColor(); ImPlot::PlotScatter("Scatter", xs, lin2, 5); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_StemPlots() { static double xs[51], ys1[51], ys2[51]; for (int i = 0; i < 51; ++i) { xs[i] = i * 0.02; ys1[i] = 1.0 + 0.5 * sin(25*xs[i])*cos(2*xs[i]); ys2[i] = 0.5 + 0.25 * sin(10*xs[i]) * sin(xs[i]); } if (ImPlot::BeginPlot("Stem Plots")) { ImPlot::SetupAxisLimits(ImAxis_X1,0,1.0); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1.6); ImPlot::PlotStems("Stems 1",xs,ys1,51); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::PlotStems("Stems 2", xs, ys2,51); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_InfiniteLines() { static double vals[] = {0.25, 0.5, 0.75}; if (ImPlot::BeginPlot("##Infinite")) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoInitialFit,ImPlotAxisFlags_NoInitialFit); ImPlot::PlotInfLines("Vertical",vals,3); ImPlot::PlotInfLines("Horizontal",vals,3,ImPlotInfLinesFlags_Horizontal); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_PieCharts() { static const char* labels1[] = {"Frogs","Hogs","Dogs","Logs"}; static float data1[] = {0.15f, 0.30f, 0.2f, 0.05f}; static ImPlotPieChartFlags flags = 0; ImGui::SetNextItemWidth(250); ImGui::DragFloat4("Values", data1, 0.01f, 0, 1); CHECKBOX_FLAG(flags, ImPlotPieChartFlags_Normalize); CHECKBOX_FLAG(flags, ImPlotPieChartFlags_IgnoreHidden); if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, 1, 0, 1); ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, "%.2f", 90, flags); ImPlot::EndPlot(); } ImGui::SameLine(); static const char* labels2[] = {"A","B","C","D","E"}; static int data2[] = {1,1,2,3,5}; ImPlot::PushColormap(ImPlotColormap_Pastel); if (ImPlot::BeginPlot("##Pie2", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, 1, 0, 1); ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, "%.0f", 180, flags); ImPlot::EndPlot(); } ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_Heatmaps() { static float values1[7][7] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f}, {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f}, {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f}, {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f}, {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f}, {1.3f, 1.2f, 0.0f, 0.0f, 0.0f, 3.2f, 5.1f}, {0.1f, 2.0f, 0.0f, 1.4f, 0.0f, 1.9f, 6.3f}}; static float scale_min = 0; static float scale_max = 6.3f; static const char* xlabels[] = {"C1","C2","C3","C4","C5","C6","C7"}; static const char* ylabels[] = {"R1","R2","R3","R4","R5","R6","R7"}; static ImPlotColormap map = ImPlotColormap_Viridis; if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) { map = (map + 1) % ImPlot::GetColormapCount(); // We bust the color cache of our plots so that item colors will // resample the new colormap in the event that they have already // been created. See documentation in implot.h. BustColorCache("##Heatmap1"); BustColorCache("##Heatmap2"); } ImGui::SameLine(); ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); ImGui::SetNextItemWidth(225); ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); static ImPlotHeatmapFlags hm_flags = 0; ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; ImPlot::PushColormap(map); if (ImPlot::BeginPlot("##Heatmap1",ImVec2(225,225),ImPlotFlags_NoLegend|ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); ImPlot::SetupAxisTicks(ImAxis_X1,0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels); ImPlot::SetupAxisTicks(ImAxis_Y1,1 - 1.0/14.0, 0 + 1.0/14.0, 7, ylabels); ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max,"%g",ImPlotPoint(0,0),ImPlotPoint(1,1),hm_flags); ImPlot::EndPlot(); } ImGui::SameLine(); ImPlot::ColormapScale("##HeatScale",scale_min, scale_max, ImVec2(60,225)); ImGui::SameLine(); const int size = 80; static double values2[size*size]; srand((unsigned int)(ImGui::GetTime()*1000000)); for (int i = 0; i < size*size; ++i) values2[i] = RandomRange(0.0,1.0); if (ImPlot::BeginPlot("##Heatmap2",ImVec2(225,225))) { ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(-1,1,-1,1); ImPlot::PlotHeatmap("heat1",values2,size,size,0,1,nullptr); ImPlot::PlotHeatmap("heat2",values2,size,size,0,1,nullptr, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); ImPlot::EndPlot(); } ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_Histogram() { static ImPlotHistogramFlags hist_flags = ImPlotHistogramFlags_Density; static int bins = 50; static double mu = 5; static double sigma = 2; ImGui::SetNextItemWidth(200); if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) { bins = ImPlotBin_Sqrt; } ImGui::SameLine(); if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine(); if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) { bins = ImPlotBin_Rice; } ImGui::SameLine(); if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) { bins = ImPlotBin_Scott; } ImGui::SameLine(); if (ImGui::RadioButton("N Bins",bins>=0)) { bins = 50; } if (bins>=0) { ImGui::SameLine(); ImGui::SetNextItemWidth(200); ImGui::SliderInt("##Bins", &bins, 1, 100); } ImGui::CheckboxFlags("Horizontal", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Horizontal); ImGui::SameLine(); ImGui::CheckboxFlags("Density", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density); ImGui::SameLine(); ImGui::CheckboxFlags("Cumulative", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Cumulative); static bool range = false; ImGui::Checkbox("Range", &range); static float rmin = -3; static float rmax = 13; if (range) { ImGui::SameLine(); ImGui::SetNextItemWidth(200); ImGui::DragFloat2("##Range",&rmin,0.1f,-3,13); ImGui::SameLine(); ImGui::CheckboxFlags("Exclude Outliers", (unsigned int*)&hist_flags, ImPlotHistogramFlags_NoOutliers); } static NormalDistribution<10000> dist(mu, sigma); static double x[100]; static double y[100]; if (hist_flags & ImPlotHistogramFlags_Density) { for (int i = 0; i < 100; ++i) { x[i] = -3 + 16 * (double)i/99.0; y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); } if (hist_flags & ImPlotHistogramFlags_Cumulative) { for (int i = 1; i < 100; ++i) y[i] += y[i-1]; for (int i = 0; i < 100; ++i) y[i] /= y[99]; } } if (ImPlot::BeginPlot("##Histograms")) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, 1.0, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), hist_flags); if ((hist_flags & ImPlotHistogramFlags_Density) && !(hist_flags & ImPlotHistogramFlags_NoOutliers)) { if (hist_flags & ImPlotHistogramFlags_Horizontal) ImPlot::PlotLine("Theoretical",y,x,100); else ImPlot::PlotLine("Theoretical",x,y,100); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_Histogram2D() { static int count = 50000; static int xybins[2] = {100,100}; static ImPlotHistogramFlags hist_flags = 0; ImGui::SliderInt("Count",&count,100,100000); ImGui::SliderInt2("Bins",xybins,1,500); ImGui::SameLine(); ImGui::CheckboxFlags("Density", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density); static NormalDistribution<100000> dist1(1, 2); static NormalDistribution<100000> dist2(1, 1); double max_count = 0; ImPlotAxisFlags flags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Foreground; ImPlot::PushColormap("Hot"); if (ImPlot::BeginPlot("##Hist2D",ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0))) { ImPlot::SetupAxes(nullptr, nullptr, flags, flags); ImPlot::SetupAxesLimits(-6,6,-6,6); max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],ImPlotRect(-6,6,-6,6), hist_flags); ImPlot::EndPlot(); } ImGui::SameLine(); ImPlot::ColormapScale(hist_flags & ImPlotHistogramFlags_Density ? "Density" : "Count",0,max_count,ImVec2(100,0)); ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_DigitalPlots() { ImGui::BulletText("Digital plots do not respond to Y drag and zoom, so that"); ImGui::Indent(); ImGui::Text("you can drag analog plots over the rising/falling digital edge."); ImGui::Unindent(); static bool paused = false; static ScrollingBuffer dataDigital[2]; static ScrollingBuffer dataAnalog[2]; static bool showDigital[2] = {true, false}; static bool showAnalog[2] = {true, false}; char label[32]; ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine(); ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine(); ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine(); ImGui::Checkbox("analog_1", &showAnalog[1]); static float t = 0; if (!paused) { t += ImGui::GetIO().DeltaTime; //digital signal values if (showDigital[0]) dataDigital[0].AddPoint(t, sinf(2*t) > 0.45); if (showDigital[1]) dataDigital[1].AddPoint(t, sinf(2*t) < 0.45); //Analog signal values if (showAnalog[0]) dataAnalog[0].AddPoint(t, sinf(2*t)); if (showAnalog[1]) dataAnalog[1].AddPoint(t, cosf(2*t)); } if (ImPlot::BeginPlot("##Digital")) { ImPlot::SetupAxisLimits(ImAxis_X1, t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1, -1, 1); for (int i = 0; i < 2; ++i) { if (showDigital[i] && dataDigital[i].Data.size() > 0) { snprintf(label, sizeof(label), "digital_%d", i); ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), 0, dataDigital[i].Offset, 2 * sizeof(float)); } } for (int i = 0; i < 2; ++i) { if (showAnalog[i]) { snprintf(label, sizeof(label), "analog_%d", i); if (dataAnalog[i].Data.size() > 0) ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), 0, dataAnalog[i].Offset, 2 * sizeof(float)); } } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_Images() { ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo."); ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data."); ImGui::BulletText("See ImGui Wiki page 'Image Loading and Displaying Examples'."); static ImVec2 bmin(0,0); static ImVec2 bmax(1,1); static ImVec2 uv0(0,0); static ImVec2 uv1(1,1); static ImVec4 tint(1,1,1,1); ImGui::SliderFloat2("Min", &bmin.x, -2, 2, "%.1f"); ImGui::SliderFloat2("Max", &bmax.x, -2, 2, "%.1f"); ImGui::SliderFloat2("UV0", &uv0.x, -2, 2, "%.1f"); ImGui::SliderFloat2("UV1", &uv1.x, -2, 2, "%.1f"); ImGui::ColorEdit4("Tint",&tint.x); if (ImPlot::BeginPlot("##image")) { #ifdef IMGUI_HAS_TEXTURES // We use the font atlas ImTextureRef for this demo, but in your real code when you submit // an image that you have loaded yourself, you would normally have a ImTextureID which works // just as well (as ImTextureRef can be constructed from ImTextureID). ImPlot::PlotImage("my image", ImGui::GetIO().Fonts->TexRef, bmin, bmax, uv0, uv1, tint); #else ImPlot::PlotImage("my image", ImGui::GetIO().Fonts->TexID, bmin, bmax, uv0, uv1, tint); #endif ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_RealtimePlots() { ImGui::BulletText("Move your mouse to change the data!"); ImGui::BulletText("This example assumes 60 FPS. Higher FPS requires larger buffer size."); static ScrollingBuffer sdata1, sdata2; static RollingBuffer rdata1, rdata2; ImVec2 mouse = ImGui::GetMousePos(); static float t = 0; t += ImGui::GetIO().DeltaTime; sdata1.AddPoint(t, mouse.x * 0.0005f); rdata1.AddPoint(t, mouse.x * 0.0005f); sdata2.AddPoint(t, mouse.y * 0.0005f); rdata2.AddPoint(t, mouse.y * 0.0005f); static float history = 10.0f; ImGui::SliderFloat("History",&history,1,30,"%.1f s"); rdata1.Span = history; rdata2.Span = history; static ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels; if (ImPlot::BeginPlot("##Scrolling", ImVec2(-1,150))) { ImPlot::SetupAxes(nullptr, nullptr, flags, flags); ImPlot::SetupAxisLimits(ImAxis_X1,t - history, t, ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); ImPlot::PlotShaded("Mouse X", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), -INFINITY, 0, sdata1.Offset, 2 * sizeof(float)); ImPlot::PlotLine("Mouse Y", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), 0, sdata2.Offset, 2*sizeof(float)); ImPlot::EndPlot(); } if (ImPlot::BeginPlot("##Rolling", ImVec2(-1,150))) { ImPlot::SetupAxes(nullptr, nullptr, flags, flags); ImPlot::SetupAxisLimits(ImAxis_X1,0,history, ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); ImPlot::PlotLine("Mouse X", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 0, 2 * sizeof(float)); ImPlot::PlotLine("Mouse Y", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 0, 2 * sizeof(float)); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_MarkersAndText() { static float mk_size = ImPlot::GetStyle().MarkerSize; static float mk_weight = ImPlot::GetStyle().MarkerWeight; ImGui::DragFloat("Marker Size",&mk_size,0.1f,2.0f,10.0f,"%.2f px"); ImGui::DragFloat("Marker Weight", &mk_weight,0.05f,0.5f,3.0f,"%.2f px"); if (ImPlot::BeginPlot("##MarkerStyles", ImVec2(-1,0), ImPlotFlags_CanvasOnly)) { ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, 10, 0, 12); ImS8 xs[2] = {1,4}; ImS8 ys[2] = {10,11}; // filled markers for (int m = 0; m < ImPlotMarker_COUNT; ++m) { ImGui::PushID(m); ImPlot::SetNextMarkerStyle(m, mk_size, IMPLOT_AUTO_COL, mk_weight); ImPlot::PlotLine("##Filled", xs, ys, 2); ImGui::PopID(); ys[0]--; ys[1]--; } xs[0] = 6; xs[1] = 9; ys[0] = 10; ys[1] = 11; // open markers for (int m = 0; m < ImPlotMarker_COUNT; ++m) { ImGui::PushID(m); ImPlot::SetNextMarkerStyle(m, mk_size, ImVec4(0,0,0,0), mk_weight); ImPlot::PlotLine("##Open", xs, ys, 2); ImGui::PopID(); ys[0]--; ys[1]--; } ImPlot::PlotText("Filled Markers", 2.5f, 6.0f); ImPlot::PlotText("Open Markers", 7.5f, 6.0f); ImPlot::PushStyleColor(ImPlotCol_InlayText, ImVec4(1,0,1,1)); ImPlot::PlotText("Vertical Text", 5.0f, 6.0f, ImVec2(0,0), ImPlotTextFlags_Vertical); ImPlot::PopStyleColor(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_NaNValues() { static bool include_nan = true; static ImPlotLineFlags flags = 0; float data1[5] = {0.0f,0.25f,0.5f,0.75f,1.0f}; float data2[5] = {0.0f,0.25f,0.5f,0.75f,1.0f}; if (include_nan) data1[2] = NAN; ImGui::Checkbox("Include NaN",&include_nan); ImGui::SameLine(); ImGui::CheckboxFlags("Skip NaN", (unsigned int*)&flags, ImPlotLineFlags_SkipNaN); if (ImPlot::BeginPlot("##NaNValues")) { ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); ImPlot::PlotLine("line", data1, data2, 5, flags); ImPlot::PlotBars("bars", data1, 5); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_LogScale() { static double xs[1001], ys1[1001], ys2[1001], ys3[1001]; for (int i = 0; i < 1001; ++i) { xs[i] = i*0.1f; ys1[i] = sin(xs[i]) + 1; ys2[i] = log(xs[i]); ys3[i] = pow(10.0, xs[i]); } if (ImPlot::BeginPlot("Log Plot", ImVec2(-1,0))) { ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Log10); ImPlot::SetupAxesLimits(0.1, 100, 0, 10); ImPlot::PlotLine("f(x) = x", xs, xs, 1001); ImPlot::PlotLine("f(x) = sin(x)+1", xs, ys1, 1001); ImPlot::PlotLine("f(x) = log(x)", xs, ys2, 1001); ImPlot::PlotLine("f(x) = 10^x", xs, ys3, 21); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_SymmetricLogScale() { static double xs[1001], ys1[1001], ys2[1001]; for (int i = 0; i < 1001; ++i) { xs[i] = i*0.1f-50; ys1[i] = sin(xs[i]); ys2[i] = i*0.002 - 1; } if (ImPlot::BeginPlot("SymLog Plot", ImVec2(-1,0))) { ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_SymLog); ImPlot::PlotLine("f(x) = a*x+b",xs,ys2,1001); ImPlot::PlotLine("f(x) = sin(x)",xs,ys1,1001); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_TimeScale() { static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC) static double t_max = 1640995200; // 01/01/2022 @ 12:00:00am (UTC) ImGui::BulletText("When ImPlotAxisFlags_Time is enabled on the X-Axis, values are interpreted as\n" "UNIX timestamps in seconds and axis labels are formated as date/time."); ImGui::BulletText("By default, labels are in UTC time but can be set to use local time instead."); ImGui::Checkbox("Local Time",&ImPlot::GetStyle().UseLocalTime); ImGui::SameLine(); ImGui::Checkbox("ISO 8601",&ImPlot::GetStyle().UseISO8601); ImGui::SameLine(); ImGui::Checkbox("24 Hour Clock",&ImPlot::GetStyle().Use24HourClock); static HugeTimeData* data = nullptr; if (data == nullptr) { ImGui::SameLine(); if (ImGui::Button("Generate Huge Data (~500MB!)")) { static HugeTimeData sdata(t_min); data = &sdata; } } if (ImPlot::BeginPlot("##Time", ImVec2(-1,0))) { ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); ImPlot::SetupAxesLimits(t_min,t_max,0,1); if (data != nullptr) { // downsample our data int downsample = (int)ImPlot::GetPlotLimits().X.Size() / 1000 + 1; int start = (int)(ImPlot::GetPlotLimits().X.Min - t_min); start = start < 0 ? 0 : start > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : start; int end = (int)(ImPlot::GetPlotLimits().X.Max - t_min) + 1000; end = end < 0 ? 0 : end > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : end; int size = (end - start)/downsample; // plot it ImPlot::PlotLine("Time Series", &data->Ts[start], &data->Ys[start], size, 0, 0, sizeof(double)*downsample); } // plot time now double t_now = (double)time(nullptr); double y_now = HugeTimeData::GetY(t_now); ImPlot::PlotScatter("Now",&t_now,&y_now,1); ImPlot::Annotation(t_now,y_now,ImPlot::GetLastItemColor(),ImVec2(10,10),false,"Now"); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- static inline double TransformForward_Sqrt(double v, void*) { return sqrt(v); } static inline double TransformInverse_Sqrt(double v, void*) { return v*v; } void Demo_CustomScale() { static float v[100]; for (int i = 0; i < 100; ++i) { v[i] = i*0.01f; } if (ImPlot::BeginPlot("Sqrt")) { ImPlot::SetupAxis(ImAxis_X1, "Linear"); ImPlot::SetupAxis(ImAxis_Y1, "Sqrt"); ImPlot::SetupAxisScale(ImAxis_Y1, TransformForward_Sqrt, TransformInverse_Sqrt); ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1, 0, INFINITY); ImPlot::PlotLine("##data",v,v,100); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_MultipleAxes() { static float xs[1001], xs2[1001], ys1[1001], ys2[1001], ys3[1001]; for (int i = 0; i < 1001; ++i) { xs[i] = (i*0.1f); xs2[i] = xs[i] + 10.0f; ys1[i] = sinf(xs[i]) * 3 + 1; ys2[i] = cosf(xs[i]) * 0.2f + 0.5f; ys3[i] = sinf(xs[i]+0.5f) * 100 + 200; } static bool x2_axis = true; static bool y2_axis = true; static bool y3_axis = true; ImGui::Checkbox("X-Axis 2", &x2_axis); ImGui::SameLine(); ImGui::Checkbox("Y-Axis 2", &y2_axis); ImGui::SameLine(); ImGui::Checkbox("Y-Axis 3", &y3_axis); ImGui::BulletText("You can drag axes to the opposite side of the plot."); ImGui::BulletText("Hover over legend items to see which axis they are plotted on."); if (ImPlot::BeginPlot("Multi-Axis Plot", ImVec2(-1,0))) { ImPlot::SetupAxes("X-Axis 1", "Y-Axis 1"); ImPlot::SetupAxesLimits(0, 100, 0, 10); if (x2_axis) { ImPlot::SetupAxis(ImAxis_X2, "X-Axis 2",ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxisLimits(ImAxis_X2, 0, 100); } if (y2_axis) { ImPlot::SetupAxis(ImAxis_Y2, "Y-Axis 2",ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxisLimits(ImAxis_Y2, 0, 1); } if (y3_axis) { ImPlot::SetupAxis(ImAxis_Y3, "Y-Axis 3",ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxisLimits(ImAxis_Y3, 0, 300); } ImPlot::PlotLine("f(x) = x", xs, xs, 1001); if (x2_axis) { ImPlot::SetAxes(ImAxis_X2, ImAxis_Y1); ImPlot::PlotLine("f(x) = sin(x)*3+1", xs2, ys1, 1001); } if (y2_axis) { ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2); ImPlot::PlotLine("f(x) = cos(x)*.2+.5", xs, ys2, 1001); } if (x2_axis && y3_axis) { ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3); ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_LinkedAxes() { static ImPlotRect lims(0,1,0,1); static bool linkx = true, linky = true; int data[2] = {0,1}; ImGui::Checkbox("Link X", &linkx); ImGui::SameLine(); ImGui::Checkbox("Link Y", &linky); ImGui::DragScalarN("Limits",ImGuiDataType_Double,&lims.X.Min,4,0.01f); if (BeginAlignedPlots("AlignedGroup")) { if (ImPlot::BeginPlot("Plot A")) { ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : nullptr, linkx ? &lims.X.Max : nullptr); ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : nullptr, linky ? &lims.Y.Max : nullptr); ImPlot::PlotLine("Line",data,2); ImPlot::EndPlot(); } if (ImPlot::BeginPlot("Plot B")) { ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : nullptr, linkx ? &lims.X.Max : nullptr); ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : nullptr, linky ? &lims.Y.Max : nullptr); ImPlot::PlotLine("Line",data,2); ImPlot::EndPlot(); } ImPlot::EndAlignedPlots(); } } //----------------------------------------------------------------------------- void Demo_AxisConstraints() { static float constraints[4] = {-10,10,1,20}; static ImPlotAxisFlags flags; ImGui::DragFloat2("Limits Constraints", &constraints[0], 0.01f); ImGui::DragFloat2("Zoom Constraints", &constraints[2], 0.01f); CHECKBOX_FLAG(flags, ImPlotAxisFlags_PanStretch); if (ImPlot::BeginPlot("##AxisConstraints",ImVec2(-1,0))) { ImPlot::SetupAxes("X","Y",flags,flags); ImPlot::SetupAxesLimits(-1,1,-1,1); ImPlot::SetupAxisLimitsConstraints(ImAxis_X1,constraints[0], constraints[1]); ImPlot::SetupAxisZoomConstraints(ImAxis_X1,constraints[2], constraints[3]); ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1,constraints[0], constraints[1]); ImPlot::SetupAxisZoomConstraints(ImAxis_Y1,constraints[2], constraints[3]); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_EqualAxes() { ImGui::BulletText("Equal constraint applies to axis pairs (e.g ImAxis_X1/Y1, ImAxis_X2/Y2)"); static double xs1[360], ys1[360]; for (int i = 0; i < 360; ++i) { double angle = i * 2 * PI / 359.0; xs1[i] = cos(angle); ys1[i] = sin(angle); } float xs2[] = {-1,0,1,0,-1}; float ys2[] = {0,1,0,-1,0}; if (ImPlot::BeginPlot("##EqualAxes",ImVec2(-1,0),ImPlotFlags_Equal)) { ImPlot::SetupAxis(ImAxis_X2, nullptr, ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxis(ImAxis_Y2, nullptr, ImPlotAxisFlags_AuxDefault); ImPlot::PlotLine("Circle",xs1,ys1,360); ImPlot::SetAxes(ImAxis_X2, ImAxis_Y2); ImPlot::PlotLine("Diamond",xs2,ys2,5); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_AutoFittingData() { ImGui::BulletText("The Y-axis has been configured to auto-fit to only the data visible in X-axis range."); ImGui::BulletText("Zoom and pan the X-axis. Disable Stems to see a difference in fit."); ImGui::BulletText("If ImPlotAxisFlags_RangeFit is disabled, the axis will fit ALL data."); static ImPlotAxisFlags xflags = ImPlotAxisFlags_None; static ImPlotAxisFlags yflags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit; ImGui::TextUnformatted("X: "); ImGui::SameLine(); ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_RangeFit); ImGui::TextUnformatted("Y: "); ImGui::SameLine(); ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_RangeFit); static double data[101]; srand(0); for (int i = 0; i < 101; ++i) data[i] = 1 + sin(i/10.0f); if (ImPlot::BeginPlot("##DataFitting")) { ImPlot::SetupAxes("X","Y",xflags,yflags); ImPlot::PlotLine("Line",data,101); ImPlot::PlotStems("Stems",data,101); ImPlot::EndPlot(); }; } //----------------------------------------------------------------------------- ImPlotPoint SinewaveGetter(int i, void* data) { float f = *(float*)data; return ImPlotPoint(i,sinf(f*i)); } void Demo_SubplotsSizing() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems|ImPlotSubplotFlags_NoLegend; ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize); ImGui::CheckboxFlags("ImPlotSubplotFlags_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle); static int rows = 3; static int cols = 3; ImGui::SliderInt("Rows",&rows,1,5); ImGui::SliderInt("Cols",&cols,1,5); if (rows < 1 || cols < 1) { ImGui::TextColored(ImVec4(1,0,0,1), "Nice try, but the number of rows and columns must be greater than 0!"); return; } static float rratios[] = {5,1,1,1,1,1}; static float cratios[] = {5,1,1,1,1,1}; ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,0.01f,nullptr); ImGui::DragScalarN("Col Ratios",ImGuiDataType_Float,cratios,cols,0.01f,nullptr); if (ImPlot::BeginSubplots("My Subplots", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) { int id = 0; for (int i = 0; i < rows*cols; ++i) { if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); float fi = 0.01f * (i+1); if (rows*cols > 1) { ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet)); } char label[16]; snprintf(label, sizeof(label), "data%d", id++); ImPlot::PlotLineG(label,SinewaveGetter,&fi,1000); ImPlot::EndPlot(); } } ImPlot::EndSubplots(); } } //----------------------------------------------------------------------------- void Demo_SubplotItemSharing() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems; ImGui::CheckboxFlags("ImPlotSubplotFlags_ShareItems", (unsigned int*)&flags, ImPlotSubplotFlags_ShareItems); ImGui::CheckboxFlags("ImPlotSubplotFlags_ColMajor", (unsigned int*)&flags, ImPlotSubplotFlags_ColMajor); ImGui::BulletText("Drag and drop items from the legend onto plots (except for 'common')"); static int rows = 2; static int cols = 3; static int id[] = {0,1,2,3,4,5}; static int curj = -1; if (ImPlot::BeginSubplots("##ItemSharing", rows, cols, ImVec2(-1,400), flags)) { ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Sort|ImPlotLegendFlags_Horizontal); for (int i = 0; i < rows*cols; ++i) { if (ImPlot::BeginPlot("")) { float fc = 0.01f; ImPlot::PlotLineG("common",SinewaveGetter,&fc,1000); for (int j = 0; j < 6; ++j) { if (id[j] == i) { char label[8]; float fj = 0.01f * (j+2); snprintf(label, sizeof(label), "data%d", j); ImPlot::PlotLineG(label,SinewaveGetter,&fj,1000); if (ImPlot::BeginDragDropSourceItem(label)) { curj = j; ImGui::SetDragDropPayload("MY_DND",nullptr,0); ImPlot::ItemIcon(GetLastItemColor()); ImGui::SameLine(); ImGui::TextUnformatted(label); ImPlot::EndDragDropSource(); } } } if (ImPlot::BeginDragDropTargetPlot()) { if (ImGui::AcceptDragDropPayload("MY_DND")) id[curj] = i; ImPlot::EndDragDropTarget(); } ImPlot::EndPlot(); } } ImPlot::EndSubplots(); } } //----------------------------------------------------------------------------- void Demo_SubplotAxisLinking() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_LinkRows | ImPlotSubplotFlags_LinkCols; ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkRows", (unsigned int*)&flags, ImPlotSubplotFlags_LinkRows); ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkCols", (unsigned int*)&flags, ImPlotSubplotFlags_LinkCols); ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkAllX", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllX); ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkAllY", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllY); static int rows = 2; static int cols = 2; if (ImPlot::BeginSubplots("##AxisLinking", rows, cols, ImVec2(-1,400), flags)) { for (int i = 0; i < rows*cols; ++i) { if (ImPlot::BeginPlot("")) { ImPlot::SetupAxesLimits(0,1000,-1,1); float fc = 0.01f; ImPlot::PlotLineG("common",SinewaveGetter,&fc,1000); ImPlot::EndPlot(); } } ImPlot::EndSubplots(); } } //----------------------------------------------------------------------------- void Demo_LegendOptions() { static ImPlotLocation loc = ImPlotLocation_East; ImGui::CheckboxFlags("North", (unsigned int*)&loc, ImPlotLocation_North); ImGui::SameLine(); ImGui::CheckboxFlags("South", (unsigned int*)&loc, ImPlotLocation_South); ImGui::SameLine(); ImGui::CheckboxFlags("West", (unsigned int*)&loc, ImPlotLocation_West); ImGui::SameLine(); ImGui::CheckboxFlags("East", (unsigned int*)&loc, ImPlotLocation_East); static ImPlotLegendFlags flags = 0; CHECKBOX_FLAG(flags, ImPlotLegendFlags_Horizontal); CHECKBOX_FLAG(flags, ImPlotLegendFlags_Outside); CHECKBOX_FLAG(flags, ImPlotLegendFlags_Sort); ImGui::SliderFloat2("LegendPadding", (float*)&GetStyle().LegendPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f"); static int num_dummy_items = 25; ImGui::SliderInt("Num Dummy Items (Demo Scrolling)", &num_dummy_items, 0, 100); if (ImPlot::BeginPlot("##Legend",ImVec2(-1,0))) { ImPlot::SetupLegend(loc, flags); static MyImPlot::WaveData data1(0.001, 0.2, 4, 0.2); static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.4); static MyImPlot::WaveData data3(0.001, 0.2, 4, 0.6); static MyImPlot::WaveData data4(0.001, 0.2, 4, 0.8); static MyImPlot::WaveData data5(0.001, 0.2, 4, 1.0); ImPlot::PlotLineG("Item 002", MyImPlot::SawWave, &data1, 1000); // "Item B" added to legend ImPlot::PlotLineG("Item 001##IDText", MyImPlot::SawWave, &data2, 1000); // "Item A" added to legend, text after ## used for ID only ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend ImPlot::PlotLineG("Item 003", MyImPlot::SawWave, &data4, 1000); // "Item C" added to legend ImPlot::PlotLineG("Item 003", MyImPlot::SawWave, &data5, 1000); // combined with previous "Item C" for (int i = 0; i < num_dummy_items; ++i) { char label[16]; snprintf(label, sizeof(label), "Item %03d", i+4); ImPlot::PlotDummy(label); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_DragPoints() { ImGui::BulletText("Click and drag each point."); static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks; bool clicked[4] = {false, false, false, false}; bool hovered[4] = {false, false, false, false}; bool held[4] = {false, false, false, false}; if (ImPlot::BeginPlot("##Bezier",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) { ImPlot::SetupAxes(nullptr,nullptr,ax_flags,ax_flags); ImPlot::SetupAxesLimits(0,1,0,1); static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)}; ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags, &clicked[0], &hovered[0], &held[0]); ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags, &clicked[1], &hovered[1], &held[1]); ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags, &clicked[2], &hovered[2], &held[2]); ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags, &clicked[3], &hovered[3], &held[3]); static ImPlotPoint B[100]; for (int i = 0; i < 100; ++i) { double t = i / 99.0; double u = 1 - t; double w1 = u*u*u; double w2 = 3*u*u*t; double w3 = 3*u*t*t; double w4 = t*t*t; B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y); } ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1),hovered[1]||held[1] ? 2.0f : 1.0f); ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint)); ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1), hovered[2]||held[2] ? 2.0f : 1.0f); ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint)); ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), hovered[0]||held[0]||hovered[3]||held[3] ? 3.0f : 2.0f); ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint)); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_DragLines() { ImGui::BulletText("Click and drag the horizontal and vertical lines."); static double x1 = 0.2; static double x2 = 0.8; static double y1 = 0.25; static double y2 = 0.75; static double f = 0.1; bool clicked = false; bool hovered = false; bool held = false; static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); if (ImPlot::BeginPlot("##lines",ImVec2(-1,0))) { ImPlot::SetupAxesLimits(0,1,0,1); ImPlot::DragLineX(0,&x1,ImVec4(1,1,1,1),1,flags); ImPlot::DragLineX(1,&x2,ImVec4(1,1,1,1),1,flags); ImPlot::DragLineY(2,&y1,ImVec4(1,1,1,1),1,flags); ImPlot::DragLineY(3,&y2,ImVec4(1,1,1,1),1,flags); double xs[1000], ys[1000]; for (int i = 0; i < 1000; ++i) { xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f); ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10); } ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags, &clicked, &hovered, &held); ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, hovered||held ? 2.0f : 1.0f); ImPlot::PlotLine("Interactive Data", xs, ys, 1000); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_DragRects() { static float x_data[512]; static float y_data1[512]; static float y_data2[512]; static float y_data3[512]; static float sampling_freq = 44100; static float freq = 500; bool clicked = false; bool hovered = false; bool held = false; for (size_t i = 0; i < 512; ++i) { const float t = i / sampling_freq; x_data[i] = t; const float arg = 2 * 3.14f * freq * t; y_data1[i] = sinf(arg); y_data2[i] = y_data1[i] * -0.6f + sinf(2 * arg) * 0.4f; y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f; } ImGui::BulletText("Click and drag the edges, corners, and center of the rect."); ImGui::BulletText("Double click edges to expand rect to plot extents."); static ImPlotRect rect(0.0025,0.0045,0,0.5); static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); if (ImPlot::BeginPlot("##Main",ImVec2(-1,150))) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoTickLabels,ImPlotAxisFlags_NoTickLabels); ImPlot::SetupAxesLimits(0,0.01,-1,1); ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags, &clicked, &hovered, &held); ImPlot::EndPlot(); } ImVec4 bg_col = held ? ImVec4(0.5f,0,0.5f,1) : (hovered ? ImVec4(0.25f,0,0.25f,1) : ImPlot::GetStyle().Colors[ImPlotCol_PlotBg]); ImPlot::PushStyleColor(ImPlotCol_PlotBg, bg_col); if (ImPlot::BeginPlot("##rect",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always); ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); ImPlot::EndPlot(); } ImPlot::PopStyleColor(); ImGui::Text("Rect is %sclicked, %shovered, %sheld", clicked ? "" : "not ", hovered ? "" : "not ", held ? "" : "not "); } //----------------------------------------------------------------------------- ImPlotPoint FindCentroid(const ImVector& data, const ImPlotRect& bounds, int& cnt) { cnt = 0; ImPlotPoint avg; ImPlotRect bounds_fixed; bounds_fixed.X.Min = bounds.X.Min < bounds.X.Max ? bounds.X.Min : bounds.X.Max; bounds_fixed.X.Max = bounds.X.Min < bounds.X.Max ? bounds.X.Max : bounds.X.Min; bounds_fixed.Y.Min = bounds.Y.Min < bounds.Y.Max ? bounds.Y.Min : bounds.Y.Max; bounds_fixed.Y.Max = bounds.Y.Min < bounds.Y.Max ? bounds.Y.Max : bounds.Y.Min; for (int i = 0; i < data.size(); ++i) { if (bounds_fixed.Contains(data[i].x, data[i].y)) { avg.x += data[i].x; avg.y += data[i].y; cnt++; } } if (cnt > 0) { avg.x = avg.x / cnt; avg.y = avg.y / cnt; } return avg; } //----------------------------------------------------------------------------- void Demo_Querying() { static ImVector data; static ImVector rects; static ImPlotRect limits, select; static bool init = true; if (init) { for (int i = 0; i < 50; ++i) { double x = RandomRange(0.1, 0.9); double y = RandomRange(0.1, 0.9); data.push_back(ImPlotPoint(x,y)); } init = false; } ImGui::BulletText("Box select and left click mouse to create a new query rect."); ImGui::BulletText("Ctrl + click in the plot area to draw points."); if (ImGui::Button("Clear Queries")) rects.shrink(0); if (ImPlot::BeginPlot("##Centroid")) { ImPlot::SetupAxesLimits(0,1,0,1); if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyCtrl) { ImPlotPoint pt = ImPlot::GetPlotMousePos(); data.push_back(pt); } ImPlot::PlotScatter("Points", &data[0].x, &data[0].y, data.size(), 0, 0, 2 * sizeof(double)); if (ImPlot::IsPlotSelected()) { select = ImPlot::GetPlotSelection(); int cnt; ImPlotPoint centroid = FindCentroid(data,select,cnt); if (cnt > 0) { ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6); ImPlot::PlotScatter("Centroid", ¢roid.x, ¢roid.y, 1); } if (ImGui::IsMouseClicked(ImPlot::GetInputMap().SelectCancel)) { CancelPlotSelection(); rects.push_back(select); } } for (int i = 0; i < rects.size(); ++i) { int cnt; ImPlotPoint centroid = FindCentroid(data,rects[i],cnt); if (cnt > 0) { ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6); ImPlot::PlotScatter("Centroid", ¢roid.x, ¢roid.y, 1); } ImPlot::DragRect(i,&rects[i].X.Min,&rects[i].Y.Min,&rects[i].X.Max,&rects[i].Y.Max,ImVec4(1,0,1,1)); } limits = ImPlot::GetPlotLimits(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_Annotations() { static bool clamp = false; ImGui::Checkbox("Clamp",&clamp); if (ImPlot::BeginPlot("##Annotations")) { ImPlot::SetupAxesLimits(0,2,0,1); static float p[] = {0.25f, 0.25f, 0.75f, 0.75f, 0.25f}; ImPlot::PlotScatter("##Points",&p[0],&p[1],4); ImVec4 col = GetLastItemColor(); ImPlot::Annotation(0.25,0.25,col,ImVec2(-15,15),clamp,"BL"); ImPlot::Annotation(0.75,0.25,col,ImVec2(15,15),clamp,"BR"); ImPlot::Annotation(0.75,0.75,col,ImVec2(15,-15),clamp,"TR"); ImPlot::Annotation(0.25,0.75,col,ImVec2(-15,-15),clamp,"TL"); ImPlot::Annotation(0.5,0.5,col,ImVec2(0,0),clamp,"Center"); ImPlot::Annotation(1.25,0.75,ImVec4(0,1,0,1),ImVec2(0,0),clamp); float bx[] = {1.2f,1.5f,1.8f}; float by[] = {0.25f, 0.5f, 0.75f}; ImPlot::PlotBars("##Bars",bx,by,3,0.2); for (int i = 0; i < 3; ++i) ImPlot::Annotation(bx[i],by[i],ImVec4(0,0,0,0),ImVec2(0,-5),clamp,"B[%d]=%.2f",i,by[i]); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_Tags() { static bool show = true; ImGui::Checkbox("Show Tags",&show); if (ImPlot::BeginPlot("##Tags")) { ImPlot::SetupAxis(ImAxis_X2); ImPlot::SetupAxis(ImAxis_Y2); if (show) { ImPlot::TagX(0.25, ImVec4(1,1,0,1)); ImPlot::TagY(0.75, ImVec4(1,1,0,1)); static double drag_tag = 0.25; ImPlot::DragLineY(0,&drag_tag,ImVec4(1,0,0,1),1,ImPlotDragToolFlags_NoFit); ImPlot::TagY(drag_tag, ImVec4(1,0,0,1), "Drag"); SetAxes(ImAxis_X2, ImAxis_Y2); ImPlot::TagX(0.5, ImVec4(0,1,1,1), "%s", "MyTag"); ImPlot::TagY(0.5, ImVec4(0,1,1,1), "Tag: %d", 42); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_DragAndDrop() { ImGui::BulletText("Drag/drop items from the left column."); ImGui::BulletText("Drag/drop items between plots."); ImGui::Indent(); ImGui::BulletText("Plot 1 Targets: Plot, Y-Axes, Legend"); ImGui::BulletText("Plot 1 Sources: Legend Item Labels"); ImGui::BulletText("Plot 2 Targets: Plot, X-Axis, Y-Axis"); ImGui::BulletText("Plot 2 Sources: Plot, X-Axis, Y-Axis (hold Ctrl)"); ImGui::Unindent(); // convenience struct to manage DND items; do this however you like struct MyDndItem { int Idx; int Plt; ImAxis Yax; char Label[16]; ImVector Data; ImVec4 Color; MyDndItem() { static int i = 0; Idx = i++; Plt = 0; Yax = ImAxis_Y1; snprintf(Label, sizeof(Label), "%02d Hz", Idx+1); Color = RandomColor(); Data.reserve(1001); for (int k = 0; k < 1001; ++k) { float t = k * 1.0f / 999; Data.push_back(ImVec2(t, 0.5f + 0.5f * sinf(2*3.14f*t*(Idx+1)))); } } void Reset() { Plt = 0; Yax = ImAxis_Y1; } }; const int k_dnd = 20; static MyDndItem dnd[k_dnd]; static MyDndItem* dndx = nullptr; // for plot 2 static MyDndItem* dndy = nullptr; // for plot 2 // child window to serve as initial source for our DND items ImGui::BeginChild("DND_LEFT",ImVec2(100,400)); if (ImGui::Button("Reset Data")) { for (int k = 0; k < k_dnd; ++k) dnd[k].Reset(); dndx = dndy = nullptr; } for (int k = 0; k < k_dnd; ++k) { if (dnd[k].Plt > 0) continue; ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); ImGui::Selectable(dnd[k].Label, false, 0, ImVec2(100, 0)); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); ImGui::TextUnformatted(dnd[k].Label); ImGui::EndDragDropSource(); } } ImGui::EndChild(); if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dnd[i].Reset(); } ImGui::EndDragDropTarget(); } ImGui::SameLine(); ImGui::BeginChild("DND_RIGHT",ImVec2(-1,400)); // plot 1 (time series) ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoHighlight; if (ImPlot::BeginPlot("##DND1", ImVec2(-1,195))) { ImPlot::SetupAxis(ImAxis_X1, nullptr, flags|ImPlotAxisFlags_Lock); ImPlot::SetupAxis(ImAxis_Y1, "[drop here]", flags); ImPlot::SetupAxis(ImAxis_Y2, "[drop here]", flags|ImPlotAxisFlags_Opposite); ImPlot::SetupAxis(ImAxis_Y3, "[drop here]", flags|ImPlotAxisFlags_Opposite); for (int k = 0; k < k_dnd; ++k) { if (dnd[k].Plt == 1 && dnd[k].Data.size() > 0) { ImPlot::SetAxis(dnd[k].Yax); ImPlot::SetNextLineStyle(dnd[k].Color); ImPlot::PlotLine(dnd[k].Label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 0, 2 * sizeof(float)); // allow legend item labels to be DND sources if (ImPlot::BeginDragDropSourceItem(dnd[k].Label)) { ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); ImGui::TextUnformatted(dnd[k].Label); ImPlot::EndDragDropSource(); } } } // allow the main plot area to be a DND target if (ImPlot::BeginDragDropTargetPlot()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1; } ImPlot::EndDragDropTarget(); } // allow each y-axis to be a DND target for (int y = ImAxis_Y1; y <= ImAxis_Y3; ++y) { if (ImPlot::BeginDragDropTargetAxis(y)) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = y; } ImPlot::EndDragDropTarget(); } } // allow the legend to be a DND target if (ImPlot::BeginDragDropTargetLegend()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1; } ImPlot::EndDragDropTarget(); } ImPlot::EndPlot(); } // plot 2 (Lissajous) if (ImPlot::BeginPlot("##DND2", ImVec2(-1,195))) { ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndx != nullptr ? dndx->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]); ImPlot::SetupAxis(ImAxis_X1, dndx == nullptr ? "[drop here]" : dndx->Label, flags); ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndy != nullptr ? dndy->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]); ImPlot::SetupAxis(ImAxis_Y1, dndy == nullptr ? "[drop here]" : dndy->Label, flags); ImPlot::PopStyleColor(2); if (dndx != nullptr && dndy != nullptr) { ImVec4 mixed((dndx->Color.x + dndy->Color.x)/2,(dndx->Color.y + dndy->Color.y)/2,(dndx->Color.z + dndy->Color.z)/2,(dndx->Color.w + dndy->Color.w)/2); ImPlot::SetNextLineStyle(mixed); ImPlot::PlotLine("##dndxy", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 0, 2 * sizeof(float)); } // allow the x-axis to be a DND target if (ImPlot::BeginDragDropTargetAxis(ImAxis_X1)) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dndx = &dnd[i]; } ImPlot::EndDragDropTarget(); } // allow the x-axis to be a DND source if (dndx != nullptr && ImPlot::BeginDragDropSourceAxis(ImAxis_X1)) { ImGui::SetDragDropPayload("MY_DND", &dndx->Idx, sizeof(int)); ImPlot::ItemIcon(dndx->Color); ImGui::SameLine(); ImGui::TextUnformatted(dndx->Label); ImPlot::EndDragDropSource(); } // allow the y-axis to be a DND target if (ImPlot::BeginDragDropTargetAxis(ImAxis_Y1)) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dndy = &dnd[i]; } ImPlot::EndDragDropTarget(); } // allow the y-axis to be a DND source if (dndy != nullptr && ImPlot::BeginDragDropSourceAxis(ImAxis_Y1)) { ImGui::SetDragDropPayload("MY_DND", &dndy->Idx, sizeof(int)); ImPlot::ItemIcon(dndy->Color); ImGui::SameLine(); ImGui::TextUnformatted(dndy->Label); ImPlot::EndDragDropSource(); } // allow the plot area to be a DND target if (ImPlot::BeginDragDropTargetPlot()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dndx = dndy = &dnd[i]; } } // allow the plot area to be a DND source if (ImPlot::BeginDragDropSourcePlot()) { ImGui::TextUnformatted("Yes, you can\ndrag this!"); ImPlot::EndDragDropSource(); } ImPlot::EndPlot(); } ImGui::EndChild(); } //----------------------------------------------------------------------------- void Demo_Tables() { #ifdef IMGUI_HAS_TABLE static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable; static bool anim = true; static int offset = 0; ImGui::BulletText("Plots can be used inside of ImGui tables as another means of creating subplots."); ImGui::Checkbox("Animate",&anim); if (anim) offset = (offset + 1) % 100; if (ImGui::BeginTable("##table", 3, flags, ImVec2(-1,0))) { ImGui::TableSetupColumn("Electrode", ImGuiTableColumnFlags_WidthFixed, 75.0f); ImGui::TableSetupColumn("Voltage", ImGuiTableColumnFlags_WidthFixed, 75.0f); ImGui::TableSetupColumn("EMG Signal"); ImGui::TableHeadersRow(); ImPlot::PushColormap(ImPlotColormap_Cool); for (int row = 0; row < 10; row++) { ImGui::TableNextRow(); static float data[100]; srand(row); for (int i = 0; i < 100; ++i) data[i] = RandomRange(0.0f,10.0f); ImGui::TableSetColumnIndex(0); ImGui::Text("EMG %d", row); ImGui::TableSetColumnIndex(1); ImGui::Text("%.3f V", data[offset]); ImGui::TableSetColumnIndex(2); ImGui::PushID(row); MyImPlot::Sparkline("##spark",data,100,0,11.0f,offset,ImPlot::GetColormapColor(row),ImVec2(-1, 35)); ImGui::PopID(); } ImPlot::PopColormap(); ImGui::EndTable(); } #else ImGui::BulletText("You need to merge the ImGui 'tables' branch for this section."); #endif } //----------------------------------------------------------------------------- void Demo_OffsetAndStride() { static const int k_circles = 11; static const int k_points_per = 50; static const int k_size = 2 * k_points_per * k_circles; static double interleaved_data[k_size]; for (int p = 0; p < k_points_per; ++p) { for (int c = 0; c < k_circles; ++c) { double r = (double)c / (k_circles - 1) * 0.2 + 0.2; interleaved_data[p*2*k_circles + 2*c + 0] = 0.5 + r * cos((double)p/k_points_per * 6.28); interleaved_data[p*2*k_circles + 2*c + 1] = 0.5 + r * sin((double)p/k_points_per * 6.28); } } static int offset = 0; ImGui::BulletText("Offsetting is useful for realtime plots (see above) and circular buffers."); ImGui::BulletText("Striding is useful for interleaved data (e.g. audio) or plotting structs."); ImGui::BulletText("Here, all circle data is stored in a single interleaved buffer:"); ImGui::BulletText("[c0.x0 c0.y0 ... cn.x0 cn.y0 c0.x1 c0.y1 ... cn.x1 cn.y1 ... cn.xm cn.ym]"); ImGui::BulletText("The offset value indicates which circle point index is considered the first."); ImGui::BulletText("Offsets can be negative and/or larger than the actual data count."); ImGui::SliderInt("Offset", &offset, -2*k_points_per, 2*k_points_per); if (ImPlot::BeginPlot("##strideoffset",ImVec2(-1,0),ImPlotFlags_Equal)) { ImPlot::PushColormap(ImPlotColormap_Jet); char buff[32]; for (int c = 0; c < k_circles; ++c) { snprintf(buff, sizeof(buff), "Circle %d", c); ImPlot::PlotLine(buff, &interleaved_data[c*2 + 0], &interleaved_data[c*2 + 1], k_points_per, 0, offset, 2*k_circles*sizeof(double)); } ImPlot::EndPlot(); ImPlot::PopColormap(); } // offset++; uncomment for animation! } //----------------------------------------------------------------------------- void Demo_CustomDataAndGetters() { ImGui::BulletText("You can plot custom structs using the stride feature."); ImGui::BulletText("Most plotters can also be passed a function pointer for getting data."); ImGui::Indent(); ImGui::BulletText("You can optionally pass user data to be given to your getter function."); ImGui::BulletText("C++ lambdas can be passed as function pointers as well!"); ImGui::Unindent(); MyImPlot::Vector2f vec2_data[2] = { MyImPlot::Vector2f(0,0), MyImPlot::Vector2f(1,1) }; if (ImPlot::BeginPlot("##Custom Data")) { // custom structs using stride example: ImPlot::PlotLine("Vector2f", &vec2_data[0].x, &vec2_data[0].y, 2, 0, 0, sizeof(MyImPlot::Vector2f) /* or sizeof(float) * 2 */); // custom getter example 1: ImPlot::PlotLineG("Spiral", MyImPlot::Spiral, nullptr, 1000); // custom getter example 2: static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75); static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25); ImPlot::PlotLineG("Waves", MyImPlot::SineWave, &data1, 1000); ImPlot::PlotLineG("Waves", MyImPlot::SawWave, &data2, 1000); ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::PlotShadedG("Waves", MyImPlot::SineWave, &data1, MyImPlot::SawWave, &data2, 1000); ImPlot::PopStyleVar(); // you can also pass C++ lambdas: // auto lamda = [](void* data, int idx) { ... return ImPlotPoint(x,y); }; // ImPlot::PlotLine("My Lambda", lambda, data, 1000); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- int MetricFormatter(double value, char* buff, int size, void* data) { const char* unit = (const char*)data; static double v[] = {1000000000,1000000,1000,1,0.001,0.000001,0.000000001}; static const char* p[] = {"G","M","k","","m","u","n"}; if (value == 0) { return snprintf(buff,size,"0 %s", unit); } for (int i = 0; i < 7; ++i) { if (fabs(value) >= v[i]) { return snprintf(buff,size,"%g %s%s",value/v[i],p[i],unit); } } return snprintf(buff,size,"%g %s%s",value/v[6],p[6],unit); } void Demo_TickLabels() { static bool custom_fmt = true; static bool custom_ticks = false; static bool custom_labels = true; ImGui::Checkbox("Show Custom Format", &custom_fmt); ImGui::SameLine(); ImGui::Checkbox("Show Custom Ticks", &custom_ticks); if (custom_ticks) { ImGui::SameLine(); ImGui::Checkbox("Show Custom Labels", &custom_labels); } const double pi = 3.14; const char* pi_str[] = {"PI"}; static double yticks[] = {100,300,700,900}; static const char* ylabels[] = {"One","Three","Seven","Nine"}; static double yticks_aux[] = {0.2,0.4,0.6}; static const char* ylabels_aux[] = {"A","B","C","D","E","F"}; if (ImPlot::BeginPlot("##Ticks")) { ImPlot::SetupAxesLimits(2.5,5,0,1000); ImPlot::SetupAxis(ImAxis_Y2, nullptr, ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxis(ImAxis_Y3, nullptr, ImPlotAxisFlags_AuxDefault); if (custom_fmt) { ImPlot::SetupAxisFormat(ImAxis_X1, "%g ms"); ImPlot::SetupAxisFormat(ImAxis_Y1, MetricFormatter, (void*)"Hz"); ImPlot::SetupAxisFormat(ImAxis_Y2, "%g dB"); ImPlot::SetupAxisFormat(ImAxis_Y3, MetricFormatter, (void*)"m"); } if (custom_ticks) { ImPlot::SetupAxisTicks(ImAxis_X1, &pi,1,custom_labels ? pi_str : nullptr, true); ImPlot::SetupAxisTicks(ImAxis_Y1, yticks, 4, custom_labels ? ylabels : nullptr, false); ImPlot::SetupAxisTicks(ImAxis_Y2, yticks_aux, 3, custom_labels ? ylabels_aux : nullptr, false); ImPlot::SetupAxisTicks(ImAxis_Y3, 0, 1, 6, custom_labels ? ylabels_aux : nullptr, false); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_CustomStyles() { ImPlot::PushColormap(ImPlotColormap_Deep); // normally you wouldn't change the entire style each frame ImPlotStyle backup = ImPlot::GetStyle(); MyImPlot::StyleSeaborn(); if (ImPlot::BeginPlot("seaborn style")) { ImPlot::SetupAxes( "x-axis", "y-axis"); ImPlot::SetupAxesLimits(-0.5f, 9.5f, 0, 10); unsigned int lin[10] = {8,8,9,7,8,8,8,9,7,8}; unsigned int bar[10] = {1,2,5,3,4,1,2,5,3,4}; unsigned int dot[10] = {7,6,6,7,8,5,6,5,8,7}; ImPlot::PlotBars("Bars", bar, 10, 0.5f); ImPlot::PlotLine("Line", lin, 10); ImPlot::NextColormapColor(); // skip green ImPlot::PlotScatter("Scatter", dot, 10); ImPlot::EndPlot(); } ImPlot::GetStyle() = backup; ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_CustomRendering() { if (ImPlot::BeginPlot("##CustomRend")) { ImVec2 cntr = ImPlot::PlotToPixels(ImPlotPoint(0.5f, 0.5f)); ImVec2 rmin = ImPlot::PlotToPixels(ImPlotPoint(0.25f, 0.75f)); ImVec2 rmax = ImPlot::PlotToPixels(ImPlotPoint(0.75f, 0.25f)); ImPlot::PushPlotClipRect(); ImPlot::GetPlotDrawList()->AddCircleFilled(cntr,20,IM_COL32(255,255,0,255),20); ImPlot::GetPlotDrawList()->AddRect(rmin, rmax, IM_COL32(128,0,255,255)); ImPlot::PopPlotClipRect(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_LegendPopups() { ImGui::BulletText("You can implement legend context menus to inject per-item controls and widgets."); ImGui::BulletText("Right click the legend label/icon to edit custom item attributes."); static float frequency = 0.1f; static float amplitude = 0.5f; static ImVec4 color = ImVec4(1,1,0,1); static float alpha = 1.0f; static bool line = false; static float thickness = 1; static bool markers = false; static bool shaded = false; static float vals[101]; for (int i = 0; i < 101; ++i) vals[i] = amplitude * sinf(frequency * i); if (ImPlot::BeginPlot("Right Click the Legend")) { ImPlot::SetupAxesLimits(0,100,-1,1); // rendering logic ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); if (!line) { ImPlot::SetNextFillStyle(color); ImPlot::PlotBars("Right Click Me", vals, 101); } else { if (markers) ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); ImPlot::SetNextLineStyle(color, thickness); ImPlot::PlotLine("Right Click Me", vals, 101); if (shaded) ImPlot::PlotShaded("Right Click Me",vals,101); } ImPlot::PopStyleVar(); // custom legend context menu if (ImPlot::BeginLegendPopup("Right Click Me")) { ImGui::SliderFloat("Frequency",&frequency,0,1,"%0.2f"); ImGui::SliderFloat("Amplitude",&litude,0,1,"%0.2f"); ImGui::Separator(); ImGui::ColorEdit3("Color",&color.x); ImGui::SliderFloat("Transparency",&alpha,0,1,"%.2f"); ImGui::Checkbox("Line Plot", &line); if (line) { ImGui::SliderFloat("Thickness", &thickness, 0, 5); ImGui::Checkbox("Markers", &markers); ImGui::Checkbox("Shaded",&shaded); } ImPlot::EndLegendPopup(); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_ColormapWidgets() { static int cmap = ImPlotColormap_Viridis; if (ImPlot::ColormapButton("Button",ImVec2(0,0),cmap)) { cmap = (cmap + 1) % ImPlot::GetColormapCount(); } static float t = 0.5f; static ImVec4 col; ImGui::ColorButton("##Display",col,ImGuiColorEditFlags_NoInputs); ImGui::SameLine(); ImPlot::ColormapSlider("Slider", &t, &col, "%.3f", cmap); ImPlot::ColormapIcon(cmap); ImGui::SameLine(); ImGui::Text("Icon"); static ImPlotColormapScaleFlags flags = 0; static float scale[2] = {0, 100}; ImPlot::ColormapScale("Scale",scale[0],scale[1],ImVec2(0,0),"%g dB",flags,cmap); ImGui::InputFloat2("Scale",scale); CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_NoLabel); CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_Opposite); CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_Invert); } //----------------------------------------------------------------------------- void Demo_CustomPlottersAndTooltips() { ImGui::BulletText("You can create custom plotters or extend ImPlot using implot_internal.h."); double dates[] = {1546300800,1546387200,1546473600,1546560000,1546819200,1546905600,1546992000,1547078400,1547164800,1547424000,1547510400,1547596800,1547683200,1547769600,1547942400,1548028800,1548115200,1548201600,1548288000,1548374400,1548633600,1548720000,1548806400,1548892800,1548979200,1549238400,1549324800,1549411200,1549497600,1549584000,1549843200,1549929600,1550016000,1550102400,1550188800,1550361600,1550448000,1550534400,1550620800,1550707200,1550793600,1551052800,1551139200,1551225600,1551312000,1551398400,1551657600,1551744000,1551830400,1551916800,1552003200,1552262400,1552348800,1552435200,1552521600,1552608000,1552867200,1552953600,1553040000,1553126400,1553212800,1553472000,1553558400,1553644800,1553731200,1553817600,1554076800,1554163200,1554249600,1554336000,1554422400,1554681600,1554768000,1554854400,1554940800,1555027200,1555286400,1555372800,1555459200,1555545600,1555632000,1555891200,1555977600,1556064000,1556150400,1556236800,1556496000,1556582400,1556668800,1556755200,1556841600,1557100800,1557187200,1557273600,1557360000,1557446400,1557705600,1557792000,1557878400,1557964800,1558051200,1558310400,1558396800,1558483200,1558569600,1558656000,1558828800,1558915200,1559001600,1559088000,1559174400,1559260800,1559520000,1559606400,1559692800,1559779200,1559865600,1560124800,1560211200,1560297600,1560384000,1560470400,1560729600,1560816000,1560902400,1560988800,1561075200,1561334400,1561420800,1561507200,1561593600,1561680000,1561939200,1562025600,1562112000,1562198400,1562284800,1562544000,1562630400,1562716800,1562803200,1562889600,1563148800,1563235200,1563321600,1563408000,1563494400,1563753600,1563840000,1563926400,1564012800,1564099200,1564358400,1564444800,1564531200,1564617600,1564704000,1564963200,1565049600,1565136000,1565222400,1565308800,1565568000,1565654400,1565740800,1565827200,1565913600,1566172800,1566259200,1566345600,1566432000,1566518400,1566777600,1566864000,1566950400,1567036800,1567123200,1567296000,1567382400,1567468800,1567555200,1567641600,1567728000,1567987200,1568073600,1568160000,1568246400,1568332800,1568592000,1568678400,1568764800,1568851200,1568937600,1569196800,1569283200,1569369600,1569456000,1569542400,1569801600,1569888000,1569974400,1570060800,1570147200,1570406400,1570492800,1570579200,1570665600,1570752000,1571011200,1571097600,1571184000,1571270400,1571356800,1571616000,1571702400,1571788800,1571875200,1571961600}; double opens[] = {1284.7,1319.9,1318.7,1328,1317.6,1321.6,1314.3,1325,1319.3,1323.1,1324.7,1321.3,1323.5,1322,1281.3,1281.95,1311.1,1315,1314,1313.1,1331.9,1334.2,1341.3,1350.6,1349.8,1346.4,1343.4,1344.9,1335.6,1337.9,1342.5,1337,1338.6,1337,1340.4,1324.65,1324.35,1349.5,1371.3,1367.9,1351.3,1357.8,1356.1,1356,1347.6,1339.1,1320.6,1311.8,1314,1312.4,1312.3,1323.5,1319.1,1327.2,1332.1,1320.3,1323.1,1328,1330.9,1338,1333,1335.3,1345.2,1341.1,1332.5,1314,1314.4,1310.7,1314,1313.1,1315,1313.7,1320,1326.5,1329.2,1314.2,1312.3,1309.5,1297.4,1293.7,1277.9,1295.8,1295.2,1290.3,1294.2,1298,1306.4,1299.8,1302.3,1297,1289.6,1302,1300.7,1303.5,1300.5,1303.2,1306,1318.7,1315,1314.5,1304.1,1294.7,1293.7,1291.2,1290.2,1300.4,1284.2,1284.25,1301.8,1295.9,1296.2,1304.4,1323.1,1340.9,1341,1348,1351.4,1351.4,1343.5,1342.3,1349,1357.6,1357.1,1354.7,1361.4,1375.2,1403.5,1414.7,1433.2,1438,1423.6,1424.4,1418,1399.5,1435.5,1421.25,1434.1,1412.4,1409.8,1412.2,1433.4,1418.4,1429,1428.8,1420.6,1441,1460.4,1441.7,1438.4,1431,1439.3,1427.4,1431.9,1439.5,1443.7,1425.6,1457.5,1451.2,1481.1,1486.7,1512.1,1515.9,1509.2,1522.3,1513,1526.6,1533.9,1523,1506.3,1518.4,1512.4,1508.8,1545.4,1537.3,1551.8,1549.4,1536.9,1535.25,1537.95,1535.2,1556,1561.4,1525.6,1516.4,1507,1493.9,1504.9,1506.5,1513.1,1506.5,1509.7,1502,1506.8,1521.5,1529.8,1539.8,1510.9,1511.8,1501.7,1478,1485.4,1505.6,1511.6,1518.6,1498.7,1510.9,1510.8,1498.3,1492,1497.7,1484.8,1494.2,1495.6,1495.6,1487.5,1491.1,1495.1,1506.4}; double highs[] = {1284.75,1320.6,1327,1330.8,1326.8,1321.6,1326,1328,1325.8,1327.1,1326,1326,1323.5,1322.1,1282.7,1282.95,1315.8,1316.3,1314,1333.2,1334.7,1341.7,1353.2,1354.6,1352.2,1346.4,1345.7,1344.9,1340.7,1344.2,1342.7,1342.1,1345.2,1342,1350,1324.95,1330.75,1369.6,1374.3,1368.4,1359.8,1359,1357,1356,1353.4,1340.6,1322.3,1314.1,1316.1,1312.9,1325.7,1323.5,1326.3,1336,1332.1,1330.1,1330.4,1334.7,1341.1,1344.2,1338.8,1348.4,1345.6,1342.8,1334.7,1322.3,1319.3,1314.7,1316.6,1316.4,1315,1325.4,1328.3,1332.2,1329.2,1316.9,1312.3,1309.5,1299.6,1296.9,1277.9,1299.5,1296.2,1298.4,1302.5,1308.7,1306.4,1305.9,1307,1297.2,1301.7,1305,1305.3,1310.2,1307,1308,1319.8,1321.7,1318.7,1316.2,1305.9,1295.8,1293.8,1293.7,1304.2,1302,1285.15,1286.85,1304,1302,1305.2,1323,1344.1,1345.2,1360.1,1355.3,1363.8,1353,1344.7,1353.6,1358,1373.6,1358.2,1369.6,1377.6,1408.9,1425.5,1435.9,1453.7,1438,1426,1439.1,1418,1435,1452.6,1426.65,1437.5,1421.5,1414.1,1433.3,1441.3,1431.4,1433.9,1432.4,1440.8,1462.3,1467,1443.5,1444,1442.9,1447,1437.6,1440.8,1445.7,1447.8,1458.2,1461.9,1481.8,1486.8,1522.7,1521.3,1521.1,1531.5,1546.1,1534.9,1537.7,1538.6,1523.6,1518.8,1518.4,1514.6,1540.3,1565,1554.5,1556.6,1559.8,1541.9,1542.9,1540.05,1558.9,1566.2,1561.9,1536.2,1523.8,1509.1,1506.2,1532.2,1516.6,1519.7,1515,1519.5,1512.1,1524.5,1534.4,1543.3,1543.3,1542.8,1519.5,1507.2,1493.5,1511.4,1525.8,1522.2,1518.8,1515.3,1518,1522.3,1508,1501.5,1503,1495.5,1501.1,1497.9,1498.7,1492.1,1499.4,1506.9,1520.9}; double lows[] = {1282.85,1315,1318.7,1309.6,1317.6,1312.9,1312.4,1319.1,1319,1321,1318.1,1321.3,1319.9,1312,1280.5,1276.15,1308,1309.9,1308.5,1312.3,1329.3,1333.1,1340.2,1347,1345.9,1338,1340.8,1335,1332,1337.9,1333,1336.8,1333.2,1329.9,1340.4,1323.85,1324.05,1349,1366.3,1351.2,1349.1,1352.4,1350.7,1344.3,1338.9,1316.3,1308.4,1306.9,1309.6,1306.7,1312.3,1315.4,1319,1327.2,1317.2,1320,1323,1328,1323,1327.8,1331.7,1335.3,1336.6,1331.8,1311.4,1310,1309.5,1308,1310.6,1302.8,1306.6,1313.7,1320,1322.8,1311,1312.1,1303.6,1293.9,1293.5,1291,1277.9,1294.1,1286,1289.1,1293.5,1296.9,1298,1299.6,1292.9,1285.1,1288.5,1296.3,1297.2,1298.4,1298.6,1302,1300.3,1312,1310.8,1301.9,1292,1291.1,1286.3,1289.2,1289.9,1297.4,1283.65,1283.25,1292.9,1295.9,1290.8,1304.2,1322.7,1336.1,1341,1343.5,1345.8,1340.3,1335.1,1341.5,1347.6,1352.8,1348.2,1353.7,1356.5,1373.3,1398,1414.7,1427,1416.4,1412.7,1420.1,1396.4,1398.8,1426.6,1412.85,1400.7,1406,1399.8,1404.4,1415.5,1417.2,1421.9,1415,1413.7,1428.1,1434,1435.7,1427.5,1429.4,1423.9,1425.6,1427.5,1434.8,1422.3,1412.1,1442.5,1448.8,1468.2,1484.3,1501.6,1506.2,1498.6,1488.9,1504.5,1518.3,1513.9,1503.3,1503,1506.5,1502.1,1503,1534.8,1535.3,1541.4,1528.6,1525.6,1535.25,1528.15,1528,1542.6,1514.3,1510.7,1505.5,1492.1,1492.9,1496.8,1493.1,1503.4,1500.9,1490.7,1496.3,1505.3,1505.3,1517.9,1507.4,1507.1,1493.3,1470.5,1465,1480.5,1501.7,1501.4,1493.3,1492.1,1505.1,1495.7,1478,1487.1,1480.8,1480.6,1487,1488.3,1484.8,1484,1490.7,1490.4,1503.1}; double closes[] = {1283.35,1315.3,1326.1,1317.4,1321.5,1317.4,1323.5,1319.2,1321.3,1323.3,1319.7,1325.1,1323.6,1313.8,1282.05,1279.05,1314.2,1315.2,1310.8,1329.1,1334.5,1340.2,1340.5,1350,1347.1,1344.3,1344.6,1339.7,1339.4,1343.7,1337,1338.9,1340.1,1338.7,1346.8,1324.25,1329.55,1369.6,1372.5,1352.4,1357.6,1354.2,1353.4,1346,1341,1323.8,1311.9,1309.1,1312.2,1310.7,1324.3,1315.7,1322.4,1333.8,1319.4,1327.1,1325.8,1330.9,1325.8,1331.6,1336.5,1346.7,1339.2,1334.7,1313.3,1316.5,1312.4,1313.4,1313.3,1312.2,1313.7,1319.9,1326.3,1331.9,1311.3,1313.4,1309.4,1295.2,1294.7,1294.1,1277.9,1295.8,1291.2,1297.4,1297.7,1306.8,1299.4,1303.6,1302.2,1289.9,1299.2,1301.8,1303.6,1299.5,1303.2,1305.3,1319.5,1313.6,1315.1,1303.5,1293,1294.6,1290.4,1291.4,1302.7,1301,1284.15,1284.95,1294.3,1297.9,1304.1,1322.6,1339.3,1340.1,1344.9,1354,1357.4,1340.7,1342.7,1348.2,1355.1,1355.9,1354.2,1362.1,1360.1,1408.3,1411.2,1429.5,1430.1,1426.8,1423.4,1425.1,1400.8,1419.8,1432.9,1423.55,1412.1,1412.2,1412.8,1424.9,1419.3,1424.8,1426.1,1423.6,1435.9,1440.8,1439.4,1439.7,1434.5,1436.5,1427.5,1432.2,1433.3,1441.8,1437.8,1432.4,1457.5,1476.5,1484.2,1519.6,1509.5,1508.5,1517.2,1514.1,1527.8,1531.2,1523.6,1511.6,1515.7,1515.7,1508.5,1537.6,1537.2,1551.8,1549.1,1536.9,1529.4,1538.05,1535.15,1555.9,1560.4,1525.5,1515.5,1511.1,1499.2,1503.2,1507.4,1499.5,1511.5,1513.4,1515.8,1506.2,1515.1,1531.5,1540.2,1512.3,1515.2,1506.4,1472.9,1489,1507.9,1513.8,1512.9,1504.4,1503.9,1512.8,1500.9,1488.7,1497.6,1483.5,1494,1498.3,1494.1,1488.1,1487.5,1495.7,1504.7,1505.3}; static bool tooltip = true; ImGui::Checkbox("Show Tooltip", &tooltip); ImGui::SameLine(); static ImVec4 bullCol = ImVec4(0.000f, 1.000f, 0.441f, 1.000f); static ImVec4 bearCol = ImVec4(0.853f, 0.050f, 0.310f, 1.000f); ImGui::SameLine(); ImGui::ColorEdit4("##Bull", &bullCol.x, ImGuiColorEditFlags_NoInputs); ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs); ImPlot::GetStyle().UseLocalTime = false; if (ImPlot::BeginPlot("Candlestick Chart",ImVec2(-1,0))) { ImPlot::SetupAxes(nullptr,nullptr,0,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit); ImPlot::SetupAxesLimits(1546300800, 1571961600, 1250, 1600); ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); ImPlot::SetupAxisLimitsConstraints(ImAxis_X1, 1546300800, 1571961600); ImPlot::SetupAxisZoomConstraints(ImAxis_X1, 60*60*24*14, 1571961600-1546300800); ImPlot::SetupAxisFormat(ImAxis_Y1, "$%.0f"); MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- // DEMO WINDOW //----------------------------------------------------------------------------- void DemoHeader(const char* label, void(*demo)()) { if (ImGui::TreeNodeEx(label)) { demo(); ImGui::TreePop(); } } void ShowDemoWindow(bool* p_open) { static bool show_implot_metrics = false; static bool show_implot_style_editor = false; static bool show_imgui_metrics = false; static bool show_imgui_style_editor = false; static bool show_imgui_demo = false; if (show_implot_metrics) { ImPlot::ShowMetricsWindow(&show_implot_metrics); } if (show_implot_style_editor) { ImGui::SetNextWindowSize(ImVec2(415,762), ImGuiCond_Appearing); ImGui::Begin("Style Editor (ImPlot)", &show_implot_style_editor); ImPlot::ShowStyleEditor(); ImGui::End(); } if (show_imgui_style_editor) { ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } if (show_imgui_metrics) { ImGui::ShowMetricsWindow(&show_imgui_metrics); } if (show_imgui_demo) { ImGui::ShowDemoWindow(&show_imgui_demo); } ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver); ImGui::Begin("ImPlot Demo", p_open, ImGuiWindowFlags_MenuBar); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Tools")) { ImGui::MenuItem("Metrics", nullptr, &show_implot_metrics); ImGui::MenuItem("Style Editor", nullptr, &show_implot_style_editor); ImGui::Separator(); ImGui::MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics); ImGui::MenuItem("ImGui Style Editor", nullptr, &show_imgui_style_editor); ImGui::MenuItem("ImGui Demo", nullptr, &show_imgui_demo); ImGui::EndMenu(); } ImGui::EndMenuBar(); } //------------------------------------------------------------------------- ImGui::Text("ImPlot says hello. (%s)", IMPLOT_VERSION); // display warning about 16-bit indices static bool showWarning = sizeof(ImDrawIdx)*8 == 16 && (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) == false; if (showWarning) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); ImGui::TextWrapped("WARNING: ImDrawIdx is 16-bit and ImGuiBackendFlags_RendererHasVtxOffset is false. Expect visual glitches and artifacts! See README for more information."); ImGui::PopStyleColor(); } ImGui::Spacing(); if (ImGui::BeginTabBar("ImPlotDemoTabs")) { if (ImGui::BeginTabItem("Plots")) { DemoHeader("Line Plots", Demo_LinePlots); DemoHeader("Filled Line Plots", Demo_FilledLinePlots); DemoHeader("Shaded Plots##", Demo_ShadedPlots); DemoHeader("Scatter Plots", Demo_ScatterPlots); DemoHeader("Realtime Plots", Demo_RealtimePlots); DemoHeader("Stairstep Plots", Demo_StairstepPlots); DemoHeader("Bar Plots", Demo_BarPlots); DemoHeader("Bar Groups", Demo_BarGroups); DemoHeader("Bar Stacks", Demo_BarStacks); DemoHeader("Error Bars", Demo_ErrorBars); DemoHeader("Stem Plots##", Demo_StemPlots); DemoHeader("Infinite Lines", Demo_InfiniteLines); DemoHeader("Pie Charts", Demo_PieCharts); DemoHeader("Heatmaps", Demo_Heatmaps); DemoHeader("Histogram", Demo_Histogram); DemoHeader("Histogram 2D", Demo_Histogram2D); DemoHeader("Digital Plots", Demo_DigitalPlots); DemoHeader("Images", Demo_Images); DemoHeader("Markers and Text", Demo_MarkersAndText); DemoHeader("NaN Values", Demo_NaNValues); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Subplots")) { DemoHeader("Sizing", Demo_SubplotsSizing); DemoHeader("Item Sharing", Demo_SubplotItemSharing); DemoHeader("Axis Linking", Demo_SubplotAxisLinking); DemoHeader("Tables", Demo_Tables); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Axes")) { DemoHeader("Log Scale", Demo_LogScale); DemoHeader("Symmetric Log Scale", Demo_SymmetricLogScale); DemoHeader("Time Scale", Demo_TimeScale); DemoHeader("Custom Scale", Demo_CustomScale); DemoHeader("Multiple Axes", Demo_MultipleAxes); DemoHeader("Tick Labels", Demo_TickLabels); DemoHeader("Linked Axes", Demo_LinkedAxes); DemoHeader("Axis Constraints", Demo_AxisConstraints); DemoHeader("Equal Axes", Demo_EqualAxes); DemoHeader("Auto-Fitting Data", Demo_AutoFittingData); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Tools")) { DemoHeader("Offset and Stride", Demo_OffsetAndStride); DemoHeader("Drag Points", Demo_DragPoints); DemoHeader("Drag Lines", Demo_DragLines); DemoHeader("Drag Rects", Demo_DragRects); DemoHeader("Querying", Demo_Querying); DemoHeader("Annotations", Demo_Annotations); DemoHeader("Tags", Demo_Tags); DemoHeader("Drag and Drop", Demo_DragAndDrop); DemoHeader("Legend Options", Demo_LegendOptions); DemoHeader("Legend Popups", Demo_LegendPopups); DemoHeader("Colormap Widgets", Demo_ColormapWidgets); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Custom")) { DemoHeader("Custom Styles", Demo_CustomStyles); DemoHeader("Custom Data and Getters", Demo_CustomDataAndGetters); DemoHeader("Custom Rendering", Demo_CustomRendering); DemoHeader("Custom Plotters and Tooltips", Demo_CustomPlottersAndTooltips); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Config")) { Demo_Config(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Help")) { Demo_Help(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } ImGui::End(); } } // namespace ImPlot namespace MyImPlot { ImPlotPoint SineWave(int idx, void* data) { WaveData* wd = (WaveData*)data; double x = idx * wd->X; return ImPlotPoint(x, wd->Offset + wd->Amp * sin(2 * 3.14 * wd->Freq * x)); } ImPlotPoint SawWave(int idx, void* data) { WaveData* wd = (WaveData*)data; double x = idx * wd->X; return ImPlotPoint(x, wd->Offset + wd->Amp * (-2 / 3.14 * atan(cos(3.14 * wd->Freq * x) / sin(3.14 * wd->Freq * x)))); } ImPlotPoint Spiral(int idx, void*) { float r = 0.9f; // outer radius float a = 0; // inner radius float b = 0.05f; // increment per rev float n = (r - a) / b; // number of revolutions double th = 2 * n * 3.14; // angle float Th = float(th * idx / (1000 - 1)); return ImPlotPoint(0.5f+(a + b*Th / (2.0f * (float) 3.14))*cos(Th), 0.5f + (a + b*Th / (2.0f * (float)3.14))*sin(Th)); } void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) { ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0)); if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly)) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always); ImPlot::SetNextLineStyle(col); ImPlot::SetNextFillStyle(col, 0.25); ImPlot::PlotLine(id, values, count, 1, 0, ImPlotLineFlags_Shaded, offset); ImPlot::EndPlot(); } ImPlot::PopStyleVar(); } void StyleSeaborn() { ImPlotStyle& style = ImPlot::GetStyle(); ImVec4* colors = style.Colors; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_PlotBg] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); colors[ImPlotCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImPlotCol_LegendBg] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); colors[ImPlotCol_LegendBorder] = ImVec4(0.80f, 0.81f, 0.85f, 1.00f); colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_AxisBgHovered] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); colors[ImPlotCol_AxisBgActive] = ImVec4(0.92f, 0.92f, 0.95f, 0.75f); colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.65f, 0.00f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.23f, 0.10f, 0.64f, 0.50f); style.LineWeight = 1.5; style.Marker = ImPlotMarker_None; style.MarkerSize = 4; style.MarkerWeight = 1; style.FillAlpha = 1.0f; style.ErrorBarSize = 5; style.ErrorBarWeight = 1.5f; style.DigitalBitHeight = 8; style.DigitalBitGap = 4; style.PlotBorderSize = 0; style.MinorAlpha = 1.0f; style.MajorTickLen = ImVec2(0,0); style.MinorTickLen = ImVec2(0,0); style.MajorTickSize = ImVec2(0,0); style.MinorTickSize = ImVec2(0,0); style.MajorGridSize = ImVec2(1.2f,1.2f); style.MinorGridSize = ImVec2(1.2f,1.2f); style.PlotPadding = ImVec2(12,12); style.LabelPadding = ImVec2(5,5); style.LegendPadding = ImVec2(5,5); style.MousePosPadding = ImVec2(5,5); style.PlotMinSize = ImVec2(300,225); } } // namespaece MyImPlot // WARNING: // // You can use "implot_internal.h" to build custom plotting fuctions or extend ImPlot. // However, note that forward compatibility of this file is not guaranteed and the // internal API is subject to change. At some point we hope to bring more of this // into the public API and expose the necessary building blocks to fully support // custom plotters. For now, proceed at your own risk! #include "implot_internal.h" namespace MyImPlot { template int BinarySearch(const T* arr, int l, int r, T x) { if (r >= l) { int mid = l + (r - l) / 2; if (arr[mid] == x) return mid; if (arr[mid] > x) return BinarySearch(arr, l, mid - 1, x); return BinarySearch(arr, mid + 1, r, x); } return -1; } void PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip, float width_percent, ImVec4 bullCol, ImVec4 bearCol) { // get ImGui window DrawList ImDrawList* draw_list = ImPlot::GetPlotDrawList(); // calc real value width double half_width = count > 1 ? (xs[1] - xs[0]) * width_percent : width_percent; // custom tool if (ImPlot::IsPlotHovered() && tooltip) { ImPlotPoint mouse = ImPlot::GetPlotMousePos(); mouse.x = ImPlot::RoundTime(ImPlotTime::FromDouble(mouse.x), ImPlotTimeUnit_Day).ToDouble(); float tool_l = ImPlot::PlotToPixels(mouse.x - half_width * 1.5, mouse.y).x; float tool_r = ImPlot::PlotToPixels(mouse.x + half_width * 1.5, mouse.y).x; float tool_t = ImPlot::GetPlotPos().y; float tool_b = tool_t + ImPlot::GetPlotSize().y; ImPlot::PushPlotClipRect(); draw_list->AddRectFilled(ImVec2(tool_l, tool_t), ImVec2(tool_r, tool_b), IM_COL32(128,128,128,64)); ImPlot::PopPlotClipRect(); // find mouse location index int idx = BinarySearch(xs, 0, count - 1, mouse.x); // render tool tip (won't be affected by plot clip rect) if (idx != -1) { ImGui::BeginTooltip(); char buff[32]; ImPlot::FormatDate(ImPlotTime::FromDouble(xs[idx]),buff,32,ImPlotDateFmt_DayMoYr,ImPlot::GetStyle().UseISO8601); ImGui::Text("Day: %s", buff); ImGui::Text("Open: $%.2f", opens[idx]); ImGui::Text("Close: $%.2f", closes[idx]); ImGui::Text("Low: $%.2f", lows[idx]); ImGui::Text("High: $%.2f", highs[idx]); ImGui::EndTooltip(); } } // begin plot item if (ImPlot::BeginItem(label_id)) { // override legend icon color ImPlot::GetCurrentItem()->Color = IM_COL32(64,64,64,255); // fit data if requested if (ImPlot::FitThisFrame()) { for (int i = 0; i < count; ++i) { ImPlot::FitPoint(ImPlotPoint(xs[i], lows[i])); ImPlot::FitPoint(ImPlotPoint(xs[i], highs[i])); } } // render data for (int i = 0; i < count; ++i) { ImVec2 open_pos = ImPlot::PlotToPixels(xs[i] - half_width, opens[i]); ImVec2 close_pos = ImPlot::PlotToPixels(xs[i] + half_width, closes[i]); ImVec2 low_pos = ImPlot::PlotToPixels(xs[i], lows[i]); ImVec2 high_pos = ImPlot::PlotToPixels(xs[i], highs[i]); ImU32 color = ImGui::GetColorU32(opens[i] > closes[i] ? bearCol : bullCol); draw_list->AddLine(low_pos, high_pos, color); draw_list->AddRectFilled(open_pos, close_pos, color); } // end plot item ImPlot::EndItem(); } } } // namespace MyImplot ================================================ FILE: lib/third_party/imgui/implot/source/implot_items.cpp ================================================ // MIT License // Copyright (c) 2023 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.17 #define IMGUI_DEFINE_MATH_OPERATORS #include "implot.h" #include "implot_internal.h" //----------------------------------------------------------------------------- // [SECTION] Macros and Defines //----------------------------------------------------------------------------- #define SQRT_1_2 0.70710678118f #define SQRT_3_2 0.86602540378f #ifndef IMPLOT_NO_FORCE_INLINE #ifdef _MSC_VER #define IMPLOT_INLINE __forceinline #elif defined(__GNUC__) #define IMPLOT_INLINE inline __attribute__((__always_inline__)) #elif defined(__CLANG__) #if __has_attribute(__always_inline__) #define IMPLOT_INLINE inline __attribute__((__always_inline__)) #else #define IMPLOT_INLINE inline #endif #else #define IMPLOT_INLINE inline #endif #else #define IMPLOT_INLINE inline #endif #if defined __SSE__ || defined __x86_64__ || defined _M_X64 #ifndef IMGUI_ENABLE_SSE #include #endif static IMPLOT_INLINE float ImInvSqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } #else static IMPLOT_INLINE float ImInvSqrt(float x) { return 1.0f / sqrtf(x); } #endif #define IMPLOT_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImInvSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) // Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. #if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All #endif //----------------------------------------------------------------------------- // [SECTION] Template instantiation utility //----------------------------------------------------------------------------- // By default, templates are instantiated for `float`, `double`, and for the following integer types, which are defined in imgui.h: // signed char ImS8; // 8-bit signed integer // unsigned char ImU8; // 8-bit unsigned integer // signed short ImS16; // 16-bit signed integer // unsigned short ImU16; // 16-bit unsigned integer // signed int ImS32; // 32-bit signed integer == int // unsigned int ImU32; // 32-bit unsigned integer // signed long long ImS64; // 64-bit signed integer // unsigned long long ImU64; // 64-bit unsigned integer // (note: this list does *not* include `long`, `unsigned long` and `long double`) // // You can customize the supported types by defining IMPLOT_CUSTOM_NUMERIC_TYPES at compile time to define your own type list. // As an example, you could use the compile time define given by the line below in order to support only float and double. // -DIMPLOT_CUSTOM_NUMERIC_TYPES="(float)(double)" // In order to support all known C++ types, use: // -DIMPLOT_CUSTOM_NUMERIC_TYPES="(signed char)(unsigned char)(signed short)(unsigned short)(signed int)(unsigned int)(signed long)(unsigned long)(signed long long)(unsigned long long)(float)(double)(long double)" #ifdef IMPLOT_CUSTOM_NUMERIC_TYPES #define IMPLOT_NUMERIC_TYPES IMPLOT_CUSTOM_NUMERIC_TYPES #else #define IMPLOT_NUMERIC_TYPES (ImS8)(ImU8)(ImS16)(ImU16)(ImS32)(ImU32)(ImS64)(ImU64)(float)(double) #endif // CALL_INSTANTIATE_FOR_NUMERIC_TYPES will duplicate the template instantion code `INSTANTIATE_MACRO(T)` on supported types. #define _CAT(x, y) _CAT_(x, y) #define _CAT_(x,y) x ## y #define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END) #define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_2 #define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_1 #define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END #define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END #define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT_NUMERIC_TYPES) namespace ImPlot { //----------------------------------------------------------------------------- // [SECTION] Utils //----------------------------------------------------------------------------- // Calc maximum index size of ImDrawIdx template struct MaxIdx { static const unsigned int Value; }; template <> const unsigned int MaxIdx::Value = 65535; template <> const unsigned int MaxIdx::Value = 4294967295; IMPLOT_INLINE void GetLineRenderProps(const ImDrawList& draw_list, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) { const bool aa = ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLines) && ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLinesUseTex); if (aa) { ImVec4 tex_uvs = draw_list._Data->TexUvLines[(int)(half_weight*2)]; tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y); tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w); half_weight += 1; } else { tex_uv0 = tex_uv1 = draw_list._Data->TexUvWhitePixel; } } IMPLOT_INLINE void PrimLine(ImDrawList& draw_list, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0, const ImVec2 tex_uv1) { float dx = P2.x - P1.x; float dy = P2.y - P1.y; IMPLOT_NORMALIZE2F_OVER_ZERO(dx, dy); dx *= half_weight; dy *= half_weight; draw_list._VtxWritePtr[0].pos.x = P1.x + dy; draw_list._VtxWritePtr[0].pos.y = P1.y - dx; draw_list._VtxWritePtr[0].uv = tex_uv0; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos.x = P2.x + dy; draw_list._VtxWritePtr[1].pos.y = P2.y - dx; draw_list._VtxWritePtr[1].uv = tex_uv0; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = P2.x - dy; draw_list._VtxWritePtr[2].pos.y = P2.y + dx; draw_list._VtxWritePtr[2].uv = tex_uv1; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = P1.x - dy; draw_list._VtxWritePtr[3].pos.y = P1.y + dx; draw_list._VtxWritePtr[3].uv = tex_uv1; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr += 4; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 4; } IMPLOT_INLINE void PrimRectFill(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, const ImVec2& uv) { draw_list._VtxWritePtr[0].pos = Pmin; draw_list._VtxWritePtr[0].uv = uv; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos = Pmax; draw_list._VtxWritePtr[1].uv = uv; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = Pmin.x; draw_list._VtxWritePtr[2].pos.y = Pmax.y; draw_list._VtxWritePtr[2].uv = uv; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = Pmax.x; draw_list._VtxWritePtr[3].pos.y = Pmin.y; draw_list._VtxWritePtr[3].uv = uv; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr += 4; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 4; } IMPLOT_INLINE void PrimRectLine(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, float weight, ImU32 col, const ImVec2& uv) { draw_list._VtxWritePtr[0].pos.x = Pmin.x; draw_list._VtxWritePtr[0].pos.y = Pmin.y; draw_list._VtxWritePtr[0].uv = uv; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos.x = Pmin.x; draw_list._VtxWritePtr[1].pos.y = Pmax.y; draw_list._VtxWritePtr[1].uv = uv; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = Pmax.x; draw_list._VtxWritePtr[2].pos.y = Pmax.y; draw_list._VtxWritePtr[2].uv = uv; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = Pmax.x; draw_list._VtxWritePtr[3].pos.y = Pmin.y; draw_list._VtxWritePtr[3].uv = uv; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr[4].pos.x = Pmin.x + weight; draw_list._VtxWritePtr[4].pos.y = Pmin.y + weight; draw_list._VtxWritePtr[4].uv = uv; draw_list._VtxWritePtr[4].col = col; draw_list._VtxWritePtr[5].pos.x = Pmin.x + weight; draw_list._VtxWritePtr[5].pos.y = Pmax.y - weight; draw_list._VtxWritePtr[5].uv = uv; draw_list._VtxWritePtr[5].col = col; draw_list._VtxWritePtr[6].pos.x = Pmax.x - weight; draw_list._VtxWritePtr[6].pos.y = Pmax.y - weight; draw_list._VtxWritePtr[6].uv = uv; draw_list._VtxWritePtr[6].col = col; draw_list._VtxWritePtr[7].pos.x = Pmax.x - weight; draw_list._VtxWritePtr[7].pos.y = Pmin.y + weight; draw_list._VtxWritePtr[7].uv = uv; draw_list._VtxWritePtr[7].col = col; draw_list._VtxWritePtr += 8; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr += 3; draw_list._VtxCurrentIdx += 8; } //----------------------------------------------------------------------------- // [SECTION] Item Utils //----------------------------------------------------------------------------- ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created) { ImPlotContext& gp = *GImPlot; ImPlotItemGroup& Items = *gp.CurrentItems; ImGuiID id = Items.GetItemID(label_id); if (just_created != nullptr) *just_created = Items.GetItem(id) == nullptr; ImPlotItem* item = Items.GetOrAddItem(id); if (item->SeenThisFrame) return item; item->SeenThisFrame = true; int idx = Items.GetItemIndex(item); item->ID = id; if (!ImHasFlag(flags, ImPlotItemFlags_NoLegend) && ImGui::FindRenderedTextEnd(label_id, nullptr) != label_id) { Items.Legend.Indices.push_back(idx); item->NameOffset = Items.Legend.Labels.size(); Items.Legend.Labels.append(label_id, label_id + strlen(label_id) + 1); } else { item->Show = true; } return item; } ImPlotItem* GetItem(const char* label_id) { ImPlotContext& gp = *GImPlot; return gp.CurrentItems->GetItem(label_id); } bool IsItemHidden(const char* label_id) { ImPlotItem* item = GetItem(label_id); return item != nullptr && !item->Show; } ImPlotItem* GetCurrentItem() { ImPlotContext& gp = *GImPlot; return gp.CurrentItem; } void SetNextLineStyle(const ImVec4& col, float weight) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_Line] = col; gp.NextItemData.LineWeight = weight; } void SetNextFillStyle(const ImVec4& col, float alpha) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_Fill] = col; gp.NextItemData.FillAlpha = alpha; } void SetNextMarkerStyle(ImPlotMarker marker, float size, const ImVec4& fill, float weight, const ImVec4& outline) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Marker = marker; gp.NextItemData.Colors[ImPlotCol_MarkerFill] = fill; gp.NextItemData.MarkerSize = size; gp.NextItemData.Colors[ImPlotCol_MarkerOutline] = outline; gp.NextItemData.MarkerWeight = weight; } void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_ErrorBar] = col; gp.NextItemData.ErrorBarSize = size; gp.NextItemData.ErrorBarWeight = weight; } ImVec4 GetLastItemColor() { ImPlotContext& gp = *GImPlot; if (gp.PreviousItem) return ImGui::ColorConvertU32ToFloat4(gp.PreviousItem->Color); return ImVec4(); } void BustItemCache() { ImPlotContext& gp = *GImPlot; for (int p = 0; p < gp.Plots.GetBufSize(); ++p) { ImPlotPlot& plot = *gp.Plots.GetByIndex(p); plot.Items.Reset(); } for (int p = 0; p < gp.Subplots.GetBufSize(); ++p) { ImPlotSubplot& subplot = *gp.Subplots.GetByIndex(p); subplot.Items.Reset(); } } void BustColorCache(const char* plot_title_id) { ImPlotContext& gp = *GImPlot; if (plot_title_id == nullptr) { BustItemCache(); } else { ImGuiID id = ImGui::GetCurrentWindow()->GetID(plot_title_id); ImPlotPlot* plot = gp.Plots.GetByKey(id); if (plot != nullptr) plot->Items.Reset(); else { ImPlotSubplot* subplot = gp.Subplots.GetByKey(id); if (subplot != nullptr) subplot->Items.Reset(); } } } //----------------------------------------------------------------------------- // [SECTION] BeginItem / EndItem //----------------------------------------------------------------------------- static const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f; static const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f; // Begins a new item. Returns false if the item should not be plotted. bool BeginItem(const char* label_id, ImPlotItemFlags flags, ImPlotCol recolor_from) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PlotX() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); bool just_created; ImPlotItem* item = RegisterOrGetItem(label_id, flags, &just_created); // set current item gp.CurrentItem = item; ImPlotNextItemData& s = gp.NextItemData; // set/override item color if (recolor_from != -1) { if (!IsColorAuto(s.Colors[recolor_from])) item->Color = ImGui::ColorConvertFloat4ToU32(s.Colors[recolor_from]); else if (!IsColorAuto(gp.Style.Colors[recolor_from])) item->Color = ImGui::ColorConvertFloat4ToU32(gp.Style.Colors[recolor_from]); else if (just_created) item->Color = NextColormapColorU32(); } else if (just_created) { item->Color = NextColormapColorU32(); } // hide/show item if (gp.NextItemData.HasHidden) { if (just_created || gp.NextItemData.HiddenCond == ImGuiCond_Always) item->Show = !gp.NextItemData.Hidden; } if (!item->Show) { // reset next item data gp.NextItemData.Reset(); gp.PreviousItem = item; gp.CurrentItem = nullptr; return false; } else { ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color); // stage next item colors s.Colors[ImPlotCol_Line] = IsColorAuto(s.Colors[ImPlotCol_Line]) ? ( IsColorAuto(ImPlotCol_Line) ? item_color : gp.Style.Colors[ImPlotCol_Line] ) : s.Colors[ImPlotCol_Line]; s.Colors[ImPlotCol_Fill] = IsColorAuto(s.Colors[ImPlotCol_Fill]) ? ( IsColorAuto(ImPlotCol_Fill) ? item_color : gp.Style.Colors[ImPlotCol_Fill] ) : s.Colors[ImPlotCol_Fill]; s.Colors[ImPlotCol_MarkerOutline] = IsColorAuto(s.Colors[ImPlotCol_MarkerOutline]) ? ( IsColorAuto(ImPlotCol_MarkerOutline) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerOutline] ) : s.Colors[ImPlotCol_MarkerOutline]; s.Colors[ImPlotCol_MarkerFill] = IsColorAuto(s.Colors[ImPlotCol_MarkerFill]) ? ( IsColorAuto(ImPlotCol_MarkerFill) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerFill] ) : s.Colors[ImPlotCol_MarkerFill]; s.Colors[ImPlotCol_ErrorBar] = IsColorAuto(s.Colors[ImPlotCol_ErrorBar]) ? ( GetStyleColorVec4(ImPlotCol_ErrorBar) ) : s.Colors[ImPlotCol_ErrorBar]; // stage next item style vars s.LineWeight = s.LineWeight < 0 ? gp.Style.LineWeight : s.LineWeight; s.Marker = s.Marker < 0 ? gp.Style.Marker : s.Marker; s.MarkerSize = s.MarkerSize < 0 ? gp.Style.MarkerSize : s.MarkerSize; s.MarkerWeight = s.MarkerWeight < 0 ? gp.Style.MarkerWeight : s.MarkerWeight; s.FillAlpha = s.FillAlpha < 0 ? gp.Style.FillAlpha : s.FillAlpha; s.ErrorBarSize = s.ErrorBarSize < 0 ? gp.Style.ErrorBarSize : s.ErrorBarSize; s.ErrorBarWeight = s.ErrorBarWeight < 0 ? gp.Style.ErrorBarWeight : s.ErrorBarWeight; s.DigitalBitHeight = s.DigitalBitHeight < 0 ? gp.Style.DigitalBitHeight : s.DigitalBitHeight; s.DigitalBitGap = s.DigitalBitGap < 0 ? gp.Style.DigitalBitGap : s.DigitalBitGap; // apply alpha modifier(s) s.Colors[ImPlotCol_Fill].w *= s.FillAlpha; s.Colors[ImPlotCol_MarkerFill].w *= s.FillAlpha; // TODO: this should be separate, if it at all // apply highlight mods if (item->LegendHovered) { if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightItem)) { s.LineWeight *= ITEM_HIGHLIGHT_LINE_SCALE; s.MarkerSize *= ITEM_HIGHLIGHT_MARK_SCALE; s.MarkerWeight *= ITEM_HIGHLIGHT_LINE_SCALE; // TODO: how to highlight fills? } if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)) { if (gp.CurrentPlot->EnabledAxesX() > 1) gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX].ColorHiLi = item->Color; if (gp.CurrentPlot->EnabledAxesY() > 1) gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY].ColorHiLi = item->Color; } } // set render flags s.RenderLine = s.Colors[ImPlotCol_Line].w > 0 && s.LineWeight > 0; s.RenderFill = s.Colors[ImPlotCol_Fill].w > 0; s.RenderMarkerFill = s.Colors[ImPlotCol_MarkerFill].w > 0; s.RenderMarkerLine = s.Colors[ImPlotCol_MarkerOutline].w > 0 && s.MarkerWeight > 0; // push rendering clip rect PushPlotClipRect(); return true; } } // Ends an item (call only if BeginItem returns true) void EndItem() { ImPlotContext& gp = *GImPlot; // pop rendering clip rect PopPlotClipRect(); // reset next item data gp.NextItemData.Reset(); // set current item gp.PreviousItem = gp.CurrentItem; gp.CurrentItem = nullptr; } //----------------------------------------------------------------------------- // [SECTION] Indexers //----------------------------------------------------------------------------- template IMPLOT_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) { const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1); switch (s) { case 3 : return data[idx]; case 2 : return data[(offset + idx) % count]; case 1 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx) ) * stride); case 0 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((offset + idx) % count) * stride); default: return T(0); } } template struct IndexerIdx { IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : Data(data), Count(count), Offset(count ? ImPosMod(offset, count) : 0), Stride(stride) { } template IMPLOT_INLINE double operator()(I idx) const { return (double)IndexData(Data, idx, Count, Offset, Stride); } const T* Data; int Count; int Offset; int Stride; }; template struct IndexerAdd { IndexerAdd(const _Indexer1& indexer1, const _Indexer2& indexer2, double scale1 = 1, double scale2 = 1) : Indexer1(indexer1), Indexer2(indexer2), Scale1(scale1), Scale2(scale2), Count(ImMin(Indexer1.Count, Indexer2.Count)) { } template IMPLOT_INLINE double operator()(I idx) const { return Scale1 * Indexer1(idx) + Scale2 * Indexer2(idx); } const _Indexer1& Indexer1; const _Indexer2& Indexer2; double Scale1; double Scale2; int Count; }; struct IndexerLin { IndexerLin(double m, double b) : M(m), B(b) { } template IMPLOT_INLINE double operator()(I idx) const { return M * idx + B; } const double M; const double B; }; struct IndexerConst { IndexerConst(double ref) : Ref(ref) { } template IMPLOT_INLINE double operator()(I) const { return Ref; } const double Ref; }; //----------------------------------------------------------------------------- // [SECTION] Getters //----------------------------------------------------------------------------- template struct GetterXY { GetterXY(_IndexerX x, _IndexerY y, int count) : IndxerX(x), IndxerY(y), Count(count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { return ImPlotPoint(IndxerX(idx),IndxerY(idx)); } const _IndexerX IndxerX; const _IndexerY IndxerY; const int Count; }; /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { GetterFuncPtr(ImPlotGetter getter, void* data, int count) : Getter(getter), Data(data), Count(count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { return Getter(idx, Data); } ImPlotGetter Getter; void* const Data; const int Count; }; template struct GetterOverrideX { GetterOverrideX(_Getter getter, double x) : Getter(getter), X(x), Count(getter.Count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { ImPlotPoint p = Getter(idx); p.x = X; return p; } const _Getter Getter; const double X; const int Count; }; template struct GetterOverrideY { GetterOverrideY(_Getter getter, double y) : Getter(getter), Y(y), Count(getter.Count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { ImPlotPoint p = Getter(idx); p.y = Y; return p; } const _Getter Getter; const double Y; const int Count; }; template struct GetterLoop { GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { idx = idx % (Count - 1); return Getter(idx); } const _Getter Getter; const int Count; }; template struct GetterError { GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) : Xs(xs), Ys(ys), Neg(neg), Pos(pos), Count(count), Offset(count ? ImPosMod(offset, count) : 0), Stride(stride) { } template IMPLOT_INLINE ImPlotPointError operator()(I idx) const { return ImPlotPointError((double)IndexData(Xs, idx, Count, Offset, Stride), (double)IndexData(Ys, idx, Count, Offset, Stride), (double)IndexData(Neg, idx, Count, Offset, Stride), (double)IndexData(Pos, idx, Count, Offset, Stride)); } const T* const Xs; const T* const Ys; const T* const Neg; const T* const Pos; const int Count; const int Offset; const int Stride; }; //----------------------------------------------------------------------------- // [SECTION] Fitters //----------------------------------------------------------------------------- template struct Fitter1 { Fitter1(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } } const _Getter1& Getter; }; template struct FitterX { FitterX(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis&) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); x_axis.ExtendFit(p.x); } } const _Getter1& Getter; }; template struct FitterY { FitterY(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis&, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); y_axis.ExtendFit(p.y); } } const _Getter1& Getter; }; template struct Fitter2 { Fitter2(const _Getter1& getter1, const _Getter2& getter2) : Getter1(getter1), Getter2(getter2) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter1.Count; ++i) { ImPlotPoint p = Getter1(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } for (int i = 0; i < Getter2.Count; ++i) { ImPlotPoint p = Getter2(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } } const _Getter1& Getter1; const _Getter2& Getter2; }; template struct FitterBarV { FitterBarV(const _Getter1& getter1, const _Getter2& getter2, double width) : Getter1(getter1), Getter2(getter2), HalfWidth(width*0.5) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { int count = ImMin(Getter1.Count, Getter2.Count); for (int i = 0; i < count; ++i) { ImPlotPoint p1 = Getter1(i); p1.x -= HalfWidth; ImPlotPoint p2 = Getter2(i); p2.x += HalfWidth; x_axis.ExtendFitWith(y_axis, p1.x, p1.y); y_axis.ExtendFitWith(x_axis, p1.y, p1.x); x_axis.ExtendFitWith(y_axis, p2.x, p2.y); y_axis.ExtendFitWith(x_axis, p2.y, p2.x); } } const _Getter1& Getter1; const _Getter2& Getter2; const double HalfWidth; }; template struct FitterBarH { FitterBarH(const _Getter1& getter1, const _Getter2& getter2, double height) : Getter1(getter1), Getter2(getter2), HalfHeight(height*0.5) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { int count = ImMin(Getter1.Count, Getter2.Count); for (int i = 0; i < count; ++i) { ImPlotPoint p1 = Getter1(i); p1.y -= HalfHeight; ImPlotPoint p2 = Getter2(i); p2.y += HalfHeight; x_axis.ExtendFitWith(y_axis, p1.x, p1.y); y_axis.ExtendFitWith(x_axis, p1.y, p1.x); x_axis.ExtendFitWith(y_axis, p2.x, p2.y); y_axis.ExtendFitWith(x_axis, p2.y, p2.x); } } const _Getter1& Getter1; const _Getter2& Getter2; const double HalfHeight; }; struct FitterRect { FitterRect(const ImPlotPoint& pmin, const ImPlotPoint& pmax) : Pmin(pmin), Pmax(pmax) { } FitterRect(const ImPlotRect& rect) : FitterRect(rect.Min(), rect.Max()) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { x_axis.ExtendFitWith(y_axis, Pmin.x, Pmin.y); y_axis.ExtendFitWith(x_axis, Pmin.y, Pmin.x); x_axis.ExtendFitWith(y_axis, Pmax.x, Pmax.y); y_axis.ExtendFitWith(x_axis, Pmax.y, Pmax.x); } const ImPlotPoint Pmin; const ImPlotPoint Pmax; }; //----------------------------------------------------------------------------- // [SECTION] Transformers //----------------------------------------------------------------------------- struct Transformer1 { Transformer1(double pixMin, double pltMin, double pltMax, double m, double scaMin, double scaMax, ImPlotTransform fwd, void* data) : ScaMin(scaMin), ScaMax(scaMax), PltMin(pltMin), PltMax(pltMax), PixMin(pixMin), M(m), TransformFwd(fwd), TransformData(data) { } template IMPLOT_INLINE float operator()(T p) const { if (TransformFwd != nullptr) { double s = TransformFwd(p, TransformData); double t = (s - ScaMin) / (ScaMax - ScaMin); p = PltMin + (PltMax - PltMin) * t; } return (float)(PixMin + M * (p - PltMin)); } double ScaMin, ScaMax, PltMin, PltMax, PixMin, M; ImPlotTransform TransformFwd; void* TransformData; }; struct Transformer2 { Transformer2(const ImPlotAxis& x_axis, const ImPlotAxis& y_axis) : Tx(x_axis.PixelMin, x_axis.Range.Min, x_axis.Range.Max, x_axis.ScaleToPixel, x_axis.ScaleMin, x_axis.ScaleMax, x_axis.TransformForward, x_axis.TransformData), Ty(y_axis.PixelMin, y_axis.Range.Min, y_axis.Range.Max, y_axis.ScaleToPixel, y_axis.ScaleMin, y_axis.ScaleMax, y_axis.TransformForward, y_axis.TransformData) { } Transformer2(const ImPlotPlot& plot) : Transformer2(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]) { } Transformer2() : Transformer2(*GImPlot->CurrentPlot) { } template IMPLOT_INLINE ImVec2 operator()(const P& plt) const { ImVec2 out; out.x = Tx(plt.x); out.y = Ty(plt.y); return out; } template IMPLOT_INLINE ImVec2 operator()(T x, T y) const { ImVec2 out; out.x = Tx(x); out.y = Ty(y); return out; } Transformer1 Tx; Transformer1 Ty; }; //----------------------------------------------------------------------------- // [SECTION] Renderers //----------------------------------------------------------------------------- struct RendererBase { RendererBase(int prims, int idx_consumed, int vtx_consumed) : Prims(prims), IdxConsumed(idx_consumed), VtxConsumed(vtx_consumed) { } const int Prims; Transformer2 Transformer; const int IdxConsumed; const int VtxConsumed; }; template struct RendererLineStrip : RendererBase { RendererLineStrip(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineStripSkip : RendererBase { RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { if (!ImNan(P2.x) && !ImNan(P2.y)) P1 = P2; return false; } PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); if (!ImNan(P2.x) && !ImNan(P2.y)) P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineSegments1 : RendererBase { RendererLineSegments1(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count / 2, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { } void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P1 = this->Transformer(Getter(prim*2+0)); ImVec2 P2 = this->Transformer(Getter(prim*2+1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineSegments2 : RendererBase { RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) {} void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P1 = this->Transformer(Getter1(prim)); ImVec2 P2 = this->Transformer(Getter2(prim)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererBarsFillV : RendererBase { RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfWidth(width/2) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.x += HalfWidth; p2.x -= HalfWidth; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float width_px = ImAbs(P1.x-P2.x); if (width_px < 1.0f) { P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectFill(draw_list,PMin,PMax,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfWidth; mutable ImVec2 UV; }; template struct RendererBarsFillH : RendererBase { RendererBarsFillH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfHeight(height/2) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.y += HalfHeight; p2.y -= HalfHeight; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float height_px = ImAbs(P1.y-P2.y); if (height_px < 1.0f) { P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectFill(draw_list,PMin,PMax,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfHeight; mutable ImVec2 UV; }; template struct RendererBarsLineV : RendererBase { RendererBarsLineV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), Getter1(getter1), Getter2(getter2), Col(col), HalfWidth(width/2), Weight(weight) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.x += HalfWidth; p2.x -= HalfWidth; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float width_px = ImAbs(P1.x-P2.x); if (width_px < 1.0f) { P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfWidth; const float Weight; mutable ImVec2 UV; }; template struct RendererBarsLineH : RendererBase { RendererBarsLineH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), Getter1(getter1), Getter2(getter2), Col(col), HalfHeight(height/2), Weight(weight) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.y += HalfHeight; p2.y -= HalfHeight; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float height_px = ImAbs(P1.y-P2.y); if (height_px < 1.0f) { P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfHeight; const float Weight; mutable ImVec2 UV; }; template struct RendererStairsPre : RendererBase { RendererStairsPre(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 12, 8), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimRectFill(draw_list, ImVec2(P1.x - HalfWeight, P1.y), ImVec2(P1.x + HalfWeight, P2.y), Col, UV); PrimRectFill(draw_list, ImVec2(P1.x, P2.y + HalfWeight), ImVec2(P2.x, P2.y - HalfWeight), Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPost : RendererBase { RendererStairsPost(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 12, 8), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight) * 0.5f) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimRectFill(draw_list, ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, UV); PrimRectFill(draw_list, ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPreShaded : RendererBase { RendererStairsPreShaded(const _Getter& getter, ImU32 col) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col) { P1 = this->Transformer(Getter(0)); Y0 = this->Transformer(ImPlotPoint(0,0)).y; } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(Y0, P2.y)); ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(Y0, P2.y)); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { P1 = P2; return false; } PrimRectFill(draw_list, PMin, PMax, Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; float Y0; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPostShaded : RendererBase { RendererStairsPostShaded(const _Getter& getter, ImU32 col) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col) { P1 = this->Transformer(Getter(0)); Y0 = this->Transformer(ImPlotPoint(0,0)).y; } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(P1.y, Y0)); ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(P1.y, Y0)); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { P1 = P2; return false; } PrimRectFill(draw_list, PMin, PMax, Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; float Y0; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererShaded : RendererBase { RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) : RendererBase(ImMin(getter1.Count, getter2.Count) - 1, 6, 5), Getter1(getter1), Getter2(getter2), Col(col) { P11 = this->Transformer(Getter1(0)); P12 = this->Transformer(Getter2(0)); } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P21 = this->Transformer(Getter1(prim+1)); ImVec2 P22 = this->Transformer(Getter2(prim+1)); ImRect rect(ImMin(ImMin(ImMin(P11,P12),P21),P22), ImMax(ImMax(ImMax(P11,P12),P21),P22)); if (!cull_rect.Overlaps(rect)) { P11 = P21; P12 = P22; return false; } const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y); const ImVec2 intersection = intersect == 0 ? ImVec2(0,0) : Intersection(P11,P21,P12,P22); draw_list._VtxWritePtr[0].pos = P11; draw_list._VtxWritePtr[0].uv = UV; draw_list._VtxWritePtr[0].col = Col; draw_list._VtxWritePtr[1].pos = P21; draw_list._VtxWritePtr[1].uv = UV; draw_list._VtxWritePtr[1].col = Col; draw_list._VtxWritePtr[2].pos = intersection; draw_list._VtxWritePtr[2].uv = UV; draw_list._VtxWritePtr[2].col = Col; draw_list._VtxWritePtr[3].pos = P12; draw_list._VtxWritePtr[3].uv = UV; draw_list._VtxWritePtr[3].col = Col; draw_list._VtxWritePtr[4].pos = P22; draw_list._VtxWritePtr[4].uv = UV; draw_list._VtxWritePtr[4].col = Col; draw_list._VtxWritePtr += 5; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1 + intersect); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3 - intersect); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 5; P11 = P21; P12 = P22; return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; mutable ImVec2 P11; mutable ImVec2 P12; mutable ImVec2 UV; }; struct RectC { ImPlotPoint Pos; ImPlotPoint HalfSize; ImU32 Color; }; template struct RendererRectC : RendererBase { RendererRectC(const _Getter& getter) : RendererBase(getter.Count, 6, 4), Getter(getter) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { RectC rect = Getter(prim); ImVec2 P1 = this->Transformer(rect.Pos.x - rect.HalfSize.x , rect.Pos.y - rect.HalfSize.y); ImVec2 P2 = this->Transformer(rect.Pos.x + rect.HalfSize.x , rect.Pos.y + rect.HalfSize.y); if ((rect.Color & IM_COL32_A_MASK) == 0 || !cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimRectFill(draw_list,P1,P2,rect.Color,UV); return true; } const _Getter& Getter; mutable ImVec2 UV; }; //----------------------------------------------------------------------------- // [SECTION] RenderPrimitives //----------------------------------------------------------------------------- /// Renders primitive shapes in bulk as efficiently as possible. template void RenderPrimitivesEx(const _Renderer& renderer, ImDrawList& draw_list, const ImRect& cull_rect) { unsigned int prims = renderer.Prims; unsigned int prims_culled = 0; unsigned int idx = 0; renderer.Init(draw_list); while (prims) { // find how many can be reserved up to end of current draw command's limit unsigned int cnt = ImMin(prims, (MaxIdx::Value - draw_list._VtxCurrentIdx) / renderer.VtxConsumed); // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time if (cnt >= ImMin(64u, prims)) { if (prims_culled >= cnt) prims_culled -= cnt; // reuse previous reservation else { // add more elements to previous reservation draw_list.PrimReserve((cnt - prims_culled) * renderer.IdxConsumed, (cnt - prims_culled) * renderer.VtxConsumed); prims_culled = 0; } } else { if (prims_culled > 0) { draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); prims_culled = 0; } cnt = ImMin(prims, (MaxIdx::Value - 0/*draw_list._VtxCurrentIdx*/) / renderer.VtxConsumed); // reserve new draw command draw_list.PrimReserve(cnt * renderer.IdxConsumed, cnt * renderer.VtxConsumed); } prims -= cnt; for (unsigned int ie = idx + cnt; idx != ie; ++idx) { if (!renderer.Render(draw_list, cull_rect, idx)) prims_culled++; } } if (prims_culled > 0) draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); } template