├── .clang-format ├── .clang-tidy ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── cmake ├── FindImGui.cmake └── ImGui-SFMLConfig.cmake ├── examples ├── CMakeLists.txt ├── minimal │ ├── CMakeLists.txt │ └── main.cpp └── multiple_windows │ ├── CMakeLists.txt │ └── main.cpp ├── imconfig-SFML.h ├── imgui-SFML.cpp ├── imgui-SFML.h ├── imgui-SFML_export.h └── tests ├── CMakeLists.txt ├── FindCatch2.cmake └── imconfig-SFML.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # Project 3 | Standard: c++17 4 | ColumnLimit: 120 5 | 6 | # Indentation 7 | AccessModifierOffset: -4 8 | BitFieldColonSpacing: Both 9 | ContinuationIndentWidth: 4 10 | IndentCaseLabels: true 11 | IndentCaseBlocks: false 12 | IndentExternBlock: Indent 13 | IndentPPDirectives: None 14 | IndentRequires: true 15 | IndentWidth: 4 16 | IndentWrappedFunctionNames: true 17 | TabWidth: 4 18 | UseTab: Never 19 | 20 | # Alignment 21 | AlignAfterOpenBracket: Align 22 | AlignConsecutiveAssignments: Consecutive 23 | AlignConsecutiveBitFields: Consecutive 24 | AlignConsecutiveDeclarations: Consecutive 25 | AlignConsecutiveMacros: Consecutive 26 | AlignEscapedNewlines: Left 27 | AlignOperands: Align 28 | AlignTrailingComments: true 29 | 30 | # Allow 31 | AllowAllArgumentsOnNextLine: false 32 | AllowAllConstructorInitializersOnNextLine: false 33 | AllowAllParametersOfDeclarationOnNextLine: false 34 | AllowShortBlocksOnASingleLine: Never 35 | AllowShortCaseLabelsOnASingleLine: false 36 | AllowShortEnumsOnASingleLine: false 37 | AllowShortFunctionsOnASingleLine: None 38 | AllowShortIfStatementsOnASingleLine: Never 39 | AllowShortLoopsOnASingleLine: false 40 | AllowShortLambdasOnASingleLine: All 41 | 42 | # Break 43 | AlwaysBreakAfterReturnType: None 44 | AlwaysBreakBeforeMultilineStrings: true 45 | AlwaysBreakTemplateDeclarations: Yes 46 | BreakBeforeConceptDeclarations: true 47 | BreakBeforeTernaryOperators: true 48 | BreakConstructorInitializers: AfterColon 49 | BreakBeforeBinaryOperators: None 50 | BreakInheritanceList: AfterColon 51 | BreakStringLiterals: true 52 | 53 | # Initializers & arguments 54 | BinPackArguments: false 55 | BinPackParameters: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 57 | ConstructorInitializerIndentWidth: 0 58 | Cpp11BracedListStyle: true 59 | 60 | # Braces 61 | BreakBeforeBraces: Custom 62 | BraceWrapping: 63 | AfterCaseLabel: true 64 | AfterClass: true 65 | AfterControlStatement: Always 66 | AfterEnum: true 67 | AfterFunction: true 68 | AfterNamespace: true 69 | AfterStruct: true 70 | AfterUnion: true 71 | AfterExternBlock: true 72 | BeforeCatch: false 73 | BeforeElse: true 74 | BeforeLambdaBody: true 75 | BeforeWhile: false 76 | IndentBraces: false 77 | SplitEmptyFunction: true 78 | SplitEmptyRecord: true 79 | SplitEmptyNamespace: true 80 | 81 | # Namespaces 82 | CompactNamespaces: false 83 | FixNamespaceComments: true 84 | NamespaceIndentation: None 85 | 86 | # Derive 87 | DeriveLineEnding: false 88 | DerivePointerAlignment: false 89 | 90 | # Empty lines 91 | EmptyLineBeforeAccessModifier: Always 92 | KeepEmptyLinesAtTheStartOfBlocks: true 93 | MaxEmptyLinesToKeep: 2 94 | 95 | # Sorting 96 | SortIncludes: true 97 | SortUsingDeclarations: true 98 | 99 | # Penalties 100 | PenaltyBreakAssignment: 1000 101 | PenaltyBreakBeforeFirstCallParameter: 1000 102 | PenaltyBreakComment: 200 103 | PenaltyBreakFirstLessLess: 100 104 | PenaltyBreakString: 1 105 | PenaltyBreakTemplateDeclaration: 0 106 | PenaltyExcessCharacter: 5 107 | PenaltyIndentedWhitespace: 1 108 | PenaltyReturnTypeOnItsOwnLine: 500 109 | 110 | # Pointer alignment 111 | PointerAlignment: Left 112 | 113 | # Comments 114 | ReflowComments: false 115 | 116 | # Spaces 117 | SpaceAfterCStyleCast: false 118 | SpaceAfterLogicalNot: false 119 | SpaceAfterTemplateKeyword: true 120 | SpaceAroundPointerQualifiers: Default 121 | SpaceBeforeAssignmentOperators: true 122 | SpaceBeforeCaseColon: false 123 | SpaceBeforeCpp11BracedList: false 124 | SpaceBeforeCtorInitializerColon: true 125 | SpaceBeforeInheritanceColon: true 126 | SpaceBeforeParens: ControlStatements 127 | SpaceBeforeRangeBasedForLoopColon: true 128 | SpaceBeforeSquareBrackets: false 129 | SpaceInEmptyBlock: false 130 | SpaceInEmptyParentheses: false 131 | SpacesBeforeTrailingComments: 1 132 | SpacesInAngles: false 133 | SpacesInCStyleCastParentheses: false 134 | SpacesInConditionalStatement: false 135 | SpacesInContainerLiterals: false 136 | SpacesInParentheses: false 137 | SpacesInSquareBrackets: false 138 | 139 | # Line endings 140 | UseCRLF: false 141 | 142 | # Qualifiers (const, volatile, static, etc) 143 | QualifierAlignment: Custom 144 | QualifierOrder: ['static', 'inline', 'constexpr', 'const', 'volatile', 'type'] 145 | 146 | --- 147 | Language: ObjC 148 | IndentWidth: 4 149 | BreakBeforeBraces: Custom 150 | BraceWrapping: 151 | AfterObjCDeclaration: true 152 | 153 | ObjCBinPackProtocolList: Never 154 | ObjCBlockIndentWidth: 4 155 | ObjCBreakBeforeNestedBlockParam: true 156 | ObjCSpaceAfterProperty: true 157 | ObjCSpaceBeforeProtocolList: false 158 | 159 | ... 160 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | -*, 3 | bugprone-*, 4 | clang-analyzer-*, 5 | concurrency-*, 6 | cppcoreguidelines-*, 7 | misc-*, 8 | modernize-*, 9 | performance-*, 10 | portability-*, 11 | readability-*, 12 | -bugprone-branch-clone, 13 | -bugprone-easily-swappable-parameters, 14 | -bugprone-multi-level-implicit-pointer-conversion, 15 | -bugprone-unchecked-optional-access, 16 | -cppcoreguidelines-avoid-c-arrays, 17 | -cppcoreguidelines-avoid-magic-numbers, 18 | -cppcoreguidelines-avoid-non-const-global-variables, 19 | -cppcoreguidelines-owning-memory, 20 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 21 | -cppcoreguidelines-pro-bounds-constant-array-index, 22 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 23 | -cppcoreguidelines-pro-type-cstyle-cast, 24 | -cppcoreguidelines-pro-type-union-access, 25 | -cppcoreguidelines-special-member-functions, 26 | -misc-include-cleaner, 27 | -misc-non-private-member-variables-in-classes, 28 | -modernize-avoid-c-arrays, 29 | -modernize-use-trailing-return-type, 30 | -performance-no-int-to-ptr, 31 | -readability-braces-around-statements, 32 | -readability-function-cognitive-complexity, 33 | -readability-identifier-length, 34 | -readability-implicit-bool-conversion, 35 | -readability-magic-numbers, 36 | -readability-uppercase-literal-suffix, 37 | CheckOptions: 38 | - { key: readability-identifier-naming.ClassCase, value: CamelCase } 39 | - { key: readability-identifier-naming.NamespaceCase, value: CamelCase } 40 | - { key: readability-identifier-naming.MemberCase, value: camelBack } 41 | - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } 42 | HeaderFilterRegex: '(./imgui-SFML*)' 43 | WarningsAsErrors: '*' 44 | UseColor: true 45 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: '15 9 * * 2' 9 | 10 | concurrency: 11 | group: environment-${{github.ref}} 12 | cancel-in-progress: true 13 | 14 | defaults: 15 | run: 16 | shell: bash 17 | 18 | jobs: 19 | build: 20 | name: ${{matrix.platform.name}} ${{matrix.config.name}} ${{matrix.type.name}} ImGui ${{matrix.imgui.version}} 21 | runs-on: ${{matrix.platform.os}} 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | platform: 27 | - { name: Windows MSVC, os: windows-2022 } 28 | - { name: Windows ClangCL, os: windows-2022, flags: -T ClangCL } 29 | - { name: Windows Clang, os: windows-2022, flags: -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ } 30 | - { name: Linux GCC, os: ubuntu-22.04, flags: -GNinja } 31 | - { name: Linux Clang, os: ubuntu-22.04, flags: -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ } 32 | - { name: macOS Clang, os: macos-14, flags: -GNinja } 33 | config: 34 | - { name: Shared, flags: -DBUILD_SHARED_LIBS=ON -DIMGUI_SFML_BUILD_TESTING=OFF } 35 | - { name: Static, flags: -DBUILD_SHARED_LIBS=OFF } 36 | type: 37 | - { name: Release } 38 | - { name: Debug } 39 | imgui: 40 | - { version: 1.91.1, flags: -DIMGUI_SFML_DISABLE_OBSOLETE_FUNCTIONS=ON } 41 | - { version: 1.91.6 } 42 | 43 | steps: 44 | - name: Get CMake and Ninja 45 | uses: lukka/get-cmake@latest 46 | with: 47 | cmakeVersion: ${{ runner.os == 'Windows' && '3.24' || '3.22' }} 48 | ninjaVersion: latest 49 | 50 | - name: Install Linux Dependencies 51 | if: runner.os == 'Linux' 52 | run: | 53 | sudo apt update 54 | sudo apt install llvm xorg-dev libxrandr-dev libxcursor-dev libudev-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev 55 | 56 | - name: Checkout ImGui 57 | uses: actions/checkout@v4 58 | with: 59 | repository: ocornut/imgui 60 | path: imgui 61 | ref: v${{matrix.imgui.version}} 62 | 63 | - name: Checkout SFML 64 | uses: actions/checkout@v4 65 | with: 66 | repository: SFML/SFML 67 | path: sfml 68 | ref: 3.0.0 69 | 70 | - name: Configure SFML 71 | run: | 72 | cmake -S sfml -B sfml/build \ 73 | -DCMAKE_INSTALL_PREFIX=sfml/install \ 74 | -DCMAKE_BUILD_TYPE=${{matrix.type.name}} \ 75 | -DSFML_BUILD_AUDIO=OFF \ 76 | -DSFML_BUILD_NETWORK=OFF \ 77 | ${{matrix.platform.flags}} \ 78 | ${{matrix.config.flags}} 79 | 80 | - name: Build SFML 81 | run: cmake --build sfml/build --config ${{matrix.type.name}} --target install 82 | 83 | - name: Checkout ImGui-SFML 84 | uses: actions/checkout@v4 85 | with: 86 | path: imgui-sfml 87 | 88 | - name: Configure ImGui-SFML 89 | run: | 90 | cmake -S imgui-sfml -B imgui-sfml/build \ 91 | --preset dev \ 92 | -DCMAKE_BUILD_TYPE=${{matrix.type.name}} \ 93 | -DCMAKE_VERBOSE_MAKEFILE=ON \ 94 | -DIMGUI_DIR=$GITHUB_WORKSPACE/imgui \ 95 | -DSFML_ROOT=$GITHUB_WORKSPACE/sfml/install \ 96 | ${{matrix.platform.flags}} \ 97 | ${{matrix.config.flags}} \ 98 | ${{matrix.imgui.flags}} 99 | 100 | - name: Build ImGui-SFML 101 | run: cmake --build imgui-sfml/build --config ${{matrix.type.name}} --target install 102 | 103 | - name: Run clang-tidy 104 | if: matrix.platform.name == 'Linux Clang' && matrix.config.name == 'Static' && matrix.type.name == 'Debug' 105 | run: cmake --build imgui-sfml/build --target tidy 106 | 107 | - name: Test ImGui-SFML 108 | run: ctest --test-dir imgui-sfml/build --config ${{matrix.type.name}} --output-on-failure 109 | 110 | clang-format: 111 | runs-on: ubuntu-22.04 112 | 113 | steps: 114 | - uses: actions/checkout@v4 115 | - uses: DoozyX/clang-format-lint-action@v0.16.2 116 | with: 117 | source: '.' 118 | extensions: 'h,cpp' 119 | clangFormatVersion: 16 120 | style: file 121 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | CMakeUserPresets.json 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(imgui_sfml VERSION 3.0.0 LANGUAGES CXX) 3 | 4 | option(IMGUI_SFML_FIND_SFML "Use find_package to find SFML" ON) 5 | option(IMGUI_SFML_ENABLE_WARNINGS "Enable compiler warnings" OFF) 6 | option(IMGUI_SFML_DISABLE_OBSOLETE_FUNCTIONS "Disable obsolete ImGui functions" OFF) 7 | 8 | # If you want to use your own user config when compiling ImGui, please set the following variables 9 | # For example, if you have your config in /path/to/dir/with/config/myconfig.h, set the variables as follows: 10 | # 11 | # IMGUI_SFML_USE_DEFAULT_CONFIG = OFF 12 | # IMGUI_SFML_CONFIG_DIR = /path/to/dir/with/config 13 | # IMGUI_SFML_CONFIG_NAME = "myconfig.h" 14 | # 15 | # If you set IMGUI_SFML_CONFIG_INSTALL_DIR, ImGui-SFML won't install your custom config, because 16 | # you might want to do it yourself 17 | option(IMGUI_SFML_USE_DEFAULT_CONFIG "Use default imconfig-SFML.h" ON) 18 | set(IMGUI_SFML_CONFIG_DIR ${PROJECT_SOURCE_DIR} CACHE PATH "Path to a directory containing user ImGui config") 19 | set(IMGUI_SFML_CONFIG_NAME imconfig-SFML.h CACHE STRING "Name of a custom user ImGui config header") 20 | set(IMGUI_SFML_CONFIG_INSTALL_DIR "" CACHE PATH "Path where user's config header will be installed") 21 | 22 | # For FindImGui.cmake 23 | list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 24 | 25 | if(IMGUI_SFML_FIND_SFML) 26 | if(NOT BUILD_SHARED_LIBS) 27 | set(SFML_STATIC_LIBRARIES ON) 28 | endif() 29 | find_package(SFML 3 REQUIRED COMPONENTS Graphics) 30 | endif() 31 | 32 | # ImGui does not provide native support for CMakeLists, workaround for now to have 33 | # users specify IMGUI_DIR. Waiting for this PR to get merged... 34 | # https://github.com/ocornut/imgui/pull/1713 35 | if(NOT IMGUI_DIR) 36 | set(IMGUI_DIR "" CACHE PATH "imgui top-level directory") 37 | message(FATAL_ERROR "ImGui directory not found. Set IMGUI_DIR to imgui's top-level path (containing 'imgui.h' and other files).\n") 38 | endif() 39 | 40 | # This uses FindImGui.cmake provided in ImGui-SFML repo for now 41 | find_package(ImGui 1.91.1 REQUIRED) 42 | 43 | # These headers will be installed alongside ImGui-SFML 44 | set(IMGUI_PUBLIC_HEADERS 45 | ${IMGUI_INCLUDE_DIR}/imconfig.h 46 | ${IMGUI_INCLUDE_DIR}/imgui.h 47 | ${IMGUI_INCLUDE_DIR}/imgui_internal.h # not actually public, but users might need it 48 | ${IMGUI_INCLUDE_DIR}/imstb_rectpack.h 49 | ${IMGUI_INCLUDE_DIR}/imstb_textedit.h 50 | ${IMGUI_INCLUDE_DIR}/imstb_truetype.h 51 | ${IMGUI_INCLUDE_DIR}/misc/cpp/imgui_stdlib.h 52 | ) 53 | 54 | # CMake 3.11 and later prefer to choose GLVND, but we choose legacy OpenGL just because it's safer 55 | # (unless the OpenGL_GL_PREFERENCE was explicitly set) 56 | # See CMP0072 for more details (cmake --help-policy CMP0072) 57 | if((NOT ${CMAKE_VERSION} VERSION_LESS 3.11) AND (NOT OpenGL_GL_PREFERENCE)) 58 | set(OpenGL_GL_PREFERENCE "LEGACY") 59 | endif() 60 | 61 | find_package(OpenGL REQUIRED) 62 | 63 | include(GNUInstallDirs) 64 | 65 | # Define ImGui-SFML 66 | add_library(ImGui-SFML imgui-SFML.cpp ${IMGUI_SOURCES}) 67 | add_library(ImGui-SFML::ImGui-SFML ALIAS ImGui-SFML) 68 | target_include_directories(ImGui-SFML PUBLIC 69 | $ 70 | $ 71 | $ 72 | ) 73 | target_link_libraries(ImGui-SFML PUBLIC SFML::Graphics OpenGL::GL) 74 | if(WIN32 AND MINGW) 75 | target_link_libraries(ImGui-SFML PUBLIC imm32) 76 | endif() 77 | if(BUILD_SHARED_LIBS) 78 | target_compile_definitions(ImGui-SFML PRIVATE IMGUI_SFML_SHARED_LIB) 79 | set_target_properties(ImGui-SFML PROPERTIES DEFINE_SYMBOL "IMGUI_SFML_EXPORTS") 80 | set_target_properties(ImGui-SFML PROPERTIES DEBUG_POSTFIX "_d") 81 | endif() 82 | if(IMGUI_SFML_DISABLE_OBSOLETE_FUNCTIONS) 83 | target_compile_definitions(ImGui-SFML PUBLIC IMGUI_DISABLE_OBSOLETE_FUNCTIONS) 84 | endif() 85 | 86 | # Add compiler warnings 87 | if(IMGUI_SFML_ENABLE_WARNINGS) 88 | if(MSVC) 89 | set(IMGUI_SFML_WARNINGS /WX /W4 /permissive-) 90 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") 91 | set(IMGUI_SFML_WARNINGS -Werror -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wsign-conversion) 92 | endif() 93 | endif() 94 | foreach(WARNING ${IMGUI_SFML_WARNINGS}) 95 | set_property(SOURCE imgui-SFML.cpp APPEND_STRING PROPERTY COMPILE_FLAGS " ${WARNING}") 96 | endforeach() 97 | 98 | # Specify ImGui user config 99 | if(NOT IMGUI_SFML_USE_DEFAULT_CONFIG) 100 | if(IMGUI_SFML_CONFIG_DIR) 101 | target_include_directories(ImGui-SFML PUBLIC $) 102 | 103 | if(IMGUI_SFML_CONFIG_INSTALL_DIR) 104 | target_include_directories(ImGui-SFML PUBLIC $) 105 | endif() 106 | else() 107 | message(FATAL_ERROR "IMGUI_SFML_CONFIG_DIR should be set if IMGUI_SFML_USE_DEFAULT_CONFIG is OFF") 108 | endif() 109 | endif() 110 | target_compile_definitions(ImGui-SFML PUBLIC IMGUI_USER_CONFIG="${IMGUI_SFML_CONFIG_NAME}") 111 | 112 | # Collect public headers 113 | set(IMGUI_SFML_PUBLIC_HEADERS 114 | ${PROJECT_SOURCE_DIR}/imgui-SFML.h 115 | ${PROJECT_SOURCE_DIR}/imgui-SFML_export.h 116 | ${IMGUI_PUBLIC_HEADERS} 117 | ) 118 | if(IMGUI_SFML_USE_DEFAULT_CONFIG OR (NOT DEFINED "${IMGUI_SFML_CONFIG_INSTALL_DIR}")) 119 | list(APPEND IMGUI_SFML_PUBLIC_HEADERS "${IMGUI_SFML_CONFIG_DIR}/${IMGUI_SFML_CONFIG_NAME}") 120 | # If user set IMGUI_SFML_CONFIG_INSTALL_DIR, it means that they'll install file themselves 121 | endif() 122 | set_target_properties(ImGui-SFML PROPERTIES PUBLIC_HEADER "${IMGUI_SFML_PUBLIC_HEADERS}") 123 | 124 | # Installation rules 125 | install(TARGETS ImGui-SFML 126 | EXPORT ImGui-SFML 127 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 128 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 129 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 130 | PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 131 | ) 132 | 133 | install(EXPORT ImGui-SFML 134 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ImGui-SFML 135 | NAMESPACE ImGui-SFML:: 136 | ) 137 | 138 | install(FILES ${PROJECT_SOURCE_DIR}/cmake/ImGui-SFMLConfig.cmake 139 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ImGui-SFML 140 | ) 141 | 142 | # Stop configuration if being consumed by a higher level project 143 | if(NOT PROJECT_IS_TOP_LEVEL) 144 | return() 145 | endif() 146 | 147 | option(IMGUI_SFML_BUILD_EXAMPLES "Build ImGui-SFML examples" OFF) 148 | if(IMGUI_SFML_BUILD_EXAMPLES) 149 | add_subdirectory(examples) 150 | endif() 151 | 152 | option(IMGUI_SFML_BUILD_TESTING "Build ImGui-SFML tests" OFF) 153 | if(IMGUI_SFML_BUILD_TESTING) 154 | enable_testing() 155 | add_subdirectory(tests) 156 | endif() 157 | 158 | add_custom_target(tidy 159 | COMMAND run-clang-tidy -quiet -p ${CMAKE_BINARY_DIR} *.cpp examples/**/*.cpp tests/*.cpp 160 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 161 | ) 162 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets":[ 4 | { 5 | "name": "dev", 6 | "binaryDir": "build", 7 | "installDir": "build/install", 8 | "cacheVariables": { 9 | "CMAKE_CXX_EXTENSIONS": "OFF", 10 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", 11 | "IMGUI_SFML_BUILD_EXAMPLES": "ON", 12 | "IMGUI_SFML_BUILD_TESTING": "ON", 13 | "IMGUI_SFML_ENABLE_WARNINGS": "ON" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Elias Daler 4 | Copyright (c) 2014-2016 Omar Cornut and ImGui contributors 5 | Copyright (c) 2014 Mischa Aster Alff 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ImGui-SFML 2 | ========== 3 | 4 | Library which allows you to use [Dear ImGui](https://github.com/ocornut/imgui) with [SFML](https://github.com/SFML/SFML) 5 | 6 | ![screenshot](https://i2.wp.com/i.imgur.com/iQibpSk.gif) 7 | 8 | Based on [this repository](https://github.com/Mischa-Alff/imgui-backends) with big improvements and changes. 9 | 10 | State of Development 11 | ----- 12 | 13 | Development is focused on version 3 in the `master` branch. 14 | No more features are planned for the 2.x release series. 15 | 16 | Dependencies 17 | ----- 18 | 19 | * [SFML](https://github.com/SFML/SFML) >= 3.0.0 20 | * [Dear ImGui](https://github.com/ocornut/imgui) >= 1.91.1 21 | 22 | Contributing 23 | ----- 24 | 25 | * The code is written in C++17 (SFML 3 uses C++17, Dear ImGui has started using C++11 since 2022) 26 | * The code should be formatted via [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) using `.clang-format` provided in the root of this repository 27 | 28 | How-to 29 | ---- 30 | 31 | - [**CMake tutorial which also shows how to use ImGui-SFML with FetchContent**](https://edw.is/using-cmake/) 32 | - [**Example project which sets up ImGui-SFML with FetchContent**](https://github.com/eliasdaler/cmake-fetchcontent-tutorial-code) 33 | - [**Detailed tutorial on Elias Daler's blog**](https://edw.is/using-imgui-with-sfml-pt1) 34 | - [**Using ImGui with modern C++ and STL**](https://edw.is/using-imgui-with-sfml-pt2/) 35 | - [**Thread on SFML forums**](https://en.sfml-dev.org/forums/index.php?topic=20137.0). Feel free to ask your questions there. 36 | 37 | Building and integrating into your CMake project 38 | --- 39 | It's highly recommended to use FetchContent or git submodules to get SFML and Dear ImGui into your build. 40 | 41 | See [this file](https://github.com/eliasdaler/imgui-sfml-fetchcontent/blob/master/dependencies/CMakeLists.txt) - if you do something similar, you can then just link to ImGui-SFML as simply as: 42 | 43 | ```cmake 44 | target_link_libraries(game 45 | PUBLIC 46 | ImGui-SFML::ImGui-SFML 47 | ) 48 | ``` 49 | 50 | Integrating into your project manually 51 | --- 52 | - Download [ImGui](https://github.com/ocornut/imgui) 53 | - Add Dear ImGui folder to your include directories 54 | - Add `imgui.cpp`, `imgui_widgets.cpp`, `imgui_draw.cpp` and `imgui_tables.cpp` to your build/project 55 | - Copy the contents of `imconfig-SFML.h` to your `imconfig.h` file. (to be able to cast `ImVec2` to `sf::Vector2f` and vice versa) 56 | - Add a folder which contains `imgui-SFML.h` to your include directories 57 | - Add `imgui-SFML.cpp` to your build/project 58 | - Link OpenGL if you get linking errors 59 | 60 | Other ways to add to your project 61 | --- 62 | Not recommended, as they're not maintained officially. Tend to lag behind and stay on older versions. 63 | 64 | - [Conan](https://github.com/bincrafters/community/tree/main/recipes/imgui-sfml) 65 | - [vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/imgui-sfml) 66 | - [Bazel](https://github.com/zpervan/ImguiSFMLBazel) 67 | 68 | Using ImGui-SFML in your code 69 | --- 70 | 71 | - Call `ImGui::SFML::Init` and pass your `sf::Window` + `sf::RenderTarget` or `sf::RenderWindow` there. You can create your font atlas and pass the pointer in Init too, otherwise the default internal font atlas will be created for you. Do this for each window you want to draw ImGui on. 72 | - For each iteration of a game loop: 73 | - Poll and process events: 74 | 75 | ```cpp 76 | while (const auto event = window.pollEvent()) { 77 | ImGui::SFML::ProcessEvent(window, *event); 78 | ... 79 | } 80 | ``` 81 | 82 | - Call `ImGui::SFML::Update(window, deltaTime)` where `deltaTime` is `sf::Time`. You can also pass `mousePosition` and `displaySize` yourself instead of passing the window. 83 | - Call ImGui functions (`ImGui::Begin()`, `ImGui::Button()`, etc.) 84 | - Call `ImGui::EndFrame` after the last `ImGui::End` in your update function, if you update more than once before rendering. (e.g. fixed delta game loops) 85 | - Call `ImGui::SFML::Render(window)` 86 | 87 | - Call `ImGui::SFML::Shutdown()` **after** `window.close()` has been called 88 | - Use `ImGui::SFML::Shutdown(window)` overload if you have multiple windows. After it's called one of the current windows will become a current "global" window. Call `SetCurrentWindow` to explicitly set which window will be used as default. 89 | 90 | **If you only draw ImGui widgets without any SFML stuff, then you'll might need to call window.resetGLStates() before rendering anything. You only need to do it once.** 91 | 92 | Example code 93 | ---- 94 | 95 | See example file [here](https://github.com/SFML/imgui-sfml/blob/master/examples/minimal/main.cpp) 96 | 97 | ```cpp 98 | #include "imgui.h" 99 | #include "imgui-SFML.h" 100 | 101 | #include 102 | #include 103 | #include 104 | #include 105 | 106 | int main() { 107 | sf::RenderWindow window(sf::VideoMode({640, 480}), "ImGui + SFML = <3"); 108 | window.setFramerateLimit(60); 109 | ImGui::SFML::Init(window); 110 | 111 | sf::CircleShape shape(100.f); 112 | shape.setFillColor(sf::Color::Green); 113 | 114 | sf::Clock deltaClock; 115 | while (window.isOpen()) { 116 | while (const auto event = window.pollEvent()) { 117 | ImGui::SFML::ProcessEvent(window, *event); 118 | 119 | if (event->is()) { 120 | window.close(); 121 | } 122 | } 123 | 124 | ImGui::SFML::Update(window, deltaClock.restart()); 125 | 126 | ImGui::ShowDemoWindow(); 127 | 128 | ImGui::Begin("Hello, world!"); 129 | ImGui::Button("Look at this pretty button"); 130 | ImGui::End(); 131 | 132 | window.clear(); 133 | window.draw(shape); 134 | ImGui::SFML::Render(window); 135 | window.display(); 136 | } 137 | 138 | ImGui::SFML::Shutdown(); 139 | } 140 | ``` 141 | 142 | Fonts how-to 143 | --- 144 | 145 | Default font is loaded if you don't pass `false` in `ImGui::SFML::Init`. Call `ImGui::SFML::Init(window, false);` if you don't want default font to be loaded. 146 | 147 | * Load your fonts like this: 148 | 149 | ```cpp 150 | IO.Fonts->Clear(); // clear fonts if you loaded some before (even if only default one was loaded) 151 | // IO.Fonts->AddFontDefault(); // this will load default font as well 152 | IO.Fonts->AddFontFromFileTTF("font1.ttf", 8.f); 153 | IO.Fonts->AddFontFromFileTTF("font2.ttf", 12.f); 154 | 155 | ImGui::SFML::UpdateFontTexture(); // important call: updates font texture 156 | ``` 157 | 158 | * And use them like this: 159 | 160 | ```cpp 161 | ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); 162 | ImGui::Button("Look at this pretty button"); 163 | ImGui::PopFont(); 164 | 165 | ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); 166 | ImGui::TextUnformatted("IT WORKS!"); 167 | ImGui::PopFont(); 168 | ``` 169 | 170 | The first loaded font is treated as the default one and doesn't need to be pushed with `ImGui::PushFont`. 171 | 172 | Multiple windows 173 | ---------------- 174 | 175 | See `examples/multiple_windows` to see how you can create multiple SFML and run different ImGui contexts in them. 176 | 177 | - Don't forget to run `ImGui::SFML::Init(const sf::Window&)` for each window you create. Same goes for `ImGui::SFML::Shutdown(const sf::Window&)` 178 | - Instead of calling `ImGui::SFML::ProcessEvent(sf::Event&)`, you need to call `ImGui::SFML::ProcessEvent(const sf::Window&, const sf::Event&)` overload for each window you create 179 | - Call `ImGui::SFML::SetCurrentWindow` before calling any `ImGui` functions (e.g. `ImGui::Begin`, `ImGui::Button` etc.) 180 | - Either call `ImGui::Render(sf::RenderWindow&)` overload for each window or manually do this: 181 | ```cpp 182 | SetCurrentWindow(window); 183 | ... // your custom rendering 184 | ImGui::Render(); 185 | 186 | SetCurrentWindow(window2); 187 | ... // your custom rendering 188 | ImGui::Render(); 189 | ``` 190 | - When closing everything: don't forget to close all windows using SFML's `sf::Window::Close` and then call `ImGui::SFML::Shutdown` to remote all ImGui-SFML window contexts and other data. 191 | 192 | SFML related ImGui overloads / new widgets 193 | --- 194 | 195 | There are some useful overloads implemented for SFML objects (see `imgui-SFML.h` for other overloads): 196 | 197 | ```cpp 198 | ImGui::Image(const sf::Sprite& sprite); 199 | ImGui::Image(const sf::Texture& texture); 200 | ImGui::Image(const sf::RenderTexture& texture); 201 | 202 | ImGui::ImageButton(const sf::Sprite& sprite); 203 | ImGui::ImageButton(const sf::Texture& texture); 204 | ImGui::ImageButton(const sf::RenderTexture& texture); 205 | ``` 206 | 207 | A note about sf::RenderTexture 208 | --- 209 | 210 | `sf::RenderTexture`'s texture is stored with pixels flipped upside down. To display it properly when drawing `ImGui::Image` or `ImGui::ImageButton`, use overloads for `sf::RenderTexture`: 211 | 212 | ```cpp 213 | sf::RenderTexture texture; 214 | sf::Sprite sprite(texture.getTexture()); 215 | ImGui::Image(texture); // OK 216 | ImGui::Image(sprite); // NOT OK 217 | ImGui::Image(texture.getTexture()); // NOT OK 218 | ``` 219 | 220 | If you want to draw only a part of `sf::RenderTexture` and you're trying to use `sf::Sprite` the texture will be displayed upside-down. To prevent this, you can do this: 221 | 222 | ```cpp 223 | // make a normal sf::Texture from sf::RenderTexture's flipped texture 224 | sf::Texture texture(renderTexture.getTexture()); 225 | 226 | sf::Sprite sprite(texture); 227 | ImGui::Image(sprite); // the texture is displayed properly 228 | ``` 229 | 230 | For more notes see [this issue](https://github.com/SFML/imgui-sfml/issues/35). 231 | 232 | Mouse cursors 233 | --- 234 | You can change your cursors in ImGui like this: 235 | 236 | ```cpp 237 | ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); 238 | ``` 239 | 240 | By default, your system cursor will change and will be rendered by your system. If you want SFML to draw your cursor with default ImGui cursors (the system cursor will be hidden), do this: 241 | 242 | ```cpp 243 | ImGuiIO& io = ImGui::GetIO(); 244 | io.MouseDrawCursor = true; 245 | ``` 246 | 247 | Keyboard/Gamepad navigation 248 | --- 249 | Starting with [ImGui 1.60](https://github.com/ocornut/imgui/releases/tag/v1.60), there's a feature to control ImGui with keyboard and gamepad. To use keyboard navigation, you just need to do this: 250 | 251 | ```cpp 252 | ImGuiIO& io = ImGui::GetIO(); 253 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; 254 | ``` 255 | 256 | Gamepad navigation requires more work, unless you have XInput gamepad, in which case the mapping is automatically set for you. But you can still set it up for your own gamepad easily, just take a look how it's done for the default mapping [here](https://github.com/SFML/imgui-sfml/blob/navigation/imgui-SFML.cpp#L697). And then you need to do this: 257 | 258 | ```cpp 259 | ImGuiIO& io = ImGui::GetIO(); 260 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; 261 | ``` 262 | By default, the first active joystick is used for navigation, but you can set joystick id explicitly like this: 263 | ```cpp 264 | ImGui::SFML::SetActiveJoystickId(5); 265 | ``` 266 | 267 | High DPI screens 268 | ---- 269 | 270 | As SFML is not currently DPI aware, your GUI may show at the incorrect scale. This is particularly noticeable on systems with "Retina" / "4K" / "UHD" displays. 271 | 272 | To work around this on macOS, you can create an app bundle (as opposed to just the exe) then modify the info.plist so that "High Resolution Capable" is set to "NO". 273 | 274 | Another option to help ameliorate this, at least for getting started and for the common ImGui use-case of "developer/debug/building UI", is to explore `FontGlobalScale`: 275 | 276 | ```cpp 277 | ImGuiIO& io = ImGui::GetIO(); 278 | io.FontGlobalScale = 2.0; // or any other value hardcoded or loaded from your config logic 279 | ``` 280 | 281 | License 282 | --- 283 | 284 | This library is licensed under the MIT License, see LICENSE for more information. 285 | -------------------------------------------------------------------------------- /cmake/FindImGui.cmake: -------------------------------------------------------------------------------- 1 | # FindImGui.cmake 2 | # 3 | # Finds ImGui library 4 | # 5 | # This will define the following variables 6 | # IMGUI_FOUND 7 | # IMGUI_INCLUDE_DIRS 8 | # IMGUI_SOURCES 9 | # IMGUI_VERSION 10 | 11 | list(APPEND IMGUI_SEARCH_PATH 12 | ${IMGUI_DIR} 13 | ) 14 | 15 | find_path(IMGUI_INCLUDE_DIR 16 | NAMES imgui.h 17 | PATHS ${IMGUI_SEARCH_PATH} 18 | NO_DEFAULT_PATH 19 | ) 20 | 21 | if(NOT IMGUI_INCLUDE_DIR) 22 | message(FATAL_ERROR "IMGUI imgui.cpp not found. Set IMGUI_DIR to imgui's top-level path (containing \"imgui.cpp\" and \"imgui.h\" files).\n") 23 | endif() 24 | 25 | set(IMGUI_SOURCES 26 | ${IMGUI_INCLUDE_DIR}/imgui.cpp 27 | ${IMGUI_INCLUDE_DIR}/imgui_demo.cpp 28 | ${IMGUI_INCLUDE_DIR}/imgui_draw.cpp 29 | ${IMGUI_INCLUDE_DIR}/imgui_tables.cpp 30 | ${IMGUI_INCLUDE_DIR}/imgui_widgets.cpp 31 | ${IMGUI_INCLUDE_DIR}/misc/cpp/imgui_stdlib.cpp 32 | ) 33 | 34 | # Extract version from header 35 | file( 36 | STRINGS 37 | ${IMGUI_INCLUDE_DIR}/imgui.h 38 | IMGUI_VERSION 39 | REGEX "#define IMGUI_VERSION " 40 | ) 41 | 42 | if(NOT IMGUI_VERSION) 43 | message(SEND_ERROR "Can't find version number in ${IMGUI_INCLUDE_DIR}/imgui.h.") 44 | endif() 45 | # Transform '#define IMGUI_VERSION "X.Y"' into 'X.Y' 46 | string(REGEX REPLACE ".*\"(.*)\".*" "\\1" IMGUI_VERSION "${IMGUI_VERSION}") 47 | 48 | # Check required version 49 | if(${IMGUI_VERSION} VERSION_LESS ${ImGui_FIND_VERSION}) 50 | set(IMGUI_FOUND OFF) 51 | message(FATAL_ERROR "ImGui at with at least v${ImGui_FIND_VERSION} was requested, but only v${IMGUI_VERSION} was found") 52 | else() 53 | set(IMGUI_FOUND ON) 54 | message(STATUS "Found ImGui v${IMGUI_VERSION} in ${IMGUI_INCLUDE_DIR}") 55 | endif() 56 | -------------------------------------------------------------------------------- /cmake/ImGui-SFMLConfig.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | find_dependency(OpenGL) 3 | find_dependency(SFML COMPONENTS Graphics) 4 | 5 | include(${CMAKE_CURRENT_LIST_DIR}/ImGui-SFML.cmake) 6 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(minimal) 2 | add_subdirectory(multiple_windows) 3 | -------------------------------------------------------------------------------- /examples/minimal/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(imgui_sfml_example_minimal main.cpp) 2 | target_link_libraries(imgui_sfml_example_minimal PRIVATE ImGui-SFML::ImGui-SFML) 3 | target_compile_options(imgui_sfml_example_minimal PRIVATE ${IMGUI_SFML_WARNINGS}) 4 | -------------------------------------------------------------------------------- /examples/minimal/main.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui.h" // necessary for ImGui::*, imgui-SFML.h doesn't include imgui.h 2 | 3 | #include "imgui-SFML.h" // for ImGui::SFML::* functions and SFML-specific overloads 4 | 5 | #include 6 | 7 | int main() 8 | { 9 | sf::RenderWindow window(sf::VideoMode({640, 480}), "ImGui + SFML = <3"); 10 | window.setFramerateLimit(60); 11 | if (!ImGui::SFML::Init(window)) 12 | return -1; 13 | 14 | sf::CircleShape shape(100.f); 15 | shape.setFillColor(sf::Color::Green); 16 | 17 | sf::Clock deltaClock; 18 | while (window.isOpen()) 19 | { 20 | while (const auto event = window.pollEvent()) 21 | { 22 | ImGui::SFML::ProcessEvent(window, *event); 23 | 24 | if (event->is()) 25 | { 26 | window.close(); 27 | } 28 | } 29 | 30 | ImGui::SFML::Update(window, deltaClock.restart()); 31 | 32 | ImGui::ShowDemoWindow(); 33 | 34 | ImGui::Begin("Hello, world!"); 35 | ImGui::Button("Look at this pretty button"); 36 | ImGui::End(); 37 | 38 | window.clear(); 39 | window.draw(shape); 40 | ImGui::SFML::Render(window); 41 | window.display(); 42 | } 43 | 44 | ImGui::SFML::Shutdown(); 45 | } 46 | -------------------------------------------------------------------------------- /examples/multiple_windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(imgui_sfml_example_multiple_windows main.cpp) 2 | target_link_libraries(imgui_sfml_example_multiple_windows PRIVATE ImGui-SFML::ImGui-SFML) 3 | target_compile_options(imgui_sfml_example_multiple_windows PRIVATE ${IMGUI_SFML_WARNINGS}) 4 | -------------------------------------------------------------------------------- /examples/multiple_windows/main.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui.h" // necessary for ImGui::*, imgui-SFML.h doesn't include imgui.h 2 | 3 | #include "imgui-SFML.h" // for ImGui::SFML::* functions and SFML-specific overloads 4 | 5 | #include 6 | 7 | int main() 8 | { 9 | sf::RenderWindow window(sf::VideoMode({1280, 720}), "ImGui + SFML = <3"); 10 | window.setFramerateLimit(60); 11 | if (!ImGui::SFML::Init(window)) 12 | return -1; 13 | 14 | sf::RenderWindow childWindow(sf::VideoMode({640, 480}), "ImGui-SFML Child window"); 15 | childWindow.setFramerateLimit(60); 16 | if (!ImGui::SFML::Init(childWindow)) 17 | return -1; 18 | 19 | sf::Clock deltaClock; 20 | while (window.isOpen()) 21 | { 22 | // Main window event processing 23 | while (const auto event = window.pollEvent()) 24 | { 25 | ImGui::SFML::ProcessEvent(window, *event); 26 | if (event->is()) 27 | { 28 | if (childWindow.isOpen()) 29 | { 30 | childWindow.close(); 31 | } 32 | window.close(); 33 | ImGui::SFML::Shutdown(); // will shutdown all windows 34 | return 0; // return here so that we don't call Update/Render 35 | } 36 | } 37 | 38 | // Child window event processing 39 | if (childWindow.isOpen()) 40 | { 41 | while (const auto event = childWindow.pollEvent()) 42 | { 43 | ImGui::SFML::ProcessEvent(childWindow, *event); 44 | if (event->is()) 45 | { 46 | childWindow.close(); 47 | ImGui::SFML::Shutdown(childWindow); 48 | } 49 | } 50 | } 51 | 52 | // Update 53 | const sf::Time dt = deltaClock.restart(); 54 | ImGui::SFML::Update(window, dt); 55 | if (childWindow.isOpen()) 56 | { 57 | ImGui::SFML::Update(childWindow, dt); 58 | } 59 | 60 | // Add ImGui widgets in the first window 61 | ImGui::SFML::SetCurrentWindow(window); 62 | ImGui::Begin("Hello, world!"); 63 | ImGui::Button("Look at this pretty button"); 64 | ImGui::End(); 65 | ImGui::ShowDemoWindow(); 66 | // Add ImGui widgets in the child window 67 | if (childWindow.isOpen()) 68 | { 69 | ImGui::SFML::SetCurrentWindow(childWindow); 70 | ImGui::Begin("Works in a second window!"); 71 | ImGui::Button("Example button"); 72 | ImGui::End(); 73 | } 74 | 75 | // Main window drawing 76 | sf::CircleShape shape(100.f); 77 | shape.setFillColor(sf::Color::Green); 78 | 79 | window.clear(); 80 | window.draw(shape); 81 | ImGui::SFML::Render(window); 82 | window.display(); 83 | 84 | // Child window drawing 85 | if (childWindow.isOpen()) 86 | { 87 | sf::CircleShape shape2(50.f); 88 | shape2.setFillColor(sf::Color::Red); 89 | 90 | childWindow.clear(); 91 | childWindow.draw(shape2); 92 | ImGui::SFML::Render(childWindow); 93 | childWindow.display(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /imconfig-SFML.h: -------------------------------------------------------------------------------- 1 | // Add this to your imconfig.h 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "imgui-SFML_export.h" 9 | 10 | #define IM_VEC2_CLASS_EXTRA \ 11 | template \ 12 | ImVec2(const sf::Vector2& v) \ 13 | { \ 14 | x = static_cast(v.x); \ 15 | y = static_cast(v.y); \ 16 | } \ 17 | \ 18 | template \ 19 | operator sf::Vector2() const \ 20 | { \ 21 | return sf::Vector2(static_cast(x), static_cast(y)); \ 22 | } 23 | 24 | #define IM_VEC4_CLASS_EXTRA \ 25 | ImVec4(const sf::Color& c) : x(c.r / 255.f), y(c.g / 255.f), z(c.b / 255.f), w(c.a / 255.f) \ 26 | { \ 27 | } \ 28 | operator sf::Color() const \ 29 | { \ 30 | return sf::Color(static_cast(x * 255.f), \ 31 | static_cast(y * 255.f), \ 32 | static_cast(z * 255.f), \ 33 | static_cast(w * 255.f)); \ 34 | } 35 | -------------------------------------------------------------------------------- /imgui-SFML.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui-SFML.h" 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #if defined(__APPLE__) 27 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 28 | #endif 29 | 30 | #ifdef ANDROID 31 | #ifdef USE_JNI 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | int openKeyboardIME() 38 | { 39 | ANativeActivity* activity = sf::getNativeActivity(); 40 | JavaVM* vm = activity->vm; 41 | JNIEnv* env = activity->env; 42 | JavaVMAttachArgs attachargs; 43 | attachargs.version = JNI_VERSION_1_6; 44 | attachargs.name = "NativeThread"; 45 | attachargs.group = nullptr; 46 | jint res = vm->AttachCurrentThread(&env, &attachargs); 47 | if (res == JNI_ERR) 48 | return EXIT_FAILURE; 49 | 50 | jclass natact = env->FindClass("android/app/NativeActivity"); 51 | jclass context = env->FindClass("android/content/Context"); 52 | 53 | jfieldID fid = env->GetStaticFieldID(context, "INPUT_METHOD_SERVICE", "Ljava/lang/String;"); 54 | jobject svcstr = env->GetStaticObjectField(context, fid); 55 | 56 | jmethodID getss = env->GetMethodID(natact, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); 57 | jobject imm_obj = env->CallObjectMethod(activity->clazz, getss, svcstr); 58 | 59 | jclass imm_cls = env->GetObjectClass(imm_obj); 60 | jmethodID toggleSoftInput = env->GetMethodID(imm_cls, "toggleSoftInput", "(II)V"); 61 | 62 | env->CallVoidMethod(imm_obj, toggleSoftInput, 2, 0); 63 | 64 | env->DeleteLocalRef(imm_obj); 65 | env->DeleteLocalRef(imm_cls); 66 | env->DeleteLocalRef(svcstr); 67 | env->DeleteLocalRef(context); 68 | env->DeleteLocalRef(natact); 69 | 70 | vm->DetachCurrentThread(); 71 | 72 | return EXIT_SUCCESS; 73 | } 74 | 75 | int closeKeyboardIME() 76 | { 77 | ANativeActivity* activity = sf::getNativeActivity(); 78 | JavaVM* vm = activity->vm; 79 | JNIEnv* env = activity->env; 80 | JavaVMAttachArgs attachargs; 81 | attachargs.version = JNI_VERSION_1_6; 82 | attachargs.name = "NativeThread"; 83 | attachargs.group = nullptr; 84 | jint res = vm->AttachCurrentThread(&env, &attachargs); 85 | if (res == JNI_ERR) 86 | return EXIT_FAILURE; 87 | 88 | jclass natact = env->FindClass("android/app/NativeActivity"); 89 | jclass context = env->FindClass("android/content/Context"); 90 | 91 | jfieldID fid = env->GetStaticFieldID(context, "INPUT_METHOD_SERVICE", "Ljava/lang/String;"); 92 | jobject svcstr = env->GetStaticObjectField(context, fid); 93 | 94 | jmethodID getss = env->GetMethodID(natact, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); 95 | jobject imm_obj = env->CallObjectMethod(activity->clazz, getss, svcstr); 96 | 97 | jclass imm_cls = env->GetObjectClass(imm_obj); 98 | jmethodID toggleSoftInput = env->GetMethodID(imm_cls, "toggleSoftInput", "(II)V"); 99 | 100 | env->CallVoidMethod(imm_obj, toggleSoftInput, 1, 0); 101 | 102 | env->DeleteLocalRef(imm_obj); 103 | env->DeleteLocalRef(imm_cls); 104 | env->DeleteLocalRef(svcstr); 105 | env->DeleteLocalRef(context); 106 | env->DeleteLocalRef(natact); 107 | 108 | vm->DetachCurrentThread(); 109 | 110 | return EXIT_SUCCESS; 111 | } 112 | 113 | #endif 114 | #endif 115 | 116 | static_assert(sizeof(GLuint) <= sizeof(ImTextureID), "ImTextureID is not large enough to fit GLuint."); 117 | 118 | namespace 119 | { 120 | // various helper functions 121 | [[nodiscard]] ImColor toImColor(sf::Color c) 122 | { 123 | return {int{c.r}, int{c.g}, int{c.b}, int{c.a}}; 124 | } 125 | 126 | [[nodiscard]] ImVec2 toImVec2(const sf::Vector2f& v) 127 | { 128 | return {v.x, v.y}; 129 | } 130 | 131 | [[nodiscard]] sf::Vector2f toSfVector2f(const ImVec2& v) 132 | { 133 | return {v.x, v.y}; 134 | } 135 | 136 | [[nodiscard]] ImVec2 getTopLeftAbsolute(const sf::FloatRect& rect) 137 | { 138 | return toImVec2(toSfVector2f(ImGui::GetCursorScreenPos()) + rect.position); 139 | } 140 | 141 | [[nodiscard]] ImVec2 getDownRightAbsolute(const sf::FloatRect& rect) 142 | { 143 | return toImVec2(toSfVector2f(ImGui::GetCursorScreenPos()) + rect.position + rect.size); 144 | } 145 | 146 | [[nodiscard]] ImTextureID convertGLTextureHandleToImTextureID(GLuint glTextureHandle) 147 | { 148 | ImTextureID textureID{}; 149 | std::memcpy(&textureID, &glTextureHandle, sizeof(GLuint)); 150 | return textureID; 151 | } 152 | 153 | [[nodiscard]] GLuint convertImTextureIDToGLTextureHandle(ImTextureID textureID) 154 | { 155 | GLuint glTextureHandle = 0; 156 | std::memcpy(&glTextureHandle, &textureID, sizeof(GLuint)); 157 | return glTextureHandle; 158 | } 159 | 160 | struct SpriteTextureData 161 | { 162 | ImVec2 uv0; 163 | ImVec2 uv1; 164 | ImTextureID textureID{}; 165 | }; 166 | 167 | [[nodiscard]] SpriteTextureData getSpriteTextureData(const sf::Sprite& sprite) 168 | { 169 | const sf::Texture& texture(sprite.getTexture()); 170 | const sf::Vector2f textureSize(texture.getSize()); 171 | const sf::FloatRect textureRect(sprite.getTextureRect()); 172 | 173 | return {toImVec2(textureRect.position.componentWiseDiv(textureSize)), 174 | toImVec2((textureRect.position + textureRect.size).componentWiseDiv(textureSize)), 175 | convertGLTextureHandleToImTextureID(texture.getNativeHandle())}; 176 | } 177 | 178 | void RenderDrawLists(ImDrawData* draw_data); // rendering callback function prototype 179 | 180 | // Default mapping is XInput gamepad mapping 181 | void initDefaultJoystickMapping(); 182 | 183 | // data 184 | constexpr unsigned int NULL_JOYSTICK_ID = sf::Joystick::Count; 185 | 186 | // Returns first id of connected joystick 187 | [[nodiscard]] unsigned int getConnectedJoystickId() 188 | { 189 | for (unsigned int i = 0; i < sf::Joystick::Count; ++i) 190 | { 191 | if (sf::Joystick::isConnected(i)) 192 | return i; 193 | } 194 | 195 | return NULL_JOYSTICK_ID; 196 | } 197 | 198 | void updateJoystickButtonState(ImGuiIO& io); 199 | void updateJoystickDPadState(ImGuiIO& io); 200 | void updateJoystickAxisState(ImGuiIO& io); 201 | 202 | // clipboard functions 203 | void setClipboardText(ImGuiContext* /*ctx*/, const char* text) 204 | { 205 | sf::Clipboard::setString(sf::String::fromUtf8(text, text + std::strlen(text))); 206 | } 207 | 208 | [[nodiscard]] const char* getClipboardText(ImGuiContext* /*ctx*/) 209 | { 210 | static std::string s_clipboardText; 211 | 212 | auto tmp = sf::Clipboard::getString().toUtf8(); 213 | s_clipboardText.assign(tmp.begin(), tmp.end()); 214 | return s_clipboardText.c_str(); 215 | } 216 | 217 | // mouse cursors 218 | void updateMouseCursor(sf::Window& window); 219 | 220 | // Key mappings 221 | [[nodiscard]] ImGuiKey keycodeToImGuiKey(sf::Keyboard::Key code); 222 | [[nodiscard]] ImGuiKey keycodeToImGuiMod(sf::Keyboard::Key code); 223 | 224 | struct StickInfo 225 | { 226 | sf::Joystick::Axis xAxis{sf::Joystick::Axis::X}; 227 | sf::Joystick::Axis yAxis{sf::Joystick::Axis::Y}; 228 | 229 | bool xInverted{false}; 230 | bool yInverted{false}; 231 | 232 | float threshold{15}; 233 | }; 234 | 235 | struct TriggerInfo 236 | { 237 | sf::Joystick::Axis axis{sf::Joystick::Axis::Z}; 238 | float threshold{0}; 239 | }; 240 | 241 | struct WindowContext 242 | { 243 | const sf::Window* window; 244 | ImGuiContext* imContext{ImGui::CreateContext()}; 245 | 246 | std::optional fontTexture; // internal font atlas which is used if user doesn't set 247 | // a custom sf::Texture. 248 | 249 | bool windowHasFocus; 250 | bool mouseMoved{false}; 251 | bool mousePressed[3] = {false}; 252 | ImGuiMouseCursor lastCursor{ImGuiMouseCursor_COUNT}; 253 | 254 | bool touchDown[3] = {false}; 255 | sf::Vector2i touchPos; 256 | 257 | unsigned int joystickId{getConnectedJoystickId()}; 258 | ImGuiKey joystickMapping[sf::Joystick::ButtonCount] = {ImGuiKey_None}; 259 | StickInfo dPadInfo; 260 | StickInfo lStickInfo; 261 | StickInfo rStickInfo; 262 | TriggerInfo lTriggerInfo; 263 | TriggerInfo rTriggerInfo; 264 | 265 | std::optional mouseCursors[ImGuiMouseCursor_COUNT]; 266 | 267 | #ifdef ANDROID 268 | #ifdef USE_JNI 269 | bool wantTextInput{false}; 270 | #endif 271 | #endif 272 | 273 | WindowContext(const sf::Window* w) : window(w), windowHasFocus(window->hasFocus()) 274 | { 275 | } 276 | ~WindowContext() 277 | { 278 | ImGui::DestroyContext(imContext); 279 | } 280 | 281 | WindowContext(const WindowContext&) = delete; // non construction-copyable 282 | WindowContext& operator=(const WindowContext&) = delete; // non copyable 283 | }; 284 | 285 | std::vector> s_windowContexts; 286 | WindowContext* s_currWindowCtx = nullptr; 287 | 288 | } // end of anonymous namespace 289 | 290 | namespace ImGui 291 | { 292 | namespace SFML 293 | { 294 | bool Init(sf::RenderWindow& window, bool loadDefaultFont) 295 | { 296 | return Init(window, window, loadDefaultFont); 297 | } 298 | 299 | bool Init(sf::Window& window, sf::RenderTarget& target, bool loadDefaultFont) 300 | { 301 | return Init(window, sf::Vector2f(target.getSize()), loadDefaultFont); 302 | } 303 | 304 | bool Init(sf::Window& window, const sf::Vector2f& displaySize, bool loadDefaultFont) 305 | { 306 | s_currWindowCtx = s_windowContexts.emplace_back(std::make_unique(&window)).get(); 307 | ImGui::SetCurrentContext(s_currWindowCtx->imContext); 308 | 309 | ImGuiIO& io = ImGui::GetIO(); 310 | ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); 311 | 312 | // tell ImGui which features we support 313 | io.BackendFlags |= ImGuiBackendFlags_HasGamepad; 314 | io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; 315 | io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; 316 | io.BackendPlatformName = "imgui_impl_sfml"; 317 | 318 | s_currWindowCtx->joystickId = getConnectedJoystickId(); 319 | 320 | initDefaultJoystickMapping(); 321 | 322 | // init rendering 323 | io.DisplaySize = toImVec2(displaySize); 324 | 325 | // clipboard 326 | platform_io.Platform_SetClipboardTextFn = setClipboardText; 327 | platform_io.Platform_GetClipboardTextFn = getClipboardText; 328 | 329 | // load mouse cursors 330 | const auto loadMouseCursor = [](ImGuiMouseCursor imguiCursorType, sf::Cursor::Type sfmlCursorType) 331 | { s_currWindowCtx->mouseCursors[imguiCursorType] = sf::Cursor::createFromSystem(sfmlCursorType); }; 332 | loadMouseCursor(ImGuiMouseCursor_Arrow, sf::Cursor::Type::Arrow); 333 | loadMouseCursor(ImGuiMouseCursor_TextInput, sf::Cursor::Type::Text); 334 | loadMouseCursor(ImGuiMouseCursor_ResizeAll, sf::Cursor::Type::SizeAll); 335 | loadMouseCursor(ImGuiMouseCursor_ResizeNS, sf::Cursor::Type::SizeVertical); 336 | loadMouseCursor(ImGuiMouseCursor_ResizeEW, sf::Cursor::Type::SizeHorizontal); 337 | loadMouseCursor(ImGuiMouseCursor_ResizeNESW, sf::Cursor::Type::SizeBottomLeftTopRight); 338 | loadMouseCursor(ImGuiMouseCursor_ResizeNWSE, sf::Cursor::Type::SizeTopLeftBottomRight); 339 | loadMouseCursor(ImGuiMouseCursor_Hand, sf::Cursor::Type::Hand); 340 | 341 | if (loadDefaultFont) 342 | { 343 | // this will load default font automatically 344 | // No need to call AddDefaultFont 345 | return UpdateFontTexture(); 346 | } 347 | 348 | return true; 349 | } 350 | 351 | void SetCurrentWindow(const sf::Window& window) 352 | { 353 | auto found = std::find_if(s_windowContexts.begin(), 354 | s_windowContexts.end(), 355 | [&](std::unique_ptr& ctx) 356 | { return ctx->window->getNativeHandle() == window.getNativeHandle(); }); 357 | assert(found != s_windowContexts.end() && 358 | "Failed to find the window. Forgot to call ImGui::SFML::Init for the window?"); 359 | s_currWindowCtx = found->get(); 360 | ImGui::SetCurrentContext(s_currWindowCtx->imContext); 361 | } 362 | 363 | void ProcessEvent(const sf::Window& window, const sf::Event& event) 364 | { 365 | SetCurrentWindow(window); 366 | assert(s_currWindowCtx && "No current window is set - forgot to call ImGui::SFML::Init?"); 367 | ImGuiIO& io = ImGui::GetIO(); 368 | 369 | if (s_currWindowCtx->windowHasFocus) 370 | { 371 | if (const auto* resized = event.getIf()) 372 | { 373 | io.DisplaySize = toImVec2(sf::Vector2f(resized->size)); 374 | } 375 | else if (const auto* mouseMoved = event.getIf()) 376 | { 377 | const auto [x, y] = sf::Vector2f(mouseMoved->position); 378 | io.AddMousePosEvent(x, y); 379 | s_currWindowCtx->mouseMoved = true; 380 | } 381 | else if (const auto* mouseButtonPressed = event.getIf()) 382 | { 383 | const int button = static_cast(mouseButtonPressed->button); 384 | if (button >= 0 && button < 3) 385 | { 386 | s_currWindowCtx->mousePressed[static_cast(mouseButtonPressed->button)] = true; 387 | io.AddMouseButtonEvent(button, true); 388 | } 389 | } 390 | else if (const auto* mouseButtonReleased = event.getIf()) 391 | { 392 | const int button = static_cast(mouseButtonReleased->button); 393 | if (button >= 0 && button < 3) 394 | io.AddMouseButtonEvent(button, false); 395 | } 396 | else if (const auto* touchBegan = event.getIf()) 397 | { 398 | s_currWindowCtx->mouseMoved = false; 399 | const unsigned int button = touchBegan->finger; 400 | if (button < 3) 401 | s_currWindowCtx->touchDown[touchBegan->finger] = true; 402 | } 403 | else if (event.is()) 404 | { 405 | s_currWindowCtx->mouseMoved = false; 406 | } 407 | else if (const auto* mouseWheelScrolled = event.getIf()) 408 | { 409 | if (mouseWheelScrolled->wheel == sf::Mouse::Wheel::Vertical || 410 | (mouseWheelScrolled->wheel == sf::Mouse::Wheel::Horizontal && io.KeyShift)) 411 | { 412 | io.AddMouseWheelEvent(0, mouseWheelScrolled->delta); 413 | } 414 | else if (mouseWheelScrolled->wheel == sf::Mouse::Wheel::Horizontal) 415 | { 416 | io.AddMouseWheelEvent(mouseWheelScrolled->delta, 0); 417 | } 418 | } 419 | const auto handleKeyChanged = [&io](const auto& keyChanged, bool down) 420 | { 421 | const ImGuiKey mod = keycodeToImGuiMod(keyChanged.code); 422 | // The modifier booleans are not reliable when it's the modifier 423 | // itself that's being pressed. Detect these presses directly. 424 | if (mod != ImGuiKey_None) 425 | { 426 | io.AddKeyEvent(mod, down); 427 | } 428 | else 429 | { 430 | io.AddKeyEvent(ImGuiMod_Ctrl, keyChanged.control); 431 | io.AddKeyEvent(ImGuiMod_Shift, keyChanged.shift); 432 | io.AddKeyEvent(ImGuiMod_Alt, keyChanged.alt); 433 | io.AddKeyEvent(ImGuiMod_Super, keyChanged.system); 434 | } 435 | 436 | const ImGuiKey key = keycodeToImGuiKey(keyChanged.code); 437 | io.AddKeyEvent(key, down); 438 | io.SetKeyEventNativeData(key, static_cast(keyChanged.code), -1); 439 | }; 440 | if (const auto* keyPressed = event.getIf()) 441 | { 442 | handleKeyChanged(*keyPressed, true); 443 | } 444 | else if (const auto* keyReleased = event.getIf()) 445 | { 446 | handleKeyChanged(*keyReleased, false); 447 | } 448 | else if (const auto* textEntered = event.getIf()) 449 | { 450 | // Don't handle the event for unprintable characters 451 | if (textEntered->unicode >= ' ' && textEntered->unicode != 127) 452 | io.AddInputCharacter(textEntered->unicode); 453 | } 454 | else if (const auto* joystickConnected = event.getIf()) 455 | { 456 | if (s_currWindowCtx->joystickId == NULL_JOYSTICK_ID) 457 | s_currWindowCtx->joystickId = joystickConnected->joystickId; 458 | } 459 | else if (const auto* joystickDisconnected = event.getIf()) 460 | { 461 | if (s_currWindowCtx->joystickId == joystickDisconnected->joystickId) 462 | // used gamepad was disconnected 463 | s_currWindowCtx->joystickId = getConnectedJoystickId(); 464 | } 465 | } 466 | 467 | if (event.is()) 468 | { 469 | io.AddFocusEvent(false); 470 | s_currWindowCtx->windowHasFocus = false; 471 | } 472 | if (event.is()) 473 | { 474 | io.AddFocusEvent(true); 475 | s_currWindowCtx->windowHasFocus = true; 476 | } 477 | } 478 | 479 | void Update(sf::RenderWindow& window, sf::Time dt) 480 | { 481 | Update(window, window, dt); 482 | } 483 | 484 | void Update(sf::Window& window, sf::RenderTarget& target, sf::Time dt) 485 | { 486 | SetCurrentWindow(window); 487 | assert(s_currWindowCtx); 488 | 489 | // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor 490 | const ImGuiMouseCursor mouse_cursor = ImGui::GetIO().MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); 491 | if (s_currWindowCtx->lastCursor != mouse_cursor) 492 | { 493 | s_currWindowCtx->lastCursor = mouse_cursor; 494 | updateMouseCursor(window); 495 | } 496 | 497 | if (!s_currWindowCtx->mouseMoved) 498 | { 499 | if (sf::Touch::isDown(0)) 500 | s_currWindowCtx->touchPos = sf::Touch::getPosition(0, window); 501 | 502 | Update(s_currWindowCtx->touchPos, sf::Vector2f(target.getSize()), dt); 503 | } 504 | else 505 | { 506 | Update(sf::Mouse::getPosition(window), sf::Vector2f(target.getSize()), dt); 507 | } 508 | } 509 | 510 | void Update(const sf::Vector2i& mousePos, const sf::Vector2f& displaySize, sf::Time dt) 511 | { 512 | assert(s_currWindowCtx && "No current window is set - forgot to call ImGui::SFML::Init?"); 513 | 514 | ImGuiIO& io = ImGui::GetIO(); 515 | io.DisplaySize = toImVec2(displaySize); 516 | io.DeltaTime = dt.asSeconds(); 517 | 518 | if (s_currWindowCtx->windowHasFocus) 519 | { 520 | if (io.WantSetMousePos) 521 | { 522 | sf::Mouse::setPosition(sf::Vector2i(toSfVector2f(io.MousePos))); 523 | } 524 | else 525 | { 526 | io.MousePos = toImVec2(sf::Vector2f(mousePos)); 527 | } 528 | for (unsigned int i = 0; i < 3; i++) 529 | { 530 | io.MouseDown[i] = s_currWindowCtx->touchDown[i] || sf::Touch::isDown(i) || 531 | s_currWindowCtx->mousePressed[i] || sf::Mouse::isButtonPressed((sf::Mouse::Button)i); 532 | s_currWindowCtx->mousePressed[i] = false; 533 | s_currWindowCtx->touchDown[i] = false; 534 | } 535 | } 536 | 537 | #ifdef ANDROID 538 | #ifdef USE_JNI 539 | if (io.WantTextInput && !s_currWindowCtx->wantTextInput) 540 | { 541 | openKeyboardIME(); 542 | s_currWindowCtx->wantTextInput = true; 543 | } 544 | 545 | if (!io.WantTextInput && s_currWindowCtx->wantTextInput) 546 | { 547 | closeKeyboardIME(); 548 | s_currWindowCtx->wantTextInput = false; 549 | } 550 | #endif 551 | #endif 552 | 553 | assert(io.Fonts->Fonts.Size > 0); // You forgot to create and set up font 554 | // atlas (see createFontTexture) 555 | 556 | // gamepad navigation 557 | if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) && s_currWindowCtx->joystickId != NULL_JOYSTICK_ID) 558 | { 559 | updateJoystickButtonState(io); 560 | updateJoystickDPadState(io); 561 | updateJoystickAxisState(io); 562 | } 563 | 564 | ImGui::NewFrame(); 565 | } 566 | 567 | void Render(sf::RenderWindow& window) 568 | { 569 | SetCurrentWindow(window); 570 | Render(static_cast(window)); 571 | } 572 | 573 | void Render(sf::RenderTarget& target) 574 | { 575 | target.resetGLStates(); 576 | target.pushGLStates(); 577 | ImGui::Render(); 578 | RenderDrawLists(ImGui::GetDrawData()); 579 | target.popGLStates(); 580 | } 581 | 582 | void Render() 583 | { 584 | ImGui::Render(); 585 | RenderDrawLists(ImGui::GetDrawData()); 586 | } 587 | 588 | void Shutdown(const sf::Window& window) 589 | { 590 | const bool needReplacement = (s_currWindowCtx->window->getNativeHandle() == window.getNativeHandle()); 591 | 592 | // remove window's context 593 | auto found = std::find_if(s_windowContexts.begin(), 594 | s_windowContexts.end(), 595 | [&](std::unique_ptr& ctx) 596 | { return ctx->window->getNativeHandle() == window.getNativeHandle(); }); 597 | assert(found != s_windowContexts.end() && 598 | "Window wasn't inited properly: forgot to call ImGui::SFML::Init(window)?"); 599 | s_windowContexts.erase(found); // s_currWindowCtx can become invalid here! 600 | 601 | // set current context to some window for convenience if needed 602 | if (needReplacement) 603 | { 604 | auto it = s_windowContexts.begin(); 605 | if (it != s_windowContexts.end()) 606 | { 607 | // set to some other window 608 | s_currWindowCtx = it->get(); 609 | ImGui::SetCurrentContext(s_currWindowCtx->imContext); 610 | } 611 | else 612 | { 613 | // no alternatives... 614 | s_currWindowCtx = nullptr; 615 | ImGui::SetCurrentContext(nullptr); 616 | } 617 | } 618 | } 619 | 620 | void Shutdown() 621 | { 622 | s_currWindowCtx = nullptr; 623 | ImGui::SetCurrentContext(nullptr); 624 | 625 | s_windowContexts.clear(); 626 | } 627 | 628 | bool UpdateFontTexture() 629 | { 630 | assert(s_currWindowCtx); 631 | 632 | ImGuiIO& io = ImGui::GetIO(); 633 | unsigned char* pixels = nullptr; 634 | int width = 0; 635 | int height = 0; 636 | 637 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 638 | 639 | sf::Texture newTexture; 640 | if (!newTexture.resize(sf::Vector2u(sf::Vector2(width, height)))) 641 | { 642 | return false; 643 | } 644 | 645 | newTexture.update(pixels); 646 | 647 | ImTextureID texID = convertGLTextureHandleToImTextureID(newTexture.getNativeHandle()); 648 | io.Fonts->SetTexID(texID); 649 | 650 | s_currWindowCtx->fontTexture = std::move(newTexture); 651 | 652 | return true; 653 | } 654 | 655 | std::optional& GetFontTexture() 656 | { 657 | assert(s_currWindowCtx); 658 | return s_currWindowCtx->fontTexture; 659 | } 660 | 661 | void SetActiveJoystickId(unsigned int joystickId) 662 | { 663 | assert(s_currWindowCtx); 664 | assert(joystickId < sf::Joystick::Count); 665 | s_currWindowCtx->joystickId = joystickId; 666 | } 667 | 668 | void SetJoystickDPadThreshold(float threshold) 669 | { 670 | assert(s_currWindowCtx); 671 | assert(threshold >= 0.f && threshold <= 100.f); 672 | s_currWindowCtx->dPadInfo.threshold = threshold; 673 | } 674 | 675 | void SetJoystickLStickThreshold(float threshold) 676 | { 677 | assert(s_currWindowCtx); 678 | assert(threshold >= 0.f && threshold <= 100.f); 679 | s_currWindowCtx->lStickInfo.threshold = threshold; 680 | } 681 | 682 | void SetJoystickRStickThreshold(float threshold) 683 | { 684 | assert(s_currWindowCtx); 685 | assert(threshold >= 0.f && threshold <= 100.f); 686 | s_currWindowCtx->rStickInfo.threshold = threshold; 687 | } 688 | 689 | void SetJoystickLTriggerThreshold(float threshold) 690 | { 691 | assert(s_currWindowCtx); 692 | assert(threshold >= -100.f && threshold <= 100.f); 693 | s_currWindowCtx->lTriggerInfo.threshold = threshold; 694 | } 695 | 696 | void SetJoystickRTriggerThreshold(float threshold) 697 | { 698 | assert(s_currWindowCtx); 699 | assert(threshold >= -100.f && threshold <= 100.f); 700 | s_currWindowCtx->rTriggerInfo.threshold = threshold; 701 | } 702 | 703 | void SetJoystickMapping(int key, unsigned int joystickButton) 704 | { 705 | assert(s_currWindowCtx); 706 | assert(key >= ImGuiKey_NamedKey_BEGIN); 707 | assert(key < ImGuiKey_NamedKey_END); 708 | assert(joystickButton < sf::Joystick::ButtonCount); 709 | s_currWindowCtx->joystickMapping[joystickButton] = static_cast(key); 710 | } 711 | 712 | void SetDPadXAxis(sf::Joystick::Axis dPadXAxis, bool inverted) 713 | { 714 | assert(s_currWindowCtx); 715 | s_currWindowCtx->dPadInfo.xAxis = dPadXAxis; 716 | s_currWindowCtx->dPadInfo.xInverted = inverted; 717 | } 718 | 719 | void SetDPadYAxis(sf::Joystick::Axis dPadYAxis, bool inverted) 720 | { 721 | assert(s_currWindowCtx); 722 | s_currWindowCtx->dPadInfo.yAxis = dPadYAxis; 723 | s_currWindowCtx->dPadInfo.yInverted = inverted; 724 | } 725 | 726 | void SetLStickXAxis(sf::Joystick::Axis lStickXAxis, bool inverted) 727 | { 728 | assert(s_currWindowCtx); 729 | s_currWindowCtx->lStickInfo.xAxis = lStickXAxis; 730 | s_currWindowCtx->lStickInfo.xInverted = inverted; 731 | } 732 | 733 | void SetLStickYAxis(sf::Joystick::Axis lStickYAxis, bool inverted) 734 | { 735 | assert(s_currWindowCtx); 736 | s_currWindowCtx->lStickInfo.yAxis = lStickYAxis; 737 | s_currWindowCtx->lStickInfo.yInverted = inverted; 738 | } 739 | 740 | void SetRStickXAxis(sf::Joystick::Axis rStickXAxis, bool inverted) 741 | { 742 | assert(s_currWindowCtx); 743 | s_currWindowCtx->rStickInfo.xAxis = rStickXAxis; 744 | s_currWindowCtx->rStickInfo.xInverted = inverted; 745 | } 746 | 747 | void SetRStickYAxis(sf::Joystick::Axis rStickYAxis, bool inverted) 748 | { 749 | assert(s_currWindowCtx); 750 | s_currWindowCtx->rStickInfo.yAxis = rStickYAxis; 751 | s_currWindowCtx->rStickInfo.yInverted = inverted; 752 | } 753 | 754 | void SetLTriggerAxis(sf::Joystick::Axis lTriggerAxis) 755 | { 756 | assert(s_currWindowCtx); 757 | s_currWindowCtx->rTriggerInfo.axis = lTriggerAxis; 758 | } 759 | 760 | void SetRTriggerAxis(sf::Joystick::Axis rTriggerAxis) 761 | { 762 | assert(s_currWindowCtx); 763 | s_currWindowCtx->rTriggerInfo.axis = rTriggerAxis; 764 | } 765 | 766 | } // end of namespace SFML 767 | 768 | /////////////// Image Overloads for sf::Texture 769 | 770 | void Image(const sf::Texture& texture, const sf::Color& tintColor, const sf::Color& borderColor) 771 | { 772 | Image(texture, sf::Vector2f(texture.getSize()), tintColor, borderColor); 773 | } 774 | 775 | void Image(const sf::Texture& texture, const sf::Vector2f& size, const sf::Color& tintColor, const sf::Color& borderColor) 776 | { 777 | ImTextureID textureID = convertGLTextureHandleToImTextureID(texture.getNativeHandle()); 778 | 779 | ImGui::Image(textureID, toImVec2(size), ImVec2(0, 0), ImVec2(1, 1), toImColor(tintColor), toImColor(borderColor)); 780 | } 781 | 782 | /////////////// Image Overloads for sf::RenderTexture 783 | void Image(const sf::RenderTexture& texture, const sf::Color& tintColor, const sf::Color& borderColor) 784 | { 785 | Image(texture, sf::Vector2f(texture.getSize()), tintColor, borderColor); 786 | } 787 | 788 | void Image(const sf::RenderTexture& texture, const sf::Vector2f& size, const sf::Color& tintColor, const sf::Color& borderColor) 789 | { 790 | ImTextureID textureID = convertGLTextureHandleToImTextureID(texture.getTexture().getNativeHandle()); 791 | 792 | ImGui::Image(textureID, 793 | toImVec2(size), 794 | ImVec2(0, 1), 795 | ImVec2(1, 0), // flipped vertically, 796 | // because textures in 797 | // sf::RenderTexture are 798 | // stored this way 799 | toImColor(tintColor), 800 | toImColor(borderColor)); 801 | } 802 | 803 | /////////////// Image Overloads for sf::Sprite 804 | 805 | void Image(const sf::Sprite& sprite, const sf::Color& tintColor, const sf::Color& borderColor) 806 | { 807 | Image(sprite, sprite.getGlobalBounds().size, tintColor, borderColor); 808 | } 809 | 810 | void Image(const sf::Sprite& sprite, const sf::Vector2f& size, const sf::Color& tintColor, const sf::Color& borderColor) 811 | { 812 | auto [uv0, uv1, textureID] = getSpriteTextureData(sprite); 813 | ImGui::Image(textureID, toImVec2(size), uv0, uv1, toImColor(tintColor), toImColor(borderColor)); 814 | } 815 | 816 | /////////////// Image Button Overloads for sf::Texture 817 | 818 | bool ImageButton(const char* id, 819 | const sf::Texture& texture, 820 | const sf::Vector2f& size, 821 | const sf::Color& bgColor, 822 | const sf::Color& tintColor) 823 | { 824 | ImTextureID textureID = convertGLTextureHandleToImTextureID(texture.getNativeHandle()); 825 | 826 | return ImGui::ImageButton(id, textureID, toImVec2(size), ImVec2(0, 0), ImVec2(1, 1), toImColor(bgColor), toImColor(tintColor)); 827 | } 828 | 829 | /////////////// Image Button Overloads for sf::RenderTexture 830 | 831 | bool ImageButton(const char* id, 832 | const sf::RenderTexture& texture, 833 | const sf::Vector2f& size, 834 | const sf::Color& bgColor, 835 | const sf::Color& tintColor) 836 | { 837 | ImTextureID textureID = convertGLTextureHandleToImTextureID(texture.getTexture().getNativeHandle()); 838 | 839 | return ImGui::ImageButton(id, 840 | textureID, 841 | toImVec2(size), 842 | ImVec2(0, 1), 843 | ImVec2(1, 0), // flipped vertically, because textures in 844 | // sf::RenderTexture are stored this way 845 | toImColor(bgColor), 846 | toImColor(tintColor)); 847 | } 848 | 849 | /////////////// Image Button Overloads for sf::Sprite 850 | 851 | bool ImageButton(const char* id, const sf::Sprite& sprite, const sf::Vector2f& size, const sf::Color& bgColor, const sf::Color& tintColor) 852 | { 853 | auto [uv0, uv1, textureID] = getSpriteTextureData(sprite); 854 | return ImGui::ImageButton(id, textureID, toImVec2(size), uv0, uv1, toImColor(bgColor), toImColor(tintColor)); 855 | } 856 | 857 | /////////////// Draw_list Overloads 858 | 859 | void DrawLine(const sf::Vector2f& a, const sf::Vector2f& b, const sf::Color& color, float thickness) 860 | { 861 | ImDrawList* draw_list = ImGui::GetWindowDrawList(); 862 | const ImVec2 pos = ImGui::GetCursorScreenPos(); 863 | draw_list->AddLine(ImVec2(a.x + pos.x, a.y + pos.y), 864 | ImVec2(b.x + pos.x, b.y + pos.y), 865 | ColorConvertFloat4ToU32(toImColor(color)), 866 | thickness); 867 | } 868 | 869 | void DrawRect(const sf::FloatRect& rect, const sf::Color& color, float rounding, int rounding_corners, float thickness) 870 | { 871 | ImDrawList* draw_list = ImGui::GetWindowDrawList(); 872 | draw_list->AddRect(getTopLeftAbsolute(rect), 873 | getDownRightAbsolute(rect), 874 | ColorConvertFloat4ToU32(toImColor(color)), 875 | rounding, 876 | rounding_corners, 877 | thickness); 878 | } 879 | 880 | void DrawRectFilled(const sf::FloatRect& rect, const sf::Color& color, float rounding, int rounding_corners) 881 | { 882 | ImDrawList* draw_list = ImGui::GetWindowDrawList(); 883 | draw_list->AddRectFilled(getTopLeftAbsolute(rect), 884 | getDownRightAbsolute(rect), 885 | ColorConvertFloat4ToU32(toImColor(color)), 886 | rounding, 887 | rounding_corners); 888 | } 889 | 890 | } // end of namespace ImGui 891 | 892 | namespace 893 | { 894 | // copied from imgui/backends/imgui_impl_opengl2.cpp 895 | void SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height) 896 | { 897 | // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor 898 | // enabled, vertex/texcoord/color pointers, polygon fill. 899 | glEnable(GL_BLEND); 900 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 901 | // glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // 902 | // In order to composite our output buffer we need to preserve alpha 903 | glDisable(GL_CULL_FACE); 904 | glDisable(GL_DEPTH_TEST); 905 | glDisable(GL_STENCIL_TEST); 906 | glDisable(GL_LIGHTING); 907 | glDisable(GL_COLOR_MATERIAL); 908 | glEnable(GL_SCISSOR_TEST); 909 | glEnableClientState(GL_VERTEX_ARRAY); 910 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 911 | glEnableClientState(GL_COLOR_ARRAY); 912 | glDisableClientState(GL_NORMAL_ARRAY); 913 | glEnable(GL_TEXTURE_2D); 914 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 915 | glShadeModel(GL_SMOOTH); 916 | glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 917 | 918 | // Setup viewport, orthographic projection matrix 919 | // Our visible imgui space lies from draw_data->DisplayPos (top left) to 920 | // draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single 921 | // viewport apps. 922 | glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); 923 | glMatrixMode(GL_PROJECTION); 924 | glPushMatrix(); 925 | glLoadIdentity(); 926 | #ifdef GL_VERSION_ES_CL_1_1 927 | glOrthof(draw_data->DisplayPos.x, 928 | draw_data->DisplayPos.x + draw_data->DisplaySize.x, 929 | draw_data->DisplayPos.y + draw_data->DisplaySize.y, 930 | draw_data->DisplayPos.y, 931 | -1.0f, 932 | +1.0f); 933 | #else 934 | glOrtho(draw_data->DisplayPos.x, 935 | draw_data->DisplayPos.x + draw_data->DisplaySize.x, 936 | draw_data->DisplayPos.y + draw_data->DisplaySize.y, 937 | draw_data->DisplayPos.y, 938 | -1.0f, 939 | +1.0f); 940 | #endif 941 | glMatrixMode(GL_MODELVIEW); 942 | glPushMatrix(); 943 | glLoadIdentity(); 944 | } 945 | 946 | // Rendering callback 947 | void RenderDrawLists(ImDrawData* draw_data) 948 | { 949 | ImGui::GetDrawData(); 950 | if (draw_data->CmdListsCount == 0) 951 | { 952 | return; 953 | } 954 | 955 | const ImGuiIO& io = ImGui::GetIO(); 956 | assert(io.Fonts->TexID != (ImTextureID) nullptr); // You forgot to create and set font texture 957 | 958 | // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != 959 | // framebuffer coordinates) 960 | const int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); 961 | const int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); 962 | if (fb_width == 0 || fb_height == 0) 963 | return; 964 | draw_data->ScaleClipRects(io.DisplayFramebufferScale); 965 | 966 | // Backup GL state 967 | // Backup GL state 968 | GLint last_texture = 0; 969 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 970 | GLint last_polygon_mode[2]; 971 | glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); 972 | GLint last_viewport[4]; 973 | glGetIntegerv(GL_VIEWPORT, last_viewport); 974 | GLint last_scissor_box[4]; 975 | glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); 976 | GLint last_shade_model = 0; 977 | glGetIntegerv(GL_SHADE_MODEL, &last_shade_model); 978 | GLint last_tex_env_mode = 0; 979 | glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &last_tex_env_mode); 980 | 981 | #ifdef GL_VERSION_ES_CL_1_1 982 | GLint last_array_buffer; 983 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); 984 | GLint last_element_array_buffer; 985 | glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); 986 | #else 987 | glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); 988 | #endif 989 | 990 | // Setup desired GL state 991 | SetupRenderState(draw_data, fb_width, fb_height); 992 | 993 | // Will project scissor/clipping rectangles into framebuffer space 994 | const ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports 995 | const ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display 996 | // which are often (2,2) 997 | 998 | // Render command lists 999 | for (int n = 0; n < draw_data->CmdListsCount; n++) 1000 | { 1001 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 1002 | const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; 1003 | const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; 1004 | glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, pos))); 1005 | glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, uv))); 1006 | glColorPointer(4, 1007 | GL_UNSIGNED_BYTE, 1008 | sizeof(ImDrawVert), 1009 | (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, col))); 1010 | 1011 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 1012 | { 1013 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 1014 | if (pcmd->UserCallback) 1015 | { 1016 | // User callback, registered via ImDrawList::AddCallback() 1017 | // (ImDrawCallback_ResetRenderState is a special callback value used by the user to 1018 | // request the renderer to reset render state.) 1019 | if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) 1020 | SetupRenderState(draw_data, fb_width, fb_height); 1021 | else 1022 | pcmd->UserCallback(cmd_list, pcmd); 1023 | } 1024 | else 1025 | { 1026 | // Project scissor/clipping rectangles into framebuffer space 1027 | ImVec4 clip_rect; 1028 | clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; 1029 | clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; 1030 | clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; 1031 | clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; 1032 | 1033 | if (clip_rect.x < static_cast(fb_width) && clip_rect.y < static_cast(fb_height) && 1034 | clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) 1035 | { 1036 | // Apply scissor/clipping rectangle 1037 | glScissor((int)clip_rect.x, 1038 | (int)(static_cast(fb_height) - clip_rect.w), 1039 | (int)(clip_rect.z - clip_rect.x), 1040 | (int)(clip_rect.w - clip_rect.y)); 1041 | 1042 | // Bind texture, Draw 1043 | const GLuint textureHandle = convertImTextureIDToGLTextureHandle(pcmd->TextureId); 1044 | glBindTexture(GL_TEXTURE_2D, textureHandle); 1045 | glDrawElements(GL_TRIANGLES, 1046 | (GLsizei)pcmd->ElemCount, 1047 | sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, 1048 | idx_buffer + pcmd->IdxOffset); 1049 | } 1050 | } 1051 | } 1052 | } 1053 | 1054 | // Restore modified GL state 1055 | glDisableClientState(GL_COLOR_ARRAY); 1056 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); 1057 | glDisableClientState(GL_VERTEX_ARRAY); 1058 | glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); 1059 | glMatrixMode(GL_MODELVIEW); 1060 | glPopMatrix(); 1061 | glMatrixMode(GL_PROJECTION); 1062 | glPopMatrix(); 1063 | glPopAttrib(); 1064 | glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); 1065 | glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); 1066 | glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); 1067 | glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); 1068 | glShadeModel((GLenum)last_shade_model); 1069 | glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, last_tex_env_mode); 1070 | 1071 | #ifdef GL_VERSION_ES_CL_1_1 1072 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); 1073 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); 1074 | glDisable(GL_SCISSOR_TEST); 1075 | #endif 1076 | } 1077 | 1078 | void initDefaultJoystickMapping() 1079 | { 1080 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadFaceDown, 0); 1081 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadFaceRight, 1); 1082 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadFaceLeft, 2); 1083 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadFaceUp, 3); 1084 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadL1, 4); 1085 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadR1, 5); 1086 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadBack, 6); 1087 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadStart, 7); 1088 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadL3, 9); 1089 | ImGui::SFML::SetJoystickMapping(ImGuiKey_GamepadR3, 10); 1090 | 1091 | ImGui::SFML::SetDPadXAxis(sf::Joystick::Axis::PovX); 1092 | // D-pad Y axis is inverted on Windows 1093 | #ifdef _WIN32 1094 | ImGui::SFML::SetDPadYAxis(sf::Joystick::Axis::PovY, true); 1095 | #else 1096 | ImGui::SFML::SetDPadYAxis(sf::Joystick::Axis::PovY); 1097 | #endif 1098 | 1099 | ImGui::SFML::SetLStickXAxis(sf::Joystick::Axis::X); 1100 | ImGui::SFML::SetLStickYAxis(sf::Joystick::Axis::Y); 1101 | ImGui::SFML::SetRStickXAxis(sf::Joystick::Axis::U); 1102 | ImGui::SFML::SetRStickYAxis(sf::Joystick::Axis::V); 1103 | ImGui::SFML::SetLTriggerAxis(sf::Joystick::Axis::Z); 1104 | ImGui::SFML::SetRTriggerAxis(sf::Joystick::Axis::R); 1105 | 1106 | ImGui::SFML::SetJoystickDPadThreshold(5.f); 1107 | ImGui::SFML::SetJoystickLStickThreshold(5.f); 1108 | ImGui::SFML::SetJoystickRStickThreshold(15.f); 1109 | ImGui::SFML::SetJoystickLTriggerThreshold(0.f); 1110 | ImGui::SFML::SetJoystickRTriggerThreshold(0.f); 1111 | } 1112 | 1113 | void updateJoystickButtonState(ImGuiIO& io) 1114 | { 1115 | for (int i = 0; i < static_cast(sf::Joystick::ButtonCount); ++i) 1116 | { 1117 | const ImGuiKey key = s_currWindowCtx->joystickMapping[i]; 1118 | if (key != ImGuiKey_None) 1119 | { 1120 | const bool isPressed = sf::Joystick::isButtonPressed(s_currWindowCtx->joystickId, static_cast(i)); 1121 | if (s_currWindowCtx->windowHasFocus || !isPressed) 1122 | { 1123 | io.AddKeyEvent(key, isPressed); 1124 | } 1125 | } 1126 | } 1127 | } 1128 | 1129 | void updateJoystickAxis(ImGuiIO& io, ImGuiKey key, sf::Joystick::Axis axis, float threshold, float maxThreshold, bool inverted) 1130 | { 1131 | float pos = sf::Joystick::getAxisPosition(s_currWindowCtx->joystickId, axis); 1132 | if (inverted) 1133 | { 1134 | pos = -pos; 1135 | } 1136 | const bool passedThreshold = (pos > threshold) == (maxThreshold > threshold); 1137 | if (passedThreshold && s_currWindowCtx->windowHasFocus) 1138 | { 1139 | io.AddKeyAnalogEvent(key, true, std::abs(pos / 100.f)); 1140 | } 1141 | else 1142 | { 1143 | io.AddKeyAnalogEvent(key, false, 0); 1144 | } 1145 | } 1146 | 1147 | void updateJoystickAxisPair(ImGuiIO& io, ImGuiKey key1, ImGuiKey key2, sf::Joystick::Axis axis, float threshold, bool inverted) 1148 | { 1149 | updateJoystickAxis(io, key1, axis, -threshold, -100, inverted); 1150 | updateJoystickAxis(io, key2, axis, threshold, 100, inverted); 1151 | } 1152 | 1153 | void updateJoystickDPadState(ImGuiIO& io) 1154 | { 1155 | updateJoystickAxisPair(io, 1156 | ImGuiKey_GamepadDpadLeft, 1157 | ImGuiKey_GamepadDpadRight, 1158 | s_currWindowCtx->dPadInfo.xAxis, 1159 | s_currWindowCtx->dPadInfo.threshold, 1160 | s_currWindowCtx->dPadInfo.xInverted); 1161 | updateJoystickAxisPair(io, 1162 | ImGuiKey_GamepadDpadUp, 1163 | ImGuiKey_GamepadDpadDown, 1164 | s_currWindowCtx->dPadInfo.yAxis, 1165 | s_currWindowCtx->dPadInfo.threshold, 1166 | s_currWindowCtx->dPadInfo.yInverted); 1167 | } 1168 | 1169 | void updateJoystickAxisState(ImGuiIO& io) 1170 | { 1171 | updateJoystickAxisPair(io, 1172 | ImGuiKey_GamepadLStickLeft, 1173 | ImGuiKey_GamepadLStickRight, 1174 | s_currWindowCtx->lStickInfo.xAxis, 1175 | s_currWindowCtx->lStickInfo.threshold, 1176 | s_currWindowCtx->lStickInfo.xInverted); 1177 | updateJoystickAxisPair(io, 1178 | ImGuiKey_GamepadLStickUp, 1179 | ImGuiKey_GamepadLStickDown, 1180 | s_currWindowCtx->lStickInfo.yAxis, 1181 | s_currWindowCtx->lStickInfo.threshold, 1182 | s_currWindowCtx->lStickInfo.yInverted); 1183 | 1184 | updateJoystickAxisPair(io, 1185 | ImGuiKey_GamepadRStickLeft, 1186 | ImGuiKey_GamepadRStickRight, 1187 | s_currWindowCtx->rStickInfo.xAxis, 1188 | s_currWindowCtx->rStickInfo.threshold, 1189 | s_currWindowCtx->rStickInfo.xInverted); 1190 | updateJoystickAxisPair(io, 1191 | ImGuiKey_GamepadRStickUp, 1192 | ImGuiKey_GamepadRStickDown, 1193 | s_currWindowCtx->rStickInfo.yAxis, 1194 | s_currWindowCtx->rStickInfo.threshold, 1195 | s_currWindowCtx->rStickInfo.yInverted); 1196 | 1197 | updateJoystickAxis(io, 1198 | ImGuiKey_GamepadL2, 1199 | s_currWindowCtx->lTriggerInfo.axis, 1200 | s_currWindowCtx->lTriggerInfo.threshold, 1201 | 100, 1202 | false); 1203 | updateJoystickAxis(io, 1204 | ImGuiKey_GamepadR2, 1205 | s_currWindowCtx->rTriggerInfo.axis, 1206 | s_currWindowCtx->rTriggerInfo.threshold, 1207 | 100, 1208 | false); 1209 | } 1210 | 1211 | void updateMouseCursor(sf::Window& window) 1212 | { 1213 | const ImGuiIO& io = ImGui::GetIO(); 1214 | if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) == 0) 1215 | { 1216 | const ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); 1217 | if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) 1218 | { 1219 | window.setMouseCursorVisible(false); 1220 | } 1221 | else 1222 | { 1223 | window.setMouseCursorVisible(true); 1224 | 1225 | const sf::Cursor& c = s_currWindowCtx->mouseCursors[cursor] 1226 | ? *s_currWindowCtx->mouseCursors[cursor] 1227 | : *s_currWindowCtx->mouseCursors[ImGuiMouseCursor_Arrow]; 1228 | window.setMouseCursor(c); 1229 | } 1230 | } 1231 | } 1232 | 1233 | ImGuiKey keycodeToImGuiKey(sf::Keyboard::Key code) 1234 | { 1235 | switch (code) 1236 | { 1237 | case sf::Keyboard::Key::Tab: 1238 | return ImGuiKey_Tab; 1239 | case sf::Keyboard::Key::Left: 1240 | return ImGuiKey_LeftArrow; 1241 | case sf::Keyboard::Key::Right: 1242 | return ImGuiKey_RightArrow; 1243 | case sf::Keyboard::Key::Up: 1244 | return ImGuiKey_UpArrow; 1245 | case sf::Keyboard::Key::Down: 1246 | return ImGuiKey_DownArrow; 1247 | case sf::Keyboard::Key::PageUp: 1248 | return ImGuiKey_PageUp; 1249 | case sf::Keyboard::Key::PageDown: 1250 | return ImGuiKey_PageDown; 1251 | case sf::Keyboard::Key::Home: 1252 | return ImGuiKey_Home; 1253 | case sf::Keyboard::Key::End: 1254 | return ImGuiKey_End; 1255 | case sf::Keyboard::Key::Insert: 1256 | return ImGuiKey_Insert; 1257 | case sf::Keyboard::Key::Delete: 1258 | return ImGuiKey_Delete; 1259 | case sf::Keyboard::Key::Backspace: 1260 | return ImGuiKey_Backspace; 1261 | case sf::Keyboard::Key::Space: 1262 | return ImGuiKey_Space; 1263 | case sf::Keyboard::Key::Enter: 1264 | return ImGuiKey_Enter; 1265 | case sf::Keyboard::Key::Escape: 1266 | return ImGuiKey_Escape; 1267 | case sf::Keyboard::Key::Apostrophe: 1268 | return ImGuiKey_Apostrophe; 1269 | case sf::Keyboard::Key::Comma: 1270 | return ImGuiKey_Comma; 1271 | case sf::Keyboard::Key::Hyphen: 1272 | return ImGuiKey_Minus; 1273 | case sf::Keyboard::Key::Period: 1274 | return ImGuiKey_Period; 1275 | case sf::Keyboard::Key::Slash: 1276 | return ImGuiKey_Slash; 1277 | case sf::Keyboard::Key::Semicolon: 1278 | return ImGuiKey_Semicolon; 1279 | case sf::Keyboard::Key::Equal: 1280 | return ImGuiKey_Equal; 1281 | case sf::Keyboard::Key::LBracket: 1282 | return ImGuiKey_LeftBracket; 1283 | case sf::Keyboard::Key::Backslash: 1284 | return ImGuiKey_Backslash; 1285 | case sf::Keyboard::Key::RBracket: 1286 | return ImGuiKey_RightBracket; 1287 | case sf::Keyboard::Key::Grave: 1288 | return ImGuiKey_GraveAccent; 1289 | // case : return ImGuiKey_CapsLock; 1290 | // case : return ImGuiKey_ScrollLock; 1291 | // case : return ImGuiKey_NumLock; 1292 | // case : return ImGuiKey_PrintScreen; 1293 | case sf::Keyboard::Key::Pause: 1294 | return ImGuiKey_Pause; 1295 | case sf::Keyboard::Key::Numpad0: 1296 | return ImGuiKey_Keypad0; 1297 | case sf::Keyboard::Key::Numpad1: 1298 | return ImGuiKey_Keypad1; 1299 | case sf::Keyboard::Key::Numpad2: 1300 | return ImGuiKey_Keypad2; 1301 | case sf::Keyboard::Key::Numpad3: 1302 | return ImGuiKey_Keypad3; 1303 | case sf::Keyboard::Key::Numpad4: 1304 | return ImGuiKey_Keypad4; 1305 | case sf::Keyboard::Key::Numpad5: 1306 | return ImGuiKey_Keypad5; 1307 | case sf::Keyboard::Key::Numpad6: 1308 | return ImGuiKey_Keypad6; 1309 | case sf::Keyboard::Key::Numpad7: 1310 | return ImGuiKey_Keypad7; 1311 | case sf::Keyboard::Key::Numpad8: 1312 | return ImGuiKey_Keypad8; 1313 | case sf::Keyboard::Key::Numpad9: 1314 | return ImGuiKey_Keypad9; 1315 | // case : return ImGuiKey_KeypadDecimal; 1316 | case sf::Keyboard::Key::Divide: 1317 | return ImGuiKey_KeypadDivide; 1318 | case sf::Keyboard::Key::Multiply: 1319 | return ImGuiKey_KeypadMultiply; 1320 | case sf::Keyboard::Key::Subtract: 1321 | return ImGuiKey_KeypadSubtract; 1322 | case sf::Keyboard::Key::Add: 1323 | return ImGuiKey_KeypadAdd; 1324 | // case : return ImGuiKey_KeypadEnter; 1325 | // case : return ImGuiKey_KeypadEqual; 1326 | case sf::Keyboard::Key::LControl: 1327 | return ImGuiKey_LeftCtrl; 1328 | case sf::Keyboard::Key::LShift: 1329 | return ImGuiKey_LeftShift; 1330 | case sf::Keyboard::Key::LAlt: 1331 | return ImGuiKey_LeftAlt; 1332 | case sf::Keyboard::Key::LSystem: 1333 | return ImGuiKey_LeftSuper; 1334 | case sf::Keyboard::Key::RControl: 1335 | return ImGuiKey_RightCtrl; 1336 | case sf::Keyboard::Key::RShift: 1337 | return ImGuiKey_RightShift; 1338 | case sf::Keyboard::Key::RAlt: 1339 | return ImGuiKey_RightAlt; 1340 | case sf::Keyboard::Key::RSystem: 1341 | return ImGuiKey_RightSuper; 1342 | case sf::Keyboard::Key::Menu: 1343 | return ImGuiKey_Menu; 1344 | case sf::Keyboard::Key::Num0: 1345 | return ImGuiKey_0; 1346 | case sf::Keyboard::Key::Num1: 1347 | return ImGuiKey_1; 1348 | case sf::Keyboard::Key::Num2: 1349 | return ImGuiKey_2; 1350 | case sf::Keyboard::Key::Num3: 1351 | return ImGuiKey_3; 1352 | case sf::Keyboard::Key::Num4: 1353 | return ImGuiKey_4; 1354 | case sf::Keyboard::Key::Num5: 1355 | return ImGuiKey_5; 1356 | case sf::Keyboard::Key::Num6: 1357 | return ImGuiKey_6; 1358 | case sf::Keyboard::Key::Num7: 1359 | return ImGuiKey_7; 1360 | case sf::Keyboard::Key::Num8: 1361 | return ImGuiKey_8; 1362 | case sf::Keyboard::Key::Num9: 1363 | return ImGuiKey_9; 1364 | case sf::Keyboard::Key::A: 1365 | return ImGuiKey_A; 1366 | case sf::Keyboard::Key::B: 1367 | return ImGuiKey_B; 1368 | case sf::Keyboard::Key::C: 1369 | return ImGuiKey_C; 1370 | case sf::Keyboard::Key::D: 1371 | return ImGuiKey_D; 1372 | case sf::Keyboard::Key::E: 1373 | return ImGuiKey_E; 1374 | case sf::Keyboard::Key::F: 1375 | return ImGuiKey_F; 1376 | case sf::Keyboard::Key::G: 1377 | return ImGuiKey_G; 1378 | case sf::Keyboard::Key::H: 1379 | return ImGuiKey_H; 1380 | case sf::Keyboard::Key::I: 1381 | return ImGuiKey_I; 1382 | case sf::Keyboard::Key::J: 1383 | return ImGuiKey_J; 1384 | case sf::Keyboard::Key::K: 1385 | return ImGuiKey_K; 1386 | case sf::Keyboard::Key::L: 1387 | return ImGuiKey_L; 1388 | case sf::Keyboard::Key::M: 1389 | return ImGuiKey_M; 1390 | case sf::Keyboard::Key::N: 1391 | return ImGuiKey_N; 1392 | case sf::Keyboard::Key::O: 1393 | return ImGuiKey_O; 1394 | case sf::Keyboard::Key::P: 1395 | return ImGuiKey_P; 1396 | case sf::Keyboard::Key::Q: 1397 | return ImGuiKey_Q; 1398 | case sf::Keyboard::Key::R: 1399 | return ImGuiKey_R; 1400 | case sf::Keyboard::Key::S: 1401 | return ImGuiKey_S; 1402 | case sf::Keyboard::Key::T: 1403 | return ImGuiKey_T; 1404 | case sf::Keyboard::Key::U: 1405 | return ImGuiKey_U; 1406 | case sf::Keyboard::Key::V: 1407 | return ImGuiKey_V; 1408 | case sf::Keyboard::Key::W: 1409 | return ImGuiKey_W; 1410 | case sf::Keyboard::Key::X: 1411 | return ImGuiKey_X; 1412 | case sf::Keyboard::Key::Y: 1413 | return ImGuiKey_Y; 1414 | case sf::Keyboard::Key::Z: 1415 | return ImGuiKey_Z; 1416 | case sf::Keyboard::Key::F1: 1417 | return ImGuiKey_F1; 1418 | case sf::Keyboard::Key::F2: 1419 | return ImGuiKey_F2; 1420 | case sf::Keyboard::Key::F3: 1421 | return ImGuiKey_F3; 1422 | case sf::Keyboard::Key::F4: 1423 | return ImGuiKey_F4; 1424 | case sf::Keyboard::Key::F5: 1425 | return ImGuiKey_F5; 1426 | case sf::Keyboard::Key::F6: 1427 | return ImGuiKey_F6; 1428 | case sf::Keyboard::Key::F7: 1429 | return ImGuiKey_F7; 1430 | case sf::Keyboard::Key::F8: 1431 | return ImGuiKey_F8; 1432 | case sf::Keyboard::Key::F9: 1433 | return ImGuiKey_F9; 1434 | case sf::Keyboard::Key::F10: 1435 | return ImGuiKey_F10; 1436 | case sf::Keyboard::Key::F11: 1437 | return ImGuiKey_F11; 1438 | case sf::Keyboard::Key::F12: 1439 | return ImGuiKey_F12; 1440 | default: 1441 | break; 1442 | } 1443 | return ImGuiKey_None; 1444 | } 1445 | 1446 | ImGuiKey keycodeToImGuiMod(sf::Keyboard::Key code) 1447 | { 1448 | switch (code) 1449 | { 1450 | case sf::Keyboard::Key::LControl: 1451 | case sf::Keyboard::Key::RControl: 1452 | return ImGuiMod_Ctrl; 1453 | case sf::Keyboard::Key::LShift: 1454 | case sf::Keyboard::Key::RShift: 1455 | return ImGuiMod_Shift; 1456 | case sf::Keyboard::Key::LAlt: 1457 | case sf::Keyboard::Key::RAlt: 1458 | return ImGuiMod_Alt; 1459 | case sf::Keyboard::Key::LSystem: 1460 | case sf::Keyboard::Key::RSystem: 1461 | return ImGuiMod_Super; 1462 | default: 1463 | break; 1464 | } 1465 | return ImGuiKey_None; 1466 | } 1467 | 1468 | } // end of anonymous namespace 1469 | -------------------------------------------------------------------------------- /imgui-SFML.h: -------------------------------------------------------------------------------- 1 | #ifndef IMGUI_SFML_H 2 | #define IMGUI_SFML_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "imgui-SFML_export.h" 13 | 14 | namespace sf 15 | { 16 | class Event; 17 | class RenderTarget; 18 | class RenderTexture; 19 | class RenderWindow; 20 | class Sprite; 21 | class Texture; 22 | class Window; 23 | } // namespace sf 24 | 25 | namespace ImGui 26 | { 27 | namespace SFML 28 | { 29 | [[nodiscard]] IMGUI_SFML_API bool Init(sf::RenderWindow& window, bool loadDefaultFont = true); 30 | [[nodiscard]] IMGUI_SFML_API bool Init(sf::Window& window, sf::RenderTarget& target, bool loadDefaultFont = true); 31 | [[nodiscard]] IMGUI_SFML_API bool Init(sf::Window& window, const sf::Vector2f& displaySize, bool loadDefaultFont = true); 32 | 33 | IMGUI_SFML_API void SetCurrentWindow(const sf::Window& window); 34 | IMGUI_SFML_API void ProcessEvent(const sf::Window& window, const sf::Event& event); 35 | 36 | IMGUI_SFML_API void Update(sf::RenderWindow& window, sf::Time dt); 37 | IMGUI_SFML_API void Update(sf::Window& window, sf::RenderTarget& target, sf::Time dt); 38 | IMGUI_SFML_API void Update(const sf::Vector2i& mousePos, const sf::Vector2f& displaySize, sf::Time dt); 39 | 40 | IMGUI_SFML_API void Render(sf::RenderWindow& window); 41 | IMGUI_SFML_API void Render(sf::RenderTarget& target); 42 | IMGUI_SFML_API void Render(); 43 | 44 | IMGUI_SFML_API void Shutdown(const sf::Window& window); 45 | // Shuts down all ImGui contexts 46 | IMGUI_SFML_API void Shutdown(); 47 | 48 | [[nodiscard]] IMGUI_SFML_API bool UpdateFontTexture(); 49 | IMGUI_SFML_API std::optional& GetFontTexture(); 50 | 51 | // joystick functions 52 | IMGUI_SFML_API void SetActiveJoystickId(unsigned int joystickId); 53 | IMGUI_SFML_API void SetJoystickDPadThreshold(float threshold); 54 | IMGUI_SFML_API void SetJoystickLStickThreshold(float threshold); 55 | IMGUI_SFML_API void SetJoystickRStickThreshold(float threshold); 56 | IMGUI_SFML_API void SetJoystickLTriggerThreshold(float threshold); 57 | IMGUI_SFML_API void SetJoystickRTriggerThreshold(float threshold); 58 | 59 | IMGUI_SFML_API void SetJoystickMapping(int key, unsigned int joystickButton); 60 | IMGUI_SFML_API void SetDPadXAxis(sf::Joystick::Axis dPadXAxis, bool inverted = false); 61 | IMGUI_SFML_API void SetDPadYAxis(sf::Joystick::Axis dPadYAxis, bool inverted = false); 62 | IMGUI_SFML_API void SetLStickXAxis(sf::Joystick::Axis lStickXAxis, bool inverted = false); 63 | IMGUI_SFML_API void SetLStickYAxis(sf::Joystick::Axis lStickYAxis, bool inverted = false); 64 | IMGUI_SFML_API void SetRStickXAxis(sf::Joystick::Axis rStickXAxis, bool inverted = false); 65 | IMGUI_SFML_API void SetRStickYAxis(sf::Joystick::Axis rStickYAxis, bool inverted = false); 66 | IMGUI_SFML_API void SetLTriggerAxis(sf::Joystick::Axis lTriggerAxis); 67 | IMGUI_SFML_API void SetRTriggerAxis(sf::Joystick::Axis rTriggerAxis); 68 | } // end of namespace SFML 69 | 70 | // custom SFML overloads for ImGui widgets 71 | 72 | // Image overloads for sf::Texture 73 | IMGUI_SFML_API void Image(const sf::Texture& texture, 74 | const sf::Color& tintColor = sf::Color::White, 75 | const sf::Color& borderColor = sf::Color::Transparent); 76 | IMGUI_SFML_API void Image(const sf::Texture& texture, 77 | const sf::Vector2f& size, 78 | const sf::Color& tintColor = sf::Color::White, 79 | const sf::Color& borderColor = sf::Color::Transparent); 80 | 81 | // Image overloads for sf::RenderTexture 82 | IMGUI_SFML_API void Image(const sf::RenderTexture& texture, 83 | const sf::Color& tintColor = sf::Color::White, 84 | const sf::Color& borderColor = sf::Color::Transparent); 85 | IMGUI_SFML_API void Image(const sf::RenderTexture& texture, 86 | const sf::Vector2f& size, 87 | const sf::Color& tintColor = sf::Color::White, 88 | const sf::Color& borderColor = sf::Color::Transparent); 89 | 90 | // Image overloads for sf::Sprite 91 | IMGUI_SFML_API void Image(const sf::Sprite& sprite, 92 | const sf::Color& tintColor = sf::Color::White, 93 | const sf::Color& borderColor = sf::Color::Transparent); 94 | IMGUI_SFML_API void Image(const sf::Sprite& sprite, 95 | const sf::Vector2f& size, 96 | const sf::Color& tintColor = sf::Color::White, 97 | const sf::Color& borderColor = sf::Color::Transparent); 98 | 99 | // ImageButton overloads for sf::Texture 100 | IMGUI_SFML_API bool ImageButton(const char* id, 101 | const sf::Texture& texture, 102 | const sf::Vector2f& size, 103 | const sf::Color& bgColor = sf::Color::Transparent, 104 | const sf::Color& tintColor = sf::Color::White); 105 | 106 | // ImageButton overloads for sf::RenderTexture 107 | IMGUI_SFML_API bool ImageButton(const char* id, 108 | const sf::RenderTexture& texture, 109 | const sf::Vector2f& size, 110 | const sf::Color& bgColor = sf::Color::Transparent, 111 | const sf::Color& tintColor = sf::Color::White); 112 | 113 | // ImageButton overloads for sf::Sprite 114 | IMGUI_SFML_API bool ImageButton(const char* id, 115 | const sf::Sprite& sprite, 116 | const sf::Vector2f& size, 117 | const sf::Color& bgColor = sf::Color::Transparent, 118 | const sf::Color& tintColor = sf::Color::White); 119 | 120 | // Draw_list overloads. All positions are in relative coordinates (relative to top-left of the 121 | // current window) 122 | IMGUI_SFML_API void DrawLine(const sf::Vector2f& a, const sf::Vector2f& b, const sf::Color& col, float thickness = 1.0f); 123 | IMGUI_SFML_API void DrawRect(const sf::FloatRect& rect, 124 | const sf::Color& color, 125 | float rounding = 0.0f, 126 | int rounding_corners = 0x0F, 127 | float thickness = 1.0f); 128 | IMGUI_SFML_API void DrawRectFilled(const sf::FloatRect& rect, 129 | const sf::Color& color, 130 | float rounding = 0.0f, 131 | int rounding_corners = 0x0F); 132 | } // end of namespace ImGui 133 | 134 | #endif // # IMGUI_SFML_H 135 | -------------------------------------------------------------------------------- /imgui-SFML_export.h: -------------------------------------------------------------------------------- 1 | #ifndef IMGUI_SFML_EXPORT_H 2 | #define IMGUI_SFML_EXPORT_H 3 | 4 | #if IMGUI_SFML_SHARED_LIB 5 | #if _WIN32 6 | #ifdef IMGUI_SFML_EXPORTS 7 | #define IMGUI_SFML_API __declspec(dllexport) 8 | #define IMGUI_API __declspec(dllexport) 9 | #else 10 | #define IMGUI_SFML_API __declspec(dllimport) 11 | #define IMGUI_API __declspec(dllexport) 12 | #endif 13 | #elif __GNUC__ >= 4 14 | #define IMGUI_SFML_API __attribute__((visibility("default"))) 15 | #define IMGUI_API __attribute__((visibility("default"))) 16 | #else 17 | #define IMGUI_SFML_API 18 | #define IMGUI_API 19 | #endif 20 | #else 21 | #define IMGUI_SFML_API 22 | #define IMGUI_API 23 | #endif 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Get Catch2 2 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 3 | find_package(Catch2 3.7.0 REQUIRED) 4 | include(Catch) 5 | 6 | # Test library 7 | add_executable(test-imgui-sfml imconfig-SFML.cpp) 8 | target_link_libraries(test-imgui-sfml PRIVATE ImGui-SFML::ImGui-SFML Catch2::Catch2WithMain) 9 | target_compile_options(test-imgui-sfml PRIVATE ${IMGUI_SFML_WARNINGS}) 10 | catch_discover_tests(test-imgui-sfml) 11 | -------------------------------------------------------------------------------- /tests/FindCatch2.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | FetchContent_Declare(Catch2 4 | GIT_REPOSITORY https://github.com/catchorg/Catch2.git 5 | GIT_TAG v${Catch2_FIND_VERSION}) 6 | FetchContent_MakeAvailable(Catch2) 7 | set_target_properties(Catch2 PROPERTIES COMPILE_OPTIONS "" EXPORT_COMPILE_COMMANDS OFF) 8 | set_target_properties(Catch2WithMain PROPERTIES EXPORT_COMPILE_COMMANDS OFF) 9 | get_target_property(CATCH2_INCLUDE_DIRS Catch2 INTERFACE_INCLUDE_DIRECTORIES) 10 | target_include_directories(Catch2 SYSTEM INTERFACE ${CATCH2_INCLUDE_DIRS}) 11 | target_compile_features(Catch2 PRIVATE cxx_std_17) 12 | -------------------------------------------------------------------------------- /tests/imconfig-SFML.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | TEST_CASE("IM_VEC2_CLASS_EXTRA") 6 | { 7 | SECTION("From sf::Vector2f") 8 | { 9 | const auto imvec = ImVec2(sf::Vector2f(1.2f, 3.4f)); 10 | CHECK(imvec.x == 1.2f); 11 | CHECK(imvec.y == 3.4f); 12 | } 13 | 14 | SECTION("From sf::Vector2i") 15 | { 16 | const auto imvec = ImVec2(sf::Vector2i(1, 2)); 17 | CHECK(imvec.x == 1); 18 | CHECK(imvec.y == 2); 19 | } 20 | 21 | SECTION("To sf::Vector2f") 22 | { 23 | CHECK(sf::Vector2i(ImVec2(1.1f, 2.2f)) == sf::Vector2i(1, 2)); 24 | CHECK(sf::Vector2f(ImVec2(1.1f, 2.2f)) == sf::Vector2f(1.1f, 2.2f)); 25 | } 26 | } 27 | 28 | TEST_CASE("IM_VEC4_CLASS_EXTRA") 29 | { 30 | SECTION("From sf::Color") 31 | { 32 | const auto imvec = ImVec4(sf::Color(12, 34, 56, 78)); 33 | CHECK(imvec.x == 12.f / 255); 34 | CHECK(imvec.y == 34.f / 255); 35 | CHECK(imvec.z == 56.f / 255); 36 | CHECK(imvec.w == 78.f / 255); 37 | } 38 | 39 | SECTION("To sf::Color") 40 | { 41 | const sf::Color color = ImVec4(0, .25f, .5f, .75f); 42 | CHECK(+color.r == 0); 43 | CHECK(+color.g == 63); 44 | CHECK(+color.b == 127); 45 | CHECK(+color.a == 191); 46 | } 47 | } 48 | --------------------------------------------------------------------------------