build/plugins: Added initial support for Rust plugins (#327)
* build: Added initial support for Rust plugins * github: Install correct rust version * github: Fixed rustup command * github: Fix swapped win/linux commands * github: Install linux rust toolchain on Linux * github: Add rustup parameters to correct command * build: libimhex-rust -> hex * rust-plugins: Disable optimization to export functions correctly * build: Use cdylib instead of dylib * build: Fixed rust building and artifact copying * build: Fixed installing plugins * build: Fix copying and installing on Windows * github: Added windows debugging * github: Use curl instead of wget * github: Added debug on failure * github: Update path variable with rust toolchain path * build/github: Set rust location so cmake can find it * build: Remove leftovers * api: Added rust wrappers for the ImHexAPI * rust: Fixed compile flags with older gcc/clang * build: Enable concepts for cxx.rs * build: Explicitly set compiler for cxx.rs * rust: Added imgui-rs to libimhex-rust * rust: Export functions with double underscore prefix on mac * rust: Export functions adjusted for ABI * Add Rust target folder to gitignore * Add vendored imgui-rs copy * Add Context::current() to vendored imgui-rs * Fix libimhex not exporting cimgui symbols * Simplify plugin export mangling * build: Fixed cimgui linking * build: Only specify --export-all-symbols on Windows * Add context setting to Rust plugins * rust: Cleanup * deps: Update curl Co-authored-by: jam1garner <8260240+jam1garner@users.noreply.github.com>
This commit is contained in:
parent
1b6035d6c6
commit
46ba46ce9d
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@ -33,6 +33,13 @@ jobs:
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo bash dist/get_deps_debian.sh
|
||||
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh
|
||||
sh rustup-init.sh -y --default-toolchain none
|
||||
rm rustup-init.sh
|
||||
$HOME/.cargo/bin/rustup install nightly
|
||||
$HOME/.cargo/bin/rustup target add x86_64-unknown-linux-gnu
|
||||
$HOME/.cargo/bin/rustup default nightly
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: |
|
||||
@ -42,6 +49,7 @@ jobs:
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DRUST_PATH="$HOME/.cargo/bin/" \
|
||||
..
|
||||
make -j 4 install
|
||||
|
||||
@ -91,6 +99,12 @@ jobs:
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
bash dist/get_deps_msys2.sh
|
||||
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://win.rustup.rs > rustup-init.exe
|
||||
./rustup-init.exe -y --default-host=x86_64-pc-windows-gnu --default-toolchain=none
|
||||
rm rustup-init.exe
|
||||
$USERPROFILE/.cargo/bin/rustup.exe target add x86_64-pc-windows-gnu
|
||||
$USERPROFILE/.cargo/bin/rustup.exe default nightly
|
||||
|
||||
- name: 📜 Prepare Cache
|
||||
id: prep-ccache
|
||||
@ -121,10 +135,11 @@ jobs:
|
||||
-DCREATE_PACKAGE=ON \
|
||||
-DPython_LIBRARY="$PYTHON_LIB_PATH" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DRUST_PATH="$USERPROFILE/.cargo/bin/" \
|
||||
..
|
||||
mingw32-make -j4 install
|
||||
cpack
|
||||
|
||||
|
||||
- name: ⬆️ Upload Portable ZIP
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -12,3 +12,6 @@ build*/
|
||||
*.mgc
|
||||
imgui.ini
|
||||
.DS_Store
|
||||
plugins/.rustc_info.json
|
||||
|
||||
**/target
|
||||
|
@ -14,7 +14,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
set(PLUGINS
|
||||
builtin
|
||||
windows
|
||||
# example
|
||||
# example_cpp
|
||||
# example_rust
|
||||
)
|
||||
|
||||
findLibraries()
|
||||
|
@ -157,17 +157,29 @@ macro(createPackage)
|
||||
add_subdirectory("plugins/${plugin}")
|
||||
if (TARGET ${plugin})
|
||||
set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
set_target_properties(${plugin} PROPERTIES CARGO_BUILD_TARGET_DIR ${CMAKE_BINARY_DIR}/plugins)
|
||||
|
||||
if (WIN32)
|
||||
install(TARGETS ${plugin} RUNTIME DESTINATION ${PLUGINS_INSTALL_LOCATION})
|
||||
elseif (APPLE)
|
||||
if (CREATE_BUNDLE)
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:imhex>/${PLUGINS_INSTALL_LOCATION})
|
||||
else ()
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
endif ()
|
||||
get_target_property(IS_RUST_PROJECT ${plugin} RUST_PROJECT)
|
||||
|
||||
if (IS_RUST_PROJECT)
|
||||
set_target_properties(${plugin} PROPERTIES CARGO_BUILD_TARGET_DIR ${CMAKE_BINARY_DIR}/plugins)
|
||||
|
||||
get_target_property(PLUGIN_LOCATION ${plugin} LOCATION)
|
||||
|
||||
install(FILES "${PLUGIN_LOCATION}/../${plugin}.hexplug" DESTINATION "${PLUGINS_INSTALL_LOCATION}")
|
||||
else ()
|
||||
install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION})
|
||||
set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
if (WIN32)
|
||||
install(TARGETS ${plugin} RUNTIME DESTINATION ${PLUGINS_INSTALL_LOCATION})
|
||||
elseif (APPLE)
|
||||
if (CREATE_BUNDLE)
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:imhex>/${PLUGINS_INSTALL_LOCATION})
|
||||
else ()
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
endif ()
|
||||
else ()
|
||||
install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
add_dependencies(imhex ${plugin})
|
||||
@ -254,4 +266,4 @@ macro(setDefaultBuiltTypeIfUnset)
|
||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Using Release build type as it was left unset" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
|
||||
endif()
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
10
external/ImGui/CMakeLists.txt
vendored
10
external/ImGui/CMakeLists.txt
vendored
@ -13,7 +13,7 @@ endif ()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
|
||||
add_library(imgui STATIC
|
||||
add_library(imgui OBJECT
|
||||
source/imgui.cpp
|
||||
source/imgui_demo.cpp
|
||||
source/imgui_draw.cpp
|
||||
@ -23,6 +23,8 @@ add_library(imgui STATIC
|
||||
source/imgui_tables.cpp
|
||||
source/imgui_widgets.cpp
|
||||
|
||||
source/cimgui.cpp
|
||||
|
||||
source/TextEditor.cpp
|
||||
|
||||
source/imgui_imhex_extensions.cpp
|
||||
@ -40,12 +42,12 @@ add_library(imgui STATIC
|
||||
|
||||
add_compile_definitions(IMGUI_IMPL_OPENGL_LOADER_GLAD)
|
||||
|
||||
target_include_directories(imgui PUBLIC include fonts ${FREETYPE_INCLUDE_DIRS} ${GLFW_INCLUDE_DIRS})
|
||||
target_include_directories(imgui PUBLIC include fonts ${CMAKE_CURRENT_SOURCE_DIR} ${FREETYPE_INCLUDE_DIRS} ${GLFW_INCLUDE_DIRS})
|
||||
|
||||
target_link_directories(imgui PUBLIC ${GLFW_LIBRARY_DIRS})
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(imgui Freetype::Freetype glfw3 opengl32.lib)
|
||||
target_link_libraries(imgui PUBLIC Freetype::Freetype glfw3 opengl32.lib)
|
||||
elseif (UNIX)
|
||||
target_link_libraries(imgui Freetype::Freetype glfw OpenGL::GL)
|
||||
target_link_libraries(imgui PUBLIC Freetype::Freetype glfw OpenGL::GL)
|
||||
endif()
|
||||
|
3485
external/ImGui/include/cimgui.h
vendored
Normal file
3485
external/ImGui/include/cimgui.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4598
external/ImGui/source/cimgui.cpp
vendored
Normal file
4598
external/ImGui/source/cimgui.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Change this to the name of your plugin #
|
||||
project(example)
|
||||
project(example_cpp)
|
||||
|
||||
# Add your source files here #
|
||||
add_library(${PROJECT_NAME} SHARED
|
@ -15,7 +15,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
IMHEX_PLUGIN_SETUP("Example", "WerWolv", "Example plugin used as template for plugin devs") {
|
||||
IMHEX_PLUGIN_SETUP("Example C++", "WerWolv", "Example C++ plugin used as template for plugin devs") {
|
||||
|
||||
ContentRegistry::Views::add<ViewExample>();
|
||||
|
44
plugins/example_rust/CMakeLists.txt
Normal file
44
plugins/example_rust/CMakeLists.txt
Normal file
@ -0,0 +1,44 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Change this to the name of your plugin #
|
||||
project(example_rust)
|
||||
|
||||
# ---- No need to change anything from here downwards unless you know what you're doing ---- #
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CARGO_CMD ${RUST_PATH}cargo build)
|
||||
set(TARGET_DIR "debug")
|
||||
else ()
|
||||
set(CARGO_CMD ${RUST_PATH}cargo build --release)
|
||||
set(TARGET_DIR "release")
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
set(PLUGIN_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
else ()
|
||||
set(PLUGIN_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
endif ()
|
||||
|
||||
get_target_property(LIBIMHEX_SOURCE_DIRECTORY libimhex SOURCE_DIR)
|
||||
|
||||
add_custom_target(${PROJECT_NAME} ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}
|
||||
LIBIMHEX_SOURCE_DIRECTORY=${LIBIMHEX_SOURCE_DIRECTORY}
|
||||
LIBIMHEX_OUTPUT_DIRECTORY=$<TARGET_FILE_DIR:libimhex>
|
||||
CXX_COMPILER=${CMAKE_CXX_COMPILER}
|
||||
${CARGO_CMD}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PLUGIN_OUTPUT_PATH} "${CMAKE_CURRENT_BINARY_DIR}/../${PROJECT_NAME}.hexplug"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
VERBATIM
|
||||
USES_TERMINAL
|
||||
)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES LOCATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES RUST_PROJECT 1)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE Rust)
|
||||
|
||||
add_compile_definitions(IMHEX_PLUGIN_NAME=${PROJECT_NAME})
|
||||
|
||||
if (NOT TARGET libimhex)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libimhex ${CMAKE_CURRENT_BINARY_DIR}/plugins/libimhex)
|
||||
endif()
|
295
plugins/example_rust/Cargo.lock
generated
Normal file
295
plugins/example_rust/Cargo.lock
generated
Normal file
@ -0,0 +1,295 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chlorine"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd650552110e39b7c5058986cf177decd3365841836578ac50a286094eac0be6"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "638fb099591b763a988ca69e5f0ee4b82bedb8bc762b3c6cfbfdd3a6dc6b54b4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83363b96cfd226eb820e37a21088c30c55e47f9fc8299c2d08a6090d50414ccc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d4bde25840be4cf0eb1d7e3a74634105189d5609b855bcc8d601ffb037f5f4"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3869bf8972075de8f0446b433d40026f2dfdbd8a9edbc24e9d645ca2ebf2f93a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example_rust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
"imgui",
|
||||
"macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgui"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"imgui-sys",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgui-sys"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"chlorine",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
15
plugins/example_rust/Cargo.toml
Normal file
15
plugins/example_rust/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "example_rust"
|
||||
version = "0.1.0"
|
||||
authors = ["Marc-André Moreau <marcandre.moreau@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
path = "source/plugin_example.rs"
|
||||
|
||||
[dependencies]
|
||||
hex = { path = "../libimhex-rust" }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
7
plugins/example_rust/source/plugin_example.rs
Normal file
7
plugins/example_rust/source/plugin_example.rs
Normal file
@ -0,0 +1,7 @@
|
||||
extern crate hex;
|
||||
|
||||
#[hex::plugin_setup("Example Rust", "WerWolv", "Example Rust plugin used as template for plugin devs")]
|
||||
fn init() {
|
||||
|
||||
}
|
||||
|
288
plugins/libimhex-rust/Cargo.lock
generated
Normal file
288
plugins/libimhex-rust/Cargo.lock
generated
Normal file
@ -0,0 +1,288 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chlorine"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd650552110e39b7c5058986cf177decd3365841836578ac50a286094eac0be6"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "638fb099591b763a988ca69e5f0ee4b82bedb8bc762b3c6cfbfdd3a6dc6b54b4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83363b96cfd226eb820e37a21088c30c55e47f9fc8299c2d08a6090d50414ccc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d4bde25840be4cf0eb1d7e3a74634105189d5609b855bcc8d601ffb037f5f4"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3869bf8972075de8f0446b433d40026f2dfdbd8a9edbc24e9d645ca2ebf2f93a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
"imgui",
|
||||
"macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgui"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"imgui-sys",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgui-sys"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"chlorine",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
16
plugins/libimhex-rust/Cargo.toml
Normal file
16
plugins/libimhex-rust/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "hex"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
macros = { path = "proc_macros" }
|
||||
imgui = { path = "imgui-rs" }
|
||||
|
||||
cxx = "1.0.55"
|
||||
|
||||
[build-dependencies]
|
||||
cxx-build = "1.0.55"
|
15
plugins/libimhex-rust/build.rs
Normal file
15
plugins/libimhex-rust/build.rs
Normal file
@ -0,0 +1,15 @@
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=dylib=imhex");
|
||||
println!("cargo:rustc-link-search=all={}", env!("LIBIMHEX_OUTPUT_DIRECTORY"));
|
||||
|
||||
println!("cargo:rerun-if-changed=src/lib.rs");
|
||||
println!("cargo:rerun-if-changed=src/imhex_api.rs");
|
||||
|
||||
cxx_build::bridge("src/imhex_api.rs")
|
||||
.include(format!("{}/include", env!("LIBIMHEX_SOURCE_DIRECTORY")))
|
||||
.flag_if_supported("-std=gnu++20")
|
||||
.flag_if_supported("-std=gnu++2a")
|
||||
.flag_if_supported("-fconcepts")
|
||||
.compiler(env!("CXX_COMPILER"))
|
||||
.compile("libimhex-bridge");
|
||||
}
|
159
plugins/libimhex-rust/imgui-rs/Cargo.lock
generated
Normal file
159
plugins/libimhex-rust/imgui-rs/Cargo.lock
generated
Normal file
@ -0,0 +1,159 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chlorine"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd650552110e39b7c5058986cf177decd3365841836578ac50a286094eac0be6"
|
||||
|
||||
[[package]]
|
||||
name = "imgui"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"imgui-sys",
|
||||
"memoffset",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgui-sys"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"chlorine",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
31
plugins/libimhex-rust/imgui-rs/Cargo.toml
Normal file
31
plugins/libimhex-rust/imgui-rs/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "imgui"
|
||||
version = "0.8.0"
|
||||
edition = "2018"
|
||||
authors = ["The imgui-rs Developers"]
|
||||
description = "High-level Rust bindings to dear imgui"
|
||||
homepage = "https://github.com/imgui-rs/imgui-rs"
|
||||
repository = "https://github.com/imgui-rs/imgui-rs"
|
||||
license = "MIT/Apache-2.0"
|
||||
categories = ["gui", "api-bindings"]
|
||||
readme = "../README.markdown"
|
||||
|
||||
exclude = ["/resources"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1"
|
||||
imgui-sys = { path = "../imgui-sys" }
|
||||
parking_lot = "0.11"
|
||||
|
||||
[features]
|
||||
default = ["min-const-generics"]
|
||||
|
||||
wasm = ["imgui-sys/wasm"]
|
||||
freetype = ["imgui-sys/freetype"]
|
||||
min-const-generics = []
|
||||
# this api is in beta in the upstream imgui crate. See issue #524 for more info.
|
||||
# it should be stable and fine to use though.
|
||||
tables-api = []
|
||||
|
||||
[dev-dependencies]
|
||||
memoffset = "0.6"
|
202
plugins/libimhex-rust/imgui-rs/LICENSE-APACHE
Normal file
202
plugins/libimhex-rust/imgui-rs/LICENSE-APACHE
Normal file
@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
19
plugins/libimhex-rust/imgui-rs/LICENSE-MIT
Normal file
19
plugins/libimhex-rust/imgui-rs/LICENSE-MIT
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015-2020 The imgui-rs Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
1
plugins/libimhex-rust/imgui-rs/README.markdown
Symbolic link
1
plugins/libimhex-rust/imgui-rs/README.markdown
Symbolic link
@ -0,0 +1 @@
|
||||
../README.markdown
|
142
plugins/libimhex-rust/imgui-rs/src/clipboard.rs
Normal file
142
plugins/libimhex-rust/imgui-rs/src/clipboard.rs
Normal file
@ -0,0 +1,142 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::panic::catch_unwind;
|
||||
use std::process;
|
||||
use std::ptr;
|
||||
|
||||
// use crate::string::{ImStr, ImString};
|
||||
use crate::Ui;
|
||||
|
||||
/// Trait for clipboard backends
|
||||
pub trait ClipboardBackend: 'static {
|
||||
/// Returns the current clipboard contents as an owned imgui-rs string, or None if the
|
||||
/// clipboard is empty or inaccessible
|
||||
fn get(&mut self) -> Option<String>;
|
||||
/// Sets the clipboard contents to the given imgui-rs string slice.
|
||||
fn set(&mut self, value: &str);
|
||||
}
|
||||
|
||||
pub(crate) struct ClipboardContext {
|
||||
backend: Box<dyn ClipboardBackend>,
|
||||
// this is needed to keep ownership of the value when the raw C callback is called
|
||||
last_value: CString,
|
||||
}
|
||||
|
||||
impl ClipboardContext {
|
||||
/// Creates a new [ClipboardContext]. This function previously took a `Box`, but now
|
||||
/// is generic over the T it takes and boxes itself (which should be less strange).
|
||||
pub fn new<T: ClipboardBackend>(backend: T) -> ClipboardContext {
|
||||
ClipboardContext {
|
||||
backend: Box::new(backend) as Box<dyn ClipboardBackend>,
|
||||
last_value: CString::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dummy() -> ClipboardContext {
|
||||
Self {
|
||||
backend: Box::new(DummyClipboardContext),
|
||||
last_value: CString::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DummyClipboardContext;
|
||||
impl ClipboardBackend for DummyClipboardContext {
|
||||
fn get(&mut self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set(&mut self, _: &str) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ClipboardContext {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ClipboardContext")
|
||||
// beautiful code, no?
|
||||
.field("backend", &(&(*self.backend) as *const _))
|
||||
.field("last_value", &self.last_value)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *const c_char {
|
||||
let result = catch_unwind(|| {
|
||||
let ctx = &mut *(user_data as *mut ClipboardContext);
|
||||
match ctx.backend.get() {
|
||||
Some(text) => {
|
||||
ctx.last_value = CString::new(text).unwrap();
|
||||
ctx.last_value.as_ptr()
|
||||
}
|
||||
None => ptr::null(),
|
||||
}
|
||||
});
|
||||
result.unwrap_or_else(|_| {
|
||||
eprintln!("Clipboard getter panicked");
|
||||
process::abort();
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn set_clipboard_text(user_data: *mut c_void, text: *const c_char) {
|
||||
let result = catch_unwind(|| {
|
||||
let ctx = &mut *(user_data as *mut ClipboardContext);
|
||||
let text = CStr::from_ptr(text).to_owned();
|
||||
ctx.backend.set(text.to_str().unwrap());
|
||||
});
|
||||
result.unwrap_or_else(|_| {
|
||||
eprintln!("Clipboard setter panicked");
|
||||
process::abort();
|
||||
});
|
||||
}
|
||||
|
||||
/// # Clipboard
|
||||
#[allow(clippy::fn_address_comparisons)] // This is allowed because although function addresses wont be unique, we just care if its OURS
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns the current clipboard contents as text, or None if the clipboard is empty or cannot
|
||||
/// be accessed
|
||||
pub fn clipboard_text(&self) -> Option<String> {
|
||||
let io = self.io();
|
||||
io.get_clipboard_text_fn.and_then(|get_clipboard_text_fn| {
|
||||
// Bypass FFI if we end up calling our own function anyway
|
||||
if get_clipboard_text_fn == get_clipboard_text {
|
||||
let ctx = unsafe { &mut *(io.clipboard_user_data as *mut ClipboardContext) };
|
||||
ctx.backend.get()
|
||||
} else {
|
||||
unsafe {
|
||||
let text_ptr = get_clipboard_text_fn(io.clipboard_user_data);
|
||||
if text_ptr.is_null() || *text_ptr == b'\0' as c_char {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
CStr::from_ptr(text_ptr)
|
||||
.to_owned()
|
||||
.to_str()
|
||||
.ok()?
|
||||
.to_owned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the clipboard contents.
|
||||
///
|
||||
/// Does nothing if the clipboard cannot be accessed.
|
||||
pub fn set_clipboard_text(&self, text: impl AsRef<str>) {
|
||||
let io = self.io();
|
||||
if let Some(set_clipboard_text_fn) = io.set_clipboard_text_fn {
|
||||
// Bypass FFI if we end up calling our own function anyway
|
||||
if set_clipboard_text_fn == set_clipboard_text {
|
||||
let ctx = unsafe { &mut *(io.clipboard_user_data as *mut ClipboardContext) };
|
||||
ctx.backend.set(text.as_ref());
|
||||
} else {
|
||||
unsafe {
|
||||
set_clipboard_text_fn(io.clipboard_user_data, self.scratch_txt(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
370
plugins/libimhex-rust/imgui-rs/src/color.rs
Normal file
370
plugins/libimhex-rust/imgui-rs/src/color.rs
Normal file
@ -0,0 +1,370 @@
|
||||
/// Wraps u32 that represents a packed RGBA color. Mostly used by types in the
|
||||
/// low level custom drawing API, such as [`DrawListMut`](crate::DrawListMut).
|
||||
///
|
||||
/// The bits of a color are in "`0xAABBGGRR`" format (e.g. RGBA as little endian
|
||||
/// bytes). For clarity: we don't support an equivalent to the
|
||||
/// `IMGUI_USE_BGRA_PACKED_COLOR` define.
|
||||
///
|
||||
/// This used to be named `ImColor32`, but was renamed to avoid confusion with
|
||||
/// the type with that name in the C++ API (which uses 32 bits per channel).
|
||||
///
|
||||
/// While it doesn't provide methods to access the fields, they can be accessed
|
||||
/// via the `Deref`/`DerefMut` impls it provides targeting
|
||||
/// [`imgui::color::ImColor32Fields`](crate::color::ImColor32Fields), which has
|
||||
/// no other meaningful uses.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// let mut c = imgui::ImColor32::from_rgba(0x80, 0xc0, 0x40, 0xff);
|
||||
/// assert_eq!(c.to_bits(), 0xff_40_c0_80); // Note: 0xAA_BB_GG_RR
|
||||
/// // Field access
|
||||
/// assert_eq!(c.r, 0x80);
|
||||
/// assert_eq!(c.g, 0xc0);
|
||||
/// assert_eq!(c.b, 0x40);
|
||||
/// assert_eq!(c.a, 0xff);
|
||||
/// c.b = 0xbb;
|
||||
/// assert_eq!(c.to_bits(), 0xff_bb_c0_80);
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct ImColor32(u32); // TBH maybe the wrapped field should be `pub`.
|
||||
|
||||
impl ImColor32 {
|
||||
/// Convenience constant for solid black.
|
||||
pub const BLACK: Self = Self(0xff_00_00_00);
|
||||
|
||||
/// Convenience constant for solid white.
|
||||
pub const WHITE: Self = Self(0xff_ff_ff_ff);
|
||||
|
||||
/// Convenience constant for full transparency.
|
||||
pub const TRANSPARENT: Self = Self(0);
|
||||
|
||||
/// Construct a color from 4 single-byte `u8` channel values, which should
|
||||
/// be between 0 and 255.
|
||||
#[inline]
|
||||
pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
Self(
|
||||
((a as u32) << Self::A_SHIFT)
|
||||
| ((r as u32) << Self::R_SHIFT)
|
||||
| ((g as u32) << Self::G_SHIFT)
|
||||
| ((b as u32) << Self::B_SHIFT),
|
||||
)
|
||||
}
|
||||
|
||||
/// Construct a fully opaque color from 3 single-byte `u8` channel values.
|
||||
/// Same as [`Self::from_rgba`] with a == 255
|
||||
#[inline]
|
||||
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
|
||||
Self::from_rgba(r, g, b, 0xff)
|
||||
}
|
||||
|
||||
/// Construct a fully opaque color from 4 `f32` channel values in the range
|
||||
/// `0.0 ..= 1.0` (values outside this range are clamped to it, with NaN
|
||||
/// mapped to 0.0).
|
||||
///
|
||||
/// Note: No alpha premultiplication is done, so your input should be have
|
||||
/// premultiplied alpha if needed.
|
||||
#[inline]
|
||||
// not const fn because no float math in const eval yet 😩
|
||||
pub fn from_rgba_f32s(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self::from_rgba(
|
||||
f32_to_u8_sat(r),
|
||||
f32_to_u8_sat(g),
|
||||
f32_to_u8_sat(b),
|
||||
f32_to_u8_sat(a),
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the channels as an array of f32 in `[r, g, b, a]` order.
|
||||
#[inline]
|
||||
pub fn to_rgba_f32s(self) -> [f32; 4] {
|
||||
let &ImColor32Fields { r, g, b, a } = &*self;
|
||||
[
|
||||
u8_to_f32_sat(r),
|
||||
u8_to_f32_sat(g),
|
||||
u8_to_f32_sat(b),
|
||||
u8_to_f32_sat(a),
|
||||
]
|
||||
}
|
||||
|
||||
/// Return the channels as an array of u8 in `[r, g, b, a]` order.
|
||||
#[inline]
|
||||
pub fn to_rgba(self) -> [u8; 4] {
|
||||
let &ImColor32Fields { r, g, b, a } = &*self;
|
||||
[r, g, b, a]
|
||||
}
|
||||
|
||||
/// Equivalent to [`Self::from_rgba_f32s`], but with an alpha of 1.0 (e.g.
|
||||
/// opaque).
|
||||
#[inline]
|
||||
pub fn from_rgb_f32s(r: f32, g: f32, b: f32) -> Self {
|
||||
Self::from_rgba(f32_to_u8_sat(r), f32_to_u8_sat(g), f32_to_u8_sat(b), 0xff)
|
||||
}
|
||||
|
||||
/// Construct a color from the `u32` that makes up the bits in `0xAABBGGRR`
|
||||
/// format.
|
||||
///
|
||||
/// Specifically, this takes the RGBA values as a little-endian u32 with 8
|
||||
/// bits per channel.
|
||||
///
|
||||
/// Note that [`ImColor32::from_rgba`] may be a bit easier to use.
|
||||
#[inline]
|
||||
pub const fn from_bits(u: u32) -> Self {
|
||||
Self(u)
|
||||
}
|
||||
|
||||
/// Return the bits of the color as a u32. These are in "`0xAABBGGRR`" format, that
|
||||
/// is, little-endian RGBA with 8 bits per channel.
|
||||
#[inline]
|
||||
pub const fn to_bits(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
// These are public in C++ ImGui, should they be public here?
|
||||
/// The number of bits to shift the byte of the red channel. Always 0.
|
||||
const R_SHIFT: u32 = 0;
|
||||
/// The number of bits to shift the byte of the green channel. Always 8.
|
||||
const G_SHIFT: u32 = 8;
|
||||
/// The number of bits to shift the byte of the blue channel. Always 16.
|
||||
const B_SHIFT: u32 = 16;
|
||||
/// The number of bits to shift the byte of the alpha channel. Always 24.
|
||||
const A_SHIFT: u32 = 24;
|
||||
}
|
||||
|
||||
impl Default for ImColor32 {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::TRANSPARENT
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ImColor32 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ImColor32")
|
||||
.field("r", &self.r)
|
||||
.field("g", &self.g)
|
||||
.field("b", &self.b)
|
||||
.field("a", &self.a)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct that exists to allow field access to [`ImColor32`]. It essentially
|
||||
/// exists to be a `Deref`/`DerefMut` target and provide field access.
|
||||
///
|
||||
/// Note that while this is repr(C), be aware that on big-endian machines
|
||||
/// (`cfg(target_endian = "big")`) the order of the fields is reversed, as this
|
||||
/// is a view into a packed u32.
|
||||
///
|
||||
/// Generally should not be used, except as the target of the `Deref` impl of
|
||||
/// [`ImColor32`].
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, align(4))]
|
||||
// Should this be #[non_exhaustive] to discourage direct use?
|
||||
#[rustfmt::skip]
|
||||
pub struct ImColor32Fields {
|
||||
#[cfg(target_endian = "little")] pub r: u8,
|
||||
#[cfg(target_endian = "little")] pub g: u8,
|
||||
#[cfg(target_endian = "little")] pub b: u8,
|
||||
#[cfg(target_endian = "little")] pub a: u8,
|
||||
// TODO(someday): i guess we should have BE tests, but for now I verified
|
||||
// this locally.
|
||||
#[cfg(target_endian = "big")] pub a: u8,
|
||||
#[cfg(target_endian = "big")] pub b: u8,
|
||||
#[cfg(target_endian = "big")] pub g: u8,
|
||||
#[cfg(target_endian = "big")] pub r: u8,
|
||||
}
|
||||
|
||||
// We assume that big and little are the only endiannesses, and that exactly one
|
||||
// is set. That is, PDP endian is not in use, and the we aren't using a
|
||||
// completely broken custom target json or something.
|
||||
#[cfg(any(
|
||||
all(target_endian = "little", target_endian = "big"),
|
||||
all(not(target_endian = "little"), not(target_endian = "big")),
|
||||
))]
|
||||
compile_error!("`cfg(target_endian = \"little\")` must be `cfg(not(target_endian = \"big\")`");
|
||||
|
||||
// static assert sizes match
|
||||
const _: [(); core::mem::size_of::<ImColor32>()] = [(); core::mem::size_of::<ImColor32Fields>()];
|
||||
const _: [(); core::mem::align_of::<ImColor32>()] = [(); core::mem::align_of::<ImColor32Fields>()];
|
||||
|
||||
impl core::ops::Deref for ImColor32 {
|
||||
type Target = ImColor32Fields;
|
||||
#[inline]
|
||||
fn deref(&self) -> &ImColor32Fields {
|
||||
// Safety: we statically assert the size and align match, and neither
|
||||
// type has any special invariants.
|
||||
unsafe { &*(self as *const Self as *const ImColor32Fields) }
|
||||
}
|
||||
}
|
||||
impl core::ops::DerefMut for ImColor32 {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut ImColor32Fields {
|
||||
// Safety: we statically assert the size and align match, and neither
|
||||
// type has any special invariants.
|
||||
unsafe { &mut *(self as *mut Self as *mut ImColor32Fields) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImColor32> for u32 {
|
||||
#[inline]
|
||||
fn from(color: ImColor32) -> Self {
|
||||
color.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for ImColor32 {
|
||||
#[inline]
|
||||
fn from(color: u32) -> Self {
|
||||
ImColor32(color)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 4]> for ImColor32 {
|
||||
#[inline]
|
||||
fn from(v: [f32; 4]) -> Self {
|
||||
Self::from_rgba_f32s(v[0], v[1], v[2], v[3])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f32, f32, f32, f32)> for ImColor32 {
|
||||
#[inline]
|
||||
fn from(v: (f32, f32, f32, f32)) -> Self {
|
||||
Self::from_rgba_f32s(v.0, v.1, v.2, v.3)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 3]> for ImColor32 {
|
||||
#[inline]
|
||||
fn from(v: [f32; 3]) -> Self {
|
||||
Self::from_rgb_f32s(v[0], v[1], v[2])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f32, f32, f32)> for ImColor32 {
|
||||
fn from(v: (f32, f32, f32)) -> Self {
|
||||
Self::from_rgb_f32s(v.0, v.1, v.2)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImColor32> for [f32; 4] {
|
||||
#[inline]
|
||||
fn from(v: ImColor32) -> Self {
|
||||
v.to_rgba_f32s()
|
||||
}
|
||||
}
|
||||
impl From<ImColor32> for (f32, f32, f32, f32) {
|
||||
#[inline]
|
||||
fn from(color: ImColor32) -> Self {
|
||||
let [r, g, b, a]: [f32; 4] = color.into();
|
||||
(r, g, b, a)
|
||||
}
|
||||
}
|
||||
|
||||
// These utilities might be worth making `pub` as free functions in
|
||||
// `crate::color` so user code can ensure their numeric handling is
|
||||
// consistent...
|
||||
|
||||
/// Clamp `v` to between 0.0 and 1.0, always returning a value between those.
|
||||
///
|
||||
/// Never returns NaN, or -0.0 — instead returns +0.0 for these (We differ from
|
||||
/// C++ Dear ImGUI here which probably is just ignoring values like these).
|
||||
#[inline]
|
||||
pub(crate) fn saturate(v: f32) -> f32 {
|
||||
// Note: written strangely so that special values (NaN/-0.0) are handled
|
||||
// automatically with no extra checks.
|
||||
if v > 0.0 {
|
||||
if v <= 1.0 {
|
||||
v
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Quantize a value in `0.0..=1.0` to `0..=u8::MAX`. Input outside 0.0..=1.0 is
|
||||
/// clamped. Uses a bias of 0.5, because we assume centered quantization is used
|
||||
/// (and because C++ imgui does it too). See:
|
||||
/// - https://github.com/ocornut/imgui/blob/e28b51786eae60f32c18214658c15952639085a2/imgui_internal.h#L218
|
||||
/// - https://cbloomrants.blogspot.com/2020/09/topics-in-quantization-for-games.html
|
||||
/// (see `quantize_centered`)
|
||||
#[inline]
|
||||
pub(crate) fn f32_to_u8_sat(f: f32) -> u8 {
|
||||
let f = saturate(f) * 255.0 + 0.5;
|
||||
// Safety: `saturate`'s result is between 0.0 and 1.0 (never NaN even for
|
||||
// NaN input), and so for all inputs, `saturate(f) * 255.0 + 0.5` is inside
|
||||
// `0.5 ..= 255.5`.
|
||||
//
|
||||
// This is verified for all f32 in `test_f32_to_u8_sat_exhaustive`.
|
||||
//
|
||||
// Also note that LLVM doesn't bother trying to figure this out so the
|
||||
// unchecked does actually help. (That said, this likely doesn't matter
|
||||
// for imgui-rs, but I had this code in another project and it felt
|
||||
// silly to needlessly pessimize it).
|
||||
unsafe { f.to_int_unchecked() }
|
||||
}
|
||||
|
||||
/// Opposite of `f32_to_u8_sat`. Since we assume centered quantization, this is
|
||||
/// equivalent to dividing by 255 (or, multiplying by 1.0/255.0)
|
||||
#[inline]
|
||||
pub(crate) fn u8_to_f32_sat(u: u8) -> f32 {
|
||||
(u as f32) * (1.0 / 255.0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_sat() {
|
||||
assert_eq!(saturate(1.0), 1.0);
|
||||
assert_eq!(saturate(0.5), 0.5);
|
||||
assert_eq!(saturate(0.0), 0.0);
|
||||
assert_eq!(saturate(-1.0), 0.0);
|
||||
// next float from 1.0
|
||||
assert_eq!(saturate(1.0 + f32::EPSILON), 1.0);
|
||||
// prev float from 0.0 (Well, from -0.0)
|
||||
assert_eq!(saturate(-f32::MIN_POSITIVE), 0.0);
|
||||
// some NaNs.
|
||||
assert_eq!(saturate(f32::NAN), 0.0);
|
||||
assert_eq!(saturate(-f32::NAN), 0.0);
|
||||
// neg zero comes through as +0
|
||||
assert_eq!(saturate(-0.0).to_bits(), 0.0f32.to_bits());
|
||||
}
|
||||
|
||||
// Check that the unsafe in `f32_to_u8_sat` is fine for all f32 (and that the
|
||||
// comments I wrote about `saturate` are actually true). This is way too slow in
|
||||
// debug mode, but finishes in ~15s on my machine for release (just this test).
|
||||
// This is tested in CI, but will only run if invoked manually with something
|
||||
// like: `cargo test -p imgui --release -- --ignored`.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_f32_to_u8_sat_exhaustive() {
|
||||
for f in (0..=u32::MAX).map(f32::from_bits) {
|
||||
let v = saturate(f);
|
||||
assert!(
|
||||
(0.0..=1.0).contains(&v) && (v.to_bits() != (-0.0f32).to_bits()),
|
||||
"sat({} [e.g. {:#x}]) => {} [e.g {:#x}]",
|
||||
f,
|
||||
f.to_bits(),
|
||||
v,
|
||||
v.to_bits(),
|
||||
);
|
||||
let sat = v * 255.0 + 0.5;
|
||||
// Note: This checks what's required by is the safety predicate for
|
||||
// `f32::to_int_unchecked`:
|
||||
// https://doc.rust-lang.org/std/primitive.f32.html#method.to_int_unchecked
|
||||
assert!(
|
||||
sat.trunc() >= 0.0 && sat.trunc() <= (u8::MAX as f32) && sat.is_finite(),
|
||||
"f32_to_u8_sat({} [e.g. {:#x}]) would be UB!",
|
||||
f,
|
||||
f.to_bits(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturate_all_u8s() {
|
||||
for u in 0..=u8::MAX {
|
||||
let v = f32_to_u8_sat(u8_to_f32_sat(u));
|
||||
assert_eq!(u, v);
|
||||
}
|
||||
}
|
68
plugins/libimhex-rust/imgui-rs/src/columns.rs
Normal file
68
plugins/libimhex-rust/imgui-rs/src/columns.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// # Columns
|
||||
impl<'ui> Ui<'ui> {
|
||||
#[doc(alias = "Columns")]
|
||||
pub fn columns(&self, count: i32, id: impl AsRef<str>, border: bool) {
|
||||
unsafe { sys::igColumns(count, self.scratch_txt(id), border) }
|
||||
}
|
||||
/// Switches to the next column.
|
||||
///
|
||||
/// If the current row is finished, switches to first column of the next row
|
||||
#[doc(alias = "NextColumn")]
|
||||
pub fn next_column(&self) {
|
||||
unsafe { sys::igNextColumn() }
|
||||
}
|
||||
/// Returns the index of the current column
|
||||
#[doc(alias = "GetColumnIndex")]
|
||||
pub fn current_column_index(&self) -> i32 {
|
||||
unsafe { sys::igGetColumnIndex() }
|
||||
}
|
||||
/// Returns the width of the current column (in pixels)
|
||||
#[doc(alias = "GetColumnWidth")]
|
||||
pub fn current_column_width(&self) -> f32 {
|
||||
unsafe { sys::igGetColumnWidth(-1) }
|
||||
}
|
||||
#[doc(alias = "GetColumnWidth")]
|
||||
/// Returns the width of the given column (in pixels)
|
||||
pub fn column_width(&self, column_index: i32) -> f32 {
|
||||
unsafe { sys::igGetColumnWidth(column_index) }
|
||||
}
|
||||
#[doc(alias = "SetColumnWidth")]
|
||||
/// Sets the width of the current column (in pixels)
|
||||
pub fn set_current_column_width(&self, width: f32) {
|
||||
unsafe { sys::igSetColumnWidth(-1, width) };
|
||||
}
|
||||
#[doc(alias = "SetColumnWidth")]
|
||||
/// Sets the width of the given column (in pixels)
|
||||
pub fn set_column_width(&self, column_index: i32, width: f32) {
|
||||
unsafe { sys::igSetColumnWidth(column_index, width) };
|
||||
}
|
||||
/// Returns the offset of the current column (in pixels from the left side of the content
|
||||
/// region)
|
||||
#[doc(alias = "GetColumnOffset")]
|
||||
pub fn current_column_offset(&self) -> f32 {
|
||||
unsafe { sys::igGetColumnOffset(-1) }
|
||||
}
|
||||
/// Returns the offset of the given column (in pixels from the left side of the content region)
|
||||
#[doc(alias = "GetColumnOffset")]
|
||||
pub fn column_offset(&self, column_index: i32) -> f32 {
|
||||
unsafe { sys::igGetColumnOffset(column_index) }
|
||||
}
|
||||
/// Sets the offset of the current column (in pixels from the left side of the content region)
|
||||
#[doc(alias = "SetColumnOffset")]
|
||||
pub fn set_current_column_offset(&self, offset_x: f32) {
|
||||
unsafe { sys::igSetColumnOffset(-1, offset_x) };
|
||||
}
|
||||
/// Sets the offset of the given column (in pixels from the left side of the content region)
|
||||
#[doc(alias = "SetColumnOffset")]
|
||||
pub fn set_column_offset(&self, column_index: i32, offset_x: f32) {
|
||||
unsafe { sys::igSetColumnOffset(column_index, offset_x) };
|
||||
}
|
||||
/// Returns the current amount of columns
|
||||
#[doc(alias = "GetColumnCount")]
|
||||
pub fn column_count(&self) -> i32 {
|
||||
unsafe { sys::igGetColumnsCount() }
|
||||
}
|
||||
}
|
582
plugins/libimhex-rust/imgui-rs/src/context.rs
Normal file
582
plugins/libimhex-rust/imgui-rs/src/context.rs
Normal file
@ -0,0 +1,582 @@
|
||||
use parking_lot::ReentrantMutex;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::Drop;
|
||||
use std::path::PathBuf;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::clipboard::{ClipboardBackend, ClipboardContext};
|
||||
use crate::fonts::atlas::{FontAtlas, FontAtlasRefMut, FontId, SharedFontAtlas};
|
||||
use crate::io::Io;
|
||||
use crate::style::Style;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// An imgui-rs context.
|
||||
///
|
||||
/// A context needs to be created to access most library functions. Due to current Dear ImGui
|
||||
/// design choices, at most one active Context can exist at any time. This limitation will likely
|
||||
/// be removed in a future Dear ImGui version.
|
||||
///
|
||||
/// If you need more than one context, you can use suspended contexts. As long as only one context
|
||||
/// is active at a time, it's possible to have multiple independent contexts.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a new active context:
|
||||
/// ```
|
||||
/// let ctx = imgui::Context::create();
|
||||
/// // ctx is dropped naturally when it goes out of scope, which deactivates and destroys the
|
||||
/// // context
|
||||
/// ```
|
||||
///
|
||||
/// Never try to create an active context when another one is active:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// let ctx1 = imgui::Context::create();
|
||||
///
|
||||
/// let ctx2 = imgui::Context::create(); // PANIC
|
||||
/// ```
|
||||
///
|
||||
/// Suspending an active context allows you to create another active context:
|
||||
///
|
||||
/// ```
|
||||
/// let ctx1 = imgui::Context::create();
|
||||
/// let suspended1 = ctx1.suspend();
|
||||
/// let ctx2 = imgui::Context::create(); // this is now OK
|
||||
/// ```
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
raw: *mut sys::ImGuiContext,
|
||||
shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>,
|
||||
ini_filename: Option<CString>,
|
||||
log_filename: Option<CString>,
|
||||
platform_name: Option<CString>,
|
||||
renderer_name: Option<CString>,
|
||||
// we need to box this because we hand imgui a pointer to it,
|
||||
// and we don't want to deal with finding `clipboard_ctx`.
|
||||
// we also put it in an unsafecell since we're going to give
|
||||
// imgui a mutable pointer to it.
|
||||
clipboard_ctx: Box<UnsafeCell<ClipboardContext>>,
|
||||
}
|
||||
|
||||
// This mutex needs to be used to guard all public functions that can affect the underlying
|
||||
// Dear ImGui active context
|
||||
static CTX_MUTEX: ReentrantMutex<()> = parking_lot::const_reentrant_mutex(());
|
||||
|
||||
fn clear_current_context() {
|
||||
unsafe {
|
||||
sys::igSetCurrentContext(ptr::null_mut());
|
||||
}
|
||||
}
|
||||
fn no_current_context() -> bool {
|
||||
let ctx = unsafe { sys::igGetCurrentContext() };
|
||||
ctx.is_null()
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Creates a new active imgui-rs context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an active context already exists
|
||||
#[doc(alias = "CreateContext")]
|
||||
pub fn create() -> Self {
|
||||
Self::create_internal(None)
|
||||
}
|
||||
/// Creates a new active imgui-rs context with a shared font atlas.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an active context already exists
|
||||
#[doc(alias = "CreateContext")]
|
||||
pub fn create_with_shared_font_atlas(shared_font_atlas: Rc<RefCell<SharedFontAtlas>>) -> Self {
|
||||
Self::create_internal(Some(shared_font_atlas))
|
||||
}
|
||||
/// Suspends this context so another context can be the active context.
|
||||
#[doc(alias = "CreateContext")]
|
||||
pub fn suspend(self) -> SuspendedContext {
|
||||
let _guard = CTX_MUTEX.lock();
|
||||
assert!(
|
||||
self.is_current_context(),
|
||||
"context to be suspended is not the active context"
|
||||
);
|
||||
clear_current_context();
|
||||
SuspendedContext(self)
|
||||
}
|
||||
/// Returns the path to the ini file, or None if not set
|
||||
pub fn ini_filename(&self) -> Option<PathBuf> {
|
||||
let io = self.io();
|
||||
if io.ini_filename.is_null() {
|
||||
None
|
||||
} else {
|
||||
let s = unsafe { CStr::from_ptr(io.ini_filename) };
|
||||
Some(PathBuf::from(s.to_str().ok()?))
|
||||
}
|
||||
}
|
||||
/// Sets the path to the ini file (default is "imgui.ini")
|
||||
///
|
||||
/// Pass None to disable automatic .Ini saving.
|
||||
pub fn set_ini_filename<T: Into<Option<PathBuf>>>(&mut self, ini_filename: T) {
|
||||
let ini_filename: Option<PathBuf> = ini_filename.into();
|
||||
let ini_filename = ini_filename.and_then(|v| CString::new(v.to_str()?).ok());
|
||||
|
||||
self.io_mut().ini_filename = ini_filename
|
||||
.as_ref()
|
||||
.map(|x| x.as_ptr())
|
||||
.unwrap_or(ptr::null());
|
||||
self.ini_filename = ini_filename;
|
||||
}
|
||||
/// Returns the path to the log file, or None if not set
|
||||
// TODO: why do we return an `Option<PathBuf>` instead of an `Option<&Path>`?
|
||||
pub fn log_filename(&self) -> Option<PathBuf> {
|
||||
let io = self.io();
|
||||
if io.log_filename.is_null() {
|
||||
None
|
||||
} else {
|
||||
let cstr = unsafe { CStr::from_ptr(io.log_filename) };
|
||||
Some(PathBuf::from(cstr.to_str().ok()?))
|
||||
}
|
||||
}
|
||||
/// Sets the log filename (default is "imgui_log.txt").
|
||||
pub fn set_log_filename<T: Into<Option<PathBuf>>>(&mut self, log_filename: T) {
|
||||
let log_filename = log_filename
|
||||
.into()
|
||||
.and_then(|v| CString::new(v.to_str()?).ok());
|
||||
|
||||
self.io_mut().log_filename = log_filename
|
||||
.as_ref()
|
||||
.map(|x| x.as_ptr())
|
||||
.unwrap_or(ptr::null());
|
||||
self.log_filename = log_filename;
|
||||
}
|
||||
/// Returns the backend platform name, or None if not set
|
||||
pub fn platform_name(&self) -> Option<&str> {
|
||||
let io = self.io();
|
||||
if io.backend_platform_name.is_null() {
|
||||
None
|
||||
} else {
|
||||
let cstr = unsafe { CStr::from_ptr(io.backend_platform_name) };
|
||||
cstr.to_str().ok()
|
||||
}
|
||||
}
|
||||
/// Sets the backend platform name
|
||||
pub fn set_platform_name<T: Into<Option<String>>>(&mut self, platform_name: T) {
|
||||
let platform_name: Option<CString> =
|
||||
platform_name.into().and_then(|v| CString::new(v).ok());
|
||||
self.io_mut().backend_platform_name = platform_name
|
||||
.as_ref()
|
||||
.map(|x| x.as_ptr())
|
||||
.unwrap_or(ptr::null());
|
||||
self.platform_name = platform_name;
|
||||
}
|
||||
/// Returns the backend renderer name, or None if not set
|
||||
pub fn renderer_name(&self) -> Option<&str> {
|
||||
let io = self.io();
|
||||
if io.backend_renderer_name.is_null() {
|
||||
None
|
||||
} else {
|
||||
let cstr = unsafe { CStr::from_ptr(io.backend_renderer_name) };
|
||||
cstr.to_str().ok()
|
||||
}
|
||||
}
|
||||
/// Sets the backend renderer name
|
||||
pub fn set_renderer_name<T: Into<Option<String>>>(&mut self, renderer_name: T) {
|
||||
let renderer_name: Option<CString> =
|
||||
renderer_name.into().and_then(|v| CString::new(v).ok());
|
||||
|
||||
self.io_mut().backend_renderer_name = renderer_name
|
||||
.as_ref()
|
||||
.map(|x| x.as_ptr())
|
||||
.unwrap_or(ptr::null());
|
||||
|
||||
self.renderer_name = renderer_name;
|
||||
}
|
||||
/// Loads settings from a string slice containing settings in .Ini file format
|
||||
#[doc(alias = "LoadIniSettingsFromMemory")]
|
||||
pub fn load_ini_settings(&mut self, data: &str) {
|
||||
unsafe { sys::igLoadIniSettingsFromMemory(data.as_ptr() as *const _, data.len()) }
|
||||
}
|
||||
/// Saves settings to a mutable string buffer in .Ini file format
|
||||
#[doc(alias = "SaveInitSettingsToMemory")]
|
||||
pub fn save_ini_settings(&mut self, buf: &mut String) {
|
||||
let data = unsafe { CStr::from_ptr(sys::igSaveIniSettingsToMemory(ptr::null_mut())) };
|
||||
buf.push_str(&data.to_string_lossy());
|
||||
}
|
||||
/// Sets the clipboard backend used for clipboard operations
|
||||
pub fn set_clipboard_backend<T: ClipboardBackend>(&mut self, backend: T) {
|
||||
let clipboard_ctx: Box<UnsafeCell<_>> = Box::new(ClipboardContext::new(backend).into());
|
||||
let io = self.io_mut();
|
||||
io.set_clipboard_text_fn = Some(crate::clipboard::set_clipboard_text);
|
||||
io.get_clipboard_text_fn = Some(crate::clipboard::get_clipboard_text);
|
||||
|
||||
io.clipboard_user_data = clipboard_ctx.get() as *mut _;
|
||||
self.clipboard_ctx = clipboard_ctx;
|
||||
}
|
||||
fn create_internal(shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>) -> Self {
|
||||
let _guard = CTX_MUTEX.lock();
|
||||
assert!(
|
||||
no_current_context(),
|
||||
"A new active context cannot be created, because another one already exists"
|
||||
);
|
||||
|
||||
let shared_font_atlas_ptr = match &shared_font_atlas {
|
||||
Some(shared_font_atlas) => {
|
||||
let borrowed_font_atlas = shared_font_atlas.borrow();
|
||||
borrowed_font_atlas.0
|
||||
}
|
||||
None => ptr::null_mut(),
|
||||
};
|
||||
// Dear ImGui implicitly sets the current context during igCreateContext if the current
|
||||
// context doesn't exist
|
||||
let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
|
||||
|
||||
Context {
|
||||
raw,
|
||||
shared_font_atlas,
|
||||
ini_filename: None,
|
||||
log_filename: None,
|
||||
platform_name: None,
|
||||
renderer_name: None,
|
||||
clipboard_ctx: Box::new(ClipboardContext::dummy().into()),
|
||||
}
|
||||
}
|
||||
fn is_current_context(&self) -> bool {
|
||||
let ctx = unsafe { sys::igGetCurrentContext() };
|
||||
self.raw == ctx
|
||||
}
|
||||
|
||||
/// Get a reference to the current context
|
||||
pub fn current() -> Option<ContextRef> {
|
||||
let _guard = CTX_MUTEX.lock();
|
||||
|
||||
let raw = unsafe { sys::igGetCurrentContext() };
|
||||
|
||||
if raw.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(ContextRef(ManuallyDrop::new(Context {
|
||||
raw,
|
||||
shared_font_atlas: None, // XXX: this might be needed tbh
|
||||
ini_filename: None,
|
||||
log_filename: None,
|
||||
platform_name: None,
|
||||
renderer_name: None,
|
||||
clipboard_ctx: Box::new(ClipboardContext::dummy().into()),
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a [`Context`] object
|
||||
#[derive(Debug)]
|
||||
pub struct ContextRef(ManuallyDrop<Context>);
|
||||
|
||||
impl core::ops::Deref for ContextRef {
|
||||
type Target = Context;
|
||||
|
||||
fn deref(&self) -> &Context {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for ContextRef {
|
||||
fn deref_mut(&mut self) -> &mut Context {
|
||||
&mut *self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
#[doc(alias = "DestroyContext")]
|
||||
fn drop(&mut self) {
|
||||
let _guard = CTX_MUTEX.lock();
|
||||
// If this context is the active context, Dear ImGui automatically deactivates it during
|
||||
// destruction
|
||||
unsafe {
|
||||
sys::igDestroyContext(self.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A suspended imgui-rs context.
|
||||
///
|
||||
/// A suspended context retains its state, but is not usable without activating it first.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Suspended contexts are not directly very useful, but you can activate them:
|
||||
///
|
||||
/// ```
|
||||
/// let suspended = imgui::SuspendedContext::create();
|
||||
/// match suspended.activate() {
|
||||
/// Ok(ctx) => {
|
||||
/// // ctx is now the active context
|
||||
/// },
|
||||
/// Err(suspended) => {
|
||||
/// // activation failed, so you get the suspended context back
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct SuspendedContext(Context);
|
||||
|
||||
impl SuspendedContext {
|
||||
/// Creates a new suspended imgui-rs context.
|
||||
#[doc(alias = "CreateContext")]
|
||||
pub fn create() -> Self {
|
||||
Self::create_internal(None)
|
||||
}
|
||||
/// Creates a new suspended imgui-rs context with a shared font atlas.
|
||||
pub fn create_with_shared_font_atlas(shared_font_atlas: Rc<RefCell<SharedFontAtlas>>) -> Self {
|
||||
Self::create_internal(Some(shared_font_atlas))
|
||||
}
|
||||
/// Attempts to activate this suspended context.
|
||||
///
|
||||
/// If there is no active context, this suspended context is activated and `Ok` is returned,
|
||||
/// containing the activated context.
|
||||
/// If there is already an active context, nothing happens and `Err` is returned, containing
|
||||
/// the original suspended context.
|
||||
#[doc(alias = "SetCurrentContext")]
|
||||
pub fn activate(self) -> Result<Context, SuspendedContext> {
|
||||
let _guard = CTX_MUTEX.lock();
|
||||
if no_current_context() {
|
||||
unsafe {
|
||||
sys::igSetCurrentContext(self.0.raw);
|
||||
}
|
||||
Ok(self.0)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
fn create_internal(shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>) -> Self {
|
||||
let _guard = CTX_MUTEX.lock();
|
||||
let raw = unsafe { sys::igCreateContext(ptr::null_mut()) };
|
||||
let ctx = Context {
|
||||
raw,
|
||||
shared_font_atlas,
|
||||
ini_filename: None,
|
||||
log_filename: None,
|
||||
platform_name: None,
|
||||
renderer_name: None,
|
||||
clipboard_ctx: Box::new(ClipboardContext::dummy().into()),
|
||||
};
|
||||
if ctx.is_current_context() {
|
||||
// Oops, the context was activated -> deactivate
|
||||
clear_current_context();
|
||||
}
|
||||
SuspendedContext(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one_context() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let _ctx = Context::create();
|
||||
assert!(!no_current_context());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_clears_current_context() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
{
|
||||
let _ctx1 = Context::create();
|
||||
assert!(!no_current_context());
|
||||
}
|
||||
assert!(no_current_context());
|
||||
{
|
||||
let _ctx2 = Context::create();
|
||||
assert!(!no_current_context());
|
||||
}
|
||||
assert!(no_current_context());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_suspended() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let ctx = Context::create();
|
||||
let _suspended = SuspendedContext::create();
|
||||
assert!(ctx.is_current_context());
|
||||
::std::mem::drop(_suspended);
|
||||
assert!(ctx.is_current_context());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_suspend() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let ctx = Context::create();
|
||||
assert!(!no_current_context());
|
||||
let _suspended = ctx.suspend();
|
||||
assert!(no_current_context());
|
||||
let _ctx2 = Context::create();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_suspended() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let suspended = Context::create().suspend();
|
||||
assert!(no_current_context());
|
||||
let ctx2 = Context::create();
|
||||
::std::mem::drop(suspended);
|
||||
assert!(ctx2.is_current_context());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_suspend_activate() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let suspended = Context::create().suspend();
|
||||
assert!(no_current_context());
|
||||
let ctx = suspended.activate().unwrap();
|
||||
assert!(ctx.is_current_context());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_suspend_failure() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let suspended = Context::create().suspend();
|
||||
let _ctx = Context::create();
|
||||
assert!(suspended.activate().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_font_atlas() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let atlas = Rc::new(RefCell::new(SharedFontAtlas::create()));
|
||||
let suspended1 = SuspendedContext::create_with_shared_font_atlas(atlas.clone());
|
||||
let mut ctx2 = Context::create_with_shared_font_atlas(atlas);
|
||||
{
|
||||
let _borrow = ctx2.fonts();
|
||||
}
|
||||
let _suspended2 = ctx2.suspend();
|
||||
let mut ctx = suspended1.activate().unwrap();
|
||||
let _borrow = ctx.fonts();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_shared_font_atlas_borrow_panic() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let atlas = Rc::new(RefCell::new(SharedFontAtlas::create()));
|
||||
let _suspended = SuspendedContext::create_with_shared_font_atlas(atlas.clone());
|
||||
let mut ctx = Context::create_with_shared_font_atlas(atlas.clone());
|
||||
let _borrow1 = atlas.borrow();
|
||||
let _borrow2 = ctx.fonts();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ini_load_save() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx();
|
||||
let data = "[Window][Debug##Default]
|
||||
Pos=60,60
|
||||
Size=400,400
|
||||
Collapsed=0";
|
||||
ctx.load_ini_settings(data);
|
||||
let mut buf = String::new();
|
||||
ctx.save_ini_settings(&mut buf);
|
||||
assert_eq!(data.trim(), buf.trim());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_ini_filename() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let ctx = Context::create();
|
||||
assert_eq!(ctx.ini_filename(), Some(PathBuf::from("imgui.ini")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_ini_filename() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx();
|
||||
ctx.set_ini_filename(Some(PathBuf::from("test.ini")));
|
||||
assert_eq!(ctx.ini_filename(), Some(PathBuf::from("test.ini")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_log_filename() {
|
||||
let _guard = crate::test::TEST_MUTEX.lock();
|
||||
let ctx = Context::create();
|
||||
assert_eq!(ctx.log_filename(), Some(PathBuf::from("imgui_log.txt")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_log_filename() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx();
|
||||
ctx.set_log_filename(Some(PathBuf::from("test.log")));
|
||||
assert_eq!(ctx.log_filename(), Some(PathBuf::from("test.log")));
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Returns an immutable reference to the inputs/outputs object
|
||||
pub fn io(&self) -> &Io {
|
||||
unsafe {
|
||||
// safe because Io is a transparent wrapper around sys::ImGuiIO
|
||||
&*(sys::igGetIO() as *const Io)
|
||||
}
|
||||
}
|
||||
/// Returns a mutable reference to the inputs/outputs object
|
||||
pub fn io_mut(&mut self) -> &mut Io {
|
||||
unsafe {
|
||||
// safe because Io is a transparent wrapper around sys::ImGuiIO
|
||||
&mut *(sys::igGetIO() as *mut Io)
|
||||
}
|
||||
}
|
||||
/// Returns an immutable reference to the user interface style
|
||||
#[doc(alias = "GetStyle")]
|
||||
pub fn style(&self) -> &Style {
|
||||
unsafe {
|
||||
// safe because Style is a transparent wrapper around sys::ImGuiStyle
|
||||
&*(sys::igGetStyle() as *const Style)
|
||||
}
|
||||
}
|
||||
/// Returns a mutable reference to the user interface style
|
||||
#[doc(alias = "GetStyle")]
|
||||
pub fn style_mut(&mut self) -> &mut Style {
|
||||
unsafe {
|
||||
// safe because Style is a transparent wrapper around sys::ImGuiStyle
|
||||
&mut *(sys::igGetStyle() as *mut Style)
|
||||
}
|
||||
}
|
||||
/// Returns a mutable reference to the font atlas.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the context uses a shared font atlas that is already borrowed
|
||||
pub fn fonts(&mut self) -> FontAtlasRefMut<'_> {
|
||||
match self.shared_font_atlas {
|
||||
Some(ref font_atlas) => FontAtlasRefMut::Shared(font_atlas.borrow_mut()),
|
||||
None => unsafe {
|
||||
// safe because FontAtlas is a transparent wrapper around sys::ImFontAtlas
|
||||
let fonts = &mut *(self.io_mut().fonts as *mut FontAtlas);
|
||||
FontAtlasRefMut::Owned(fonts)
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Starts a new frame and returns an `Ui` instance for constructing a user interface.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the context uses a shared font atlas that is already borrowed
|
||||
#[doc(alias = "NewFame")]
|
||||
pub fn frame(&mut self) -> Ui<'_> {
|
||||
// Clear default font if it no longer exists. This could be an error in the future
|
||||
let default_font = self.io().font_default;
|
||||
if !default_font.is_null() && self.fonts().get_font(FontId(default_font)).is_none() {
|
||||
self.io_mut().font_default = ptr::null_mut();
|
||||
}
|
||||
// NewFrame/Render/EndFrame mutate the font atlas so we need exclusive access to it
|
||||
let font_atlas = self
|
||||
.shared_font_atlas
|
||||
.as_ref()
|
||||
.map(|font_atlas| font_atlas.borrow_mut());
|
||||
// TODO: precondition checks
|
||||
unsafe {
|
||||
sys::igNewFrame();
|
||||
}
|
||||
Ui {
|
||||
ctx: self,
|
||||
font_atlas,
|
||||
buffer: crate::UiBuffer::new(1024).into(),
|
||||
}
|
||||
}
|
||||
}
|
594
plugins/libimhex-rust/imgui-rs/src/drag_drop.rs
Normal file
594
plugins/libimhex-rust/imgui-rs/src/drag_drop.rs
Normal file
@ -0,0 +1,594 @@
|
||||
//! Structs to create a Drag and Drop sequence. Almost all structs are re-exported
|
||||
//! and can be accessed from the crate root; some additional utilities can be found in here.
|
||||
//!
|
||||
//! A DragDrop is a UI mechanism where users can appear to "drag"
|
||||
//! some data from one [source](DragDropSource) to one [target](DragDropTarget).
|
||||
//! A source and a target must both have some `name` identifier, which is declared when they
|
||||
//! are created. If these names are equal, then a `payload` of some kind
|
||||
//! will be given to the target caller whne the user releases their mouse button over
|
||||
//! the target (additionally, the UI will reflect that the payload *can* be deposited
|
||||
//! in the target).
|
||||
//!
|
||||
//! The complexity of this implementation is primarily in managing this payload. Users
|
||||
//! can provide three different kinds of payloads:
|
||||
//!
|
||||
//! 1. Users can give an [empty payload](DragDropPayloadEmpty) with [begin](DragDropSource::begin).
|
||||
//! This payload type is essentially just a notification system, but using some shared state,
|
||||
//! this can be reasonably powerful, and is the safest way to transfer non-Copy data offered
|
||||
//! right now.
|
||||
//! 2. Users can give a [simple Copy payload](DragDropPayloadPod) with [begin](DragDropSource::begin_payload).
|
||||
//! This allows users to copy data to Dear ImGui, which will take ownership over it, and then be given
|
||||
//! it back to the Target. Please note: users are of course free to not drop any drag (cancel a drag),
|
||||
//! so this data could easily be lost forever. Our `'static + Copy` bound is intended to keep users
|
||||
//! to simplistic types.
|
||||
//! 3. An unsafe implementation is provided which allows for any data to be unsafely copied. Note that once
|
||||
//! you use this method, the safe implementations in #1 and #2 can create memory unsafety problems; notably,
|
||||
//! they both assume that a payload has certain header information within it.
|
||||
//!
|
||||
//! For examples of each payload type, see [DragDropSource].
|
||||
use std::{any, ffi, marker::PhantomData};
|
||||
|
||||
use crate::{sys, Condition, Ui};
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags!(
|
||||
/// Flags for igBeginDragDropSource(), igAcceptDragDropPayload()
|
||||
#[repr(transparent)]
|
||||
pub struct DragDropFlags: u32 {
|
||||
/// By default, a successful call to igBeginDragDropSource opens a tooltip so you can
|
||||
/// display a preview or description of the source contents. This flag disable this
|
||||
/// behavior.
|
||||
const SOURCE_NO_PREVIEW_TOOLTIP = sys::ImGuiDragDropFlags_SourceNoPreviewTooltip;
|
||||
/// By default, when dragging we clear data so that igIsItemHovered() will return false, to
|
||||
/// avoid subsequent user code submitting tooltips. This flag disable this behavior so you
|
||||
/// can still call igIsItemHovered() on the source item.
|
||||
const SOURCE_NO_DISABLE_HOVER = sys::ImGuiDragDropFlags_SourceNoDisableHover;
|
||||
/// Disable the behavior that allows to open tree nodes and collapsing header by holding
|
||||
/// over them while dragging a source item.
|
||||
const SOURCE_NO_HOLD_TO_OPEN_OTHERS = sys::ImGuiDragDropFlags_SourceNoHoldToOpenOthers;
|
||||
/// Allow items such as igText(), igImage() that have no unique identifier to be used as
|
||||
/// drag source, by manufacturing a temporary identifier based on their window-relative
|
||||
/// position. This is extremely unusual within the dear imgui ecosystem and so we made it
|
||||
/// explicit.
|
||||
const SOURCE_ALLOW_NULL_ID = sys::ImGuiDragDropFlags_SourceAllowNullID;
|
||||
/// External source (from outside of imgui), won't attempt to read current item/window
|
||||
/// info. Will always return true. Only one Extern source can be active simultaneously.
|
||||
const SOURCE_EXTERN = sys::ImGuiDragDropFlags_SourceExtern;
|
||||
/// Automatically expire the payload if the source ceases to be submitted (otherwise
|
||||
/// payloads are persisting while being dragged)
|
||||
const SOURCE_AUTO_EXPIRE_PAYLOAD = sys::ImGuiDragDropFlags_SourceAutoExpirePayload;
|
||||
/// igAcceptDragDropPayload() will returns true even before the mouse button is released.
|
||||
/// You can then call igIsDelivery() to test if the payload needs to be delivered.
|
||||
const ACCEPT_BEFORE_DELIVERY = sys::ImGuiDragDropFlags_AcceptBeforeDelivery;
|
||||
/// Do not draw the default highlight rectangle when hovering over target.
|
||||
const ACCEPT_NO_DRAW_DEFAULT_RECT = sys::ImGuiDragDropFlags_AcceptNoDrawDefaultRect;
|
||||
/// Request hiding the igBeginDragDropSource tooltip from the igBeginDragDropTarget site.
|
||||
const ACCEPT_NO_PREVIEW_TOOLTIP = sys::ImGuiDragDropFlags_AcceptNoPreviewTooltip;
|
||||
/// For peeking ahead and inspecting the payload before delivery. This is just a convenience
|
||||
/// flag for the intersection of `ACCEPT_BEFORE_DELIVERY` and `ACCEPT_NO_DRAW_DEFAULT_RECT`
|
||||
const ACCEPT_PEEK_ONLY = sys::ImGuiDragDropFlags_AcceptPeekOnly;
|
||||
}
|
||||
);
|
||||
|
||||
/// Creates a source for drag drop data out of the last ID created.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// fn show_ui(ui: &Ui<'_>) {
|
||||
/// ui.button("Hello, I am a drag source!");
|
||||
///
|
||||
/// // Creates an empty DragSource with no tooltip
|
||||
/// DragDropSource::new("BUTTON_DRAG").begin(ui);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Notice especially the `"BUTTON_DRAG"` name -- this is the identifier of this
|
||||
/// DragDropSource; [DragDropTarget]'s will specify an identifier to *receive*, and these
|
||||
/// names must match up. A single item should only have one [DragDropSource], though
|
||||
/// a target may have multiple different targets.
|
||||
///
|
||||
/// DropDropSources don't do anything until you use one of the three `begin_` methods
|
||||
/// on this struct. Each of these methods describes how you handle the Payload which ImGui
|
||||
/// will manage, and then give to a [DragDropTarget], which will received the payload. The
|
||||
/// simplest and safest Payload is the empty payload, created with [begin](Self::begin).
|
||||
#[derive(Debug)]
|
||||
pub struct DragDropSource<T> {
|
||||
name: T,
|
||||
flags: DragDropFlags,
|
||||
cond: Condition,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> DragDropSource<T> {
|
||||
/// Creates a new [DragDropSource] with no flags and the `Condition::Always` with the given name.
|
||||
/// ImGui refers to this `name` field as a `type`, but really it's just an identifier to match up
|
||||
/// Source/Target for DragDrop.
|
||||
pub fn new(name: T) -> Self {
|
||||
Self {
|
||||
name,
|
||||
flags: DragDropFlags::empty(),
|
||||
cond: Condition::Always,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the flags on the [DragDropSource]. Only the flags `SOURCE_NO_PREVIEW_TOOLTIP`,
|
||||
/// `SOURCE_NO_DISABLE_HOVER`, `SOURCE_NO_HOLD_TO_OPEN_OTHERS`, `SOURCE_ALLOW_NULL_ID`,
|
||||
/// `SOURCE_EXTERN`, `SOURCE_AUTO_EXPIRE_PAYLOAD` make semantic sense, but any other flags will
|
||||
/// be accepted without panic.
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: DragDropFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the condition on the [DragDropSource]. Defaults to [Always](Condition::Always).
|
||||
#[inline]
|
||||
pub fn condition(mut self, cond: Condition) -> Self {
|
||||
self.cond = cond;
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates the source of a drag and returns a handle on the tooltip.
|
||||
/// This handle can be immediately dropped without binding it, in which case a default empty
|
||||
/// circle will be used for the "blank" tooltip as this item is being dragged around.
|
||||
///
|
||||
/// Otherwise, use this tooltip to add data which will display as this item is dragged.
|
||||
/// If `SOURCE_NO_PREVIEW_TOOLTIP` is enabled, however, no preview will be displayed
|
||||
/// and this returned token does nothing. Additionally, a given target may use the flag
|
||||
/// `ACCEPT_NO_PREVIEW_TOOLTIP`, which will also prevent this tooltip from being shown.
|
||||
///
|
||||
/// This drag has no payload, but is still probably the most useful way in imgui-rs to handle payloads.
|
||||
/// Using `once_cell` or some shared data, this pattern can be very powerful:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// fn show_ui(ui: &Ui<'_>, drop_message: &mut Option<String>) {
|
||||
/// ui.button("Drag me!");
|
||||
///
|
||||
/// let drag_drop_name = "Test Drag";
|
||||
///
|
||||
/// // drag drop SOURCE
|
||||
/// if DragDropSource::new(drag_drop_name).begin(ui).is_some() {
|
||||
/// // warning -- this would allocate every frame if `DragDropSource` has
|
||||
/// // condition `Always`, which it does by default. We're okay with that for
|
||||
/// // this example, but real code probably wouldn't want to allocate so much.
|
||||
/// *drop_message = Some("Test Payload".to_string());
|
||||
/// }
|
||||
///
|
||||
/// ui.button("Target me!");
|
||||
///
|
||||
/// // drag drop TARGET
|
||||
/// if let Some(target) = imgui::DragDropTarget::new(ui) {
|
||||
/// if target
|
||||
/// .accept_payload_empty(drag_drop_name, DragDropFlags::empty())
|
||||
/// .is_some()
|
||||
/// {
|
||||
/// let msg = drop_message.take().unwrap();
|
||||
/// assert_eq!(msg, "Test Payload");
|
||||
/// }
|
||||
///
|
||||
/// target.pop();
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// In the above, you'll see how the payload is really just a message passing service.
|
||||
/// If you want to pass a simple integer or other "plain old data", take a look at
|
||||
/// [begin_payload](Self::begin_payload).
|
||||
#[inline]
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<DragDropSourceToolTip<'ui>> {
|
||||
self.begin_payload(ui, ())
|
||||
}
|
||||
|
||||
/// Creates the source of a drag and returns a handle on the tooltip.
|
||||
/// This handle can be immediately dropped without binding it, in which case a default empty
|
||||
/// circle will be used for the "blank" tooltip as this item is being dragged around.
|
||||
///
|
||||
/// Otherwise, use this tooltip to add data which will display as this item is dragged.
|
||||
/// If `SOURCE_NO_PREVIEW_TOOLTIP` is enabled, however, no preview will be displayed
|
||||
/// and this returned token does nothing. Additionally, a given target may use the flag
|
||||
/// `ACCEPT_NO_PREVIEW_TOOLTIP`, which will also prevent this tooltip from being shown.
|
||||
///
|
||||
/// This function also takes a payload in the form of `T: Copy + 'static`. ImGui will
|
||||
/// memcpy this data immediately to an internally held buffer, and will return the data
|
||||
/// to [DragDropTarget].
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// fn show_ui(ui: &Ui<'_>) {
|
||||
/// ui.button("Drag me!");
|
||||
///
|
||||
/// let drag_drop_name = "Test Drag";
|
||||
/// let msg_to_send = "hello there sailor";
|
||||
///
|
||||
/// // drag drop SOURCE
|
||||
/// if let Some(tooltip) = DragDropSource::new(drag_drop_name).begin_payload(ui, msg_to_send) {
|
||||
/// ui.text("Sending message!");
|
||||
/// tooltip.end();
|
||||
/// }
|
||||
///
|
||||
/// ui.button("Target me!");
|
||||
///
|
||||
/// // drag drop TARGET
|
||||
/// if let Some(target) = imgui::DragDropTarget::new(ui) {
|
||||
/// if let Some(Ok(payload_data)) = target
|
||||
/// .accept_payload::<&'static str, _>(drag_drop_name, DragDropFlags::empty())
|
||||
/// {
|
||||
/// let msg = payload_data.data;
|
||||
/// assert_eq!(msg, msg_to_send);
|
||||
/// }
|
||||
///
|
||||
/// target.pop();
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn begin_payload<'ui, P: Copy + 'static>(
|
||||
self,
|
||||
ui: &Ui<'ui>,
|
||||
payload: P,
|
||||
) -> Option<DragDropSourceToolTip<'ui>> {
|
||||
unsafe {
|
||||
let payload = TypedPayload::new(payload);
|
||||
self.begin_payload_unchecked(
|
||||
ui,
|
||||
&payload as *const _ as *const ffi::c_void,
|
||||
std::mem::size_of::<TypedPayload<P>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the source of a drag and returns a handle on the tooltip.
|
||||
/// This handle can be immediately dropped without binding it, in which case a default empty
|
||||
/// circle will be used for the "blank" tooltip as this item is being dragged around.
|
||||
///
|
||||
/// Otherwise, use this tooltip to add data which will display as this item is dragged.
|
||||
/// If `SOURCE_NO_PREVIEW_TOOLTIP` is enabled, however, no preview will be displayed
|
||||
/// and this returned token does nothing. Additionally, a given target may use the flag
|
||||
/// `ACCEPT_NO_PREVIEW_TOOLTIP`, which will also prevent this tooltip from being shown.
|
||||
///
|
||||
/// This function also takes a payload of any `*const T`. ImGui will
|
||||
/// memcpy this data immediately to an internally held buffer, and will return the data
|
||||
/// to [DragDropTarget].
|
||||
///
|
||||
/// ## Safety
|
||||
/// This function itself will not cause a panic, but using it directly opts you into
|
||||
/// managing the lifetime of the pointer provided yourself. Dear ImGui will execute a memcpy on
|
||||
/// the data passed in with the size (in bytes) given, but this is, of course, just a copy,
|
||||
/// so if you pass in an `&String`, for example, the underlying String data will not be cloned,
|
||||
/// and could easily dangle if the `String` is dropped.
|
||||
///
|
||||
/// Moreover, if `Condition::Always` is set (as it is by default), you will be copying in your data
|
||||
/// every time this function is ran in your update loop, which if it involves an allocating and then
|
||||
/// handing the allocation to ImGui, would result in a significant amount of data created.
|
||||
///
|
||||
/// Overall, users should be very sure that this function is needed before they reach for it, and instead
|
||||
/// should consider either [begin](Self::begin) or [begin_payload](Self::begin_payload).
|
||||
#[inline]
|
||||
pub unsafe fn begin_payload_unchecked<'ui>(
|
||||
&self,
|
||||
ui: &Ui<'ui>,
|
||||
ptr: *const ffi::c_void,
|
||||
size: usize,
|
||||
) -> Option<DragDropSourceToolTip<'ui>> {
|
||||
let should_begin = sys::igBeginDragDropSource(self.flags.bits() as i32);
|
||||
|
||||
if should_begin {
|
||||
sys::igSetDragDropPayload(ui.scratch_txt(&self.name), ptr, size, self.cond as i32);
|
||||
|
||||
Some(DragDropSourceToolTip::push())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper struct for RAII drap-drop support.
|
||||
pub struct DragDropSourceToolTip<'ui>(PhantomData<Ui<'ui>>);
|
||||
|
||||
impl DragDropSourceToolTip<'_> {
|
||||
/// Creates a new tooltip internally.
|
||||
#[inline]
|
||||
fn push() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
|
||||
/// Ends the tooltip directly. You could choose to simply allow this to drop
|
||||
/// by not calling this, which will also be fine.
|
||||
#[inline]
|
||||
pub fn end(self) {
|
||||
// left empty to invoke drop...
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DragDropSourceToolTip<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { sys::igEndDragDropSource() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a target for drag drop data out of the last ID created.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// fn show_ui(ui: &Ui<'_>) {
|
||||
/// // Drop something on this button please!
|
||||
/// ui.button("Hello, I am a drag Target!");
|
||||
///
|
||||
/// if let Some(target) = DragDropTarget::new(ui) {
|
||||
/// // accepting an empty payload (which is really just raising an event)
|
||||
/// if let Some(_payload_data) = target.accept_payload_empty("BUTTON_DRAG", DragDropFlags::empty()) {
|
||||
/// println!("Nice job getting on the payload!");
|
||||
/// }
|
||||
///
|
||||
/// // and we can accept multiple, different types of payloads with one drop target.
|
||||
/// // this is a good pattern for handling different kinds of drag/drop situations with
|
||||
/// // different kinds of data in them.
|
||||
/// if let Some(Ok(payload_data)) = target.accept_payload::<usize, _>("BUTTON_ID", DragDropFlags::empty()) {
|
||||
/// println!("Our payload's data was {}", payload_data.data);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Notice especially the `"BUTTON_DRAG"` and `"BUTTON_ID"` name -- this is the identifier of this
|
||||
/// DragDropTarget; [DragDropSource]s will specify an identifier when they send a payload, and these
|
||||
/// names must match up. Notice how a target can have multiple acceptances on them -- this is a good
|
||||
/// pattern to handle multiple kinds of data which could be passed around.
|
||||
///
|
||||
/// DropDropTargets don't do anything until you use one of the three `accept_` methods
|
||||
/// on this struct. Each of these methods will spit out a _Payload struct with an increasing
|
||||
/// amount of information on the Payload. The absolute safest solution is [accept_payload_empty](Self::accept_payload_empty).
|
||||
#[derive(Debug)]
|
||||
pub struct DragDropTarget<'ui>(&'ui Ui<'ui>);
|
||||
|
||||
impl<'ui> DragDropTarget<'ui> {
|
||||
/// Creates a new DragDropTarget, holding the [Ui]'s lifetime for the duration
|
||||
/// of its existence. This is required since this struct runs some code on its Drop
|
||||
/// to end the DragDropTarget code.
|
||||
#[doc(alias = "BeginDragDropTarget")]
|
||||
pub fn new(ui: &'ui Ui<'ui>) -> Option<Self> {
|
||||
let should_begin = unsafe { sys::igBeginDragDropTarget() };
|
||||
if should_begin {
|
||||
Some(Self(ui))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Accepts an empty payload. This is the safest option for raising named events
|
||||
/// in the DragDrop API. See [DragDropSource::begin] for more information on how you
|
||||
/// might use this pattern.
|
||||
///
|
||||
/// Note: If you began this operation with `begin_payload_unchecked` it always incorrect
|
||||
/// to use this function. Use `accept_payload_unchecked` instead
|
||||
pub fn accept_payload_empty(
|
||||
&self,
|
||||
name: impl AsRef<str>,
|
||||
flags: DragDropFlags,
|
||||
) -> Option<DragDropPayloadEmpty> {
|
||||
self.accept_payload(name, flags)?
|
||||
.ok()
|
||||
.map(|payload_pod: DragDropPayloadPod<()>| DragDropPayloadEmpty {
|
||||
preview: payload_pod.preview,
|
||||
delivery: payload_pod.delivery,
|
||||
})
|
||||
}
|
||||
|
||||
/// Accepts a payload with plain old data in it. This returns a Result, since you can specify any
|
||||
/// type. The sent type must match the return type (via TypeId) to receive an `Ok`.
|
||||
///
|
||||
/// Note: If you began this operation with `begin_payload_unchecked` it always incorrect
|
||||
/// to use this function. Use `accept_payload_unchecked` instead
|
||||
pub fn accept_payload<T: 'static + Copy, Name: AsRef<str>>(
|
||||
&self,
|
||||
name: Name,
|
||||
flags: DragDropFlags,
|
||||
) -> Option<Result<DragDropPayloadPod<T>, PayloadIsWrongType>> {
|
||||
let output = unsafe { self.accept_payload_unchecked(name, flags) };
|
||||
|
||||
// convert the unsafe payload to our Result
|
||||
output.map(|unsafe_payload| {
|
||||
// sheering off the typeid...
|
||||
let received =
|
||||
unsafe { (unsafe_payload.data as *const TypedPayloadHeader).read_unaligned() };
|
||||
let expected = any::TypeId::of::<T>();
|
||||
|
||||
if received.type_id == expected {
|
||||
let data =
|
||||
unsafe { (unsafe_payload.data as *const TypedPayload<T>).read_unaligned() }
|
||||
.data;
|
||||
Ok(DragDropPayloadPod {
|
||||
data,
|
||||
preview: unsafe_payload.preview,
|
||||
delivery: unsafe_payload.delivery,
|
||||
})
|
||||
} else {
|
||||
Err(PayloadIsWrongType {
|
||||
received,
|
||||
expected: TypedPayloadHeader::new::<T>(),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Accepts a drag and drop payload which contains a raw pointer to [c_void](std::ffi::c_void)
|
||||
/// and a size in bytes. Users should generally avoid using this function
|
||||
/// if one of the safer variants is acceptable.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// Because this pointer comes from ImGui, absolutely no promises can be made on its
|
||||
/// contents, alignment, or lifetime. Interacting with it is therefore extremely unsafe.
|
||||
/// **Important:** a special note needs to be made to the [ACCEPT_BEFORE_DELIVERY](DragDropFlags::ACCEPT_BEFORE_DELIVERY) flag --
|
||||
/// passing this flag will make this function return `Some(DragDropPayload)` **even before
|
||||
/// the user has actually "dropped" the payload by release their mouse button.**
|
||||
///
|
||||
/// In safe functions, this works just fine, since the data can be freely copied
|
||||
/// (or doesn't exist at all!). However, if you are working with your own data, you must
|
||||
/// be extremely careful with this data, as you may, effectively, only have immutable access to it.
|
||||
///
|
||||
/// Moreover, if the `DragDropSource` has also used `Condition::Once` or similar when they sent the data,
|
||||
/// ImGui will assume its data is still valid even after your preview, so corrupting that data could
|
||||
/// lead to all sorts of unsafe behvaior on ImGui's side. In summary, using this function for any data
|
||||
/// which isn't truly `Copy` or "plain old data" is difficult, and requires substantial knowledge
|
||||
/// of the various edge cases.
|
||||
pub unsafe fn accept_payload_unchecked(
|
||||
&self,
|
||||
name: impl AsRef<str>,
|
||||
flags: DragDropFlags,
|
||||
) -> Option<DragDropPayload> {
|
||||
let inner = sys::igAcceptDragDropPayload(self.0.scratch_txt(name), flags.bits() as i32);
|
||||
if inner.is_null() {
|
||||
None
|
||||
} else {
|
||||
let inner = *inner;
|
||||
|
||||
// @fixme: there are actually other fields on `inner` which I have shorn -- they're
|
||||
// considered internal to imgui (such as id of who sent this), so i've left it for
|
||||
// now this way.
|
||||
Some(DragDropPayload {
|
||||
data: inner.Data,
|
||||
size: inner.DataSize as usize,
|
||||
preview: inner.Preview,
|
||||
delivery: inner.Delivery,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Ends the current target. Ironically, this doesn't really do anything in ImGui
|
||||
/// or in imgui-rs, but it might in the future.
|
||||
pub fn pop(self) {
|
||||
// omitted...exists just to run Drop.
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DragDropTarget<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { sys::igEndDragDropTarget() }
|
||||
}
|
||||
}
|
||||
|
||||
/// An empty DragDropPayload. It has no data in it, and just includes
|
||||
/// two bools with status information.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct DragDropPayloadEmpty {
|
||||
/// Set when [`accept_payload_empty`](DragDropTarget::accept_payload_empty) was called
|
||||
/// and mouse has been hovering the target item.
|
||||
pub preview: bool,
|
||||
|
||||
/// Set when [`accept_payload_empty`](DragDropTarget::accept_payload_empty) was
|
||||
/// called and mouse button is released over the target item.
|
||||
pub delivery: bool,
|
||||
}
|
||||
|
||||
/// A DragDropPayload with status information and some POD, or plain old data,
|
||||
/// in it.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub struct DragDropPayloadPod<T: 'static + Copy> {
|
||||
/// The kind data which was requested.
|
||||
pub data: T,
|
||||
|
||||
/// Set when [`accept_payload`](DragDropTarget::accept_payload) was called
|
||||
/// and mouse has been hovering the target item.
|
||||
pub preview: bool,
|
||||
|
||||
/// Set when [`accept_payload`](DragDropTarget::accept_payload) was
|
||||
/// called and mouse button is released over the target item.
|
||||
pub delivery: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct DragDropPayload {
|
||||
/// Data which is copied and owned by ImGui. If you have accepted the payload, you can
|
||||
/// take ownership of the data; otherwise, view it immutably. Interacting with `data` is
|
||||
/// very unsafe.
|
||||
pub data: *const ffi::c_void,
|
||||
|
||||
/// The size of the data in bytes.
|
||||
pub size: usize,
|
||||
|
||||
/// Set when [`accept_payload_unchecked`](DragDropTarget::accept_payload_unchecked) was called
|
||||
/// and mouse has been hovering the target item.
|
||||
pub preview: bool,
|
||||
|
||||
/// Set when [`accept_payload_unchecked`](DragDropTarget::accept_payload_unchecked) was
|
||||
/// called and mouse button is released over the target item. If this is set to false, then you
|
||||
/// set DragDropFlags::ACCEPT_BEFORE_DELIVERY and shouldn't mutate `data`.
|
||||
pub delivery: bool,
|
||||
}
|
||||
|
||||
/// A typed payload.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
struct TypedPayload<T> {
|
||||
header: TypedPayloadHeader,
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Copy + 'static> TypedPayload<T> {
|
||||
/// Creates a new typed payload which contains this data.
|
||||
fn new(data: T) -> Self {
|
||||
Self {
|
||||
header: TypedPayloadHeader::new::<T>(),
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A header for a typed payload.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
|
||||
#[repr(C)]
|
||||
struct TypedPayloadHeader {
|
||||
type_id: any::TypeId,
|
||||
#[cfg(debug_assertions)]
|
||||
type_name: &'static str,
|
||||
}
|
||||
|
||||
impl TypedPayloadHeader {
|
||||
#[cfg(debug_assertions)]
|
||||
fn new<T: 'static>() -> Self {
|
||||
Self {
|
||||
type_id: any::TypeId::of::<T>(),
|
||||
type_name: any::type_name::<T>(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn new<T: 'static>() -> Self {
|
||||
Self {
|
||||
type_id: any::TypeId::of::<T>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates that an incorrect payload type was received. It is opaque,
|
||||
/// but you can view useful information with Debug formatting when
|
||||
/// `debug_assertions` are enabled.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct PayloadIsWrongType {
|
||||
expected: TypedPayloadHeader,
|
||||
received: TypedPayloadHeader,
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
impl std::fmt::Display for PayloadIsWrongType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Payload is {} -- expected {}",
|
||||
self.received.type_name, self.expected.type_name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
impl std::fmt::Display for PayloadIsWrongType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.pad("Payload is wrong type")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for PayloadIsWrongType {}
|
1041
plugins/libimhex-rust/imgui-rs/src/draw_list.rs
Normal file
1041
plugins/libimhex-rust/imgui-rs/src/draw_list.rs
Normal file
File diff suppressed because it is too large
Load Diff
508
plugins/libimhex-rust/imgui-rs/src/fonts/atlas.rs
Normal file
508
plugins/libimhex-rust/imgui-rs/src/fonts/atlas.rs
Normal file
@ -0,0 +1,508 @@
|
||||
use bitflags::bitflags;
|
||||
use std::cell;
|
||||
use std::f32;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_int, c_uchar, c_void};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::fonts::font::Font;
|
||||
use crate::fonts::glyph_ranges::FontGlyphRanges;
|
||||
use crate::internal::{ImVector, RawCast};
|
||||
use crate::sys;
|
||||
use crate::TextureId;
|
||||
|
||||
bitflags! {
|
||||
/// Font atlas configuration flags
|
||||
#[repr(transparent)]
|
||||
pub struct FontAtlasFlags: u32 {
|
||||
/// Don't round the height to next power of two
|
||||
const NO_POWER_OF_TWO_HEIGHT = sys::ImFontAtlasFlags_NoPowerOfTwoHeight;
|
||||
/// Don't build software mouse cursors into the atlas
|
||||
const NO_MOUSE_CURSORS = sys::ImFontAtlasFlags_NoMouseCursors;
|
||||
/// Don't build thick line textures into the atlas
|
||||
const NO_BAKED_LINES = sys::ImFontAtlasFlags_NoBakedLines;
|
||||
}
|
||||
}
|
||||
|
||||
/// A font identifier
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FontId(pub(crate) *const Font);
|
||||
|
||||
/// A font atlas that builds a single texture
|
||||
#[repr(C)]
|
||||
pub struct FontAtlas {
|
||||
/// Configuration flags
|
||||
pub flags: FontAtlasFlags,
|
||||
/// Texture identifier
|
||||
pub tex_id: TextureId,
|
||||
/// Texture width desired by user before building the atlas.
|
||||
///
|
||||
/// Must be a power-of-two. If you have many glyphs and your graphics API has texture size
|
||||
/// restrictions, you may want to increase texture width to decrease the height.
|
||||
pub tex_desired_width: i32,
|
||||
/// Padding between glyphs within texture in pixels.
|
||||
///
|
||||
/// Defaults to 1. If your rendering method doesn't rely on bilinear filtering, you may set
|
||||
/// this to 0.
|
||||
pub tex_glyph_padding: i32,
|
||||
|
||||
locked: bool,
|
||||
text_ready: bool,
|
||||
tex_pixels_use_colors: bool,
|
||||
tex_pixels_alpha8: *mut u8,
|
||||
tex_pixels_rgba32: *mut u32,
|
||||
tex_width: i32,
|
||||
tex_height: i32,
|
||||
tex_uv_scale: [f32; 2],
|
||||
tex_uv_white_pixel: [f32; 2],
|
||||
fonts: ImVector<*mut Font>,
|
||||
custom_rects: sys::ImVector_ImFontAtlasCustomRect,
|
||||
config_data: sys::ImVector_ImFontConfig,
|
||||
tex_uv_lines: [[f32; 4]; 64],
|
||||
font_builder_io: *const sys::ImFontBuilderIO,
|
||||
font_builder_flags: i32,
|
||||
pack_id_mouse_cursors: i32,
|
||||
pack_id_lines: i32,
|
||||
}
|
||||
|
||||
unsafe impl RawCast<sys::ImFontAtlas> for FontAtlas {}
|
||||
|
||||
impl FontAtlas {
|
||||
#[doc(alias = "AddFontDefault", alias = "AddFont")]
|
||||
pub fn add_font(&mut self, font_sources: &[FontSource<'_>]) -> FontId {
|
||||
let (head, tail) = font_sources.split_first().unwrap();
|
||||
let font_id = self.add_font_internal(head, false);
|
||||
for font in tail {
|
||||
self.add_font_internal(font, true);
|
||||
}
|
||||
font_id
|
||||
}
|
||||
fn add_font_internal(&mut self, font_source: &FontSource<'_>, merge_mode: bool) -> FontId {
|
||||
let mut raw_config = sys_font_config_default();
|
||||
raw_config.MergeMode = merge_mode;
|
||||
let raw_font = match font_source {
|
||||
FontSource::DefaultFontData { config } => unsafe {
|
||||
if let Some(config) = config {
|
||||
config.apply_to_raw_config(&mut raw_config, self.raw_mut());
|
||||
}
|
||||
sys::ImFontAtlas_AddFontDefault(self.raw_mut(), &raw_config)
|
||||
},
|
||||
FontSource::TtfData {
|
||||
data,
|
||||
size_pixels,
|
||||
config,
|
||||
} => {
|
||||
if let Some(config) = config {
|
||||
unsafe {
|
||||
config.apply_to_raw_config(&mut raw_config, self.raw_mut());
|
||||
}
|
||||
}
|
||||
// We can't guarantee `data` is alive when the font atlas is built, so
|
||||
// make a copy and move ownership of the data to the atlas
|
||||
let data_copy = unsafe {
|
||||
let ptr = sys::igMemAlloc(data.len()) as *mut u8;
|
||||
assert!(!ptr.is_null());
|
||||
slice::from_raw_parts_mut(ptr, data.len())
|
||||
};
|
||||
data_copy.copy_from_slice(data);
|
||||
raw_config.FontData = data_copy.as_mut_ptr() as *mut c_void;
|
||||
raw_config.FontDataSize = data_copy.len() as i32;
|
||||
raw_config.FontDataOwnedByAtlas = true;
|
||||
raw_config.SizePixels = *size_pixels;
|
||||
unsafe { sys::ImFontAtlas_AddFont(self.raw_mut(), &raw_config) }
|
||||
}
|
||||
};
|
||||
FontId(raw_font as *const _)
|
||||
}
|
||||
pub fn fonts(&self) -> Vec<FontId> {
|
||||
let mut result = Vec::new();
|
||||
unsafe {
|
||||
for &font in self.fonts.as_slice() {
|
||||
result.push((*font).id());
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
pub fn get_font(&self, id: FontId) -> Option<&Font> {
|
||||
unsafe {
|
||||
for &font in self.fonts.as_slice() {
|
||||
if id == FontId(font) {
|
||||
return Some(&*(font as *const Font));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
/// Returns true if the font atlas has been built
|
||||
#[doc(alias = "IsBuilt")]
|
||||
pub fn is_built(&self) -> bool {
|
||||
unsafe { sys::ImFontAtlas_IsBuilt(self.raw() as *const sys::ImFontAtlas as *mut _) }
|
||||
}
|
||||
/// Builds a 1 byte per-pixel font atlas texture
|
||||
#[doc(alias = "GetTextDataAsAlpha8")]
|
||||
pub fn build_alpha8_texture(&mut self) -> FontAtlasTexture<'_> {
|
||||
let mut pixels: *mut c_uchar = ptr::null_mut();
|
||||
let mut width: c_int = 0;
|
||||
let mut height: c_int = 0;
|
||||
let mut bytes_per_pixel: c_int = 0;
|
||||
unsafe {
|
||||
sys::ImFontAtlas_GetTexDataAsAlpha8(
|
||||
self.raw_mut(),
|
||||
&mut pixels,
|
||||
&mut width,
|
||||
&mut height,
|
||||
&mut bytes_per_pixel,
|
||||
);
|
||||
assert!(width >= 0, "font texture width must be positive");
|
||||
assert!(height >= 0, "font texture height must be positive");
|
||||
assert!(
|
||||
bytes_per_pixel >= 0,
|
||||
"font texture bytes per pixel must be positive"
|
||||
);
|
||||
let height = height as usize;
|
||||
// Check multiplication to avoid constructing an invalid slice in case of overflow
|
||||
let pitch = width
|
||||
.checked_mul(bytes_per_pixel)
|
||||
.expect("Overflow in font texture pitch calculation")
|
||||
as usize;
|
||||
FontAtlasTexture {
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
data: slice::from_raw_parts(pixels, pitch * height),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Builds a 4 byte per-pixel font atlas texture
|
||||
#[doc(alias = "GetTextDataAsRGBA32")]
|
||||
pub fn build_rgba32_texture(&mut self) -> FontAtlasTexture<'_> {
|
||||
let mut pixels: *mut c_uchar = ptr::null_mut();
|
||||
let mut width: c_int = 0;
|
||||
let mut height: c_int = 0;
|
||||
let mut bytes_per_pixel: c_int = 0;
|
||||
unsafe {
|
||||
sys::ImFontAtlas_GetTexDataAsRGBA32(
|
||||
self.raw_mut(),
|
||||
&mut pixels,
|
||||
&mut width,
|
||||
&mut height,
|
||||
&mut bytes_per_pixel,
|
||||
);
|
||||
assert!(width >= 0, "font texture width must be positive");
|
||||
assert!(height >= 0, "font texture height must be positive");
|
||||
assert!(
|
||||
bytes_per_pixel >= 0,
|
||||
"font texture bytes per pixel must be positive"
|
||||
);
|
||||
let height = height as usize;
|
||||
// Check multiplication to avoid constructing an invalid slice in case of overflow
|
||||
let pitch = width
|
||||
.checked_mul(bytes_per_pixel)
|
||||
.expect("Overflow in font texture pitch calculation")
|
||||
as usize;
|
||||
FontAtlasTexture {
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
data: slice::from_raw_parts(pixels, pitch * height),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Clears the font atlas completely (both input and output data)
|
||||
#[doc(alias = "Clear")]
|
||||
pub fn clear(&mut self) {
|
||||
unsafe {
|
||||
sys::ImFontAtlas_Clear(self.raw_mut());
|
||||
}
|
||||
}
|
||||
/// Clears output font data (glyph storage, UV coordinates)
|
||||
#[doc(alias = "ClearFonts")]
|
||||
pub fn clear_fonts(&mut self) {
|
||||
unsafe {
|
||||
sys::ImFontAtlas_ClearFonts(self.raw_mut());
|
||||
}
|
||||
}
|
||||
/// Clears output texture data.
|
||||
///
|
||||
/// Can be used to save RAM once the texture has been transferred to the GPU.
|
||||
#[doc(alias = "ClearTexData")]
|
||||
pub fn clear_tex_data(&mut self) {
|
||||
unsafe {
|
||||
sys::ImFontAtlas_ClearTexData(self.raw_mut());
|
||||
}
|
||||
}
|
||||
/// Clears all the data used to build the textures and fonts
|
||||
#[doc(alias = "ClearInputData")]
|
||||
pub fn clear_input_data(&mut self) {
|
||||
unsafe {
|
||||
sys::ImFontAtlas_ClearInputData(self.raw_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn test_font_atlas_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(
|
||||
mem::size_of::<FontAtlas>(),
|
||||
mem::size_of::<sys::ImFontAtlas>()
|
||||
);
|
||||
assert_eq!(
|
||||
mem::align_of::<FontAtlas>(),
|
||||
mem::align_of::<sys::ImFontAtlas>()
|
||||
);
|
||||
use sys::ImFontAtlas;
|
||||
macro_rules! assert_field_offset {
|
||||
($l:ident, $r:ident) => {
|
||||
assert_eq!(
|
||||
memoffset::offset_of!(FontAtlas, $l),
|
||||
memoffset::offset_of!(ImFontAtlas, $r)
|
||||
);
|
||||
};
|
||||
}
|
||||
assert_field_offset!(locked, Locked);
|
||||
assert_field_offset!(flags, Flags);
|
||||
assert_field_offset!(tex_id, TexID);
|
||||
assert_field_offset!(tex_desired_width, TexDesiredWidth);
|
||||
assert_field_offset!(tex_glyph_padding, TexGlyphPadding);
|
||||
assert_field_offset!(tex_pixels_use_colors, TexPixelsUseColors);
|
||||
assert_field_offset!(tex_pixels_alpha8, TexPixelsAlpha8);
|
||||
assert_field_offset!(tex_pixels_rgba32, TexPixelsRGBA32);
|
||||
assert_field_offset!(tex_width, TexWidth);
|
||||
assert_field_offset!(tex_height, TexHeight);
|
||||
assert_field_offset!(tex_uv_scale, TexUvScale);
|
||||
assert_field_offset!(tex_uv_white_pixel, TexUvWhitePixel);
|
||||
assert_field_offset!(fonts, Fonts);
|
||||
assert_field_offset!(custom_rects, CustomRects);
|
||||
assert_field_offset!(config_data, ConfigData);
|
||||
assert_field_offset!(tex_uv_lines, TexUvLines);
|
||||
assert_field_offset!(pack_id_mouse_cursors, PackIdMouseCursors);
|
||||
assert_field_offset!(pack_id_lines, PackIdLines);
|
||||
}
|
||||
|
||||
/// A source for binary font data
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FontSource<'a> {
|
||||
/// Default font included with the library (ProggyClean.ttf)
|
||||
DefaultFontData { config: Option<FontConfig> },
|
||||
/// Binary TTF/OTF font data
|
||||
TtfData {
|
||||
data: &'a [u8],
|
||||
size_pixels: f32,
|
||||
config: Option<FontConfig>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Configuration settings for a font
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FontConfig {
|
||||
/// Size in pixels for the rasterizer
|
||||
pub size_pixels: f32,
|
||||
/// Horizontal oversampling
|
||||
pub oversample_h: i32,
|
||||
/// Vertical oversampling
|
||||
pub oversample_v: i32,
|
||||
/// Align every glyph to pixel boundary
|
||||
pub pixel_snap_h: bool,
|
||||
/// Extra spacing (in pixels) between glyphs
|
||||
pub glyph_extra_spacing: [f32; 2],
|
||||
/// Offset for all glyphs in this font
|
||||
pub glyph_offset: [f32; 2],
|
||||
/// Unicode ranges to use from this font
|
||||
pub glyph_ranges: FontGlyphRanges,
|
||||
/// Minimum advance_x for glyphs
|
||||
pub glyph_min_advance_x: f32,
|
||||
/// Maximum advance_x for glyphs
|
||||
pub glyph_max_advance_x: f32,
|
||||
/// Settings for a custom font rasterizer if used
|
||||
pub font_builder_flags: u32,
|
||||
/// Brighten (>1.0) or darken (<1.0) font output
|
||||
pub rasterizer_multiply: f32,
|
||||
/// Explicitly specify the ellipsis character.
|
||||
///
|
||||
/// With multiple font sources the first specified ellipsis is used.
|
||||
pub ellipsis_char: Option<char>,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for FontConfig {
|
||||
fn default() -> FontConfig {
|
||||
FontConfig {
|
||||
size_pixels: 0.0,
|
||||
oversample_h: 3,
|
||||
oversample_v: 1,
|
||||
pixel_snap_h: false,
|
||||
glyph_extra_spacing: [0.0, 0.0],
|
||||
glyph_offset: [0.0, 0.0],
|
||||
glyph_ranges: FontGlyphRanges::default(),
|
||||
glyph_min_advance_x: 0.0,
|
||||
glyph_max_advance_x: f32::MAX,
|
||||
font_builder_flags: 0,
|
||||
rasterizer_multiply: 1.0,
|
||||
ellipsis_char: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FontConfig {
|
||||
fn apply_to_raw_config(&self, raw: &mut sys::ImFontConfig, atlas: *mut sys::ImFontAtlas) {
|
||||
raw.SizePixels = self.size_pixels;
|
||||
raw.OversampleH = self.oversample_h;
|
||||
raw.OversampleV = self.oversample_v;
|
||||
raw.PixelSnapH = self.pixel_snap_h;
|
||||
raw.GlyphExtraSpacing = self.glyph_extra_spacing.into();
|
||||
raw.GlyphOffset = self.glyph_offset.into();
|
||||
raw.GlyphRanges = unsafe { self.glyph_ranges.to_ptr(atlas) };
|
||||
raw.GlyphMinAdvanceX = self.glyph_min_advance_x;
|
||||
raw.GlyphMaxAdvanceX = self.glyph_max_advance_x;
|
||||
raw.FontBuilderFlags = self.font_builder_flags;
|
||||
raw.RasterizerMultiply = self.rasterizer_multiply;
|
||||
// char is used as "unset" for EllipsisChar
|
||||
raw.EllipsisChar = self.ellipsis_char.map(|c| c as u32).unwrap_or(!0);
|
||||
if let Some(name) = self.name.as_ref() {
|
||||
let bytes = name.as_bytes();
|
||||
let mut len = bytes.len().min(raw.Name.len() - 1);
|
||||
while !name.is_char_boundary(len) {
|
||||
len -= 1;
|
||||
}
|
||||
unsafe {
|
||||
bytes.as_ptr().copy_to(raw.Name.as_mut_ptr() as _, len);
|
||||
raw.Name[len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sys_font_config_default() -> sys::ImFontConfig {
|
||||
unsafe {
|
||||
let heap_allocated = sys::ImFontConfig_ImFontConfig();
|
||||
let copy = *heap_allocated;
|
||||
sys::ImFontConfig_destroy(heap_allocated);
|
||||
copy
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_font_config_default() {
|
||||
let sys_font_config = sys_font_config_default();
|
||||
let font_config = FontConfig::default();
|
||||
assert_eq!(font_config.size_pixels, sys_font_config.SizePixels);
|
||||
assert_eq!(font_config.oversample_h, sys_font_config.OversampleH);
|
||||
assert_eq!(font_config.oversample_v, sys_font_config.OversampleV);
|
||||
assert_eq!(font_config.pixel_snap_h, sys_font_config.PixelSnapH);
|
||||
assert_eq!(
|
||||
font_config.glyph_extra_spacing[0],
|
||||
sys_font_config.GlyphExtraSpacing.x
|
||||
);
|
||||
assert_eq!(
|
||||
font_config.glyph_extra_spacing[1],
|
||||
sys_font_config.GlyphExtraSpacing.y
|
||||
);
|
||||
assert_eq!(font_config.glyph_offset[0], sys_font_config.GlyphOffset.x);
|
||||
assert_eq!(font_config.glyph_offset[1], sys_font_config.GlyphOffset.y);
|
||||
assert_eq!(
|
||||
font_config.glyph_min_advance_x,
|
||||
sys_font_config.GlyphMinAdvanceX
|
||||
);
|
||||
assert_eq!(
|
||||
font_config.glyph_max_advance_x,
|
||||
sys_font_config.GlyphMaxAdvanceX
|
||||
);
|
||||
assert_eq!(
|
||||
font_config.font_builder_flags,
|
||||
sys_font_config.FontBuilderFlags
|
||||
);
|
||||
assert_eq!(
|
||||
font_config.rasterizer_multiply,
|
||||
sys_font_config.RasterizerMultiply
|
||||
);
|
||||
}
|
||||
|
||||
/// Handle to a font atlas texture
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FontAtlasTexture<'a> {
|
||||
/// Texture width (in pixels)
|
||||
pub width: u32,
|
||||
/// Texture height (in pixels)
|
||||
pub height: u32,
|
||||
/// Raw texture data (in bytes).
|
||||
///
|
||||
/// The format depends on which function was called to obtain this data.
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
/// A font atlas that can be shared between contexts
|
||||
#[derive(Debug)]
|
||||
pub struct SharedFontAtlas(pub(crate) *mut sys::ImFontAtlas);
|
||||
|
||||
impl SharedFontAtlas {
|
||||
#[doc(alias = "ImFontAtlas", alias = "ImFontAtlas::ImFontAtlas")]
|
||||
pub fn create() -> SharedFontAtlas {
|
||||
SharedFontAtlas(unsafe { sys::ImFontAtlas_ImFontAtlas() })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SharedFontAtlas {
|
||||
#[doc(alias = "ImFontAtlas::Destory")]
|
||||
fn drop(&mut self) {
|
||||
unsafe { sys::ImFontAtlas_destroy(self.0) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SharedFontAtlas {
|
||||
type Target = FontAtlas;
|
||||
fn deref(&self) -> &FontAtlas {
|
||||
unsafe { &*(self.0 as *const FontAtlas) }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SharedFontAtlas {
|
||||
fn deref_mut(&mut self) -> &mut FontAtlas {
|
||||
unsafe { &mut *(self.0 as *mut FontAtlas) }
|
||||
}
|
||||
}
|
||||
|
||||
/// An immutably borrowed reference to a (possibly shared) font atlas
|
||||
pub enum FontAtlasRef<'a> {
|
||||
Owned(&'a FontAtlas),
|
||||
Shared(&'a cell::RefMut<'a, SharedFontAtlas>),
|
||||
}
|
||||
|
||||
impl<'a> Deref for FontAtlasRef<'a> {
|
||||
type Target = FontAtlas;
|
||||
fn deref(&self) -> &FontAtlas {
|
||||
use self::FontAtlasRef::*;
|
||||
match self {
|
||||
Owned(atlas) => atlas,
|
||||
Shared(cell) => cell,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A mutably borrowed reference to a (possibly shared) font atlas
|
||||
pub enum FontAtlasRefMut<'a> {
|
||||
Owned(&'a mut FontAtlas),
|
||||
Shared(cell::RefMut<'a, SharedFontAtlas>),
|
||||
}
|
||||
|
||||
impl<'a> Deref for FontAtlasRefMut<'a> {
|
||||
type Target = FontAtlas;
|
||||
fn deref(&self) -> &FontAtlas {
|
||||
use self::FontAtlasRefMut::*;
|
||||
match self {
|
||||
Owned(atlas) => atlas,
|
||||
Shared(cell) => cell,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for FontAtlasRefMut<'a> {
|
||||
fn deref_mut(&mut self) -> &mut FontAtlas {
|
||||
use self::FontAtlasRefMut::*;
|
||||
match self {
|
||||
Owned(atlas) => atlas,
|
||||
Shared(cell) => cell,
|
||||
}
|
||||
}
|
||||
}
|
71
plugins/libimhex-rust/imgui-rs/src/fonts/font.rs
Normal file
71
plugins/libimhex-rust/imgui-rs/src/fonts/font.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::fonts::atlas::{FontAtlas, FontId};
|
||||
use crate::fonts::glyph::FontGlyph;
|
||||
use crate::internal::{ImVector, RawCast};
|
||||
use crate::sys;
|
||||
|
||||
/// Runtime data for a single font within a font atlas
|
||||
#[repr(C)]
|
||||
pub struct Font {
|
||||
index_advance_x: ImVector<f32>,
|
||||
pub fallback_advance_x: f32,
|
||||
pub font_size: f32,
|
||||
index_lookup: ImVector<sys::ImWchar>,
|
||||
glyphs: ImVector<FontGlyph>,
|
||||
fallback_glyph: *const FontGlyph,
|
||||
container_atlas: *mut FontAtlas,
|
||||
config_data: *const sys::ImFontConfig,
|
||||
pub config_data_count: i16,
|
||||
pub fallback_char: sys::ImWchar,
|
||||
pub ellipsis_char: sys::ImWchar,
|
||||
pub dot_char: sys::ImWchar,
|
||||
pub dirty_lookup_tables: bool,
|
||||
pub scale: f32,
|
||||
pub ascent: f32,
|
||||
pub descent: f32,
|
||||
pub metrics_total_surface: c_int,
|
||||
pub used_4k_pages_map: [u8; 34],
|
||||
}
|
||||
|
||||
unsafe impl RawCast<sys::ImFont> for Font {}
|
||||
|
||||
impl Font {
|
||||
/// Returns the identifier of this font
|
||||
pub fn id(&self) -> FontId {
|
||||
FontId(self as *const _)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_font_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(mem::size_of::<Font>(), mem::size_of::<sys::ImFont>());
|
||||
assert_eq!(mem::align_of::<Font>(), mem::align_of::<sys::ImFont>());
|
||||
use sys::ImFont;
|
||||
macro_rules! assert_field_offset {
|
||||
($l:ident, $r:ident) => {
|
||||
assert_eq!(
|
||||
memoffset::offset_of!(Font, $l),
|
||||
memoffset::offset_of!(ImFont, $r)
|
||||
);
|
||||
};
|
||||
}
|
||||
assert_field_offset!(index_advance_x, IndexAdvanceX);
|
||||
assert_field_offset!(fallback_advance_x, FallbackAdvanceX);
|
||||
assert_field_offset!(font_size, FontSize);
|
||||
assert_field_offset!(index_lookup, IndexLookup);
|
||||
assert_field_offset!(glyphs, Glyphs);
|
||||
assert_field_offset!(fallback_glyph, FallbackGlyph);
|
||||
assert_field_offset!(container_atlas, ContainerAtlas);
|
||||
assert_field_offset!(config_data, ConfigData);
|
||||
assert_field_offset!(config_data_count, ConfigDataCount);
|
||||
assert_field_offset!(fallback_char, FallbackChar);
|
||||
assert_field_offset!(ellipsis_char, EllipsisChar);
|
||||
assert_field_offset!(dirty_lookup_tables, DirtyLookupTables);
|
||||
assert_field_offset!(scale, Scale);
|
||||
assert_field_offset!(ascent, Ascent);
|
||||
assert_field_offset!(descent, Descent);
|
||||
assert_field_offset!(metrics_total_surface, MetricsTotalSurface);
|
||||
assert_field_offset!(used_4k_pages_map, Used4kPagesMap);
|
||||
}
|
67
plugins/libimhex-rust/imgui-rs/src/fonts/glyph.rs
Normal file
67
plugins/libimhex-rust/imgui-rs/src/fonts/glyph.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use crate::internal::RawCast;
|
||||
use crate::sys;
|
||||
|
||||
/// A single font glyph
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct FontGlyph {
|
||||
bitfields: u32,
|
||||
pub advance_x: f32,
|
||||
pub x0: f32,
|
||||
pub y0: f32,
|
||||
pub x1: f32,
|
||||
pub y1: f32,
|
||||
pub u0: f32,
|
||||
pub v0: f32,
|
||||
pub u1: f32,
|
||||
pub v1: f32,
|
||||
}
|
||||
|
||||
impl FontGlyph {
|
||||
pub fn codepoint(&self) -> u32 {
|
||||
unsafe { self.raw().Codepoint() }
|
||||
}
|
||||
pub fn set_codepoint(&mut self, codepoint: u32) {
|
||||
unsafe { self.raw_mut().set_Codepoint(codepoint) };
|
||||
}
|
||||
pub fn visible(&self) -> bool {
|
||||
unsafe { self.raw().Visible() != 0 }
|
||||
}
|
||||
pub fn set_visible(&mut self, visible: bool) {
|
||||
unsafe { self.raw_mut().set_Visible(visible as u32) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RawCast<sys::ImFontGlyph> for FontGlyph {}
|
||||
|
||||
#[test]
|
||||
fn test_font_glyph_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(
|
||||
mem::size_of::<FontGlyph>(),
|
||||
mem::size_of::<sys::ImFontGlyph>()
|
||||
);
|
||||
assert_eq!(
|
||||
mem::align_of::<FontGlyph>(),
|
||||
mem::align_of::<sys::ImFontGlyph>()
|
||||
);
|
||||
use sys::ImFontGlyph;
|
||||
macro_rules! assert_field_offset {
|
||||
($l:ident, $r:ident) => {
|
||||
assert_eq!(
|
||||
memoffset::offset_of!(FontGlyph, $l),
|
||||
memoffset::offset_of!(ImFontGlyph, $r)
|
||||
);
|
||||
};
|
||||
}
|
||||
assert_field_offset!(bitfields, _bitfield_1);
|
||||
assert_field_offset!(advance_x, AdvanceX);
|
||||
assert_field_offset!(x0, X0);
|
||||
assert_field_offset!(y0, Y0);
|
||||
assert_field_offset!(x1, X1);
|
||||
assert_field_offset!(y1, Y1);
|
||||
assert_field_offset!(u0, U0);
|
||||
assert_field_offset!(v0, V0);
|
||||
assert_field_offset!(u1, U1);
|
||||
assert_field_offset!(v1, V1);
|
||||
}
|
164
plugins/libimhex-rust/imgui-rs/src/fonts/glyph_ranges.rs
Normal file
164
plugins/libimhex-rust/imgui-rs/src/fonts/glyph_ranges.rs
Normal file
@ -0,0 +1,164 @@
|
||||
use crate::sys;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
enum FontGlyphRangeData {
|
||||
ChineseSimplifiedCommon,
|
||||
ChineseFull,
|
||||
Cyrillic,
|
||||
Default,
|
||||
Japanese,
|
||||
Korean,
|
||||
Thai,
|
||||
Vietnamese,
|
||||
Custom(*const sys::ImWchar),
|
||||
}
|
||||
|
||||
/// A set of Unicode codepoints
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct FontGlyphRanges(FontGlyphRangeData);
|
||||
impl FontGlyphRanges {
|
||||
/// The default set of glyph ranges used by imgui.
|
||||
pub fn default() -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::Default)
|
||||
}
|
||||
/// A set of glyph ranges appropriate for use with simplified common Chinese text.
|
||||
pub fn chinese_simplified_common() -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::ChineseSimplifiedCommon)
|
||||
}
|
||||
/// A set of glyph ranges appropriate for use with Chinese text.
|
||||
pub fn chinese_full() -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::ChineseFull)
|
||||
}
|
||||
/// A set of glyph ranges appropriate for use with Cyrillic text.
|
||||
pub fn cyrillic() -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::Cyrillic)
|
||||
}
|
||||
/// A set of glyph ranges appropriate for use with Japanese text.
|
||||
pub fn japanese() -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::Japanese)
|
||||
}
|
||||
/// A set of glyph ranges appropriate for use with Korean text.
|
||||
pub fn korean() -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::Korean)
|
||||
}
|
||||
/// A set of glyph ranges appropriate for use with Thai text.
|
||||
pub fn thai() -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::Thai)
|
||||
}
|
||||
/// A set of glyph ranges appropriate for use with Vietnamese text.
|
||||
pub fn vietnamese() -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::Vietnamese)
|
||||
}
|
||||
|
||||
/// Creates a glyph range from a static slice. The expected format is a series of pairs of
|
||||
/// non-zero codepoints, each representing an inclusive range, followed by a single
|
||||
/// zero terminating the range. The ranges must not overlap.
|
||||
///
|
||||
/// As the slice is expected to last as long as a font is used, and is written into global
|
||||
/// state, it must be `'static`.
|
||||
///
|
||||
/// Panics
|
||||
/// ======
|
||||
///
|
||||
/// This function will panic if the given slice is not a valid font range.
|
||||
// TODO(thom): This takes `u32` for now, since I believe it's fine for it to
|
||||
// contain surrogates? (It seems plausible that font data can describe what
|
||||
// to show for unpaired surrogates) Would be nice to be sure, if so, this
|
||||
// should accept `char` (we'd still have to check that the range doesn't
|
||||
// fully contain the surrogate range though)
|
||||
pub fn from_slice(slice: &'static [u32]) -> FontGlyphRanges {
|
||||
assert_eq!(
|
||||
slice.len() % 2,
|
||||
1,
|
||||
"The length of a glyph range must be odd."
|
||||
);
|
||||
assert_eq!(
|
||||
slice.last(),
|
||||
Some(&0),
|
||||
"A glyph range must be zero-terminated."
|
||||
);
|
||||
|
||||
for (i, &glyph) in slice.iter().enumerate().take(slice.len() - 1) {
|
||||
assert_ne!(
|
||||
glyph, 0,
|
||||
"A glyph in a range cannot be zero. \
|
||||
(Glyph is zero at index {})",
|
||||
i
|
||||
);
|
||||
assert!(
|
||||
glyph <= core::char::MAX as u32,
|
||||
"A glyph in a range cannot exceed the maximum codepoint. \
|
||||
(Glyph is {:#x} at index {})",
|
||||
glyph,
|
||||
i,
|
||||
);
|
||||
}
|
||||
|
||||
let mut ranges = Vec::new();
|
||||
for i in 0..slice.len() / 2 {
|
||||
let (start, end) = (slice[i * 2], slice[i * 2 + 1]);
|
||||
assert!(
|
||||
start <= end,
|
||||
"The start of a range cannot be larger than its end. \
|
||||
(At index {}, {} > {})",
|
||||
i * 2,
|
||||
start,
|
||||
end
|
||||
);
|
||||
ranges.push((start, end));
|
||||
}
|
||||
ranges.sort_unstable_by_key(|x| x.0);
|
||||
for i in 0..ranges.len() - 1 {
|
||||
let (range_a, range_b) = (ranges[i], ranges[i + 1]);
|
||||
if range_a.1 >= range_b.0 {
|
||||
panic!(
|
||||
"The glyph ranges {:?} and {:?} overlap between {:?}.",
|
||||
range_a,
|
||||
range_b,
|
||||
(range_a.1, range_b.0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { FontGlyphRanges::from_slice_unchecked(slice) }
|
||||
}
|
||||
|
||||
/// Creates a glyph range from a static slice without checking its validity.
|
||||
///
|
||||
/// See [`FontGlyphRanges::from_slice`] for more information.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the slice contents are valid.
|
||||
pub unsafe fn from_slice_unchecked(slice: &'static [u32]) -> FontGlyphRanges {
|
||||
FontGlyphRanges::from_ptr(slice.as_ptr())
|
||||
}
|
||||
|
||||
/// Creates a glyph range from a pointer, without checking its validity or enforcing its
|
||||
/// lifetime. The memory the pointer points to must be valid for as long as the font is
|
||||
/// in use.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the pointer is not null, remains valid forever, and
|
||||
/// points to valid data.
|
||||
pub unsafe fn from_ptr(ptr: *const u32) -> FontGlyphRanges {
|
||||
FontGlyphRanges(FontGlyphRangeData::Custom(ptr))
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn to_ptr(&self, atlas: *mut sys::ImFontAtlas) -> *const sys::ImWchar {
|
||||
match self.0 {
|
||||
FontGlyphRangeData::ChineseFull => sys::ImFontAtlas_GetGlyphRangesChineseFull(atlas),
|
||||
FontGlyphRangeData::ChineseSimplifiedCommon => {
|
||||
sys::ImFontAtlas_GetGlyphRangesChineseSimplifiedCommon(atlas)
|
||||
}
|
||||
FontGlyphRangeData::Cyrillic => sys::ImFontAtlas_GetGlyphRangesCyrillic(atlas),
|
||||
FontGlyphRangeData::Default => sys::ImFontAtlas_GetGlyphRangesDefault(atlas),
|
||||
FontGlyphRangeData::Japanese => sys::ImFontAtlas_GetGlyphRangesJapanese(atlas),
|
||||
FontGlyphRangeData::Korean => sys::ImFontAtlas_GetGlyphRangesKorean(atlas),
|
||||
FontGlyphRangeData::Thai => sys::ImFontAtlas_GetGlyphRangesThai(atlas),
|
||||
FontGlyphRangeData::Vietnamese => sys::ImFontAtlas_GetGlyphRangesVietnamese(atlas),
|
||||
FontGlyphRangeData::Custom(ptr) => ptr,
|
||||
}
|
||||
}
|
||||
}
|
36
plugins/libimhex-rust/imgui-rs/src/fonts/mod.rs
Normal file
36
plugins/libimhex-rust/imgui-rs/src/fonts/mod.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use crate::fonts::font::Font;
|
||||
use crate::internal::RawCast;
|
||||
use crate::Ui;
|
||||
|
||||
pub mod atlas;
|
||||
pub mod font;
|
||||
pub mod glyph;
|
||||
pub mod glyph_ranges;
|
||||
|
||||
/// # Fonts
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns the current font
|
||||
#[doc(alias = "GetFont")]
|
||||
pub fn current_font(&self) -> &Font {
|
||||
unsafe { Font::from_raw(&*sys::igGetFont()) }
|
||||
}
|
||||
/// Returns the current font size (= height in pixels) with font scale applied
|
||||
#[doc(alias = "GetFontSize")]
|
||||
pub fn current_font_size(&self) -> f32 {
|
||||
unsafe { sys::igGetFontSize() }
|
||||
}
|
||||
/// Returns the UV coordinate for a white pixel.
|
||||
///
|
||||
/// Useful for drawing custom shapes with the draw list API.
|
||||
#[doc(alias = "FontTexUvWhitePixel")]
|
||||
pub fn font_tex_uv_white_pixel(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetFontTexUvWhitePixel(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
/// Sets the font scale of the current window
|
||||
#[doc(alias = "SetWindowFontScale")]
|
||||
pub fn set_window_font_scale(&self, scale: f32) {
|
||||
unsafe { sys::igSetWindowFontScale(scale) }
|
||||
}
|
||||
}
|
215
plugins/libimhex-rust/imgui-rs/src/input/keyboard.rs
Normal file
215
plugins/libimhex-rust/imgui-rs/src/input/keyboard.rs
Normal file
@ -0,0 +1,215 @@
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// A key identifier
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub enum Key {
|
||||
Tab = sys::ImGuiKey_Tab,
|
||||
LeftArrow = sys::ImGuiKey_LeftArrow,
|
||||
RightArrow = sys::ImGuiKey_RightArrow,
|
||||
UpArrow = sys::ImGuiKey_UpArrow,
|
||||
DownArrow = sys::ImGuiKey_DownArrow,
|
||||
PageUp = sys::ImGuiKey_PageUp,
|
||||
PageDown = sys::ImGuiKey_PageDown,
|
||||
Home = sys::ImGuiKey_Home,
|
||||
End = sys::ImGuiKey_End,
|
||||
Insert = sys::ImGuiKey_Insert,
|
||||
Delete = sys::ImGuiKey_Delete,
|
||||
Backspace = sys::ImGuiKey_Backspace,
|
||||
Space = sys::ImGuiKey_Space,
|
||||
Enter = sys::ImGuiKey_Enter,
|
||||
Escape = sys::ImGuiKey_Escape,
|
||||
KeyPadEnter = sys::ImGuiKey_KeyPadEnter,
|
||||
A = sys::ImGuiKey_A,
|
||||
C = sys::ImGuiKey_C,
|
||||
V = sys::ImGuiKey_V,
|
||||
X = sys::ImGuiKey_X,
|
||||
Y = sys::ImGuiKey_Y,
|
||||
Z = sys::ImGuiKey_Z,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
/// All possible `Key` variants
|
||||
pub const VARIANTS: [Key; Key::COUNT] = [
|
||||
Key::Tab,
|
||||
Key::LeftArrow,
|
||||
Key::RightArrow,
|
||||
Key::UpArrow,
|
||||
Key::DownArrow,
|
||||
Key::PageUp,
|
||||
Key::PageDown,
|
||||
Key::Home,
|
||||
Key::End,
|
||||
Key::Insert,
|
||||
Key::Delete,
|
||||
Key::Backspace,
|
||||
Key::Space,
|
||||
Key::Enter,
|
||||
Key::Escape,
|
||||
Key::KeyPadEnter,
|
||||
Key::A,
|
||||
Key::C,
|
||||
Key::V,
|
||||
Key::X,
|
||||
Key::Y,
|
||||
Key::Z,
|
||||
];
|
||||
/// Total count of `Key` variants
|
||||
pub const COUNT: usize = sys::ImGuiKey_COUNT as usize;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_variants() {
|
||||
for (idx, &value) in Key::VARIANTS.iter().enumerate() {
|
||||
assert_eq!(idx, value as usize);
|
||||
}
|
||||
}
|
||||
|
||||
/// Target widget selection for keyboard focus
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub enum FocusedWidget {
|
||||
/// Previous widget
|
||||
Previous,
|
||||
/// Next widget
|
||||
Next,
|
||||
/// Widget using a relative positive offset (0 is the next widget).
|
||||
///
|
||||
/// Use this to access sub components of a multiple component widget.
|
||||
Offset(u32),
|
||||
}
|
||||
|
||||
impl FocusedWidget {
|
||||
#[inline]
|
||||
fn as_offset(self) -> i32 {
|
||||
match self {
|
||||
FocusedWidget::Previous => -1,
|
||||
FocusedWidget::Next => 0,
|
||||
FocusedWidget::Offset(offset) => offset as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Input: Keyboard
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns the key index of the given key identifier.
|
||||
///
|
||||
/// Equivalent to indexing the Io struct `key_map` field: `ui.io().key_map[key]`
|
||||
#[inline]
|
||||
#[doc(alias = "GetKeyIndex")]
|
||||
fn key_index(&self, key: Key) -> i32 {
|
||||
unsafe { sys::igGetKeyIndex(key as i32) }
|
||||
}
|
||||
/// Returns true if the key is being held.
|
||||
///
|
||||
/// Equivalent to indexing the Io struct `keys_down` field: `ui.io().keys_down[key_index]`
|
||||
#[inline]
|
||||
#[doc(alias = "IsKeyDown")]
|
||||
pub fn is_key_down(&self, key: Key) -> bool {
|
||||
let key_index = self.key_index(key);
|
||||
self.is_key_index_down(key_index)
|
||||
}
|
||||
|
||||
/// Same as [`is_key_down`](Self::is_key_down) but takes a key index. The meaning of
|
||||
/// index is defined by your backend implementation.
|
||||
#[inline]
|
||||
#[doc(alias = "IsKeyDown")]
|
||||
pub fn is_key_index_down(&self, key_index: i32) -> bool {
|
||||
unsafe { sys::igIsKeyDown(key_index) }
|
||||
}
|
||||
|
||||
/// Returns true if the key was pressed (went from !down to down).
|
||||
///
|
||||
/// Affected by key repeat settings (`io.key_repeat_delay`, `io.key_repeat_rate`)
|
||||
#[inline]
|
||||
#[doc(alias = "IsKeyPressed")]
|
||||
pub fn is_key_pressed(&self, key: Key) -> bool {
|
||||
let key_index = self.key_index(key);
|
||||
self.is_key_index_pressed(key_index)
|
||||
}
|
||||
|
||||
/// Same as [`is_key_pressed`](Self::is_key_pressed) but takes a key index.
|
||||
///
|
||||
/// The meaning of index is defined by your backend
|
||||
/// implementation.
|
||||
#[inline]
|
||||
#[doc(alias = "IsKeyPressed")]
|
||||
pub fn is_key_index_pressed(&self, key_index: i32) -> bool {
|
||||
unsafe { sys::igIsKeyPressed(key_index, true) }
|
||||
}
|
||||
|
||||
/// Returns true if the key was pressed (went from !down to down).
|
||||
///
|
||||
/// Is **not** affected by key repeat settings (`io.key_repeat_delay`, `io.key_repeat_rate`)
|
||||
#[inline]
|
||||
#[doc(alias = "IsKeyPressed")]
|
||||
pub fn is_key_pressed_no_repeat(&self, key: Key) -> bool {
|
||||
let key_index = self.key_index(key);
|
||||
self.is_key_index_pressed_no_repeat(key_index)
|
||||
}
|
||||
|
||||
/// Same as [`is_key_pressed_no_repeat`](Self::is_key_pressed_no_repeat)
|
||||
/// but takes a key index.
|
||||
///
|
||||
/// The meaning of index is defined by your backend
|
||||
/// implementation.
|
||||
#[inline]
|
||||
#[doc(alias = "IsKeyPressed")]
|
||||
pub fn is_key_index_pressed_no_repeat(&self, key_index: i32) -> bool {
|
||||
unsafe { sys::igIsKeyPressed(key_index, false) }
|
||||
}
|
||||
|
||||
/// Returns true if the key was released (went from down to !down)
|
||||
#[inline]
|
||||
#[doc(alias = "IsKeyReleased")]
|
||||
pub fn is_key_released(&self, key: Key) -> bool {
|
||||
let key_index = self.key_index(key);
|
||||
self.is_key_index_released(key_index)
|
||||
}
|
||||
|
||||
/// Same as [`is_key_released`](Self::is_key_released) but takes a key index.
|
||||
///
|
||||
/// The meaning of index is defined by your backend
|
||||
/// implementation.
|
||||
#[inline]
|
||||
#[doc(alias = "IsKeyReleased")]
|
||||
pub fn is_key_index_released(&self, key_index: i32) -> bool {
|
||||
unsafe { sys::igIsKeyReleased(key_index) }
|
||||
}
|
||||
|
||||
/// Returns a count of key presses using the given repeat rate/delay settings.
|
||||
///
|
||||
/// Usually returns 0 or 1, but might be >1 if `rate` is small enough that `io.delta_time` >
|
||||
/// `rate`.
|
||||
#[inline]
|
||||
#[doc(alias = "GetKeyPressedAmount")]
|
||||
pub fn key_pressed_amount(&self, key: Key, repeat_delay: f32, rate: f32) -> u32 {
|
||||
let key_index = self.key_index(key);
|
||||
self.key_index_pressed_amount(key_index, repeat_delay, rate)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(alias = "GetKeyPressedAmount")]
|
||||
pub fn key_index_pressed_amount(&self, key_index: i32, repeat_delay: f32, rate: f32) -> u32 {
|
||||
unsafe { sys::igGetKeyPressedAmount(key_index, repeat_delay, rate) as u32 }
|
||||
}
|
||||
|
||||
/// Focuses keyboard on the next widget.
|
||||
///
|
||||
/// This is the equivalent to [set_keyboard_focus_here_with_offset](Self::set_keyboard_focus_here_with_offset)
|
||||
/// with `target_widget` set to `FocusedWidget::Next`.
|
||||
#[inline]
|
||||
#[doc(alias = "SetKeyboardFocusHere")]
|
||||
pub fn set_keyboard_focus_here(&self) {
|
||||
self.set_keyboard_focus_here_with_offset(FocusedWidget::Next);
|
||||
}
|
||||
|
||||
/// Focuses keyboard on a widget relative to current position.
|
||||
#[inline]
|
||||
#[doc(alias = "SetKeyboardFocusHere")]
|
||||
pub fn set_keyboard_focus_here_with_offset(&self, target_widget: FocusedWidget) {
|
||||
unsafe {
|
||||
sys::igSetKeyboardFocusHere(target_widget.as_offset());
|
||||
}
|
||||
}
|
||||
}
|
2
plugins/libimhex-rust/imgui-rs/src/input/mod.rs
Normal file
2
plugins/libimhex-rust/imgui-rs/src/input/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod keyboard;
|
||||
pub mod mouse;
|
478
plugins/libimhex-rust/imgui-rs/src/input/mouse.rs
Normal file
478
plugins/libimhex-rust/imgui-rs/src/input/mouse.rs
Normal file
@ -0,0 +1,478 @@
|
||||
use std::ptr;
|
||||
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// Represents one of the supported mouse buttons
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum MouseButton {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
Middle = 2,
|
||||
Extra1 = 3,
|
||||
Extra2 = 4,
|
||||
}
|
||||
|
||||
impl MouseButton {
|
||||
/// All possible `MouseButton` varirants
|
||||
pub const VARIANTS: [MouseButton; MouseButton::COUNT] = [
|
||||
MouseButton::Left,
|
||||
MouseButton::Right,
|
||||
MouseButton::Middle,
|
||||
MouseButton::Extra1,
|
||||
MouseButton::Extra2,
|
||||
];
|
||||
/// Total count of `MouseButton` variants
|
||||
pub const COUNT: usize = 5;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mouse_button_variants() {
|
||||
for (idx, &value) in MouseButton::VARIANTS.iter().enumerate() {
|
||||
assert_eq!(idx, value as usize);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mouse cursor type identifier
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
// TODO: this should just be `#[allow(clippy::upper_case_acronyms)]`, but doing
|
||||
// so in a way that works before it stabilizes is a pain (in part because
|
||||
// `unknown_clippy_lints` was renamed to unknown_lints). Oh well, it's over a
|
||||
// small amount of code.
|
||||
#[allow(warnings)]
|
||||
pub enum MouseCursor {
|
||||
Arrow = sys::ImGuiMouseCursor_Arrow,
|
||||
/// Automatically used when hovering over text inputs, etc.
|
||||
TextInput = sys::ImGuiMouseCursor_TextInput,
|
||||
/// Not used automatically
|
||||
ResizeAll = sys::ImGuiMouseCursor_ResizeAll,
|
||||
/// Automatically used when hovering over a horizontal border
|
||||
ResizeNS = sys::ImGuiMouseCursor_ResizeNS,
|
||||
/// Automatically used when hovering over a vertical border or a column
|
||||
ResizeEW = sys::ImGuiMouseCursor_ResizeEW,
|
||||
/// Automatically used when hovering over the bottom-left corner of a window
|
||||
ResizeNESW = sys::ImGuiMouseCursor_ResizeNESW,
|
||||
/// Automatically used when hovering over the bottom-right corner of a window
|
||||
ResizeNWSE = sys::ImGuiMouseCursor_ResizeNWSE,
|
||||
/// Not used automatically, use for e.g. hyperlinks
|
||||
Hand = sys::ImGuiMouseCursor_Hand,
|
||||
/// When hovering something with disallowed interactions.
|
||||
///
|
||||
/// Usually a crossed circle.
|
||||
NotAllowed = sys::ImGuiMouseCursor_NotAllowed,
|
||||
}
|
||||
|
||||
impl MouseCursor {
|
||||
/// All possible `MouseCursor` varirants
|
||||
pub const VARIANTS: [MouseCursor; MouseCursor::COUNT] = [
|
||||
MouseCursor::Arrow,
|
||||
MouseCursor::TextInput,
|
||||
MouseCursor::ResizeAll,
|
||||
MouseCursor::ResizeNS,
|
||||
MouseCursor::ResizeEW,
|
||||
MouseCursor::ResizeNESW,
|
||||
MouseCursor::ResizeNWSE,
|
||||
MouseCursor::Hand,
|
||||
MouseCursor::NotAllowed,
|
||||
];
|
||||
/// Total count of `MouseCursor` variants
|
||||
pub const COUNT: usize = sys::ImGuiMouseCursor_COUNT as usize;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mouse_cursor_variants() {
|
||||
for (idx, &value) in MouseCursor::VARIANTS.iter().enumerate() {
|
||||
assert_eq!(idx, value as usize);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Input: Mouse
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns true if the given mouse button is held down.
|
||||
///
|
||||
/// Equivalent to indexing the Io struct with the button, e.g. `ui.io()[button]`.
|
||||
#[doc(alias = "IsMouseDown")]
|
||||
pub fn is_mouse_down(&self, button: MouseButton) -> bool {
|
||||
unsafe { sys::igIsMouseDown(button as i32) }
|
||||
}
|
||||
/// Returns true if any mouse button is held down
|
||||
#[doc(alias = "IsAnyMouseDown")]
|
||||
pub fn is_any_mouse_down(&self) -> bool {
|
||||
unsafe { sys::igIsAnyMouseDown() }
|
||||
}
|
||||
/// Returns true if the given mouse button was clicked (went from !down to down)
|
||||
#[doc(alias = "IsMouseClicked")]
|
||||
pub fn is_mouse_clicked(&self, button: MouseButton) -> bool {
|
||||
unsafe { sys::igIsMouseClicked(button as i32, false) }
|
||||
}
|
||||
/// Returns true if the given mouse button was double-clicked
|
||||
#[doc(alias = "IsMouseDoubleClicked")]
|
||||
pub fn is_mouse_double_clicked(&self, button: MouseButton) -> bool {
|
||||
unsafe { sys::igIsMouseDoubleClicked(button as i32) }
|
||||
}
|
||||
/// Returns true if the given mouse button was released (went from down to !down)
|
||||
#[doc(alias = "IsMouseReleased")]
|
||||
pub fn is_mouse_released(&self, button: MouseButton) -> bool {
|
||||
unsafe { sys::igIsMouseReleased(button as i32) }
|
||||
}
|
||||
/// Returns true if the mouse is currently dragging with the given mouse button held down
|
||||
#[doc(alias = "IsMouseDragging")]
|
||||
pub fn is_mouse_dragging(&self, button: MouseButton) -> bool {
|
||||
unsafe { sys::igIsMouseDragging(button as i32, -1.0) }
|
||||
}
|
||||
/// Returns true if the mouse is currently dragging with the given mouse button held down.
|
||||
///
|
||||
/// If the given threshold is invalid or negative, the global distance threshold is used
|
||||
/// (`io.mouse_drag_threshold`).
|
||||
#[doc(alias = "IsMouseDragging")]
|
||||
pub fn is_mouse_dragging_with_threshold(&self, button: MouseButton, threshold: f32) -> bool {
|
||||
unsafe { sys::igIsMouseDragging(button as i32, threshold) }
|
||||
}
|
||||
/// Returns true if the mouse is hovering over the given bounding rect.
|
||||
///
|
||||
/// Clipped by current clipping settings, but disregards other factors like focus, window
|
||||
/// ordering, modal popup blocking.
|
||||
pub fn is_mouse_hovering_rect(&self, r_min: [f32; 2], r_max: [f32; 2]) -> bool {
|
||||
unsafe { sys::igIsMouseHoveringRect(r_min.into(), r_max.into(), true) }
|
||||
}
|
||||
/// Returns the mouse position backed up at the time of opening a popup
|
||||
#[doc(alias = "GetMousePosOnOpeningCurrentPopup")]
|
||||
pub fn mouse_pos_on_opening_current_popup(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetMousePosOnOpeningCurrentPopup(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
|
||||
/// Returns the delta from the initial position when the left mouse button clicked.
|
||||
///
|
||||
/// This is locked and returns [0.0, 0.0] until the mouse has moved past the global distance
|
||||
/// threshold (`io.mouse_drag_threshold`).
|
||||
///
|
||||
/// This is the same as [mouse_drag_delta_with_button](Self::mouse_drag_delta_with_button) with
|
||||
/// `button` set to `MouseButton::Left`.
|
||||
#[doc(alias = "GetMouseDragDelta")]
|
||||
pub fn mouse_drag_delta(&self) -> [f32; 2] {
|
||||
self.mouse_drag_delta_with_button(MouseButton::Left)
|
||||
}
|
||||
|
||||
/// Returns the delta from the initial position when the given button was clicked.
|
||||
///
|
||||
/// This is locked and returns [0.0, 0.0] until the mouse has moved past the global distance
|
||||
/// threshold (`io.mouse_drag_threshold`).
|
||||
///
|
||||
/// This is the same as [mouse_drag_delta_with_threshold](Self::mouse_drag_delta_with_threshold) with
|
||||
/// `threshold` set to `-1.0`, which uses the global threshold `io.mouse_drag_threshold`.
|
||||
#[doc(alias = "GetMouseDragDelta")]
|
||||
pub fn mouse_drag_delta_with_button(&self, button: MouseButton) -> [f32; 2] {
|
||||
self.mouse_drag_delta_with_threshold(button, -1.0)
|
||||
}
|
||||
/// Returns the delta from the initial clicking position.
|
||||
///
|
||||
/// This is locked and returns [0.0, 0.0] until the mouse has moved past the given threshold.
|
||||
/// If the given threshold is invalid or negative, the global distance threshold is used
|
||||
/// (`io.mouse_drag_threshold`).
|
||||
#[doc(alias = "GetMouseDragDelta")]
|
||||
pub fn mouse_drag_delta_with_threshold(&self, button: MouseButton, threshold: f32) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetMouseDragDelta(&mut out, button as i32, threshold) };
|
||||
out.into()
|
||||
}
|
||||
/// Resets the current delta from initial clicking position.
|
||||
#[doc(alias = "ResetMouseDragDelta")]
|
||||
pub fn reset_mouse_drag_delta(&self, button: MouseButton) {
|
||||
// This mutates the Io struct, but targets an internal field so there can't be any
|
||||
// references to it
|
||||
unsafe { sys::igResetMouseDragDelta(button as i32) }
|
||||
}
|
||||
/// Returns the currently desired mouse cursor type.
|
||||
///
|
||||
/// Returns `None` if no cursor should be displayed
|
||||
#[doc(alias = "GetMouseCursor")]
|
||||
pub fn mouse_cursor(&self) -> Option<MouseCursor> {
|
||||
match unsafe { sys::igGetMouseCursor() } {
|
||||
sys::ImGuiMouseCursor_Arrow => Some(MouseCursor::Arrow),
|
||||
sys::ImGuiMouseCursor_TextInput => Some(MouseCursor::TextInput),
|
||||
sys::ImGuiMouseCursor_ResizeAll => Some(MouseCursor::ResizeAll),
|
||||
sys::ImGuiMouseCursor_ResizeNS => Some(MouseCursor::ResizeNS),
|
||||
sys::ImGuiMouseCursor_ResizeEW => Some(MouseCursor::ResizeEW),
|
||||
sys::ImGuiMouseCursor_ResizeNESW => Some(MouseCursor::ResizeNESW),
|
||||
sys::ImGuiMouseCursor_ResizeNWSE => Some(MouseCursor::ResizeNWSE),
|
||||
sys::ImGuiMouseCursor_Hand => Some(MouseCursor::Hand),
|
||||
sys::ImGuiMouseCursor_NotAllowed => Some(MouseCursor::NotAllowed),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Sets the desired mouse cursor type.
|
||||
///
|
||||
/// Passing `None` hides the mouse cursor.
|
||||
#[doc(alias = "SetMouseCursor")]
|
||||
pub fn set_mouse_cursor(&self, cursor_type: Option<MouseCursor>) {
|
||||
unsafe {
|
||||
sys::igSetMouseCursor(
|
||||
cursor_type
|
||||
.map(|x| x as i32)
|
||||
.unwrap_or(sys::ImGuiMouseCursor_None),
|
||||
);
|
||||
}
|
||||
}
|
||||
#[doc(alias = "IsMousePosValid")]
|
||||
pub fn is_current_mouse_pos_valid(&self) -> bool {
|
||||
unsafe { sys::igIsMousePosValid(ptr::null()) }
|
||||
}
|
||||
#[doc(alias = "IsMousePosValid")]
|
||||
pub fn is_mouse_pos_valid(&self, mouse_pos: [f32; 2]) -> bool {
|
||||
unsafe { sys::igIsMousePosValid(&mouse_pos.into()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mouse_down_clicked_released() {
|
||||
for &button in MouseButton::VARIANTS.iter() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx_initialized();
|
||||
{
|
||||
ctx.io_mut().mouse_down = [false; 5];
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_down(button));
|
||||
assert!(!ui.is_any_mouse_down());
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_released(button));
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = true;
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_down(button));
|
||||
assert!(ui.is_any_mouse_down());
|
||||
assert!(ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_released(button));
|
||||
}
|
||||
{
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_down(button));
|
||||
assert!(ui.is_any_mouse_down());
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_released(button));
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = false;
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_down(button));
|
||||
assert!(!ui.is_any_mouse_down());
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(ui.is_mouse_released(button));
|
||||
}
|
||||
{
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_down(button));
|
||||
assert!(!ui.is_any_mouse_down());
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_released(button));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mouse_double_click() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx_initialized();
|
||||
// Workaround for dear imgui bug/feature:
|
||||
// If a button is clicked before io.mouse_double_click_time seconds has passed after the
|
||||
// context is initialized, the single click is interpreted as a double-click. This happens
|
||||
// because internally g.IO.MouseClickedTime is set to 0.0, so the context creation is
|
||||
// considered a "click".
|
||||
{
|
||||
// Pass one second of time
|
||||
ctx.io_mut().delta_time = 1.0;
|
||||
let _ = ctx.frame();
|
||||
}
|
||||
// Fast clicks
|
||||
ctx.io_mut().delta_time = 1.0 / 60.0;
|
||||
for &button in MouseButton::VARIANTS.iter() {
|
||||
{
|
||||
ctx.io_mut().mouse_down = [false; 5];
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = true;
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = false;
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = true;
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_clicked(button));
|
||||
assert!(ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
}
|
||||
// Slow clicks
|
||||
ctx.io_mut().delta_time = 1.0;
|
||||
for &button in MouseButton::VARIANTS.iter() {
|
||||
{
|
||||
ctx.io_mut().mouse_down = [false; 5];
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = true;
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = false;
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = true;
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
{
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_clicked(button));
|
||||
assert!(!ui.is_mouse_double_clicked(button));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_get_mouse_cursor() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx_initialized();
|
||||
let ui = ctx.frame();
|
||||
ui.set_mouse_cursor(None);
|
||||
assert_eq!(None, ui.mouse_cursor());
|
||||
ui.set_mouse_cursor(Some(MouseCursor::Hand));
|
||||
assert_eq!(Some(MouseCursor::Hand), ui.mouse_cursor());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mouse_drags() {
|
||||
for &button in MouseButton::VARIANTS.iter() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx_initialized();
|
||||
{
|
||||
ctx.io_mut().mouse_pos = [0.0, 0.0];
|
||||
ctx.io_mut().mouse_down = [false; 5];
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_dragging(button));
|
||||
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [0.0, 0.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[0.0, 0.0]
|
||||
);
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = true;
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_dragging(button));
|
||||
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [0.0, 0.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[0.0, 0.0]
|
||||
);
|
||||
}
|
||||
{
|
||||
ctx.io_mut().mouse_pos = [0.0, 100.0];
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_dragging(button));
|
||||
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [0.0, 100.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[0.0, 0.0]
|
||||
);
|
||||
}
|
||||
{
|
||||
ctx.io_mut().mouse_pos = [0.0, 200.0];
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_dragging(button));
|
||||
assert!(ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [0.0, 200.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[0.0, 200.0]
|
||||
);
|
||||
}
|
||||
{
|
||||
ctx.io_mut().mouse_pos = [10.0, 10.0];
|
||||
ctx.io_mut()[button] = false;
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_dragging(button));
|
||||
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [10.0, 10.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[10.0, 10.0]
|
||||
);
|
||||
}
|
||||
{
|
||||
ctx.io_mut()[button] = true;
|
||||
let ui = ctx.frame();
|
||||
assert!(!ui.is_mouse_dragging(button));
|
||||
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [0.0, 0.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[0.0, 0.0]
|
||||
);
|
||||
}
|
||||
{
|
||||
ctx.io_mut().mouse_pos = [180.0, 180.0];
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_dragging(button));
|
||||
assert!(ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [170.0, 170.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[170.0, 170.0]
|
||||
);
|
||||
ui.reset_mouse_drag_delta(button);
|
||||
assert!(ui.is_mouse_dragging(button));
|
||||
assert!(ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [0.0, 0.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[0.0, 0.0]
|
||||
);
|
||||
}
|
||||
{
|
||||
ctx.io_mut().mouse_pos = [200.0, 200.0];
|
||||
let ui = ctx.frame();
|
||||
assert!(ui.is_mouse_dragging(button));
|
||||
assert!(ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||
assert_eq!(ui.mouse_drag_delta_with_button(button), [20.0, 20.0]);
|
||||
assert_eq!(
|
||||
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||
[20.0, 20.0]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
1023
plugins/libimhex-rust/imgui-rs/src/input_widget.rs
Normal file
1023
plugins/libimhex-rust/imgui-rs/src/input_widget.rs
Normal file
File diff suppressed because it is too large
Load Diff
159
plugins/libimhex-rust/imgui-rs/src/internal.rs
Normal file
159
plugins/libimhex-rust/imgui-rs/src/internal.rs
Normal file
@ -0,0 +1,159 @@
|
||||
//! Internal raw utilities (don't use unless you know what you're doing!)
|
||||
|
||||
use std::slice;
|
||||
|
||||
/// A generic version of the raw imgui-sys ImVector struct types
|
||||
#[repr(C)]
|
||||
pub struct ImVector<T> {
|
||||
size: i32,
|
||||
capacity: i32,
|
||||
pub(crate) data: *mut T,
|
||||
}
|
||||
|
||||
impl<T> ImVector<T> {
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
unsafe { slice::from_raw_parts(self.data, self.size as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn test_imvector_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(
|
||||
mem::size_of::<ImVector<u8>>(),
|
||||
mem::size_of::<sys::ImVector_char>()
|
||||
);
|
||||
assert_eq!(
|
||||
mem::align_of::<ImVector<u8>>(),
|
||||
mem::align_of::<sys::ImVector_char>()
|
||||
);
|
||||
use sys::ImVector_char;
|
||||
type VectorChar = ImVector<u8>;
|
||||
macro_rules! assert_field_offset {
|
||||
($l:ident, $r:ident) => {
|
||||
assert_eq!(
|
||||
memoffset::offset_of!(VectorChar, $l),
|
||||
memoffset::offset_of!(ImVector_char, $r)
|
||||
);
|
||||
};
|
||||
}
|
||||
assert_field_offset!(size, Size);
|
||||
assert_field_offset!(capacity, Capacity);
|
||||
assert_field_offset!(data, Data);
|
||||
}
|
||||
|
||||
/// Marks a type as a transparent wrapper over a raw type
|
||||
pub trait RawWrapper {
|
||||
/// Wrapped raw type
|
||||
type Raw;
|
||||
/// Returns an immutable reference to the wrapped raw value
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to use the returned raw reference without causing undefined
|
||||
/// behaviour or breaking safety rules.
|
||||
unsafe fn raw(&self) -> &Self::Raw;
|
||||
/// Returns a mutable reference to the wrapped raw value
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to use the returned mutable raw reference without causing undefined
|
||||
/// behaviour or breaking safety rules.
|
||||
unsafe fn raw_mut(&mut self) -> &mut Self::Raw;
|
||||
}
|
||||
|
||||
/// Casting from/to a raw type that has the same layout and alignment as the target type
|
||||
pub unsafe trait RawCast<T>: Sized {
|
||||
/// Casts an immutable reference from the raw type
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the cast is valid.
|
||||
#[inline]
|
||||
unsafe fn from_raw(raw: &T) -> &Self {
|
||||
&*(raw as *const _ as *const Self)
|
||||
}
|
||||
/// Casts a mutable reference from the raw type
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the cast is valid.
|
||||
#[inline]
|
||||
unsafe fn from_raw_mut(raw: &mut T) -> &mut Self {
|
||||
&mut *(raw as *mut _ as *mut Self)
|
||||
}
|
||||
/// Casts an immutable reference to the raw type
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the cast is valid.
|
||||
#[inline]
|
||||
unsafe fn raw(&self) -> &T {
|
||||
&*(self as *const _ as *const T)
|
||||
}
|
||||
/// Casts a mutable reference to the raw type
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the cast is valid.
|
||||
#[inline]
|
||||
unsafe fn raw_mut(&mut self) -> &mut T {
|
||||
&mut *(self as *mut _ as *mut T)
|
||||
}
|
||||
}
|
||||
|
||||
/// A primary data type
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum DataType {
|
||||
I8 = sys::ImGuiDataType_S8,
|
||||
U8 = sys::ImGuiDataType_U8,
|
||||
I16 = sys::ImGuiDataType_S16,
|
||||
U16 = sys::ImGuiDataType_U16,
|
||||
I32 = sys::ImGuiDataType_S32,
|
||||
U32 = sys::ImGuiDataType_U32,
|
||||
I64 = sys::ImGuiDataType_S64,
|
||||
U64 = sys::ImGuiDataType_U64,
|
||||
F32 = sys::ImGuiDataType_Float,
|
||||
F64 = sys::ImGuiDataType_Double,
|
||||
}
|
||||
|
||||
/// Primitive type marker.
|
||||
///
|
||||
/// If this trait is implemented for a type, it is assumed to have *exactly* the same
|
||||
/// representation in memory as the primitive value described by the associated `KIND` constant.
|
||||
pub unsafe trait DataTypeKind: Copy {
|
||||
const KIND: DataType;
|
||||
}
|
||||
unsafe impl DataTypeKind for i8 {
|
||||
const KIND: DataType = DataType::I8;
|
||||
}
|
||||
unsafe impl DataTypeKind for u8 {
|
||||
const KIND: DataType = DataType::U8;
|
||||
}
|
||||
unsafe impl DataTypeKind for i16 {
|
||||
const KIND: DataType = DataType::I16;
|
||||
}
|
||||
unsafe impl DataTypeKind for u16 {
|
||||
const KIND: DataType = DataType::U16;
|
||||
}
|
||||
unsafe impl DataTypeKind for i32 {
|
||||
const KIND: DataType = DataType::I32;
|
||||
}
|
||||
unsafe impl DataTypeKind for u32 {
|
||||
const KIND: DataType = DataType::U32;
|
||||
}
|
||||
unsafe impl DataTypeKind for i64 {
|
||||
const KIND: DataType = DataType::I64;
|
||||
}
|
||||
unsafe impl DataTypeKind for u64 {
|
||||
const KIND: DataType = DataType::U64;
|
||||
}
|
||||
unsafe impl DataTypeKind for f32 {
|
||||
const KIND: DataType = DataType::F32;
|
||||
}
|
||||
unsafe impl DataTypeKind for f64 {
|
||||
const KIND: DataType = DataType::F64;
|
||||
}
|
506
plugins/libimhex-rust/imgui-rs/src/io.rs
Normal file
506
plugins/libimhex-rust/imgui-rs/src/io.rs
Normal file
@ -0,0 +1,506 @@
|
||||
use bitflags::bitflags;
|
||||
use std::f32;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::fonts::atlas::FontAtlas;
|
||||
use crate::fonts::font::Font;
|
||||
use crate::input::keyboard::Key;
|
||||
use crate::input::mouse::MouseButton;
|
||||
use crate::internal::{ImVector, RawCast};
|
||||
use crate::sys;
|
||||
|
||||
bitflags! {
|
||||
/// Configuration flags
|
||||
#[repr(transparent)]
|
||||
pub struct ConfigFlags: u32 {
|
||||
/// Master keyboard navigation enable flag.
|
||||
///
|
||||
/// `frame()` will automatically fill `io.nav_inputs` based on `io.keys_down`.
|
||||
const NAV_ENABLE_KEYBOARD = sys::ImGuiConfigFlags_NavEnableKeyboard;
|
||||
/// Master gamepad navigation enable flag.
|
||||
///
|
||||
/// This is mostly to instruct the backend to fill `io.nav_inputs`. The backend
|
||||
/// also needs to set `BackendFlags::HasGamepad`.
|
||||
const NAV_ENABLE_GAMEPAD = sys::ImGuiConfigFlags_NavEnableGamepad;
|
||||
/// Instruction navigation to move the mouse cursor.
|
||||
///
|
||||
/// May be useful on TV/console systems where moving a virtual mouse is awkward.
|
||||
/// Will update `io.mouse_pos` and set `io.want_set_mouse_pos = true`. If enabled,
|
||||
/// you *must* honor `io.want_set_mouse_pos`, or imgui-rs will react as if the mouse is
|
||||
/// jumping around back and forth.
|
||||
const NAV_ENABLE_SET_MOUSE_POS = sys::ImGuiConfigFlags_NavEnableSetMousePos;
|
||||
/// Instruction navigation to not set the `io.want_capture_keyboard` flag when
|
||||
/// `io.nav_active` is set.
|
||||
const NAV_NO_CAPTURE_KEYBOARD = sys::ImGuiConfigFlags_NavNoCaptureKeyboard;
|
||||
/// Instruction imgui-rs to clear mouse position/buttons in `frame()`.
|
||||
///
|
||||
/// This allows ignoring the mouse information set by the backend.
|
||||
const NO_MOUSE = sys::ImGuiConfigFlags_NoMouse;
|
||||
/// Instruction backend to not alter mouse cursor shape and visibility.
|
||||
///
|
||||
/// Use if the backend cursor changes are interfering with yours and you don't want to use
|
||||
/// `set_mouse_cursor` to change the mouse cursor. You may want to honor requests from
|
||||
/// imgui-rs by reading `get_mouse_cursor` yourself instead.
|
||||
const NO_MOUSE_CURSOR_CHANGE = sys::ImGuiConfigFlags_NoMouseCursorChange;
|
||||
/// Application is SRGB-aware.
|
||||
///
|
||||
/// Not used by core imgui-rs.
|
||||
const IS_SRGB = sys::ImGuiConfigFlags_IsSRGB;
|
||||
/// Application is using a touch screen instead of a mouse.
|
||||
///
|
||||
/// Not used by core imgui-rs.
|
||||
const IS_TOUCH_SCREEN = sys::ImGuiConfigFlags_IsTouchScreen;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Backend capabilities
|
||||
#[repr(transparent)]
|
||||
pub struct BackendFlags: u32 {
|
||||
/// Backend supports gamepad and currently has one connected
|
||||
const HAS_GAMEPAD = sys::ImGuiBackendFlags_HasGamepad;
|
||||
/// Backend supports honoring `get_mouse_cursor` value to change the OS cursor shape
|
||||
const HAS_MOUSE_CURSORS = sys::ImGuiBackendFlags_HasMouseCursors;
|
||||
/// Backend supports `io.want_set_mouse_pos` requests to reposition the OS mouse position.
|
||||
///
|
||||
/// Only used if `ConfigFlags::NavEnableSetMousePos` is set.
|
||||
const HAS_SET_MOUSE_POS = sys::ImGuiBackendFlags_HasSetMousePos;
|
||||
/// Backend renderer supports DrawCmd::vtx_offset.
|
||||
///
|
||||
/// This enables output of large meshes (64K+ vertices) while still using 16-bits indices.
|
||||
const RENDERER_HAS_VTX_OFFSET = sys::ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/// An input identifier for navigation
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub enum NavInput {
|
||||
Activate = sys::ImGuiNavInput_Activate,
|
||||
Cancel = sys::ImGuiNavInput_Cancel,
|
||||
Input = sys::ImGuiNavInput_Input,
|
||||
Menu = sys::ImGuiNavInput_Menu,
|
||||
DpadLeft = sys::ImGuiNavInput_DpadLeft,
|
||||
DpadRight = sys::ImGuiNavInput_DpadRight,
|
||||
DpadUp = sys::ImGuiNavInput_DpadUp,
|
||||
DpadDown = sys::ImGuiNavInput_DpadDown,
|
||||
LStickLeft = sys::ImGuiNavInput_LStickLeft,
|
||||
LStickRight = sys::ImGuiNavInput_LStickRight,
|
||||
LStickUp = sys::ImGuiNavInput_LStickUp,
|
||||
LStickDown = sys::ImGuiNavInput_LStickDown,
|
||||
FocusPrev = sys::ImGuiNavInput_FocusPrev,
|
||||
FocusNext = sys::ImGuiNavInput_FocusNext,
|
||||
TweakSlow = sys::ImGuiNavInput_TweakSlow,
|
||||
TweakFast = sys::ImGuiNavInput_TweakFast,
|
||||
}
|
||||
|
||||
impl NavInput {
|
||||
/// All possible `NavInput` variants
|
||||
pub const VARIANTS: [NavInput; NavInput::COUNT] = [
|
||||
NavInput::Activate,
|
||||
NavInput::Cancel,
|
||||
NavInput::Input,
|
||||
NavInput::Menu,
|
||||
NavInput::DpadLeft,
|
||||
NavInput::DpadRight,
|
||||
NavInput::DpadUp,
|
||||
NavInput::DpadDown,
|
||||
NavInput::LStickLeft,
|
||||
NavInput::LStickRight,
|
||||
NavInput::LStickUp,
|
||||
NavInput::LStickDown,
|
||||
NavInput::FocusPrev,
|
||||
NavInput::FocusNext,
|
||||
NavInput::TweakSlow,
|
||||
NavInput::TweakFast,
|
||||
];
|
||||
/// Amount of internal/hidden variants (not exposed by imgui-rs)
|
||||
const INTERNAL_COUNT: usize = 4;
|
||||
/// Total count of `NavInput` variants
|
||||
pub const COUNT: usize = sys::ImGuiNavInput_COUNT as usize - NavInput::INTERNAL_COUNT;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nav_input_variants() {
|
||||
for (idx, &value) in NavInput::VARIANTS.iter().enumerate() {
|
||||
assert_eq!(idx, value as usize);
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings and inputs/outputs for imgui-rs
|
||||
#[repr(C)]
|
||||
pub struct Io {
|
||||
/// Flags set by user/application
|
||||
pub config_flags: ConfigFlags,
|
||||
/// Flags set by backend
|
||||
pub backend_flags: BackendFlags,
|
||||
/// Main display size in pixels
|
||||
pub display_size: [f32; 2],
|
||||
/// Time elapsed since last frame, in seconds
|
||||
pub delta_time: f32,
|
||||
/// Minimum time between saving positions/sizes to .ini file, in seconds
|
||||
pub ini_saving_rate: f32,
|
||||
|
||||
pub(crate) ini_filename: *const c_char,
|
||||
pub(crate) log_filename: *const c_char,
|
||||
|
||||
/// Time for a double-click, in seconds
|
||||
pub mouse_double_click_time: f32,
|
||||
/// Distance threshold to stay in to validate a double-click, in pixels
|
||||
pub mouse_double_click_max_dist: f32,
|
||||
/// Distance threshold before considering we are dragging
|
||||
pub mouse_drag_threshold: f32,
|
||||
/// Map of indices into the `keys_down` entries array, which represent your "native" keyboard
|
||||
/// state
|
||||
pub key_map: [u32; sys::ImGuiKey_COUNT as usize],
|
||||
/// When holding a key/button, time before it starts repeating, in seconds
|
||||
pub key_repeat_delay: f32,
|
||||
/// When holding a key/button, rate at which it repeats, in seconds
|
||||
pub key_repeat_rate: f32,
|
||||
|
||||
user_data: *mut c_void,
|
||||
pub(crate) fonts: *mut FontAtlas,
|
||||
|
||||
/// Global scale for all fonts
|
||||
pub font_global_scale: f32,
|
||||
/// Allow user to scale text of individual window with CTRL+wheel
|
||||
pub font_allow_user_scaling: bool,
|
||||
|
||||
pub(crate) font_default: *mut Font,
|
||||
|
||||
/// For retina display or other situations where window coordinates are different from
|
||||
/// framebuffer coordinates
|
||||
pub display_framebuffer_scale: [f32; 2],
|
||||
|
||||
/// Request imgui-rs to draw a mouse cursor for you
|
||||
pub mouse_draw_cursor: bool,
|
||||
/// macOS-style input behavior.
|
||||
///
|
||||
/// Defaults to true on Apple platforms. Changes in behavior:
|
||||
///
|
||||
/// * Text editing cursor movement using Alt instead of Ctrl
|
||||
/// * Shortcuts using Cmd/Super instead of Ctrl
|
||||
/// * Line/text start and end using Cmd+Arrows instead of Home/End
|
||||
/// * Double-click selects by word instead of selecting the whole text
|
||||
/// * Multi-selection in lists uses Cmd/Super instead of Ctrl
|
||||
pub config_mac_os_behaviors: bool,
|
||||
/// Set to false to disable blinking cursor
|
||||
pub config_input_text_cursor_blink: bool,
|
||||
/// Enable turning DragXXX widgets into text input with a simple mouse
|
||||
/// click-release (without moving). Not desirable on devices without a
|
||||
/// keyboard.
|
||||
pub config_drag_click_to_input_text: bool,
|
||||
/// Enable resizing of windows from their edges and from the lower-left corner.
|
||||
///
|
||||
/// Requires `HasMouserCursors` in `backend_flags`, because it needs mouse cursor feedback.
|
||||
pub config_windows_resize_from_edges: bool,
|
||||
/// Set to true to only allow moving windows when clicked+dragged from the title bar.
|
||||
///
|
||||
/// Windows without a title bar are not affected.
|
||||
pub config_windows_move_from_title_bar_only: bool,
|
||||
/// Compact memory usage when unused.
|
||||
///
|
||||
/// Set to -1.0 to disable.
|
||||
pub config_memory_compact_timer: f32,
|
||||
|
||||
pub(crate) backend_platform_name: *const c_char,
|
||||
pub(crate) backend_renderer_name: *const c_char,
|
||||
backend_platform_user_data: *mut c_void,
|
||||
backend_renderer_user_data: *mut c_void,
|
||||
backend_language_user_data: *mut c_void,
|
||||
pub(crate) get_clipboard_text_fn:
|
||||
Option<unsafe extern "C" fn(user_data: *mut c_void) -> *const c_char>,
|
||||
pub(crate) set_clipboard_text_fn:
|
||||
Option<unsafe extern "C" fn(user_data: *mut c_void, text: *const c_char)>,
|
||||
pub(crate) clipboard_user_data: *mut c_void,
|
||||
ime_set_input_screen_pos_fn: Option<unsafe extern "C" fn(x: c_int, y: c_int)>,
|
||||
ime_window_handle: *mut c_void,
|
||||
/// Mouse position, in pixels.
|
||||
///
|
||||
/// Set to [f32::MAX, f32::MAX] if mouse is unavailable (on another screen, etc.).
|
||||
pub mouse_pos: [f32; 2],
|
||||
/// Mouse buttons: 0=left, 1=right, 2=middle + extras
|
||||
pub mouse_down: [bool; 5],
|
||||
/// Mouse wheel (vertical).
|
||||
///
|
||||
/// 1 unit scrolls about 5 lines of text.
|
||||
pub mouse_wheel: f32,
|
||||
/// Mouse wheel (horizontal).
|
||||
///
|
||||
/// Most users don't have a mouse with a horizontal wheel, and may not be filled by all
|
||||
/// backends.
|
||||
pub mouse_wheel_h: f32,
|
||||
/// Keyboard modifier pressed: Control
|
||||
pub key_ctrl: bool,
|
||||
/// Keyboard modifier pressed: Shift
|
||||
pub key_shift: bool,
|
||||
/// Keyboard modifier pressed: Alt
|
||||
pub key_alt: bool,
|
||||
/// Keyboard modifier pressed: Cmd/Super/Windows
|
||||
pub key_super: bool,
|
||||
/// Keyboard keys that are pressed (indexing defined by the user/application)
|
||||
pub keys_down: [bool; 512],
|
||||
/// Gamepad inputs.
|
||||
///
|
||||
/// Cleared back to zero after each frame. Keyboard keys will be auto-mapped and written
|
||||
/// here by `frame()`.
|
||||
pub nav_inputs: [f32; NavInput::COUNT + NavInput::INTERNAL_COUNT],
|
||||
/// When true, imgui-rs will use the mouse inputs, so do not dispatch them to your main
|
||||
/// game/application
|
||||
pub want_capture_mouse: bool,
|
||||
/// When true, imgui-rs will use the keyboard inputs, so do not dispatch them to your main
|
||||
/// game/application
|
||||
pub want_capture_keyboard: bool,
|
||||
/// Mobile/console: when true, you may display an on-screen keyboard.
|
||||
///
|
||||
/// This is set by imgui-rs when it wants textual keyboard input to happen.
|
||||
pub want_text_input: bool,
|
||||
/// Mouse position has been altered, so the backend should reposition the mouse on the next
|
||||
/// frame.
|
||||
///
|
||||
/// Set only when `ConfigFlags::NavEnableSetMousePos` is enabled.
|
||||
pub want_set_mouse_pos: bool,
|
||||
/// When manual .ini load/save is active (`ini_filename` is `None`), this will be set to notify
|
||||
/// your application that you can call `save_ini_settings` and save the settings yourself.
|
||||
///
|
||||
/// *Important*: You need to clear this flag yourself
|
||||
pub want_save_ini_settings: bool,
|
||||
/// Keyboard/Gamepad navigation is currently allowed
|
||||
pub nav_active: bool,
|
||||
/// Keyboard/Gamepad navigation is visible and allowed
|
||||
pub nav_visible: bool,
|
||||
/// Application framerate estimation, in frames per second.
|
||||
///
|
||||
/// Rolling average estimation based on `io.delta_time` over 120 frames.
|
||||
pub framerate: f32,
|
||||
/// Vertices output during last rendering
|
||||
pub metrics_render_vertices: i32,
|
||||
/// Indices output during last rendering (= number of triangles * 3)
|
||||
pub metrics_render_indices: i32,
|
||||
/// Number of visible windows
|
||||
pub metrics_render_windows: i32,
|
||||
/// Number of active windows
|
||||
pub metrics_active_windows: i32,
|
||||
/// Number of active internal imgui-rs allocations
|
||||
pub metrics_active_allocations: i32,
|
||||
/// Mouse delta.
|
||||
///
|
||||
/// Note that this is zero if either current or previous position is invalid ([f32::MAX,
|
||||
/// f32::MAX]), so a disappearing/reappearing mouse won't have a huge delta.
|
||||
pub mouse_delta: [f32; 2],
|
||||
|
||||
key_mods: sys::ImGuiKeyModFlags,
|
||||
key_mods_prev: sys::ImGuiKeyModFlags,
|
||||
mouse_pos_prev: [f32; 2],
|
||||
mouse_clicked_pos: [[f32; 2]; 5],
|
||||
mouse_clicked_time: [f64; 5],
|
||||
mouse_clicked: [bool; 5],
|
||||
mouse_double_clicked: [bool; 5],
|
||||
mouse_released: [bool; 5],
|
||||
mouse_down_owned: [bool; 5],
|
||||
mouse_down_was_double_click: [bool; 5],
|
||||
mouse_down_duration: [f32; 5],
|
||||
mouse_down_duration_prev: [f32; 5],
|
||||
mouse_drag_max_distance_abs: [[f32; 2]; 5],
|
||||
mouse_drag_max_distance_sqr: [f32; 5],
|
||||
keys_down_duration: [f32; 512],
|
||||
keys_down_duration_prev: [f32; 512],
|
||||
nav_inputs_down_duration: [f32; NavInput::COUNT + NavInput::INTERNAL_COUNT],
|
||||
nav_inputs_down_duration_prev: [f32; NavInput::COUNT + NavInput::INTERNAL_COUNT],
|
||||
pen_pressure: f32,
|
||||
input_queue_surrogate: sys::ImWchar16,
|
||||
input_queue_characters: ImVector<sys::ImWchar>,
|
||||
}
|
||||
|
||||
unsafe impl RawCast<sys::ImGuiIO> for Io {}
|
||||
|
||||
impl Io {
|
||||
/// Queue new character input
|
||||
#[doc(alias = "AddInputCharactersUTF8")]
|
||||
pub fn add_input_character(&mut self, character: char) {
|
||||
let mut buf = [0; 5];
|
||||
character.encode_utf8(&mut buf);
|
||||
unsafe {
|
||||
sys::ImGuiIO_AddInputCharactersUTF8(self.raw_mut(), buf.as_ptr() as *const _);
|
||||
}
|
||||
}
|
||||
/// Clear character input buffer
|
||||
#[doc(alias = "ClearCharacters")]
|
||||
pub fn clear_input_characters(&mut self) {
|
||||
unsafe {
|
||||
sys::ImGuiIO_ClearInputCharacters(self.raw_mut());
|
||||
}
|
||||
}
|
||||
/// Peek character input buffer, return a copy of entire buffer
|
||||
pub fn peek_input_characters(&self) -> String {
|
||||
self.input_queue_characters().collect()
|
||||
}
|
||||
|
||||
/// Returns a view of the data in the input queue (without copying it).
|
||||
///
|
||||
/// The returned iterator is a simple mapping over a slice more or less what
|
||||
/// you need for random access to the data (Rust has no
|
||||
/// `RandomAccessIterator`, or we'd use that).
|
||||
pub fn input_queue_characters(
|
||||
&self,
|
||||
) -> impl Iterator<Item = char> + DoubleEndedIterator + ExactSizeIterator + Clone + '_ {
|
||||
self.input_queue_characters
|
||||
.as_slice()
|
||||
.iter()
|
||||
// TODO: are the values in the buffer guaranteed to be valid unicode
|
||||
// scalar values? if so we can just expose this as a `&[char]`...
|
||||
.map(|c| core::char::from_u32(*c).unwrap_or(core::char::REPLACEMENT_CHARACTER))
|
||||
}
|
||||
|
||||
pub fn update_delta_time(&mut self, delta: Duration) {
|
||||
let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
|
||||
if delta_s > 0.0 {
|
||||
self.delta_time = delta_s;
|
||||
} else {
|
||||
self.delta_time = f32::MIN_POSITIVE;
|
||||
}
|
||||
self.delta_time = delta_s;
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Key> for Io {
|
||||
type Output = u32;
|
||||
fn index(&self, index: Key) -> &u32 {
|
||||
&self.key_map[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<Key> for Io {
|
||||
fn index_mut(&mut self, index: Key) -> &mut u32 {
|
||||
&mut self.key_map[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<NavInput> for Io {
|
||||
type Output = f32;
|
||||
fn index(&self, index: NavInput) -> &f32 {
|
||||
&self.nav_inputs[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<NavInput> for Io {
|
||||
fn index_mut(&mut self, index: NavInput) -> &mut f32 {
|
||||
&mut self.nav_inputs[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<MouseButton> for Io {
|
||||
type Output = bool;
|
||||
fn index(&self, index: MouseButton) -> &bool {
|
||||
&self.mouse_down[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<MouseButton> for Io {
|
||||
fn index_mut(&mut self, index: MouseButton) -> &mut bool {
|
||||
&mut self.mouse_down[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn test_io_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(mem::size_of::<Io>(), mem::size_of::<sys::ImGuiIO>());
|
||||
assert_eq!(mem::align_of::<Io>(), mem::align_of::<sys::ImGuiIO>());
|
||||
use sys::ImGuiIO;
|
||||
macro_rules! assert_field_offset {
|
||||
($l:ident, $r:ident) => {
|
||||
assert_eq!(
|
||||
memoffset::offset_of!(Io, $l),
|
||||
memoffset::offset_of!(ImGuiIO, $r)
|
||||
);
|
||||
};
|
||||
}
|
||||
assert_field_offset!(config_flags, ConfigFlags);
|
||||
assert_field_offset!(backend_flags, BackendFlags);
|
||||
assert_field_offset!(display_size, DisplaySize);
|
||||
assert_field_offset!(delta_time, DeltaTime);
|
||||
assert_field_offset!(ini_saving_rate, IniSavingRate);
|
||||
assert_field_offset!(ini_filename, IniFilename);
|
||||
assert_field_offset!(log_filename, LogFilename);
|
||||
assert_field_offset!(mouse_double_click_time, MouseDoubleClickTime);
|
||||
assert_field_offset!(mouse_double_click_max_dist, MouseDoubleClickMaxDist);
|
||||
assert_field_offset!(mouse_drag_threshold, MouseDragThreshold);
|
||||
assert_field_offset!(key_map, KeyMap);
|
||||
assert_field_offset!(key_repeat_delay, KeyRepeatDelay);
|
||||
assert_field_offset!(key_repeat_rate, KeyRepeatRate);
|
||||
assert_field_offset!(user_data, UserData);
|
||||
assert_field_offset!(fonts, Fonts);
|
||||
assert_field_offset!(font_global_scale, FontGlobalScale);
|
||||
assert_field_offset!(font_allow_user_scaling, FontAllowUserScaling);
|
||||
assert_field_offset!(font_default, FontDefault);
|
||||
assert_field_offset!(display_framebuffer_scale, DisplayFramebufferScale);
|
||||
assert_field_offset!(mouse_draw_cursor, MouseDrawCursor);
|
||||
assert_field_offset!(config_mac_os_behaviors, ConfigMacOSXBehaviors);
|
||||
assert_field_offset!(config_input_text_cursor_blink, ConfigInputTextCursorBlink);
|
||||
assert_field_offset!(
|
||||
config_windows_resize_from_edges,
|
||||
ConfigWindowsResizeFromEdges
|
||||
);
|
||||
assert_field_offset!(
|
||||
config_windows_move_from_title_bar_only,
|
||||
ConfigWindowsMoveFromTitleBarOnly
|
||||
);
|
||||
assert_field_offset!(backend_platform_name, BackendPlatformName);
|
||||
assert_field_offset!(backend_renderer_name, BackendRendererName);
|
||||
assert_field_offset!(backend_platform_user_data, BackendPlatformUserData);
|
||||
assert_field_offset!(backend_renderer_user_data, BackendRendererUserData);
|
||||
assert_field_offset!(backend_language_user_data, BackendLanguageUserData);
|
||||
assert_field_offset!(get_clipboard_text_fn, GetClipboardTextFn);
|
||||
assert_field_offset!(set_clipboard_text_fn, SetClipboardTextFn);
|
||||
assert_field_offset!(clipboard_user_data, ClipboardUserData);
|
||||
assert_field_offset!(ime_set_input_screen_pos_fn, ImeSetInputScreenPosFn);
|
||||
assert_field_offset!(ime_window_handle, ImeWindowHandle);
|
||||
assert_field_offset!(mouse_pos, MousePos);
|
||||
assert_field_offset!(mouse_down, MouseDown);
|
||||
assert_field_offset!(mouse_wheel, MouseWheel);
|
||||
assert_field_offset!(mouse_wheel_h, MouseWheelH);
|
||||
assert_field_offset!(key_ctrl, KeyCtrl);
|
||||
assert_field_offset!(key_shift, KeyShift);
|
||||
assert_field_offset!(key_alt, KeyAlt);
|
||||
assert_field_offset!(key_super, KeySuper);
|
||||
assert_field_offset!(keys_down, KeysDown);
|
||||
assert_field_offset!(nav_inputs, NavInputs);
|
||||
assert_field_offset!(want_capture_mouse, WantCaptureMouse);
|
||||
assert_field_offset!(want_capture_keyboard, WantCaptureKeyboard);
|
||||
assert_field_offset!(want_text_input, WantTextInput);
|
||||
assert_field_offset!(want_set_mouse_pos, WantSetMousePos);
|
||||
assert_field_offset!(want_save_ini_settings, WantSaveIniSettings);
|
||||
assert_field_offset!(nav_active, NavActive);
|
||||
assert_field_offset!(nav_visible, NavVisible);
|
||||
assert_field_offset!(framerate, Framerate);
|
||||
assert_field_offset!(metrics_render_vertices, MetricsRenderVertices);
|
||||
assert_field_offset!(metrics_render_indices, MetricsRenderIndices);
|
||||
assert_field_offset!(metrics_render_windows, MetricsRenderWindows);
|
||||
assert_field_offset!(metrics_active_windows, MetricsActiveWindows);
|
||||
assert_field_offset!(metrics_active_allocations, MetricsActiveAllocations);
|
||||
assert_field_offset!(mouse_delta, MouseDelta);
|
||||
assert_field_offset!(key_mods, KeyMods);
|
||||
assert_field_offset!(mouse_pos_prev, MousePosPrev);
|
||||
assert_field_offset!(mouse_clicked_pos, MouseClickedPos);
|
||||
assert_field_offset!(mouse_clicked_time, MouseClickedTime);
|
||||
assert_field_offset!(mouse_clicked, MouseClicked);
|
||||
assert_field_offset!(mouse_double_clicked, MouseDoubleClicked);
|
||||
assert_field_offset!(mouse_released, MouseReleased);
|
||||
assert_field_offset!(mouse_down_owned, MouseDownOwned);
|
||||
assert_field_offset!(mouse_down_was_double_click, MouseDownWasDoubleClick);
|
||||
assert_field_offset!(mouse_down_duration, MouseDownDuration);
|
||||
assert_field_offset!(mouse_down_duration_prev, MouseDownDurationPrev);
|
||||
assert_field_offset!(mouse_drag_max_distance_abs, MouseDragMaxDistanceAbs);
|
||||
assert_field_offset!(mouse_drag_max_distance_sqr, MouseDragMaxDistanceSqr);
|
||||
assert_field_offset!(keys_down_duration, KeysDownDuration);
|
||||
assert_field_offset!(keys_down_duration_prev, KeysDownDurationPrev);
|
||||
assert_field_offset!(nav_inputs_down_duration, NavInputsDownDuration);
|
||||
assert_field_offset!(nav_inputs_down_duration_prev, NavInputsDownDurationPrev);
|
||||
assert_field_offset!(pen_pressure, PenPressure);
|
||||
assert_field_offset!(input_queue_surrogate, InputQueueSurrogate);
|
||||
assert_field_offset!(input_queue_characters, InputQueueCharacters);
|
||||
}
|
176
plugins/libimhex-rust/imgui-rs/src/layout.rs
Normal file
176
plugins/libimhex-rust/imgui-rs/src/layout.rs
Normal file
@ -0,0 +1,176 @@
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
create_token!(
|
||||
/// Tracks a layout group that can be ended with `end` or by dropping.
|
||||
pub struct GroupToken<'ui>;
|
||||
|
||||
/// Drops the layout group manually. You can also just allow this token
|
||||
/// to drop on its own.
|
||||
drop { sys::igEndGroup() }
|
||||
);
|
||||
|
||||
/// # Cursor / Layout
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Renders a separator (generally horizontal).
|
||||
///
|
||||
/// This becomes a vertical separator inside a menu bar or in horizontal layout mode.
|
||||
#[doc(alias = "Separator")]
|
||||
pub fn separator(&self) {
|
||||
unsafe { sys::igSeparator() }
|
||||
}
|
||||
|
||||
/// Call between widgets or groups to layout them horizontally.
|
||||
///
|
||||
/// X position is given in window coordinates.
|
||||
///
|
||||
/// This is equivalent to calling [same_line_with_pos](Self::same_line_with_pos)
|
||||
/// with the `pos` set to 0.0, which uses `Style::item_spacing`.
|
||||
#[doc(alias = "SameLine")]
|
||||
pub fn same_line(&self) {
|
||||
self.same_line_with_pos(0.0);
|
||||
}
|
||||
|
||||
/// Call between widgets or groups to layout them horizontally.
|
||||
///
|
||||
/// X position is given in window coordinates.
|
||||
///
|
||||
/// This is equivalent to calling [same_line_with_spacing](Self::same_line_with_spacing)
|
||||
/// with the `spacing` set to -1.0, which means no extra spacing.
|
||||
#[doc(alias = "SameLine")]
|
||||
pub fn same_line_with_pos(&self, pos_x: f32) {
|
||||
self.same_line_with_spacing(pos_x, -1.0)
|
||||
}
|
||||
|
||||
/// Call between widgets or groups to layout them horizontally.
|
||||
///
|
||||
/// X position is given in window coordinates.
|
||||
#[doc(alias = "SameLine")]
|
||||
pub fn same_line_with_spacing(&self, pos_x: f32, spacing_w: f32) {
|
||||
unsafe { sys::igSameLine(pos_x, spacing_w) }
|
||||
}
|
||||
|
||||
/// Undo a `same_line` call or force a new line when in horizontal layout mode
|
||||
#[doc(alias = "NewLine")]
|
||||
pub fn new_line(&self) {
|
||||
unsafe { sys::igNewLine() }
|
||||
}
|
||||
/// Adds vertical spacing
|
||||
#[doc(alias = "Spacing")]
|
||||
pub fn spacing(&self) {
|
||||
unsafe { sys::igSpacing() }
|
||||
}
|
||||
/// Fills a space of `size` in pixels with nothing on the current window.
|
||||
///
|
||||
/// Can be used to move the cursor on the window.
|
||||
#[doc(alias = "Dummy")]
|
||||
pub fn dummy(&self, size: [f32; 2]) {
|
||||
unsafe { sys::igDummy(size.into()) }
|
||||
}
|
||||
|
||||
/// Moves content position to the right by `Style::indent_spacing`
|
||||
///
|
||||
/// This is equivalent to [indent_by](Self::indent_by) with `width` set to
|
||||
/// `Style::ident_spacing`.
|
||||
#[doc(alias = "Indent")]
|
||||
pub fn indent(&self) {
|
||||
self.indent_by(0.0)
|
||||
}
|
||||
|
||||
/// Moves content position to the right by `width`
|
||||
#[doc(alias = "Indent")]
|
||||
pub fn indent_by(&self, width: f32) {
|
||||
unsafe { sys::igIndent(width) };
|
||||
}
|
||||
/// Moves content position to the left by `Style::indent_spacing`
|
||||
///
|
||||
/// This is equivalent to [unindent_by](Self::unindent_by) with `width` set to
|
||||
/// `Style::ident_spacing`.
|
||||
#[doc(alias = "Unindent")]
|
||||
pub fn unindent(&self) {
|
||||
self.unindent_by(0.0)
|
||||
}
|
||||
/// Moves content position to the left by `width`
|
||||
#[doc(alias = "Unindent")]
|
||||
pub fn unindent_by(&self, width: f32) {
|
||||
unsafe { sys::igUnindent(width) };
|
||||
}
|
||||
/// Groups items together as a single item.
|
||||
///
|
||||
/// May be useful to handle the same mouse event on a group of items, for example.
|
||||
///
|
||||
/// Returns a `GroupToken` that must be ended by calling `.end()`
|
||||
#[doc(alias = "BeginGroup")]
|
||||
pub fn begin_group(&self) -> GroupToken<'_> {
|
||||
unsafe { sys::igBeginGroup() };
|
||||
GroupToken::new(self)
|
||||
}
|
||||
/// Creates a layout group and runs a closure to construct the contents.
|
||||
///
|
||||
/// May be useful to handle the same mouse event on a group of items, for example.
|
||||
#[doc(alias = "BeginGroup")]
|
||||
pub fn group<R, F: FnOnce() -> R>(&self, f: F) -> R {
|
||||
let group = self.begin_group();
|
||||
let result = f();
|
||||
group.end();
|
||||
result
|
||||
}
|
||||
/// Returns the cursor position (in window coordinates)
|
||||
#[doc(alias = "GetCursorPos")]
|
||||
pub fn cursor_pos(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetCursorPos(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
/// Sets the cursor position (in window coordinates).
|
||||
///
|
||||
/// This sets the point on which the next widget will be drawn.
|
||||
#[doc(alias = "SetCursorPos")]
|
||||
pub fn set_cursor_pos(&self, pos: [f32; 2]) {
|
||||
unsafe { sys::igSetCursorPos(pos.into()) };
|
||||
}
|
||||
/// Returns the initial cursor position (in window coordinates)
|
||||
#[doc(alias = "GetCursorStartPos")]
|
||||
pub fn cursor_start_pos(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetCursorStartPos(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
/// Returns the cursor position (in absolute screen coordinates).
|
||||
///
|
||||
/// This is especially useful for drawing, as the drawing API uses screen coordinates.
|
||||
#[doc(alias = "GetCursorScreenPos")]
|
||||
pub fn cursor_screen_pos(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetCursorScreenPos(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
/// Sets the cursor position (in absolute screen coordinates)
|
||||
#[doc(alias = "SetCursorScreenPos")]
|
||||
pub fn set_cursor_screen_pos(&self, pos: [f32; 2]) {
|
||||
unsafe { sys::igSetCursorScreenPos(pos.into()) }
|
||||
}
|
||||
/// Vertically aligns text baseline so that it will align properly to regularly frame items.
|
||||
///
|
||||
/// Call this if you have text on a line before a framed item.
|
||||
#[doc(alias = "AlignTextToFramePadding")]
|
||||
pub fn align_text_to_frame_padding(&self) {
|
||||
unsafe { sys::igAlignTextToFramePadding() };
|
||||
}
|
||||
#[doc(alias = "GetTextLineHeight")]
|
||||
pub fn text_line_height(&self) -> f32 {
|
||||
unsafe { sys::igGetTextLineHeight() }
|
||||
}
|
||||
#[doc(alias = "GetTextLineHeightWithSpacing")]
|
||||
pub fn text_line_height_with_spacing(&self) -> f32 {
|
||||
unsafe { sys::igGetTextLineHeightWithSpacing() }
|
||||
}
|
||||
#[doc(alias = "GetFrameHeight")]
|
||||
pub fn frame_height(&self) -> f32 {
|
||||
unsafe { sys::igGetFrameHeight() }
|
||||
}
|
||||
#[doc(alias = "GetFrameLineHeightWithSpacing")]
|
||||
pub fn frame_height_with_spacing(&self) -> f32 {
|
||||
unsafe { sys::igGetFrameHeightWithSpacing() }
|
||||
}
|
||||
}
|
753
plugins/libimhex-rust/imgui-rs/src/lib.rs
Normal file
753
plugins/libimhex-rust/imgui-rs/src/lib.rs
Normal file
@ -0,0 +1,753 @@
|
||||
#![cfg_attr(test, allow(clippy::float_cmp))]
|
||||
#![deny(rust_2018_idioms)]
|
||||
// #![deny(missing_docs)]
|
||||
|
||||
pub extern crate imgui_sys as sys;
|
||||
|
||||
use std::cell;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::thread;
|
||||
|
||||
pub use self::clipboard::*;
|
||||
pub use self::color::ImColor32;
|
||||
pub use self::context::*;
|
||||
pub use self::drag_drop::{DragDropFlags, DragDropSource, DragDropTarget};
|
||||
pub use self::draw_list::{ChannelsSplit, DrawListMut};
|
||||
pub use self::fonts::atlas::*;
|
||||
pub use self::fonts::font::*;
|
||||
pub use self::fonts::glyph::*;
|
||||
pub use self::fonts::glyph_ranges::*;
|
||||
pub use self::input::keyboard::*;
|
||||
pub use self::input::mouse::*;
|
||||
pub use self::input_widget::*;
|
||||
pub use self::io::*;
|
||||
pub use self::layout::*;
|
||||
pub use self::list_clipper::ListClipper;
|
||||
pub use self::plothistogram::PlotHistogram;
|
||||
pub use self::plotlines::PlotLines;
|
||||
pub use self::popups::*;
|
||||
pub use self::render::draw_data::*;
|
||||
pub use self::render::renderer::*;
|
||||
pub use self::stacks::*;
|
||||
pub use self::string::*;
|
||||
pub use self::style::*;
|
||||
|
||||
#[cfg(feature = "tables-api")]
|
||||
pub use self::tables::*;
|
||||
pub use self::utils::*;
|
||||
pub use self::widget::color_editors::*;
|
||||
pub use self::widget::combo_box::*;
|
||||
pub use self::widget::drag::*;
|
||||
pub use self::widget::image::*;
|
||||
pub use self::widget::list_box::*;
|
||||
pub use self::widget::menu::*;
|
||||
pub use self::widget::misc::*;
|
||||
pub use self::widget::progress_bar::*;
|
||||
pub use self::widget::selectable::*;
|
||||
pub use self::widget::slider::*;
|
||||
pub use self::widget::tab::*;
|
||||
pub use self::widget::tree::*;
|
||||
pub use self::window::child_window::*;
|
||||
pub use self::window::*;
|
||||
use internal::RawCast;
|
||||
|
||||
#[macro_use]
|
||||
mod string;
|
||||
|
||||
#[macro_use]
|
||||
mod tokens;
|
||||
|
||||
mod clipboard;
|
||||
pub mod color;
|
||||
mod columns;
|
||||
mod context;
|
||||
pub mod drag_drop;
|
||||
pub mod draw_list;
|
||||
mod fonts;
|
||||
mod input;
|
||||
mod input_widget;
|
||||
pub mod internal;
|
||||
mod io;
|
||||
mod layout;
|
||||
mod list_clipper;
|
||||
mod plothistogram;
|
||||
mod plotlines;
|
||||
mod popups;
|
||||
mod render;
|
||||
mod stacks;
|
||||
mod style;
|
||||
#[cfg(feature = "tables-api")]
|
||||
mod tables;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod utils;
|
||||
mod widget;
|
||||
mod window;
|
||||
|
||||
// Used by macros. Underscores are just to make it clear it's not part of the
|
||||
// public API.
|
||||
#[doc(hidden)]
|
||||
pub use core as __core;
|
||||
|
||||
/// Returns the underlying Dear ImGui library version
|
||||
#[doc(alias = "GetVersion")]
|
||||
pub fn dear_imgui_version() -> &'static str {
|
||||
unsafe {
|
||||
let bytes = CStr::from_ptr(sys::igGetVersion()).to_bytes();
|
||||
str::from_utf8_unchecked(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Returns the global imgui-rs time.
|
||||
///
|
||||
/// Incremented by Io::delta_time every frame.
|
||||
#[doc(alias = "GetTime")]
|
||||
pub fn time(&self) -> f64 {
|
||||
unsafe { sys::igGetTime() }
|
||||
}
|
||||
/// Returns the global imgui-rs frame count.
|
||||
///
|
||||
/// Incremented by 1 every frame.
|
||||
#[doc(alias = "GetFrameCount")]
|
||||
pub fn frame_count(&self) -> i32 {
|
||||
unsafe { sys::igGetFrameCount() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A temporary reference for building the user interface for one frame
|
||||
#[derive(Debug)]
|
||||
pub struct Ui<'ui> {
|
||||
ctx: &'ui Context,
|
||||
font_atlas: Option<cell::RefMut<'ui, SharedFontAtlas>>,
|
||||
// imgui isn't mutli-threaded -- so no one will ever access twice.
|
||||
buffer: cell::UnsafeCell<string::UiBuffer>,
|
||||
}
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Internal method to push a single text to our scratch buffer.
|
||||
fn scratch_txt(&self, txt: impl AsRef<str>) -> *const sys::cty::c_char {
|
||||
unsafe {
|
||||
let handle = &mut *self.buffer.get();
|
||||
handle.scratch_txt(txt)
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal method to push an option text to our scratch buffer.
|
||||
fn scratch_txt_opt(&self, txt: Option<impl AsRef<str>>) -> *const sys::cty::c_char {
|
||||
unsafe {
|
||||
let handle = &mut *self.buffer.get();
|
||||
handle.scratch_txt_opt(txt)
|
||||
}
|
||||
}
|
||||
|
||||
fn scratch_txt_two(
|
||||
&self,
|
||||
txt_0: impl AsRef<str>,
|
||||
txt_1: impl AsRef<str>,
|
||||
) -> (*const sys::cty::c_char, *const sys::cty::c_char) {
|
||||
unsafe {
|
||||
let handle = &mut *self.buffer.get();
|
||||
handle.scratch_txt_two(txt_0, txt_1)
|
||||
}
|
||||
}
|
||||
|
||||
fn scratch_txt_with_opt(
|
||||
&self,
|
||||
txt_0: impl AsRef<str>,
|
||||
txt_1: Option<impl AsRef<str>>,
|
||||
) -> (*const sys::cty::c_char, *const sys::cty::c_char) {
|
||||
unsafe {
|
||||
let handle = &mut *self.buffer.get();
|
||||
handle.scratch_txt_with_opt(txt_0, txt_1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the inputs/outputs object
|
||||
#[doc(alias = "GetIO")]
|
||||
pub fn io(&self) -> &Io {
|
||||
unsafe { &*(sys::igGetIO() as *const Io) }
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the font atlas
|
||||
pub fn fonts(&self) -> FontAtlasRef<'_> {
|
||||
match self.font_atlas {
|
||||
Some(ref font_atlas) => FontAtlasRef::Shared(font_atlas),
|
||||
None => unsafe {
|
||||
let fonts = &*(self.io().fonts as *const FontAtlas);
|
||||
FontAtlasRef::Owned(fonts)
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Returns a clone of the user interface style
|
||||
pub fn clone_style(&self) -> Style {
|
||||
*self.ctx.style()
|
||||
}
|
||||
/// Renders the frame and returns a reference to the resulting draw data
|
||||
#[doc(alias = "Render", alias = "GetDrawData")]
|
||||
pub fn render(self) -> &'ui DrawData {
|
||||
unsafe {
|
||||
sys::igRender();
|
||||
&*(sys::igGetDrawData() as *mut DrawData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Ui<'a> {
|
||||
#[doc(alias = "EndFrame")]
|
||||
fn drop(&mut self) {
|
||||
if !thread::panicking() {
|
||||
unsafe {
|
||||
sys::igEndFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Demo, debug, information
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Renders a demo window (previously called a test window), which demonstrates most
|
||||
/// Dear Imgui features.
|
||||
#[doc(alias = "ShowDemoWindow")]
|
||||
pub fn show_demo_window(&self, opened: &mut bool) {
|
||||
unsafe {
|
||||
sys::igShowDemoWindow(opened);
|
||||
}
|
||||
}
|
||||
/// Renders an about window.
|
||||
///
|
||||
/// Displays the Dear ImGui version/credits, and build/system information.
|
||||
#[doc(alias = "ShowAboutWindow")]
|
||||
pub fn show_about_window(&self, opened: &mut bool) {
|
||||
unsafe {
|
||||
sys::igShowAboutWindow(opened);
|
||||
}
|
||||
}
|
||||
/// Renders a metrics/debug window.
|
||||
///
|
||||
/// Displays Dear ImGui internals: draw commands (with individual draw calls and vertices),
|
||||
/// window list, basic internal state, etc.
|
||||
#[doc(alias = "ShowMetricsWindow")]
|
||||
pub fn show_metrics_window(&self, opened: &mut bool) {
|
||||
unsafe {
|
||||
sys::igShowMetricsWindow(opened);
|
||||
}
|
||||
}
|
||||
/// Renders a style editor block (not a window) for the given `Style` structure
|
||||
#[doc(alias = "ShowStyleEditor")]
|
||||
pub fn show_style_editor(&self, style: &mut Style) {
|
||||
unsafe {
|
||||
sys::igShowStyleEditor(style.raw_mut());
|
||||
}
|
||||
}
|
||||
/// Renders a style editor block (not a window) for the currently active style
|
||||
#[doc(alias = "ShowStyleEditor")]
|
||||
pub fn show_default_style_editor(&self) {
|
||||
unsafe { sys::igShowStyleEditor(ptr::null_mut()) };
|
||||
}
|
||||
/// Renders a basic help/info block (not a window)
|
||||
#[doc(alias = "ShowUserGuide")]
|
||||
pub fn show_user_guide(&self) {
|
||||
unsafe { sys::igShowUserGuide() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Unique ID used by widgets
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Id<'a> {
|
||||
Int(i32),
|
||||
Str(&'a str),
|
||||
Ptr(*const c_void),
|
||||
}
|
||||
|
||||
impl From<i32> for Id<'static> {
|
||||
#[inline]
|
||||
fn from(i: i32) -> Self {
|
||||
Id::Int(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + AsRef<str>> From<&'a T> for Id<'a> {
|
||||
#[inline]
|
||||
fn from(s: &'a T) -> Self {
|
||||
Id::Str(s.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<*const T> for Id<'static> {
|
||||
#[inline]
|
||||
fn from(p: *const T) -> Self {
|
||||
Id::Ptr(p as *const c_void)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<*mut T> for Id<'static> {
|
||||
#[inline]
|
||||
fn from(p: *mut T) -> Self {
|
||||
Id::Ptr(p as *const T as *const c_void)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Id<'a> {
|
||||
// this is used in the tables-api and possibly elsewhere,
|
||||
// but not with just default features...
|
||||
#[allow(dead_code)]
|
||||
fn as_imgui_id(&self) -> sys::ImGuiID {
|
||||
unsafe {
|
||||
match self {
|
||||
Id::Ptr(p) => sys::igGetID_Ptr(*p),
|
||||
Id::Str(s) => {
|
||||
let s1 = s.as_ptr() as *const std::os::raw::c_char;
|
||||
let s2 = s1.add(s.len());
|
||||
sys::igGetID_StrStr(s1, s2)
|
||||
}
|
||||
Id::Int(i) => {
|
||||
let p = *i as *const std::os::raw::c_void;
|
||||
sys::igGetID_Ptr(p)
|
||||
} // Id::ImGuiID(n) => *n,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for Id<'a> {
|
||||
fn default() -> Self {
|
||||
Self::Int(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Widgets: Input
|
||||
impl<'ui> Ui<'ui> {
|
||||
#[doc(alias = "InputText", alias = "InputTextWithHint")]
|
||||
pub fn input_text<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
buf: &'p mut String,
|
||||
) -> InputText<'ui, 'p, L> {
|
||||
InputText::new(self, label, buf)
|
||||
}
|
||||
#[doc(alias = "InputText", alias = "InputTextMultiline")]
|
||||
pub fn input_text_multiline<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
buf: &'p mut String,
|
||||
size: [f32; 2],
|
||||
) -> InputTextMultiline<'ui, 'p, L> {
|
||||
InputTextMultiline::new(self, label, buf, size)
|
||||
}
|
||||
#[doc(alias = "InputFloat2")]
|
||||
pub fn input_float<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
value: &'p mut f32,
|
||||
) -> InputFloat<'ui, 'p, L> {
|
||||
InputFloat::new(self, label, value)
|
||||
}
|
||||
pub fn input_float2<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
value: &'p mut [f32; 2],
|
||||
) -> InputFloat2<'ui, 'p, L> {
|
||||
InputFloat2::new(self, label, value)
|
||||
}
|
||||
#[doc(alias = "InputFloat3")]
|
||||
pub fn input_float3<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
value: &'p mut [f32; 3],
|
||||
) -> InputFloat3<'ui, 'p, L> {
|
||||
InputFloat3::new(self, label, value)
|
||||
}
|
||||
#[doc(alias = "InputFloat4")]
|
||||
pub fn input_float4<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
value: &'p mut [f32; 4],
|
||||
) -> InputFloat4<'ui, 'p, L> {
|
||||
InputFloat4::new(self, label, value)
|
||||
}
|
||||
#[doc(alias = "InputInt")]
|
||||
pub fn input_int<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
value: &'p mut i32,
|
||||
) -> InputInt<'ui, 'p, L> {
|
||||
InputInt::new(self, label, value)
|
||||
}
|
||||
#[doc(alias = "InputInt2")]
|
||||
pub fn input_int2<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
value: &'p mut [i32; 2],
|
||||
) -> InputInt2<'ui, 'p, L> {
|
||||
InputInt2::new(self, label, value)
|
||||
}
|
||||
#[doc(alias = "InputInt3")]
|
||||
pub fn input_int3<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
value: &'p mut [i32; 3],
|
||||
) -> InputInt3<'ui, 'p, L> {
|
||||
InputInt3::new(self, label, value)
|
||||
}
|
||||
#[doc(alias = "InputInt4")]
|
||||
pub fn input_int4<'p, L: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: L,
|
||||
value: &'p mut [i32; 4],
|
||||
) -> InputInt4<'ui, 'p, L> {
|
||||
InputInt4::new(self, label, value)
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a layout tooltip that can be ended by calling `.end()` or by dropping.
|
||||
pub struct TooltipToken<'ui>;
|
||||
|
||||
/// Drops the layout tooltip manually. You can also just allow this token
|
||||
/// to drop on its own.
|
||||
drop { sys::igEndTooltip() }
|
||||
);
|
||||
|
||||
/// # Tooltips
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Construct a tooltip window that can have any kind of content.
|
||||
///
|
||||
/// Typically used with `Ui::is_item_hovered()` or some other conditional check.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use imgui::*;
|
||||
/// fn user_interface(ui: &Ui) {
|
||||
/// ui.text("Hover over me");
|
||||
/// if ui.is_item_hovered() {
|
||||
/// ui.tooltip(|| {
|
||||
/// ui.text_colored([1.0, 0.0, 0.0, 1.0], im_str!("I'm red!"));
|
||||
/// });
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(alias = "BeginTooltip", alias = "EndTootip")]
|
||||
pub fn tooltip<F: FnOnce()>(&self, f: F) {
|
||||
unsafe { sys::igBeginTooltip() };
|
||||
f();
|
||||
unsafe { sys::igEndTooltip() };
|
||||
}
|
||||
/// Construct a tooltip window that can have any kind of content.
|
||||
///
|
||||
/// Returns a `TooltipToken` that must be ended by calling `.end()`
|
||||
#[doc(alias = "BeginTooltip")]
|
||||
pub fn begin_tooltip(&self) -> TooltipToken<'_> {
|
||||
unsafe { sys::igBeginTooltip() };
|
||||
TooltipToken::new(self)
|
||||
}
|
||||
/// Construct a tooltip window with simple text content.
|
||||
///
|
||||
/// Typically used with `Ui::is_item_hovered()` or some other conditional check.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use imgui::*;
|
||||
/// fn user_interface(ui: &Ui) {
|
||||
/// ui.text("Hover over me");
|
||||
/// if ui.is_item_hovered() {
|
||||
/// ui.tooltip_text("I'm a tooltip!");
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(alias = "BeginTooltip", alias = "EndTootip")]
|
||||
pub fn tooltip_text<T: AsRef<str>>(&self, text: T) {
|
||||
self.tooltip(|| self.text(text));
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Starts a scope where interaction is disabled. Ends be calling `.end()` or when the token is dropped.
|
||||
pub struct DisabledToken<'ui>;
|
||||
|
||||
/// Drops the layout tooltip manually. You can also just allow this token
|
||||
/// to drop on its own.
|
||||
drop { sys::igEndDisabled() }
|
||||
);
|
||||
|
||||
/// # Disabling widgets
|
||||
///
|
||||
/// imgui can disable widgets so they don't react to mouse/keyboard
|
||||
/// inputs, and are displayed differently (currently dimmed by an
|
||||
/// amount set in [`Style::disabled_alpha`])
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Creates a scope where interactions are disabled.
|
||||
///
|
||||
/// Scope ends when returned token is dropped, or `.end()` is
|
||||
/// explicitly called
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use imgui::*;
|
||||
/// fn user_interface(ui: &Ui) {
|
||||
/// let disable_buttons = true;
|
||||
/// let _d = ui.begin_disabled(disable_buttons);
|
||||
/// ui.button(im_str!("Dangerous button"));
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
#[doc(alias = "BeginDisabled")]
|
||||
pub fn begin_disabled(&self, disabled: bool) -> DisabledToken<'_> {
|
||||
unsafe { sys::igBeginDisabled(disabled) };
|
||||
DisabledToken::new(self)
|
||||
}
|
||||
|
||||
/// Identical to [`Ui::begin_disabled`] but exists to allow avoiding a
|
||||
/// double-negative, for example `begin_enabled(enable_buttons)`
|
||||
/// instead of `begin_disabled(!enable_buttons)`)
|
||||
#[doc(alias = "BeginDisabled")]
|
||||
pub fn begin_enabled(&self, enabled: bool) -> DisabledToken<'_> {
|
||||
self.begin_disabled(!enabled)
|
||||
}
|
||||
|
||||
/// Helper to create a disabled section of widgets
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use imgui::*;
|
||||
/// fn user_interface(ui: &Ui) {
|
||||
/// let safe_mode = true;
|
||||
/// ui.disabled(safe_mode, || {
|
||||
/// ui.button(im_str!("Dangerous button"));
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(alias = "BeginDisabled", alias = "EndDisabled")]
|
||||
pub fn disabled<F: FnOnce()>(&self, disabled: bool, f: F) {
|
||||
unsafe { sys::igBeginDisabled(disabled) };
|
||||
f();
|
||||
unsafe { sys::igEndDisabled() };
|
||||
}
|
||||
|
||||
/// Same as [`Ui::disabled`] but with logic reversed. See
|
||||
/// [`Ui::begin_enabled`].
|
||||
#[doc(alias = "BeginDisabled", alias = "EndDisabled")]
|
||||
pub fn enabled<F: FnOnce()>(&self, enabled: bool, f: F) {
|
||||
unsafe { sys::igBeginDisabled(!enabled) };
|
||||
f();
|
||||
unsafe { sys::igEndDisabled() };
|
||||
}
|
||||
}
|
||||
|
||||
// Widgets: ListBox
|
||||
impl<'ui> Ui<'ui> {
|
||||
#[doc(alias = "ListBox")]
|
||||
pub fn list_box<'p, StringType: AsRef<str> + ?Sized>(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
current_item: &mut i32,
|
||||
items: &'p [&'p StringType],
|
||||
height_in_items: i32,
|
||||
) -> bool {
|
||||
let (label_ptr, items_inner) = unsafe {
|
||||
let handle = &mut *self.buffer.get();
|
||||
|
||||
handle.refresh_buffer();
|
||||
let label_ptr = handle.push(label);
|
||||
|
||||
let items_inner: Vec<_> = items.iter().map(|&v| handle.push(v)).collect();
|
||||
|
||||
(label_ptr, items_inner)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
sys::igListBox_Str_arr(
|
||||
label_ptr,
|
||||
current_item,
|
||||
items_inner.as_ptr() as *mut *const c_char,
|
||||
items_inner.len() as i32,
|
||||
height_in_items,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// written out for the future times...
|
||||
// #[doc(alias = "ListBox")]
|
||||
// pub fn list_box_const<'p, StringType: AsRef<str> + ?Sized, const N: usize>(
|
||||
// &self,
|
||||
// label: impl AsRef<str>,
|
||||
// current_item: &mut i32,
|
||||
// items: [&'p StringType; N],
|
||||
// height_in_items: i32,
|
||||
// ) -> bool {
|
||||
// let (label_ptr, items_inner) = unsafe {
|
||||
// let handle = &mut *self.buffer.get();
|
||||
|
||||
// handle.refresh_buffer();
|
||||
// let label_ptr = handle.push(label);
|
||||
|
||||
// let mut items_inner: [*const i8; N] = [std::ptr::null(); N];
|
||||
|
||||
// for (i, item) in items.iter().enumerate() {
|
||||
// items_inner[i] = handle.push(item);
|
||||
// }
|
||||
|
||||
// (label_ptr, items_inner)
|
||||
// };
|
||||
|
||||
// unsafe {
|
||||
// sys::igListBoxStr_arr(
|
||||
// label_ptr,
|
||||
// current_item,
|
||||
// items_inner.as_ptr() as *mut *const c_char,
|
||||
// items_inner.len() as i32,
|
||||
// height_in_items,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
#[doc(alias = "PlotLines")]
|
||||
pub fn plot_lines<'p, Label: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: Label,
|
||||
values: &'p [f32],
|
||||
) -> PlotLines<'ui, 'p, Label> {
|
||||
PlotLines::new(self, label, values)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
#[doc(alias = "PlotHistogram")]
|
||||
pub fn plot_histogram<'p, Label: AsRef<str>>(
|
||||
&'ui self,
|
||||
label: Label,
|
||||
values: &'p [f32],
|
||||
) -> PlotHistogram<'ui, 'p, Label> {
|
||||
PlotHistogram::new(self, label, values)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Calculate the size required for a given text string.
|
||||
///
|
||||
/// This is the same as [calc_text_size_with_opts](Self::calc_text_size_with_opts)
|
||||
/// with `hide_text_after_double_hash` set to false and `wrap_width` set to `-1.0`.
|
||||
#[doc(alias = "CalcTextSize")]
|
||||
pub fn calc_text_size<T: AsRef<str>>(&self, text: T) -> [f32; 2] {
|
||||
self.calc_text_size_with_opts(text, false, -1.0)
|
||||
}
|
||||
|
||||
/// Calculate the size required for a given text string.
|
||||
///
|
||||
/// hide_text_after_double_hash allows the user to insert comments into their text, using a double hash-tag prefix.
|
||||
/// This is a feature of imgui.
|
||||
///
|
||||
/// wrap_width allows you to request a width at which to wrap the text to a newline for the calculation.
|
||||
#[doc(alias = "CalcTextSize")]
|
||||
pub fn calc_text_size_with_opts<T: AsRef<str>>(
|
||||
&self,
|
||||
text: T,
|
||||
hide_text_after_double_hash: bool,
|
||||
wrap_width: f32,
|
||||
) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
let text = text.as_ref();
|
||||
|
||||
unsafe {
|
||||
let start = text.as_ptr();
|
||||
let end = start.add(text.len());
|
||||
|
||||
sys::igCalcTextSize(
|
||||
&mut out,
|
||||
start as *const c_char,
|
||||
end as *const c_char,
|
||||
hide_text_after_double_hash,
|
||||
wrap_width,
|
||||
)
|
||||
};
|
||||
out.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// # Draw list for custom drawing
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Get access to drawing API
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use imgui::*;
|
||||
/// fn custom_draw(ui: &Ui) {
|
||||
/// let draw_list = ui.get_window_draw_list();
|
||||
/// // Draw a line
|
||||
/// const WHITE: [f32; 3] = [1.0, 1.0, 1.0];
|
||||
/// draw_list.add_line([100.0, 100.0], [200.0, 200.0], WHITE).build();
|
||||
/// // Continue drawing ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This function will panic if several instances of [`DrawListMut`]
|
||||
/// coexist. Before a new instance is got, a previous instance should be
|
||||
/// dropped.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use imgui::*;
|
||||
/// fn custom_draw(ui: &Ui) {
|
||||
/// let draw_list = ui.get_window_draw_list();
|
||||
/// // Draw something...
|
||||
///
|
||||
/// // This second call will panic!
|
||||
/// let draw_list = ui.get_window_draw_list();
|
||||
/// }
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[doc(alias = "GetWindowDrawList")]
|
||||
pub fn get_window_draw_list(&'ui self) -> DrawListMut<'ui> {
|
||||
DrawListMut::window(self)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[doc(alias = "GetBackgroundDrawList")]
|
||||
pub fn get_background_draw_list(&'ui self) -> DrawListMut<'ui> {
|
||||
DrawListMut::background(self)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[doc(alias = "GetForegroundDrawList")]
|
||||
pub fn get_foreground_draw_list(&'ui self) -> DrawListMut<'ui> {
|
||||
DrawListMut::foreground(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Condition for applying a setting
|
||||
#[repr(i8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Condition {
|
||||
/// Never apply the setting
|
||||
Never = -1,
|
||||
/// Always apply the setting
|
||||
Always = sys::ImGuiCond_Always as i8,
|
||||
/// Apply the setting once per runtime session (only the first call will succeed)
|
||||
Once = sys::ImGuiCond_Once as i8,
|
||||
/// Apply the setting if the object/window has no persistently saved data (no entry in .ini
|
||||
/// file)
|
||||
FirstUseEver = sys::ImGuiCond_FirstUseEver as i8,
|
||||
/// Apply the setting if the object/window is appearing after being hidden/inactive (or the
|
||||
/// first time)
|
||||
Appearing = sys::ImGuiCond_Appearing as i8,
|
||||
}
|
||||
|
||||
/// A cardinal direction
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Direction {
|
||||
None = sys::ImGuiDir_None,
|
||||
Left = sys::ImGuiDir_Left,
|
||||
Right = sys::ImGuiDir_Right,
|
||||
Up = sys::ImGuiDir_Up,
|
||||
Down = sys::ImGuiDir_Down,
|
||||
}
|
81
plugins/libimhex-rust/imgui-rs/src/list_clipper.rs
Normal file
81
plugins/libimhex-rust/imgui-rs/src/list_clipper.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::thread;
|
||||
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
pub struct ListClipper {
|
||||
items_count: i32,
|
||||
items_height: f32,
|
||||
}
|
||||
|
||||
impl ListClipper {
|
||||
pub const fn new(items_count: i32) -> Self {
|
||||
ListClipper {
|
||||
items_count,
|
||||
items_height: -1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn items_height(mut self, items_height: f32) -> Self {
|
||||
self.items_height = items_height;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> ListClipperToken<'ui> {
|
||||
let list_clipper = unsafe {
|
||||
let list_clipper = sys::ImGuiListClipper_ImGuiListClipper();
|
||||
sys::ImGuiListClipper_Begin(list_clipper, self.items_count, self.items_height);
|
||||
list_clipper
|
||||
};
|
||||
ListClipperToken::new(ui, list_clipper)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListClipperToken<'ui> {
|
||||
list_clipper: *mut sys::ImGuiListClipper,
|
||||
_phantom: PhantomData<&'ui Ui<'ui>>,
|
||||
}
|
||||
|
||||
impl<'ui> ListClipperToken<'ui> {
|
||||
fn new(_: &Ui<'ui>, list_clipper: *mut sys::ImGuiListClipper) -> Self {
|
||||
Self {
|
||||
list_clipper,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self) -> bool {
|
||||
unsafe { sys::ImGuiListClipper_Step(self.list_clipper) }
|
||||
}
|
||||
|
||||
pub fn end(&mut self) {
|
||||
unsafe {
|
||||
sys::ImGuiListClipper_End(self.list_clipper);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_start(&self) -> i32 {
|
||||
unsafe { (*self.list_clipper).DisplayStart }
|
||||
}
|
||||
|
||||
pub fn display_end(&self) -> i32 {
|
||||
unsafe { (*self.list_clipper).DisplayEnd }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for ListClipperToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
if !self.step() {
|
||||
unsafe {
|
||||
sys::ImGuiListClipper_destroy(self.list_clipper);
|
||||
};
|
||||
} else if !thread::panicking() {
|
||||
panic!(
|
||||
"Forgot to call End(), or to Step() until false? \
|
||||
This is the only token in the repository which users must call `.end()` or `.step()` \
|
||||
with. See https://github.com/imgui-rs/imgui-rs/issues/438"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
87
plugins/libimhex-rust/imgui-rs/src/plothistogram.rs
Normal file
87
plugins/libimhex-rust/imgui-rs/src/plothistogram.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use std::os::raw::c_float;
|
||||
use std::{f32, mem};
|
||||
|
||||
use super::Ui;
|
||||
|
||||
#[must_use]
|
||||
pub struct PlotHistogram<'ui, 'p, Label, Overlay = &'static str> {
|
||||
label: Label,
|
||||
values: &'p [f32],
|
||||
values_offset: usize,
|
||||
overlay_text: Option<Overlay>,
|
||||
scale_min: f32,
|
||||
scale_max: f32,
|
||||
graph_size: [f32; 2],
|
||||
ui: &'ui Ui<'ui>,
|
||||
}
|
||||
|
||||
impl<'ui, 'p, Label: AsRef<str>> PlotHistogram<'ui, 'p, Label> {
|
||||
pub fn new(ui: &'ui Ui<'ui>, label: Label, values: &'p [f32]) -> Self {
|
||||
PlotHistogram {
|
||||
label,
|
||||
values,
|
||||
values_offset: 0usize,
|
||||
overlay_text: None,
|
||||
scale_min: f32::MAX,
|
||||
scale_max: f32::MAX,
|
||||
graph_size: [0.0, 0.0],
|
||||
ui,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui, 'p, Label: AsRef<str>, Overlay: AsRef<str>> PlotHistogram<'ui, 'p, Label, Overlay> {
|
||||
pub fn values_offset(mut self, values_offset: usize) -> Self {
|
||||
self.values_offset = values_offset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn overlay_text<NewOverlay: AsRef<str>>(
|
||||
self,
|
||||
overlay_text: NewOverlay,
|
||||
) -> PlotHistogram<'ui, 'p, Label, NewOverlay> {
|
||||
PlotHistogram {
|
||||
label: self.label,
|
||||
values: self.values,
|
||||
values_offset: self.values_offset,
|
||||
overlay_text: Some(overlay_text),
|
||||
scale_min: self.scale_min,
|
||||
scale_max: self.scale_max,
|
||||
graph_size: self.graph_size,
|
||||
ui: self.ui,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale_min(mut self, scale_min: f32) -> Self {
|
||||
self.scale_min = scale_min;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn scale_max(mut self, scale_max: f32) -> Self {
|
||||
self.scale_max = scale_max;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self {
|
||||
self.graph_size = graph_size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) {
|
||||
unsafe {
|
||||
let (label, overlay_text) = self.ui.scratch_txt_with_opt(self.label, self.overlay_text);
|
||||
|
||||
sys::igPlotHistogram_FloatPtr(
|
||||
label,
|
||||
self.values.as_ptr() as *const c_float,
|
||||
self.values.len() as i32,
|
||||
self.values_offset as i32,
|
||||
overlay_text,
|
||||
self.scale_min,
|
||||
self.scale_max,
|
||||
self.graph_size.into(),
|
||||
mem::size_of::<f32>() as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
87
plugins/libimhex-rust/imgui-rs/src/plotlines.rs
Normal file
87
plugins/libimhex-rust/imgui-rs/src/plotlines.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use std::os::raw::c_float;
|
||||
use std::{f32, mem};
|
||||
|
||||
use super::Ui;
|
||||
|
||||
#[must_use]
|
||||
pub struct PlotLines<'ui, 'p, Label, Overlay = &'static str> {
|
||||
label: Label,
|
||||
values: &'p [f32],
|
||||
values_offset: usize,
|
||||
overlay_text: Option<Overlay>,
|
||||
scale_min: f32,
|
||||
scale_max: f32,
|
||||
graph_size: [f32; 2],
|
||||
ui: &'ui Ui<'ui>,
|
||||
}
|
||||
|
||||
impl<'ui, 'p, Label: AsRef<str>> PlotLines<'ui, 'p, Label> {
|
||||
pub fn new(ui: &'ui Ui<'ui>, label: Label, values: &'p [f32]) -> Self {
|
||||
PlotLines {
|
||||
label,
|
||||
values,
|
||||
values_offset: 0usize,
|
||||
overlay_text: None,
|
||||
scale_min: f32::MAX,
|
||||
scale_max: f32::MAX,
|
||||
graph_size: [0.0, 0.0],
|
||||
ui,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui, 'p, Label: AsRef<str>, Overlay: AsRef<str>> PlotLines<'ui, 'p, Label, Overlay> {
|
||||
pub fn values_offset(mut self, values_offset: usize) -> Self {
|
||||
self.values_offset = values_offset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn overlay_text<Overlay2: AsRef<str>>(
|
||||
self,
|
||||
overlay_text: Overlay2,
|
||||
) -> PlotLines<'ui, 'p, Label, Overlay2> {
|
||||
PlotLines {
|
||||
label: self.label,
|
||||
values: self.values,
|
||||
values_offset: self.values_offset,
|
||||
overlay_text: Some(overlay_text),
|
||||
scale_min: self.scale_min,
|
||||
scale_max: self.scale_max,
|
||||
graph_size: self.graph_size,
|
||||
ui: self.ui,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale_min(mut self, scale_min: f32) -> Self {
|
||||
self.scale_min = scale_min;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn scale_max(mut self, scale_max: f32) -> Self {
|
||||
self.scale_max = scale_max;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self {
|
||||
self.graph_size = graph_size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) {
|
||||
unsafe {
|
||||
let (label, overlay) = self.ui.scratch_txt_with_opt(self.label, self.overlay_text);
|
||||
|
||||
sys::igPlotLines_FloatPtr(
|
||||
label,
|
||||
self.values.as_ptr() as *const c_float,
|
||||
self.values.len() as i32,
|
||||
self.values_offset as i32,
|
||||
overlay,
|
||||
self.scale_min,
|
||||
self.scale_max,
|
||||
self.graph_size.into(),
|
||||
mem::size_of::<f32>() as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
214
plugins/libimhex-rust/imgui-rs/src/popups.rs
Normal file
214
plugins/libimhex-rust/imgui-rs/src/popups.rs
Normal file
@ -0,0 +1,214 @@
|
||||
use std::ptr;
|
||||
|
||||
use crate::sys;
|
||||
use crate::window::WindowFlags;
|
||||
use crate::Ui;
|
||||
|
||||
/// Create a modal pop-up.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut imgui = Context::create();
|
||||
/// # let ui = imgui.frame();
|
||||
/// if ui.button(im_str!("Show modal")) {
|
||||
/// ui.open_popup(im_str!("modal"));
|
||||
/// }
|
||||
/// if let Some(_token) = PopupModal::new(im_str!("modal")).begin_popup(&ui) {
|
||||
/// ui.text("Content of my modal");
|
||||
/// if ui.button(im_str!("OK")) {
|
||||
/// ui.close_current_popup();
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub struct PopupModal<'p, Label> {
|
||||
label: Label,
|
||||
opened: Option<&'p mut bool>,
|
||||
flags: WindowFlags,
|
||||
}
|
||||
|
||||
impl<'p, Label: AsRef<str>> PopupModal<'p, Label> {
|
||||
pub fn new(label: Label) -> Self {
|
||||
PopupModal {
|
||||
label,
|
||||
opened: None,
|
||||
flags: WindowFlags::empty(),
|
||||
}
|
||||
}
|
||||
/// Pass a mutable boolean which will be updated to refer to the current
|
||||
/// "open" state of the modal.
|
||||
pub fn opened(mut self, opened: &'p mut bool) -> Self {
|
||||
self.opened = Some(opened);
|
||||
self
|
||||
}
|
||||
pub fn flags(mut self, flags: WindowFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
pub fn title_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_TITLE_BAR, !value);
|
||||
self
|
||||
}
|
||||
pub fn resizable(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_RESIZE, !value);
|
||||
self
|
||||
}
|
||||
pub fn movable(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_MOVE, !value);
|
||||
self
|
||||
}
|
||||
pub fn scroll_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_SCROLLBAR, !value);
|
||||
self
|
||||
}
|
||||
pub fn scrollable(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_SCROLL_WITH_MOUSE, !value);
|
||||
self
|
||||
}
|
||||
pub fn collapsible(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_COLLAPSE, !value);
|
||||
self
|
||||
}
|
||||
pub fn always_auto_resize(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::ALWAYS_AUTO_RESIZE, value);
|
||||
self
|
||||
}
|
||||
pub fn save_settings(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_SAVED_SETTINGS, !value);
|
||||
self
|
||||
}
|
||||
pub fn inputs(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_INPUTS, !value);
|
||||
self
|
||||
}
|
||||
pub fn menu_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::MENU_BAR, value);
|
||||
self
|
||||
}
|
||||
pub fn horizontal_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::HORIZONTAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
pub fn no_focus_on_appearing(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_FOCUS_ON_APPEARING, value);
|
||||
self
|
||||
}
|
||||
pub fn no_bring_to_front_on_focus(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::NO_BRING_TO_FRONT_ON_FOCUS, value);
|
||||
self
|
||||
}
|
||||
pub fn always_vertical_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_VERTICAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
pub fn always_horizontal_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_HORIZONTAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
pub fn always_use_window_padding(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_USE_WINDOW_PADDING, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Consume and draw the PopupModal.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
#[doc(alias = "BeginPopupModal")]
|
||||
pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
|
||||
self.begin_popup(ui).map(|_popup| f())
|
||||
}
|
||||
|
||||
/// Consume and draw the PopupModal.
|
||||
/// Construct a popup that can have any kind of content.
|
||||
///
|
||||
/// This should be called *per frame*, whereas [`Ui::open_popup`]
|
||||
/// should be called *once* when you want to actual create the popup.
|
||||
#[doc(alias = "BeginPopupModal")]
|
||||
pub fn begin_popup<'ui>(self, ui: &Ui<'ui>) -> Option<PopupToken<'ui>> {
|
||||
let render = unsafe {
|
||||
sys::igBeginPopupModal(
|
||||
ui.scratch_txt(self.label),
|
||||
self.opened
|
||||
.map(|x| x as *mut bool)
|
||||
.unwrap_or(ptr::null_mut()),
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
};
|
||||
|
||||
if render {
|
||||
Some(PopupToken::new(ui))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Widgets: Popups
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Instructs ImGui to open a popup, which must be began with either [`begin_popup`](Self::begin_popup)
|
||||
/// or [`popup`](Self::popup). You also use this function to begin [PopupModal].
|
||||
///
|
||||
/// The confusing aspect to popups is that ImGui holds "control" over the popup fundamentally, so that ImGui
|
||||
/// can also force close a popup when a user clicks outside a popup. If you do not want users to be
|
||||
/// able to close a popup without selected an option, use [`PopupModal`].
|
||||
#[doc(alias = "OpenPopup")]
|
||||
pub fn open_popup(&self, str_id: impl AsRef<str>) {
|
||||
unsafe { sys::igOpenPopup_Str(self.scratch_txt(str_id), 0) };
|
||||
}
|
||||
|
||||
/// Construct a popup that can have any kind of content.
|
||||
///
|
||||
/// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
|
||||
/// when you want to actual create the popup.
|
||||
#[doc(alias = "BeginPopup")]
|
||||
pub fn begin_popup(&self, str_id: impl AsRef<str>) -> Option<PopupToken<'_>> {
|
||||
let render = unsafe {
|
||||
sys::igBeginPopup(self.scratch_txt(str_id), WindowFlags::empty().bits() as i32)
|
||||
};
|
||||
|
||||
if render {
|
||||
Some(PopupToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a popup that can have any kind of content.
|
||||
///
|
||||
/// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
|
||||
/// when you want to actual create the popup.
|
||||
#[doc(alias = "BeginPopup")]
|
||||
pub fn popup<F>(&self, str_id: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
if let Some(_t) = self.begin_popup(str_id) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a PopupModal directly.
|
||||
pub fn popup_modal<'p, Label: AsRef<str>>(&self, str_id: Label) -> PopupModal<'p, Label> {
|
||||
PopupModal::new(str_id)
|
||||
}
|
||||
|
||||
/// Close a popup. Should be called within the closure given as argument to
|
||||
/// [`Ui::popup`] or [`Ui::popup_modal`].
|
||||
#[doc(alias = "CloseCurrentPopup")]
|
||||
pub fn close_current_popup(&self) {
|
||||
unsafe { sys::igCloseCurrentPopup() };
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a popup token that can be ended with `end` or by dropping.
|
||||
pub struct PopupToken<'ui>;
|
||||
|
||||
/// Drops the popup token manually. You can also just allow this token
|
||||
/// to drop on its own.
|
||||
drop { sys::igEndPopup() }
|
||||
);
|
285
plugins/libimhex-rust/imgui-rs/src/render/draw_data.rs
Normal file
285
plugins/libimhex-rust/imgui-rs/src/render/draw_data.rs
Normal file
@ -0,0 +1,285 @@
|
||||
use std::slice;
|
||||
|
||||
use crate::internal::{RawCast, RawWrapper};
|
||||
use crate::render::renderer::TextureId;
|
||||
use crate::sys;
|
||||
|
||||
/// All draw data to render a Dear ImGui frame.
|
||||
#[repr(C)]
|
||||
pub struct DrawData {
|
||||
/// Only valid after render() is called and before the next new frame() is called.
|
||||
valid: bool,
|
||||
/// Number of DrawList to render.
|
||||
cmd_lists_count: i32,
|
||||
/// For convenience, sum of all draw list index buffer sizes.
|
||||
pub total_idx_count: i32,
|
||||
/// For convenience, sum of all draw list vertex buffer sizes.
|
||||
pub total_vtx_count: i32,
|
||||
// Array of DrawList.
|
||||
cmd_lists: *mut *mut DrawList,
|
||||
/// Upper-left position of the viewport to render.
|
||||
///
|
||||
/// (= upper-left corner of the orthogonal projection matrix to use)
|
||||
pub display_pos: [f32; 2],
|
||||
/// Size of the viewport to render.
|
||||
///
|
||||
/// (= display_pos + display_size == lower-right corner of the orthogonal matrix to use)
|
||||
pub display_size: [f32; 2],
|
||||
/// Amount of pixels for each unit of display_size.
|
||||
///
|
||||
/// Based on io.display_frame_buffer_scale. Typically [1.0, 1.0] on normal displays, and
|
||||
/// [2.0, 2.0] on Retina displays, but fractional values are also possible.
|
||||
pub framebuffer_scale: [f32; 2],
|
||||
}
|
||||
|
||||
unsafe impl RawCast<sys::ImDrawData> for DrawData {}
|
||||
|
||||
impl DrawData {
|
||||
/// Returns an iterator over the draw lists included in the draw data.
|
||||
#[inline]
|
||||
pub fn draw_lists(&self) -> DrawListIterator<'_> {
|
||||
unsafe {
|
||||
DrawListIterator {
|
||||
iter: self.cmd_lists().iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Returns the number of draw lists included in the draw data.
|
||||
#[inline]
|
||||
pub fn draw_lists_count(&self) -> usize {
|
||||
use std::convert::TryInto;
|
||||
self.cmd_lists_count.try_into().unwrap()
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) unsafe fn cmd_lists(&self) -> &[*const DrawList] {
|
||||
slice::from_raw_parts(
|
||||
self.cmd_lists as *const *const DrawList,
|
||||
self.cmd_lists_count as usize,
|
||||
)
|
||||
}
|
||||
/// Converts all buffers from indexed to non-indexed, in case you cannot render indexed
|
||||
/// buffers.
|
||||
///
|
||||
/// **This is slow and most likely a waste of resources. Always prefer indexed rendering!**
|
||||
#[doc(alias = "DeIndexAllBuffers")]
|
||||
pub fn deindex_all_buffers(&mut self) {
|
||||
unsafe {
|
||||
sys::ImDrawData_DeIndexAllBuffers(self.raw_mut());
|
||||
}
|
||||
}
|
||||
/// Scales the clip rect of each draw command.
|
||||
///
|
||||
/// Can be used if your final output buffer is at a different scale than imgui-rs expects, or
|
||||
/// if there is a difference between your window resolution and framebuffer resolution.
|
||||
#[doc(alias = "ScaleClipRects")]
|
||||
pub fn scale_clip_rects(&mut self, fb_scale: [f32; 2]) {
|
||||
unsafe {
|
||||
sys::ImDrawData_ScaleClipRects(self.raw_mut(), fb_scale.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over draw lists
|
||||
pub struct DrawListIterator<'a> {
|
||||
iter: std::slice::Iter<'a, *const DrawList>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DrawListIterator<'a> {
|
||||
type Item = &'a DrawList;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|&ptr| unsafe { &*ptr })
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn test_drawdata_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(
|
||||
mem::size_of::<DrawData>(),
|
||||
mem::size_of::<sys::ImDrawData>()
|
||||
);
|
||||
assert_eq!(
|
||||
mem::align_of::<DrawData>(),
|
||||
mem::align_of::<sys::ImDrawData>()
|
||||
);
|
||||
use sys::ImDrawData;
|
||||
macro_rules! assert_field_offset {
|
||||
($l:ident, $r:ident) => {
|
||||
assert_eq!(
|
||||
memoffset::offset_of!(DrawData, $l),
|
||||
memoffset::offset_of!(ImDrawData, $r)
|
||||
);
|
||||
};
|
||||
}
|
||||
assert_field_offset!(valid, Valid);
|
||||
assert_field_offset!(cmd_lists, CmdLists);
|
||||
assert_field_offset!(cmd_lists_count, CmdListsCount);
|
||||
assert_field_offset!(total_idx_count, TotalIdxCount);
|
||||
assert_field_offset!(total_vtx_count, TotalVtxCount);
|
||||
assert_field_offset!(display_pos, DisplayPos);
|
||||
assert_field_offset!(display_size, DisplaySize);
|
||||
assert_field_offset!(framebuffer_scale, FramebufferScale);
|
||||
}
|
||||
|
||||
/// Draw command list
|
||||
#[repr(transparent)]
|
||||
pub struct DrawList(sys::ImDrawList);
|
||||
|
||||
impl RawWrapper for DrawList {
|
||||
type Raw = sys::ImDrawList;
|
||||
#[inline]
|
||||
unsafe fn raw(&self) -> &sys::ImDrawList {
|
||||
&self.0
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn raw_mut(&mut self) -> &mut sys::ImDrawList {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawList {
|
||||
#[inline]
|
||||
pub(crate) unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] {
|
||||
slice::from_raw_parts(
|
||||
self.0.CmdBuffer.Data as *const sys::ImDrawCmd,
|
||||
self.0.CmdBuffer.Size as usize,
|
||||
)
|
||||
}
|
||||
#[inline]
|
||||
pub fn idx_buffer(&self) -> &[DrawIdx] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
self.0.IdxBuffer.Data as *const DrawIdx,
|
||||
self.0.IdxBuffer.Size as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn vtx_buffer(&self) -> &[DrawVert] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
self.0.VtxBuffer.Data as *const DrawVert,
|
||||
self.0.VtxBuffer.Size as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This is equivalent to `transmute(self.vtx_buffer())` with a little more
|
||||
/// checking, and thus inherits the safety considerations of `transmute`ing
|
||||
/// slices.
|
||||
pub unsafe fn transmute_vtx_buffer<VTy: Copy>(&self) -> &[VTy] {
|
||||
// these checks are constant and thus are removed from release builds
|
||||
assert_eq!(
|
||||
core::mem::size_of::<VTy>(),
|
||||
core::mem::size_of::<DrawVert>(),
|
||||
);
|
||||
assert!(core::mem::align_of::<VTy>() <= core::mem::align_of::<DrawVert>());
|
||||
slice::from_raw_parts(self.0.VtxBuffer.Data.cast(), self.0.VtxBuffer.Size as usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn commands(&self) -> DrawCmdIterator<'_> {
|
||||
unsafe {
|
||||
DrawCmdIterator {
|
||||
iter: self.cmd_buffer().iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawCmdIterator<'a> {
|
||||
iter: std::slice::Iter<'a, sys::ImDrawCmd>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DrawCmdIterator<'a> {
|
||||
type Item = DrawCmd;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|cmd| {
|
||||
let cmd_params = DrawCmdParams {
|
||||
clip_rect: cmd.ClipRect.into(),
|
||||
texture_id: TextureId::from(cmd.TextureId),
|
||||
vtx_offset: cmd.VtxOffset as usize,
|
||||
idx_offset: cmd.IdxOffset as usize,
|
||||
};
|
||||
match cmd.UserCallback {
|
||||
Some(raw_callback) if raw_callback as usize == -1isize as usize => {
|
||||
DrawCmd::ResetRenderState
|
||||
}
|
||||
Some(raw_callback) => DrawCmd::RawCallback {
|
||||
callback: raw_callback,
|
||||
raw_cmd: cmd,
|
||||
},
|
||||
None => DrawCmd::Elements {
|
||||
count: cmd.ElemCount as usize,
|
||||
cmd_params,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A vertex index
|
||||
pub type DrawIdx = sys::ImDrawIdx;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct DrawCmdParams {
|
||||
/// left, up, right, down
|
||||
pub clip_rect: [f32; 4],
|
||||
pub texture_id: TextureId,
|
||||
pub vtx_offset: usize,
|
||||
pub idx_offset: usize,
|
||||
}
|
||||
|
||||
/// A draw command
|
||||
pub enum DrawCmd {
|
||||
Elements {
|
||||
/// The number of indices used for this draw command
|
||||
count: usize,
|
||||
cmd_params: DrawCmdParams,
|
||||
},
|
||||
ResetRenderState,
|
||||
RawCallback {
|
||||
callback: unsafe extern "C" fn(*const sys::ImDrawList, cmd: *const sys::ImDrawCmd),
|
||||
raw_cmd: *const sys::ImDrawCmd,
|
||||
},
|
||||
}
|
||||
|
||||
/// A single vertex
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct DrawVert {
|
||||
pub pos: [f32; 2],
|
||||
pub uv: [f32; 2],
|
||||
pub col: [u8; 4],
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn test_drawvert_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(
|
||||
mem::size_of::<DrawVert>(),
|
||||
mem::size_of::<sys::ImDrawVert>()
|
||||
);
|
||||
assert_eq!(
|
||||
mem::align_of::<DrawVert>(),
|
||||
mem::align_of::<sys::ImDrawVert>()
|
||||
);
|
||||
use sys::ImDrawVert;
|
||||
macro_rules! assert_field_offset {
|
||||
($l:ident, $r:ident) => {
|
||||
assert_eq!(
|
||||
memoffset::offset_of!(DrawVert, $l),
|
||||
memoffset::offset_of!(ImDrawVert, $r)
|
||||
);
|
||||
};
|
||||
}
|
||||
assert_field_offset!(pos, pos);
|
||||
assert_field_offset!(uv, uv);
|
||||
assert_field_offset!(col, col);
|
||||
}
|
2
plugins/libimhex-rust/imgui-rs/src/render/mod.rs
Normal file
2
plugins/libimhex-rust/imgui-rs/src/render/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod draw_data;
|
||||
pub mod renderer;
|
95
plugins/libimhex-rust/imgui-rs/src/render/renderer.rs
Normal file
95
plugins/libimhex-rust/imgui-rs/src/render/renderer.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// An opaque texture identifier
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct TextureId(usize);
|
||||
|
||||
impl TextureId {
|
||||
/// Creates a new texture id with the given identifier.
|
||||
#[inline]
|
||||
pub const fn new(id: usize) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
/// Returns the id of the TextureId.
|
||||
#[inline]
|
||||
pub const fn id(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for TextureId {
|
||||
#[inline]
|
||||
fn from(id: usize) -> Self {
|
||||
TextureId(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<*const T> for TextureId {
|
||||
#[inline]
|
||||
fn from(ptr: *const T) -> Self {
|
||||
TextureId(ptr as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<*mut T> for TextureId {
|
||||
#[inline]
|
||||
fn from(ptr: *mut T) -> Self {
|
||||
TextureId(ptr as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_texture_id_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(
|
||||
mem::size_of::<TextureId>(),
|
||||
mem::size_of::<sys::ImTextureID>()
|
||||
);
|
||||
assert_eq!(
|
||||
mem::align_of::<TextureId>(),
|
||||
mem::align_of::<sys::ImTextureID>()
|
||||
);
|
||||
}
|
||||
|
||||
/// Generic texture mapping for use by renderers.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Textures<T> {
|
||||
textures: HashMap<usize, T>,
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl<T> Textures<T> {
|
||||
// TODO: hasher like rustc_hash::FxHashMap or something would let this be
|
||||
// `const fn`
|
||||
pub fn new() -> Self {
|
||||
Textures {
|
||||
textures: HashMap::new(),
|
||||
next: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, texture: T) -> TextureId {
|
||||
let id = self.next;
|
||||
self.textures.insert(id, texture);
|
||||
self.next += 1;
|
||||
TextureId::from(id)
|
||||
}
|
||||
|
||||
pub fn replace(&mut self, id: TextureId, texture: T) -> Option<T> {
|
||||
self.textures.insert(id.0, texture)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: TextureId) -> Option<T> {
|
||||
self.textures.remove(&id.0)
|
||||
}
|
||||
|
||||
pub fn get(&self, id: TextureId) -> Option<&T> {
|
||||
self.textures.get(&id.0)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: TextureId) -> Option<&mut T> {
|
||||
self.textures.get_mut(&id.0)
|
||||
}
|
||||
}
|
471
plugins/libimhex-rust/imgui-rs/src/stacks.rs
Normal file
471
plugins/libimhex-rust/imgui-rs/src/stacks.rs
Normal file
@ -0,0 +1,471 @@
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::ptr;
|
||||
use std::thread;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::fonts::atlas::FontId;
|
||||
use crate::internal::RawCast;
|
||||
use crate::style::{StyleColor, StyleVar};
|
||||
use crate::sys;
|
||||
use crate::{Id, Ui};
|
||||
|
||||
/// # Parameter stacks (shared)
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Switches to the given font by pushing it to the font stack.
|
||||
///
|
||||
/// Returns a `FontStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the font atlas does not contain the given font
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut ctx = Context::create();
|
||||
/// # let font_data_sources = [];
|
||||
/// // At initialization time
|
||||
/// let my_custom_font = ctx.fonts().add_font(&font_data_sources);
|
||||
/// # let ui = ctx.frame();
|
||||
/// // During UI construction
|
||||
/// let font = ui.push_font(my_custom_font);
|
||||
/// ui.text("I use the custom font!");
|
||||
/// font.pop();
|
||||
/// ```
|
||||
#[doc(alias = "PushFont")]
|
||||
pub fn push_font(&self, id: FontId) -> FontStackToken<'_> {
|
||||
let fonts = self.fonts();
|
||||
let font = fonts
|
||||
.get_font(id)
|
||||
.expect("Font atlas did not contain the given font");
|
||||
unsafe { sys::igPushFont(font.raw() as *const _ as *mut _) };
|
||||
FontStackToken::new(self)
|
||||
}
|
||||
/// Changes a style color by pushing a change to the color stack.
|
||||
///
|
||||
/// Returns a `ColorStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut ctx = Context::create();
|
||||
/// # let ui = ctx.frame();
|
||||
/// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
|
||||
/// let color = ui.push_style_color(StyleColor::Text, RED);
|
||||
/// ui.text("I'm red!");
|
||||
/// color.pop();
|
||||
/// ```
|
||||
#[doc(alias = "PushStyleColorVec4")]
|
||||
pub fn push_style_color(
|
||||
&self,
|
||||
style_color: StyleColor,
|
||||
color: [f32; 4],
|
||||
) -> ColorStackToken<'_> {
|
||||
unsafe { sys::igPushStyleColor_Vec4(style_color as i32, color.into()) };
|
||||
ColorStackToken::new(self)
|
||||
}
|
||||
|
||||
/// Changes style colors by pushing several changes to the color stack.
|
||||
///
|
||||
/// Returns a `ColorStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut ctx = Context::create();
|
||||
/// # let ui = ctx.frame();
|
||||
/// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
|
||||
/// const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
|
||||
/// let colors = ui.push_style_colors(&[
|
||||
/// (StyleColor::Text, RED),
|
||||
/// (StyleColor::TextDisabled, GREEN),
|
||||
/// ]);
|
||||
/// ui.text("I'm red!");
|
||||
/// ui.text_disabled("I'm green!");
|
||||
/// colors.pop(&ui);
|
||||
/// ```
|
||||
#[deprecated = "deprecated in 0.7.0. Use `push_style_color` multiple times for similar effect."]
|
||||
pub fn push_style_colors<'a, I>(&self, style_colors: I) -> MultiColorStackToken
|
||||
where
|
||||
I: IntoIterator<Item = &'a (StyleColor, [f32; 4])>,
|
||||
{
|
||||
let mut count = 0;
|
||||
for &(style_color, color) in style_colors {
|
||||
unsafe { sys::igPushStyleColor_Vec4(style_color as i32, color.into()) };
|
||||
count += 1;
|
||||
}
|
||||
MultiColorStackToken {
|
||||
count,
|
||||
ctx: self.ctx,
|
||||
}
|
||||
}
|
||||
/// Changes a style variable by pushing a change to the style stack.
|
||||
///
|
||||
/// Returns a `StyleStackToken` that can be popped by calling `.end()`
|
||||
/// or by allowing to drop.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut ctx = Context::create();
|
||||
/// # let ui = ctx.frame();
|
||||
/// let style = ui.push_style_var(StyleVar::Alpha(0.2));
|
||||
/// ui.text("I'm transparent!");
|
||||
/// style.pop();
|
||||
/// ```
|
||||
#[doc(alias = "PushStyleVar")]
|
||||
pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken<'_> {
|
||||
unsafe { push_style_var(style_var) };
|
||||
StyleStackToken::new(self)
|
||||
}
|
||||
/// Changes style variables by pushing several changes to the style stack.
|
||||
///
|
||||
/// Returns a `StyleStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut ctx = Context::create();
|
||||
/// # let ui = ctx.frame();
|
||||
/// let styles = ui.push_style_vars(&[
|
||||
/// StyleVar::Alpha(0.2),
|
||||
/// StyleVar::ItemSpacing([50.0, 50.0])
|
||||
/// ]);
|
||||
/// ui.text("We're transparent...");
|
||||
/// ui.text("...with large spacing as well");
|
||||
/// styles.pop(&ui);
|
||||
/// ```
|
||||
#[deprecated = "deprecated in 0.7.0. Use `push_style_var` multiple times for similar effect."]
|
||||
pub fn push_style_vars<'a, I>(&self, style_vars: I) -> MultiStyleStackToken
|
||||
where
|
||||
I: IntoIterator<Item = &'a StyleVar>,
|
||||
{
|
||||
let mut count = 0;
|
||||
for &style_var in style_vars {
|
||||
unsafe { push_style_var(style_var) };
|
||||
count += 1;
|
||||
}
|
||||
MultiStyleStackToken {
|
||||
count,
|
||||
ctx: self.ctx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a font pushed to the font stack that can be popped by calling `.end()`
|
||||
/// or by dropping.
|
||||
pub struct FontStackToken<'ui>;
|
||||
|
||||
/// Pops a change from the font stack.
|
||||
drop { sys::igPopFont() }
|
||||
);
|
||||
|
||||
impl FontStackToken<'_> {
|
||||
/// Pops a change from the font stack.
|
||||
pub fn pop(self) {
|
||||
self.end()
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a color pushed to the color stack that can be popped by calling `.end()`
|
||||
/// or by dropping.
|
||||
pub struct ColorStackToken<'ui>;
|
||||
|
||||
/// Pops a change from the color stack.
|
||||
drop { sys::igPopStyleColor(1) }
|
||||
);
|
||||
|
||||
impl ColorStackToken<'_> {
|
||||
/// Pops a change from the color stack.
|
||||
pub fn pop(self) {
|
||||
self.end()
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks one or more changes pushed to the color stack that must be popped by calling `.pop()`
|
||||
#[must_use]
|
||||
pub struct MultiColorStackToken {
|
||||
count: usize,
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl MultiColorStackToken {
|
||||
/// Pops changes from the color stack
|
||||
#[doc(alias = "PopStyleColor")]
|
||||
pub fn pop(mut self, _: &Ui<'_>) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igPopStyleColor(self.count as i32) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MultiColorStackToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A ColorStackToken was leaked. Did you call .pop()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a style pushed to the style stack that can be popped by calling `.end()`
|
||||
/// or by dropping.
|
||||
pub struct StyleStackToken<'ui>;
|
||||
|
||||
/// Pops a change from the style stack.
|
||||
drop { sys::igPopStyleVar(1) }
|
||||
);
|
||||
|
||||
impl StyleStackToken<'_> {
|
||||
/// Pops a change from the style stack.
|
||||
pub fn pop(self) {
|
||||
self.end()
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks one or more changes pushed to the style stack that must be popped by calling `.pop()`
|
||||
#[must_use]
|
||||
pub struct MultiStyleStackToken {
|
||||
count: usize,
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl MultiStyleStackToken {
|
||||
/// Pops changes from the style stack
|
||||
#[doc(alias = "PopStyleVar")]
|
||||
pub fn pop(mut self, _: &Ui<'_>) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igPopStyleVar(self.count as i32) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MultiStyleStackToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A StyleStackToken was leaked. Did you call .pop()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_style_var(style_var: StyleVar) {
|
||||
use crate::style::StyleVar::*;
|
||||
use crate::sys::{igPushStyleVar_Float, igPushStyleVar_Vec2};
|
||||
match style_var {
|
||||
Alpha(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_Alpha as i32, v),
|
||||
WindowPadding(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowPadding as i32, v.into()),
|
||||
WindowRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_WindowRounding as i32, v),
|
||||
WindowBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_WindowBorderSize as i32, v),
|
||||
WindowMinSize(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowMinSize as i32, v.into()),
|
||||
WindowTitleAlign(v) => {
|
||||
igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowTitleAlign as i32, v.into())
|
||||
}
|
||||
ChildRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ChildRounding as i32, v),
|
||||
ChildBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ChildBorderSize as i32, v),
|
||||
PopupRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_PopupRounding as i32, v),
|
||||
PopupBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_PopupBorderSize as i32, v),
|
||||
FramePadding(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_FramePadding as i32, v.into()),
|
||||
FrameRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_FrameRounding as i32, v),
|
||||
FrameBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_FrameBorderSize as i32, v),
|
||||
ItemSpacing(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemSpacing as i32, v.into()),
|
||||
ItemInnerSpacing(v) => {
|
||||
igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemInnerSpacing as i32, v.into())
|
||||
}
|
||||
IndentSpacing(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_IndentSpacing as i32, v),
|
||||
ScrollbarSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarSize as i32, v),
|
||||
ScrollbarRounding(v) => {
|
||||
igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarRounding as i32, v)
|
||||
}
|
||||
GrabMinSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_GrabMinSize as i32, v),
|
||||
GrabRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_GrabRounding as i32, v),
|
||||
TabRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_TabRounding as i32, v),
|
||||
ButtonTextAlign(v) => {
|
||||
igPushStyleVar_Vec2(sys::ImGuiStyleVar_ButtonTextAlign as i32, v.into())
|
||||
}
|
||||
SelectableTextAlign(v) => {
|
||||
igPushStyleVar_Vec2(sys::ImGuiStyleVar_SelectableTextAlign as i32, v.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Parameter stacks (current window)
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Changes the item width by pushing a change to the item width stack.
|
||||
///
|
||||
/// Returns an `ItemWidthStackToken` that may be popped by calling `.pop()`
|
||||
///
|
||||
/// - `> 0.0`: width is `item_width` pixels
|
||||
/// - `= 0.0`: default to ~2/3 of window width
|
||||
/// - `< 0.0`: `item_width` pixels relative to the right of window (-1.0 always aligns width to
|
||||
/// the right side)
|
||||
#[doc(alias = "PushItemWith")]
|
||||
pub fn push_item_width(&self, item_width: f32) -> ItemWidthStackToken {
|
||||
unsafe { sys::igPushItemWidth(item_width) };
|
||||
ItemWidthStackToken { _ctx: self.ctx }
|
||||
}
|
||||
/// Sets the width of the next item.
|
||||
///
|
||||
/// - `> 0.0`: width is `item_width` pixels
|
||||
/// - `= 0.0`: default to ~2/3 of window width
|
||||
/// - `< 0.0`: `item_width` pixels relative to the right of window (-1.0 always aligns width to
|
||||
/// the right side)
|
||||
#[doc(alias = "SetNextItemWidth")]
|
||||
pub fn set_next_item_width(&self, item_width: f32) {
|
||||
unsafe { sys::igSetNextItemWidth(item_width) };
|
||||
}
|
||||
/// Returns the width of the item given the pushed settings and the current cursor position.
|
||||
///
|
||||
/// This is NOT necessarily the width of last item.
|
||||
#[doc(alias = "CalcItemWidth")]
|
||||
pub fn calc_item_width(&self) -> f32 {
|
||||
unsafe { sys::igCalcItemWidth() }
|
||||
}
|
||||
|
||||
/// Changes the text wrapping position to the end of window (or column), which
|
||||
/// is generally the default.
|
||||
///
|
||||
/// This is the same as calling [push_text_wrap_pos_with_pos](Self::push_text_wrap_pos_with_pos)
|
||||
/// with `wrap_pos_x` set to 0.0.
|
||||
///
|
||||
/// Returns a `TextWrapPosStackToken` that may be popped by calling `.pop()`
|
||||
#[doc(alias = "PushTextWrapPos")]
|
||||
pub fn push_text_wrap_pos(&self) -> TextWrapPosStackToken {
|
||||
self.push_text_wrap_pos_with_pos(0.0)
|
||||
}
|
||||
|
||||
/// Changes the text wrapping position by pushing a change to the text wrapping position stack.
|
||||
///
|
||||
/// Returns a `TextWrapPosStackToken` that may be popped by calling `.pop()`
|
||||
///
|
||||
/// - `> 0.0`: wrap at `wrap_pos_x` position in window local space
|
||||
/// - `= 0.0`: wrap to end of window (or column)
|
||||
/// - `< 0.0`: no wrapping
|
||||
#[doc(alias = "PushTextWrapPos")]
|
||||
pub fn push_text_wrap_pos_with_pos(&self, wrap_pos_x: f32) -> TextWrapPosStackToken {
|
||||
unsafe { sys::igPushTextWrapPos(wrap_pos_x) };
|
||||
TextWrapPosStackToken { _ctx: self.ctx }
|
||||
}
|
||||
|
||||
/// Changes an item flag by pushing a change to the item flag stack.
|
||||
///
|
||||
/// Returns a `ItemFlagsStackToken` that may be popped by calling `.pop()`
|
||||
#[doc(alias = "PushItemFlag")]
|
||||
pub fn push_item_flag(&self, item_flag: ItemFlag) -> ItemFlagsStackToken {
|
||||
use self::ItemFlag::*;
|
||||
match item_flag {
|
||||
AllowKeyboardFocus(v) => unsafe { sys::igPushAllowKeyboardFocus(v) },
|
||||
ButtonRepeat(v) => unsafe { sys::igPushButtonRepeat(v) },
|
||||
}
|
||||
ItemFlagsStackToken {
|
||||
discriminant: mem::discriminant(&item_flag),
|
||||
_ctx: self.ctx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A temporary change in item flags
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ItemFlag {
|
||||
AllowKeyboardFocus(bool),
|
||||
ButtonRepeat(bool),
|
||||
}
|
||||
|
||||
pub struct ItemWidthStackToken {
|
||||
_ctx: *const Context,
|
||||
}
|
||||
|
||||
impl ItemWidthStackToken {
|
||||
/// Pops a change from the item width stack
|
||||
#[doc(alias = "PopItemWidth")]
|
||||
pub fn pop(mut self, _: &Ui<'_>) {
|
||||
self._ctx = ptr::null();
|
||||
unsafe { sys::igPopItemWidth() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks a change pushed to the text wrap position stack
|
||||
pub struct TextWrapPosStackToken {
|
||||
_ctx: *const Context,
|
||||
}
|
||||
|
||||
impl TextWrapPosStackToken {
|
||||
/// Pops a change from the text wrap position stack
|
||||
#[doc(alias = "PopTextWrapPos")]
|
||||
pub fn pop(mut self, _: &Ui<'_>) {
|
||||
self._ctx = ptr::null();
|
||||
unsafe { sys::igPopTextWrapPos() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks a change pushed to the item flags stack
|
||||
pub struct ItemFlagsStackToken {
|
||||
discriminant: mem::Discriminant<ItemFlag>,
|
||||
_ctx: *const Context,
|
||||
}
|
||||
|
||||
impl ItemFlagsStackToken {
|
||||
/// Pops a change from the item flags stack
|
||||
|
||||
#[doc(alias = "PopAllowKeyboardFocus", alias = "PopButtonRepeat")]
|
||||
pub fn pop(mut self, _: &Ui<'_>) {
|
||||
self._ctx = ptr::null();
|
||||
const ALLOW_KEYBOARD_FOCUS: ItemFlag = ItemFlag::AllowKeyboardFocus(true);
|
||||
const BUTTON_REPEAT: ItemFlag = ItemFlag::ButtonRepeat(true);
|
||||
|
||||
if self.discriminant == mem::discriminant(&ALLOW_KEYBOARD_FOCUS) {
|
||||
unsafe { sys::igPopAllowKeyboardFocus() };
|
||||
} else if self.discriminant == mem::discriminant(&BUTTON_REPEAT) {
|
||||
unsafe { sys::igPopButtonRepeat() };
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks an ID pushed to the ID stack that can be popped by calling `.pop()`
|
||||
/// or by dropping.
|
||||
pub struct IdStackToken<'ui>;
|
||||
|
||||
/// Pops a change from the ID stack
|
||||
drop { sys::igPopID() }
|
||||
);
|
||||
|
||||
impl IdStackToken<'_> {
|
||||
/// Pops a change from the ID stack
|
||||
pub fn pop(self) {
|
||||
self.end()
|
||||
}
|
||||
}
|
||||
|
||||
/// # ID stack
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Pushes an identifier to the ID stack.
|
||||
///
|
||||
/// Returns an `IdStackToken` that can be popped by calling `.end()`
|
||||
/// or by dropping manually.
|
||||
#[doc(alias = "PushId")]
|
||||
pub fn push_id<'a, I: Into<Id<'a>>>(&self, id: I) -> IdStackToken<'ui> {
|
||||
let id = id.into();
|
||||
|
||||
unsafe {
|
||||
match id {
|
||||
Id::Int(i) => sys::igPushID_Int(i),
|
||||
Id::Str(s) => {
|
||||
let start = s.as_ptr() as *const c_char;
|
||||
let end = start.add(s.len());
|
||||
sys::igPushID_StrStr(start, end)
|
||||
}
|
||||
Id::Ptr(p) => sys::igPushID_Ptr(p as *const c_void),
|
||||
}
|
||||
}
|
||||
IdStackToken::new(self)
|
||||
}
|
||||
}
|
544
plugins/libimhex-rust/imgui-rs/src/string.rs
Normal file
544
plugins/libimhex-rust/imgui-rs/src/string.rs
Normal file
@ -0,0 +1,544 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::ffi::CStr;
|
||||
use std::ops::{Deref, Index, RangeFull};
|
||||
use std::os::raw::c_char;
|
||||
use std::str;
|
||||
use std::{fmt, ptr};
|
||||
|
||||
/// this is the unsafe cell upon which we build our abstraction.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UiBuffer {
|
||||
buffer: Vec<u8>,
|
||||
max_len: usize,
|
||||
}
|
||||
|
||||
impl UiBuffer {
|
||||
/// Creates a new max buffer with the given length.
|
||||
pub fn new(max_len: usize) -> Self {
|
||||
Self {
|
||||
buffer: Vec::with_capacity(max_len),
|
||||
max_len,
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal method to push a single text to our scratch buffer.
|
||||
pub fn scratch_txt(&mut self, txt: impl AsRef<str>) -> *const sys::cty::c_char {
|
||||
self.refresh_buffer();
|
||||
self.push(txt)
|
||||
}
|
||||
|
||||
/// Internal method to push an option text to our scratch buffer.
|
||||
pub fn scratch_txt_opt(&mut self, txt: Option<impl AsRef<str>>) -> *const sys::cty::c_char {
|
||||
match txt {
|
||||
Some(v) => self.scratch_txt(v),
|
||||
None => ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scratch_txt_two(
|
||||
&mut self,
|
||||
txt_0: impl AsRef<str>,
|
||||
txt_1: impl AsRef<str>,
|
||||
) -> (*const sys::cty::c_char, *const sys::cty::c_char) {
|
||||
self.refresh_buffer();
|
||||
(self.push(txt_0), self.push(txt_1))
|
||||
}
|
||||
|
||||
pub fn scratch_txt_with_opt(
|
||||
&mut self,
|
||||
txt_0: impl AsRef<str>,
|
||||
txt_1: Option<impl AsRef<str>>,
|
||||
) -> (*const sys::cty::c_char, *const sys::cty::c_char) {
|
||||
match txt_1 {
|
||||
Some(value) => self.scratch_txt_two(txt_0, value),
|
||||
None => (self.scratch_txt(txt_0), ptr::null()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to clear the buffer if it's over the maximum length allowed.
|
||||
pub fn refresh_buffer(&mut self) {
|
||||
if self.buffer.len() > self.max_len {
|
||||
self.buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes a new scratch sheet text, which means it's not handling any clearing at all.
|
||||
pub fn push(&mut self, txt: impl AsRef<str>) -> *const sys::cty::c_char {
|
||||
unsafe {
|
||||
let len = self.buffer.len();
|
||||
self.buffer.extend(txt.as_ref().as_bytes());
|
||||
self.buffer.push(b'\0');
|
||||
|
||||
self.buffer.as_ptr().add(len) as *const _
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[deprecated = "all functions take AsRef<str> now -- use inline strings or `format` instead"]
|
||||
macro_rules! im_str {
|
||||
($e:literal $(,)?) => {{
|
||||
const __INPUT: &str = concat!($e, "\0");
|
||||
{
|
||||
// Trigger a compile error if there's an interior NUL character.
|
||||
const _CHECK_NUL: [(); 0] = [(); {
|
||||
let bytes = __INPUT.as_bytes();
|
||||
let mut i = 0;
|
||||
let mut found_nul = 0;
|
||||
while i < bytes.len() - 1 && found_nul == 0 {
|
||||
if bytes[i] == 0 {
|
||||
found_nul = 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
found_nul
|
||||
}];
|
||||
const RESULT: &'static $crate::ImStr = unsafe {
|
||||
$crate::__core::mem::transmute::<&'static [u8], &'static $crate::ImStr>(__INPUT.as_bytes())
|
||||
};
|
||||
RESULT
|
||||
}
|
||||
}};
|
||||
($e:literal, $($arg:tt)+) => ({
|
||||
$crate::ImString::new(format!($e, $($arg)*))
|
||||
});
|
||||
}
|
||||
|
||||
/// A UTF-8 encoded, growable, implicitly nul-terminated string.
|
||||
#[derive(Clone, Hash, Ord, Eq, PartialOrd, PartialEq)]
|
||||
pub struct ImString(pub(crate) Vec<u8>);
|
||||
|
||||
impl ImString {
|
||||
/// Creates a new `ImString` from an existing string.
|
||||
pub fn new<T: Into<String>>(value: T) -> ImString {
|
||||
unsafe {
|
||||
let mut s = ImString::from_utf8_unchecked(value.into().into_bytes());
|
||||
s.refresh_len();
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new empty `ImString` with a particular capacity
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> ImString {
|
||||
let mut v = Vec::with_capacity(capacity + 1);
|
||||
v.push(b'\0');
|
||||
ImString(v)
|
||||
}
|
||||
|
||||
/// Converts a vector of bytes to a `ImString` without checking that the string contains valid
|
||||
/// UTF-8
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the vector contains valid UTF-8 and no null terminator.
|
||||
#[inline]
|
||||
pub unsafe fn from_utf8_unchecked(mut v: Vec<u8>) -> ImString {
|
||||
v.push(b'\0');
|
||||
ImString(v)
|
||||
}
|
||||
|
||||
/// Converts a vector of bytes to a `ImString` without checking that the string contains valid
|
||||
/// UTF-8
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the vector contains valid UTF-8 and a null terminator.
|
||||
#[inline]
|
||||
pub unsafe fn from_utf8_with_nul_unchecked(v: Vec<u8>) -> ImString {
|
||||
ImString(v)
|
||||
}
|
||||
|
||||
/// Truncates this `ImString`, removing all contents
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
self.0.push(b'\0');
|
||||
}
|
||||
|
||||
/// Appends the given character to the end of this `ImString`
|
||||
#[inline]
|
||||
pub fn push(&mut self, ch: char) {
|
||||
let mut buf = [0; 4];
|
||||
self.push_str(ch.encode_utf8(&mut buf));
|
||||
}
|
||||
|
||||
/// Appends a given string slice to the end of this `ImString`
|
||||
#[inline]
|
||||
pub fn push_str(&mut self, string: &str) {
|
||||
self.0.pop();
|
||||
self.0.extend(string.bytes());
|
||||
self.0.push(b'\0');
|
||||
unsafe {
|
||||
self.refresh_len();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the capacity of this `ImString` in bytes
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.0.capacity() - 1
|
||||
}
|
||||
|
||||
/// Returns the capacity of this `ImString` in bytes, including the implicit null byte
|
||||
#[inline]
|
||||
pub fn capacity_with_nul(&self) -> usize {
|
||||
self.0.capacity()
|
||||
}
|
||||
|
||||
/// Ensures that the capacity of this `ImString` is at least `additional` bytes larger than the
|
||||
/// current length.
|
||||
///
|
||||
/// The capacity may be increased by more than `additional` bytes.
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
self.0.reserve(additional);
|
||||
}
|
||||
|
||||
/// Ensures that the capacity of this `ImString` is at least `additional` bytes larger than the
|
||||
/// current length
|
||||
pub fn reserve_exact(&mut self, additional: usize) {
|
||||
self.0.reserve_exact(additional);
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the underlying buffer
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *const c_char {
|
||||
self.0.as_ptr() as *const c_char
|
||||
}
|
||||
|
||||
/// Returns a raw mutable pointer to the underlying buffer.
|
||||
///
|
||||
/// If the underlying data is modified, `refresh_len` *must* be called afterwards.
|
||||
#[inline]
|
||||
pub fn as_mut_ptr(&mut self) -> *mut c_char {
|
||||
self.0.as_mut_ptr() as *mut c_char
|
||||
}
|
||||
|
||||
/// Updates the underlying buffer length based on the current contents.
|
||||
///
|
||||
/// This function *must* be called if the underlying data is modified via a pointer
|
||||
/// obtained by `as_mut_ptr`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the this ImString contains valid UTF-8 and a null
|
||||
/// terminator.
|
||||
#[inline]
|
||||
pub unsafe fn refresh_len(&mut self) {
|
||||
let len = CStr::from_ptr(self.0.as_ptr() as *const c_char)
|
||||
.to_bytes_with_nul()
|
||||
.len();
|
||||
self.0.set_len(len);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for ImString {
|
||||
#[inline]
|
||||
fn default() -> ImString {
|
||||
ImString(vec![b'\0'])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ImString {
|
||||
#[inline]
|
||||
fn from(s: String) -> ImString {
|
||||
ImString::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<ImString> for Cow<'a, ImStr> {
|
||||
#[inline]
|
||||
fn from(s: ImString) -> Cow<'a, ImStr> {
|
||||
Cow::Owned(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ImString> for Cow<'a, ImStr> {
|
||||
#[inline]
|
||||
fn from(s: &'a ImString) -> Cow<'a, ImStr> {
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + AsRef<ImStr>> From<&'a T> for ImString {
|
||||
#[inline]
|
||||
fn from(s: &'a T) -> ImString {
|
||||
s.as_ref().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<ImStr> for ImString {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &ImStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<ImStr> for ImString {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &ImStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for ImString {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.to_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for ImString {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &str {
|
||||
self.to_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<RangeFull> for ImString {
|
||||
type Output = ImStr;
|
||||
#[inline]
|
||||
fn index(&self, _index: RangeFull) -> &ImStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ImString {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.to_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ImString {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.to_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ImString {
|
||||
type Target = ImStr;
|
||||
#[inline]
|
||||
fn deref(&self) -> &ImStr {
|
||||
// as_ptr() is used, because we need to look at the bytes to figure out the length
|
||||
// self.0.len() is incorrect, because there might be more than one nul byte in the end, or
|
||||
// some interior nuls in the data
|
||||
unsafe {
|
||||
&*(CStr::from_ptr(self.0.as_ptr() as *const c_char) as *const CStr as *const ImStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for ImString {
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.push_str(s);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
self.push(c);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A UTF-8 encoded, implicitly nul-terminated string slice.
|
||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct ImStr([u8]);
|
||||
|
||||
impl<'a> Default for &'a ImStr {
|
||||
#[inline]
|
||||
fn default() -> &'a ImStr {
|
||||
static SLICE: &[u8] = &[0];
|
||||
unsafe { ImStr::from_utf8_with_nul_unchecked(SLICE) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ImStr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ImStr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.to_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ImStr {
|
||||
/// Wraps a raw UTF-8 encoded C string
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the pointer is not null and it points to a
|
||||
/// null-terminated UTF-8 string valid for the duration of the arbitrary lifetime 'a.
|
||||
#[inline]
|
||||
pub unsafe fn from_ptr_unchecked<'a>(ptr: *const c_char) -> &'a ImStr {
|
||||
ImStr::from_cstr_unchecked(CStr::from_ptr(ptr))
|
||||
}
|
||||
|
||||
/// Converts a slice of bytes to an imgui-rs string slice without checking for valid UTF-8 or
|
||||
/// null termination.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the slice contains valid UTF-8 and a null terminator.
|
||||
#[inline]
|
||||
pub unsafe fn from_utf8_with_nul_unchecked(bytes: &[u8]) -> &ImStr {
|
||||
&*(bytes as *const [u8] as *const ImStr)
|
||||
}
|
||||
|
||||
/// Converts a CStr reference to an imgui-rs string slice without checking for valid UTF-8.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is up to the caller to guarantee the CStr reference contains valid UTF-8.
|
||||
#[inline]
|
||||
pub unsafe fn from_cstr_unchecked(value: &CStr) -> &ImStr {
|
||||
&*(value.to_bytes_with_nul() as *const [u8] as *const ImStr)
|
||||
}
|
||||
|
||||
/// Converts an imgui-rs string slice to a raw pointer
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *const c_char {
|
||||
self.0.as_ptr() as *const c_char
|
||||
}
|
||||
|
||||
/// Converts an imgui-rs string slice to a normal string slice
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> &str {
|
||||
self.sanity_check();
|
||||
unsafe { str::from_utf8_unchecked(&self.0[..(self.0.len() - 1)]) }
|
||||
}
|
||||
|
||||
/// Returns true if the imgui-rs string slice is empty
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
debug_assert!(self.0.len() != 0);
|
||||
self.0.len() == 1
|
||||
}
|
||||
|
||||
// TODO: if this is too slow, avoid the UTF8 validation except if we'd
|
||||
// already be doing O(n) stuff.
|
||||
#[inline]
|
||||
fn sanity_check(&self) {
|
||||
debug_assert!(
|
||||
str::from_utf8(&self.0).is_ok()
|
||||
&& !self.0.is_empty()
|
||||
&& !self.0[..(self.0.len() - 1)].contains(&0u8)
|
||||
&& self.0[self.0.len() - 1] == 0,
|
||||
"bad ImStr: {:?}",
|
||||
&self.0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<CStr> for ImStr {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &CStr {
|
||||
// Safety: our safety requirements are a superset of CStr's, so this is fine
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<ImStr> for ImStr {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &ImStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for ImStr {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.to_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ImStr> for Cow<'a, ImStr> {
|
||||
#[inline]
|
||||
fn from(s: &'a ImStr) -> Cow<'a, ImStr> {
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for ImStr {
|
||||
type Owned = ImString;
|
||||
#[inline]
|
||||
fn to_owned(&self) -> ImString {
|
||||
self.sanity_check();
|
||||
ImString(self.0.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imstring_constructors() {
|
||||
let s = ImString::new("test");
|
||||
assert_eq!(s.0, b"test\0");
|
||||
|
||||
let s = ImString::with_capacity(100);
|
||||
assert_eq!(s.0, b"\0");
|
||||
|
||||
let s = unsafe { ImString::from_utf8_unchecked(vec![b't', b'e', b's', b't']) };
|
||||
assert_eq!(s.0, b"test\0");
|
||||
|
||||
let s = unsafe { ImString::from_utf8_with_nul_unchecked(vec![b't', b'e', b's', b't', b'\0']) };
|
||||
assert_eq!(s.0, b"test\0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imstring_operations() {
|
||||
let mut s = ImString::new("test");
|
||||
s.clear();
|
||||
assert_eq!(s.0, b"\0");
|
||||
s.push('z');
|
||||
assert_eq!(s.0, b"z\0");
|
||||
s.push('ä');
|
||||
assert_eq!(s.0, b"z\xc3\xa4\0");
|
||||
s.clear();
|
||||
s.push_str("imgui-rs");
|
||||
assert_eq!(s.0, b"imgui-rs\0");
|
||||
s.push_str("öä");
|
||||
assert_eq!(s.0, b"imgui-rs\xc3\xb6\xc3\xa4\0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imstring_fmt_write() {
|
||||
use std::fmt::Write;
|
||||
let mut s = ImString::default();
|
||||
let _ = write!(s, "format {:02x}", 0x42);
|
||||
assert_eq!(s.0, b"format 42\0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imstring_refresh_len() {
|
||||
let mut s = ImString::new("testing");
|
||||
unsafe {
|
||||
let mut ptr = s.as_mut_ptr() as *mut u8;
|
||||
ptr = ptr.wrapping_add(2);
|
||||
*ptr = b'z';
|
||||
ptr = ptr.wrapping_add(1);
|
||||
*ptr = b'\0';
|
||||
}
|
||||
assert_eq!(s.0, b"tez\0ing\0");
|
||||
unsafe { s.refresh_len() };
|
||||
assert_eq!(s.0, b"tez\0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imstring_interior_nul() {
|
||||
let s = ImString::new("test\0ohno");
|
||||
assert_eq!(s.0, b"test\0");
|
||||
assert_eq!(s.to_str(), "test");
|
||||
assert!(!s.is_empty());
|
||||
|
||||
let s = ImString::new("\0ohno");
|
||||
assert_eq!(s.to_str(), "");
|
||||
assert!(s.is_empty());
|
||||
}
|
506
plugins/libimhex-rust/imgui-rs/src/style.rs
Normal file
506
plugins/libimhex-rust/imgui-rs/src/style.rs
Normal file
@ -0,0 +1,506 @@
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use crate::internal::RawCast;
|
||||
use crate::sys;
|
||||
use crate::Direction;
|
||||
|
||||
/// User interface style/colors
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Style {
|
||||
/// Global alpha applies to everything
|
||||
pub alpha: f32,
|
||||
/// Additional alpha multiplier applied to disabled elements. Multiplies over current value of [`Style::alpha`].
|
||||
pub disabled_alpha: f32,
|
||||
/// Padding within a window
|
||||
pub window_padding: [f32; 2],
|
||||
/// Rounding radius of window corners.
|
||||
///
|
||||
/// Set to 0.0 to have rectangular windows.
|
||||
/// Large values tend to lead to a variety of artifacts and are not recommended.
|
||||
pub window_rounding: f32,
|
||||
/// Thickness of border around windows.
|
||||
///
|
||||
/// Generally set to 0.0 or 1.0 (other values are not well tested and cost more CPU/GPU).
|
||||
pub window_border_size: f32,
|
||||
/// Minimum window size
|
||||
pub window_min_size: [f32; 2],
|
||||
/// Alignment for title bar text.
|
||||
///
|
||||
/// Defaults to [0.5, 0.5] for left-aligned, vertically centered.
|
||||
pub window_title_align: [f32; 2],
|
||||
/// Side of the collapsing/docking button in the title bar (left/right).
|
||||
///
|
||||
/// Defaults to Direction::Left.
|
||||
pub window_menu_button_position: Direction,
|
||||
/// Rounding radius of child window corners.
|
||||
///
|
||||
/// Set to 0.0 to have rectangular child windows.
|
||||
pub child_rounding: f32,
|
||||
/// Thickness of border around child windows.
|
||||
///
|
||||
/// Generally set to 0.0 or 1.0 (other values are not well tested and cost more CPU/GPU).
|
||||
pub child_border_size: f32,
|
||||
/// Rounding radius of popup window corners.
|
||||
///
|
||||
/// Note that tooltip windows use `window_rounding` instead.
|
||||
pub popup_rounding: f32,
|
||||
/// Thickness of border around popup/tooltip windows.
|
||||
///
|
||||
/// Generally set to 0.0 or 1.0 (other values are not well tested and cost more CPU/GPU).
|
||||
pub popup_border_size: f32,
|
||||
/// Padding within a framed rectangle (used by most widgets)
|
||||
pub frame_padding: [f32; 2],
|
||||
/// Rounding radius of frame corners (used by most widgets).
|
||||
///
|
||||
/// Set to 0.0 to have rectangular frames.
|
||||
pub frame_rounding: f32,
|
||||
/// Thickness of border around frames.
|
||||
///
|
||||
/// Generally set to 0.0 or 1.0 (other values are not well tested and cost more CPU/GPU).
|
||||
pub frame_border_size: f32,
|
||||
/// Horizontal and vertical spacing between widgets/lines
|
||||
pub item_spacing: [f32; 2],
|
||||
/// Horizontal and vertical spacing between elements of a composed widget (e.g. a slider and
|
||||
/// its label)
|
||||
pub item_inner_spacing: [f32; 2],
|
||||
/// Padding within a table cell.
|
||||
pub cell_padding: [f32; 2],
|
||||
/// Expand reactive bounding box for touch-based system where touch position is not accurate
|
||||
/// enough.
|
||||
///
|
||||
/// Unfortunately we don't sort widgets so priority on overlap will always be given to the
|
||||
/// first widget, so don't grow this too much.
|
||||
pub touch_extra_padding: [f32; 2],
|
||||
/// Horizontal indentation when e.g. entering a tree node.
|
||||
///
|
||||
/// Generally equal to (font size + horizontal frame padding * 2).
|
||||
pub indent_spacing: f32,
|
||||
/// Minimum horizontal spacing between two columns
|
||||
pub columns_min_spacing: f32,
|
||||
/// Width of the vertical scrollbar, height of the horizontal scrollbar
|
||||
pub scrollbar_size: f32,
|
||||
/// Rounding radius of scrollbar grab corners
|
||||
pub scrollbar_rounding: f32,
|
||||
/// Minimum width/height of a grab box for slider/scrollbar
|
||||
pub grab_min_size: f32,
|
||||
/// Rounding radius of grab corners.
|
||||
///
|
||||
/// Set to 0.0 to have rectangular slider grabs.
|
||||
pub grab_rounding: f32,
|
||||
/// The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero
|
||||
pub log_slider_deadzone: f32,
|
||||
/// Rounding radius of upper corners of tabs.
|
||||
///
|
||||
/// Set to 0.0 to have rectangular tabs.
|
||||
pub tab_rounding: f32,
|
||||
/// Thickness of border around tabs
|
||||
pub tab_border_size: f32,
|
||||
/// Minimum width for close button to appear on an unselected tab when hovered.
|
||||
///
|
||||
/// `= 0.0`: always show when hovering
|
||||
/// `= f32::MAX`: never show close button unless selected
|
||||
pub tab_min_width_for_close_button: f32,
|
||||
/// Side of the color buttonton pubin color editor widgets (left/right).
|
||||
pub color_button_position: Direction,
|
||||
/// Alignment of button text when button is larger than text.
|
||||
///
|
||||
/// Defaults to [0.5, 0.5] (centered).
|
||||
pub button_text_align: [f32; 2],
|
||||
/// Alignment of selectable text when selectable is larger than text.
|
||||
///
|
||||
/// Defaults to [0.5, 0.5] (top-left aligned).
|
||||
pub selectable_text_align: [f32; 2],
|
||||
/// Window positions are clamped to be visible within the display area or monitors by at least
|
||||
/// this amount.
|
||||
///
|
||||
/// Only applies to regular windows.
|
||||
pub display_window_padding: [f32; 2],
|
||||
/// If you cannot see the edges of your screen (e.g. on a TV), increase the safe area padding.
|
||||
///
|
||||
/// Also applies to popups/tooltips in addition to regular windows.
|
||||
pub display_safe_area_padding: [f32; 2],
|
||||
/// Scale software-rendered mouse cursor.
|
||||
///
|
||||
/// May be removed later.
|
||||
pub mouse_cursor_scale: f32,
|
||||
/// Enable anti-aliased lines/borders.
|
||||
///
|
||||
/// Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame.
|
||||
pub anti_aliased_lines: bool,
|
||||
/// Enable anti-aliased lines/borders using textures where possible.
|
||||
///
|
||||
/// Require back-end to render with bilinear filtering. Latched at the beginning of the frame.
|
||||
pub anti_aliased_lines_use_tex: bool,
|
||||
/// Enable anti-aliased edges around filled shapes (rounded recatngles, circles, etc.).
|
||||
///
|
||||
/// Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame.
|
||||
pub anti_aliased_fill: bool,
|
||||
/// Tessellation tolerance when using path_bezier_curve_to without a specific number of
|
||||
/// segments.
|
||||
///
|
||||
/// Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce
|
||||
/// quality.
|
||||
pub curve_tessellation_tol: f32,
|
||||
/// Maximum error (in pixels) allowed when drawing circles or rounded corner rectangles with no
|
||||
/// explicit segment count specified.
|
||||
///
|
||||
/// Decrease for higher quality but more geometry.
|
||||
pub circle_tesselation_max_error: f32,
|
||||
/// Style colors.
|
||||
pub colors: [[f32; 4]; StyleColor::COUNT],
|
||||
}
|
||||
|
||||
unsafe impl RawCast<sys::ImGuiStyle> for Style {}
|
||||
|
||||
impl Style {
|
||||
/// Scales all sizes in the style
|
||||
#[doc(alias = "ScaleAllSizes")]
|
||||
pub fn scale_all_sizes(&mut self, scale_factor: f32) {
|
||||
unsafe {
|
||||
sys::ImGuiStyle_ScaleAllSizes(self.raw_mut(), scale_factor);
|
||||
}
|
||||
}
|
||||
/// Replaces current colors with classic Dear ImGui style
|
||||
#[doc(alias = "StyleColors", alias = "StlyeColorsClassic")]
|
||||
pub fn use_classic_colors(&mut self) -> &mut Self {
|
||||
unsafe {
|
||||
sys::igStyleColorsClassic(self.raw_mut());
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Replaces current colors with a new, recommended style
|
||||
#[doc(alias = "StyleColors", alias = "StyleColorsDark")]
|
||||
pub fn use_dark_colors(&mut self) -> &mut Self {
|
||||
unsafe {
|
||||
sys::igStyleColorsDark(self.raw_mut());
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Replaces current colors with a light style. Best used with borders and a custom, thicker
|
||||
/// font
|
||||
#[doc(alias = "StyleColors", alias = "StyleColorsLight")]
|
||||
pub fn use_light_colors(&mut self) -> &mut Self {
|
||||
unsafe {
|
||||
sys::igStyleColorsLight(self.raw_mut());
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<StyleColor> for Style {
|
||||
type Output = [f32; 4];
|
||||
#[inline]
|
||||
fn index(&self, index: StyleColor) -> &[f32; 4] {
|
||||
&self.colors[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<StyleColor> for Style {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: StyleColor) -> &mut [f32; 4] {
|
||||
&mut self.colors[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
/// A color identifier for styling
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub enum StyleColor {
|
||||
Text = sys::ImGuiCol_Text,
|
||||
TextDisabled = sys::ImGuiCol_TextDisabled,
|
||||
/// Background of normal windows
|
||||
WindowBg = sys::ImGuiCol_WindowBg,
|
||||
/// Background of child windows
|
||||
ChildBg = sys::ImGuiCol_ChildBg,
|
||||
/// Background of popups, menus, tooltips windows
|
||||
PopupBg = sys::ImGuiCol_PopupBg,
|
||||
Border = sys::ImGuiCol_Border,
|
||||
BorderShadow = sys::ImGuiCol_BorderShadow,
|
||||
/// Background of checkbox, radio button, plot, slider, text input
|
||||
FrameBg = sys::ImGuiCol_FrameBg,
|
||||
FrameBgHovered = sys::ImGuiCol_FrameBgHovered,
|
||||
FrameBgActive = sys::ImGuiCol_FrameBgActive,
|
||||
TitleBg = sys::ImGuiCol_TitleBg,
|
||||
TitleBgActive = sys::ImGuiCol_TitleBgActive,
|
||||
TitleBgCollapsed = sys::ImGuiCol_TitleBgCollapsed,
|
||||
MenuBarBg = sys::ImGuiCol_MenuBarBg,
|
||||
ScrollbarBg = sys::ImGuiCol_ScrollbarBg,
|
||||
ScrollbarGrab = sys::ImGuiCol_ScrollbarGrab,
|
||||
ScrollbarGrabHovered = sys::ImGuiCol_ScrollbarGrabHovered,
|
||||
ScrollbarGrabActive = sys::ImGuiCol_ScrollbarGrabActive,
|
||||
CheckMark = sys::ImGuiCol_CheckMark,
|
||||
SliderGrab = sys::ImGuiCol_SliderGrab,
|
||||
SliderGrabActive = sys::ImGuiCol_SliderGrabActive,
|
||||
Button = sys::ImGuiCol_Button,
|
||||
ButtonHovered = sys::ImGuiCol_ButtonHovered,
|
||||
ButtonActive = sys::ImGuiCol_ButtonActive,
|
||||
Header = sys::ImGuiCol_Header,
|
||||
HeaderHovered = sys::ImGuiCol_HeaderHovered,
|
||||
HeaderActive = sys::ImGuiCol_HeaderActive,
|
||||
Separator = sys::ImGuiCol_Separator,
|
||||
SeparatorHovered = sys::ImGuiCol_SeparatorHovered,
|
||||
SeparatorActive = sys::ImGuiCol_SeparatorActive,
|
||||
ResizeGrip = sys::ImGuiCol_ResizeGrip,
|
||||
ResizeGripHovered = sys::ImGuiCol_ResizeGripHovered,
|
||||
ResizeGripActive = sys::ImGuiCol_ResizeGripActive,
|
||||
Tab = sys::ImGuiCol_Tab,
|
||||
TabHovered = sys::ImGuiCol_TabHovered,
|
||||
TabActive = sys::ImGuiCol_TabActive,
|
||||
TabUnfocused = sys::ImGuiCol_TabUnfocused,
|
||||
TabUnfocusedActive = sys::ImGuiCol_TabUnfocusedActive,
|
||||
PlotLines = sys::ImGuiCol_PlotLines,
|
||||
PlotLinesHovered = sys::ImGuiCol_PlotLinesHovered,
|
||||
PlotHistogram = sys::ImGuiCol_PlotHistogram,
|
||||
PlotHistogramHovered = sys::ImGuiCol_PlotHistogramHovered,
|
||||
TableHeaderBg = sys::ImGuiCol_TableHeaderBg,
|
||||
TableBorderStrong = sys::ImGuiCol_TableBorderStrong,
|
||||
TableBorderLight = sys::ImGuiCol_TableBorderLight,
|
||||
TableRowBg = sys::ImGuiCol_TableRowBg,
|
||||
TableRowBgAlt = sys::ImGuiCol_TableRowBgAlt,
|
||||
TextSelectedBg = sys::ImGuiCol_TextSelectedBg,
|
||||
DragDropTarget = sys::ImGuiCol_DragDropTarget,
|
||||
/// Gamepad/keyboard: current highlighted item
|
||||
NavHighlight = sys::ImGuiCol_NavHighlight,
|
||||
/// Highlight window when using CTRL+TAB
|
||||
NavWindowingHighlight = sys::ImGuiCol_NavWindowingHighlight,
|
||||
/// Darken/colorize entire screen behind the CTRL+TAB window list, when active
|
||||
NavWindowingDimBg = sys::ImGuiCol_NavWindowingDimBg,
|
||||
/// Darken/colorize entire screen behind a modal window, when one is active
|
||||
ModalWindowDimBg = sys::ImGuiCol_ModalWindowDimBg,
|
||||
}
|
||||
|
||||
impl StyleColor {
|
||||
/// All possible `StyleColor` variants
|
||||
pub const VARIANTS: [StyleColor; StyleColor::COUNT] = [
|
||||
StyleColor::Text,
|
||||
StyleColor::TextDisabled,
|
||||
StyleColor::WindowBg,
|
||||
StyleColor::ChildBg,
|
||||
StyleColor::PopupBg,
|
||||
StyleColor::Border,
|
||||
StyleColor::BorderShadow,
|
||||
StyleColor::FrameBg,
|
||||
StyleColor::FrameBgHovered,
|
||||
StyleColor::FrameBgActive,
|
||||
StyleColor::TitleBg,
|
||||
StyleColor::TitleBgActive,
|
||||
StyleColor::TitleBgCollapsed,
|
||||
StyleColor::MenuBarBg,
|
||||
StyleColor::ScrollbarBg,
|
||||
StyleColor::ScrollbarGrab,
|
||||
StyleColor::ScrollbarGrabHovered,
|
||||
StyleColor::ScrollbarGrabActive,
|
||||
StyleColor::CheckMark,
|
||||
StyleColor::SliderGrab,
|
||||
StyleColor::SliderGrabActive,
|
||||
StyleColor::Button,
|
||||
StyleColor::ButtonHovered,
|
||||
StyleColor::ButtonActive,
|
||||
StyleColor::Header,
|
||||
StyleColor::HeaderHovered,
|
||||
StyleColor::HeaderActive,
|
||||
StyleColor::Separator,
|
||||
StyleColor::SeparatorHovered,
|
||||
StyleColor::SeparatorActive,
|
||||
StyleColor::ResizeGrip,
|
||||
StyleColor::ResizeGripHovered,
|
||||
StyleColor::ResizeGripActive,
|
||||
StyleColor::Tab,
|
||||
StyleColor::TabHovered,
|
||||
StyleColor::TabActive,
|
||||
StyleColor::TabUnfocused,
|
||||
StyleColor::TabUnfocusedActive,
|
||||
StyleColor::PlotLines,
|
||||
StyleColor::PlotLinesHovered,
|
||||
StyleColor::PlotHistogram,
|
||||
StyleColor::PlotHistogramHovered,
|
||||
StyleColor::TableHeaderBg,
|
||||
StyleColor::TableBorderStrong,
|
||||
StyleColor::TableBorderLight,
|
||||
StyleColor::TableRowBg,
|
||||
StyleColor::TableRowBgAlt,
|
||||
StyleColor::TextSelectedBg,
|
||||
StyleColor::DragDropTarget,
|
||||
StyleColor::NavHighlight,
|
||||
StyleColor::NavWindowingHighlight,
|
||||
StyleColor::NavWindowingDimBg,
|
||||
StyleColor::ModalWindowDimBg,
|
||||
];
|
||||
/// Total count of `StyleColor` variants
|
||||
pub const COUNT: usize = sys::ImGuiCol_COUNT as usize;
|
||||
}
|
||||
|
||||
/// A temporary change in user interface style
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum StyleVar {
|
||||
/// Global alpha applies to everything
|
||||
Alpha(f32),
|
||||
/// Padding within a window
|
||||
WindowPadding([f32; 2]),
|
||||
/// Rounding radius of window corners
|
||||
WindowRounding(f32),
|
||||
/// Thickness of border around windows
|
||||
WindowBorderSize(f32),
|
||||
/// Minimum window size
|
||||
WindowMinSize([f32; 2]),
|
||||
/// Alignment for title bar text
|
||||
WindowTitleAlign([f32; 2]),
|
||||
/// Rounding radius of child window corners
|
||||
ChildRounding(f32),
|
||||
/// Thickness of border around child windows
|
||||
ChildBorderSize(f32),
|
||||
/// Rounding radius of popup window corners
|
||||
PopupRounding(f32),
|
||||
/// Thickness of border around popup/tooltip windows
|
||||
PopupBorderSize(f32),
|
||||
/// Padding within a framed rectangle (used by most widgets)
|
||||
FramePadding([f32; 2]),
|
||||
/// Rounding radius of frame corners (used by most widgets)
|
||||
FrameRounding(f32),
|
||||
/// Thickness of border around frames
|
||||
FrameBorderSize(f32),
|
||||
/// Horizontal and vertical spacing between widgets/lines
|
||||
ItemSpacing([f32; 2]),
|
||||
/// Horizontal and vertical spacing between elements of a composed widget (e.g. a slider and
|
||||
/// its label)
|
||||
ItemInnerSpacing([f32; 2]),
|
||||
/// Horizontal indentation when e.g. entering a tree node
|
||||
IndentSpacing(f32),
|
||||
/// Width of the vertical scrollbar, height of the horizontal scrollbar
|
||||
ScrollbarSize(f32),
|
||||
/// Rounding radius of scrollbar grab corners
|
||||
ScrollbarRounding(f32),
|
||||
/// Minimum width/height of a grab box for slider/scrollbar
|
||||
GrabMinSize(f32),
|
||||
/// Rounding radius of grab corners
|
||||
GrabRounding(f32),
|
||||
/// Rounding radius of upper corners of tabs
|
||||
TabRounding(f32),
|
||||
/// Alignment of button text when button is larger than text
|
||||
ButtonTextAlign([f32; 2]),
|
||||
/// Alignment of selectable text when selectable is larger than text
|
||||
SelectableTextAlign([f32; 2]),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_scaling() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx();
|
||||
let style = ctx.style_mut();
|
||||
style.window_padding = [1.0, 2.0];
|
||||
style.window_rounding = 3.0;
|
||||
style.window_min_size = [4.0, 5.0];
|
||||
style.child_rounding = 6.0;
|
||||
style.popup_rounding = 7.0;
|
||||
style.frame_padding = [8.0, 9.0];
|
||||
style.frame_rounding = 10.0;
|
||||
style.item_spacing = [11.0, 12.0];
|
||||
style.item_inner_spacing = [13.0, 14.0];
|
||||
style.touch_extra_padding = [15.0, 16.0];
|
||||
style.indent_spacing = 17.0;
|
||||
style.columns_min_spacing = 18.0;
|
||||
style.scrollbar_size = 19.0;
|
||||
style.scrollbar_rounding = 20.0;
|
||||
style.grab_min_size = 21.0;
|
||||
style.grab_rounding = 22.0;
|
||||
style.log_slider_deadzone = 29.0;
|
||||
style.tab_rounding = 23.0;
|
||||
style.display_window_padding = [24.0, 25.0];
|
||||
style.display_safe_area_padding = [26.0, 27.0];
|
||||
style.mouse_cursor_scale = 28.0;
|
||||
style.scale_all_sizes(2.0);
|
||||
assert_eq!(style.window_padding, [2.0, 4.0]);
|
||||
assert_eq!(style.window_rounding, 6.0);
|
||||
assert_eq!(style.window_min_size, [8.0, 10.0]);
|
||||
assert_eq!(style.child_rounding, 12.0);
|
||||
assert_eq!(style.popup_rounding, 14.0);
|
||||
assert_eq!(style.frame_padding, [16.0, 18.0]);
|
||||
assert_eq!(style.frame_rounding, 20.0);
|
||||
assert_eq!(style.item_spacing, [22.0, 24.0]);
|
||||
assert_eq!(style.item_inner_spacing, [26.0, 28.0]);
|
||||
assert_eq!(style.touch_extra_padding, [30.0, 32.0]);
|
||||
assert_eq!(style.indent_spacing, 34.0);
|
||||
assert_eq!(style.columns_min_spacing, 36.0);
|
||||
assert_eq!(style.scrollbar_size, 38.0);
|
||||
assert_eq!(style.scrollbar_rounding, 40.0);
|
||||
assert_eq!(style.grab_min_size, 42.0);
|
||||
assert_eq!(style.grab_rounding, 44.0);
|
||||
assert_eq!(style.log_slider_deadzone, 58.0);
|
||||
assert_eq!(style.tab_rounding, 46.0);
|
||||
assert_eq!(style.display_window_padding, [48.0, 50.0]);
|
||||
assert_eq!(style.display_safe_area_padding, [52.0, 54.0]);
|
||||
assert_eq!(style.mouse_cursor_scale, 56.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_color_indexing() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx();
|
||||
let style = ctx.style_mut();
|
||||
let value = [0.1, 0.2, 0.3, 1.0];
|
||||
style[StyleColor::Tab] = value;
|
||||
assert_eq!(style[StyleColor::Tab], value);
|
||||
assert_eq!(style.colors[StyleColor::Tab as usize], value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn test_style_memory_layout() {
|
||||
use std::mem;
|
||||
assert_eq!(mem::size_of::<Style>(), mem::size_of::<sys::ImGuiStyle>());
|
||||
assert_eq!(mem::align_of::<Style>(), mem::align_of::<sys::ImGuiStyle>());
|
||||
use sys::ImGuiStyle;
|
||||
macro_rules! assert_field_offset {
|
||||
($l:ident, $r:ident) => {
|
||||
assert_eq!(
|
||||
memoffset::offset_of!(Style, $l),
|
||||
memoffset::offset_of!(ImGuiStyle, $r)
|
||||
);
|
||||
};
|
||||
}
|
||||
assert_field_offset!(alpha, Alpha);
|
||||
assert_field_offset!(window_padding, WindowPadding);
|
||||
assert_field_offset!(window_rounding, WindowRounding);
|
||||
assert_field_offset!(window_border_size, WindowBorderSize);
|
||||
assert_field_offset!(window_min_size, WindowMinSize);
|
||||
assert_field_offset!(window_title_align, WindowTitleAlign);
|
||||
assert_field_offset!(window_menu_button_position, WindowMenuButtonPosition);
|
||||
assert_field_offset!(child_rounding, ChildRounding);
|
||||
assert_field_offset!(child_border_size, ChildBorderSize);
|
||||
assert_field_offset!(popup_rounding, PopupRounding);
|
||||
assert_field_offset!(popup_border_size, PopupBorderSize);
|
||||
assert_field_offset!(frame_padding, FramePadding);
|
||||
assert_field_offset!(frame_rounding, FrameRounding);
|
||||
assert_field_offset!(frame_border_size, FrameBorderSize);
|
||||
assert_field_offset!(item_spacing, ItemSpacing);
|
||||
assert_field_offset!(item_inner_spacing, ItemInnerSpacing);
|
||||
assert_field_offset!(touch_extra_padding, TouchExtraPadding);
|
||||
assert_field_offset!(indent_spacing, IndentSpacing);
|
||||
assert_field_offset!(columns_min_spacing, ColumnsMinSpacing);
|
||||
assert_field_offset!(scrollbar_size, ScrollbarSize);
|
||||
assert_field_offset!(scrollbar_rounding, ScrollbarRounding);
|
||||
assert_field_offset!(grab_min_size, GrabMinSize);
|
||||
assert_field_offset!(grab_rounding, GrabRounding);
|
||||
assert_field_offset!(log_slider_deadzone, LogSliderDeadzone);
|
||||
assert_field_offset!(tab_rounding, TabRounding);
|
||||
assert_field_offset!(tab_border_size, TabBorderSize);
|
||||
assert_field_offset!(tab_min_width_for_close_button, TabMinWidthForCloseButton);
|
||||
assert_field_offset!(color_button_position, ColorButtonPosition);
|
||||
assert_field_offset!(button_text_align, ButtonTextAlign);
|
||||
assert_field_offset!(selectable_text_align, SelectableTextAlign);
|
||||
assert_field_offset!(display_window_padding, DisplayWindowPadding);
|
||||
assert_field_offset!(display_safe_area_padding, DisplaySafeAreaPadding);
|
||||
assert_field_offset!(mouse_cursor_scale, MouseCursorScale);
|
||||
assert_field_offset!(anti_aliased_lines, AntiAliasedLines);
|
||||
assert_field_offset!(anti_aliased_lines_use_tex, AntiAliasedLinesUseTex);
|
||||
assert_field_offset!(anti_aliased_fill, AntiAliasedFill);
|
||||
assert_field_offset!(curve_tessellation_tol, CurveTessellationTol);
|
||||
assert_field_offset!(circle_tesselation_max_error, CircleTessellationMaxError);
|
||||
assert_field_offset!(colors, Colors);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_color_variants() {
|
||||
for (idx, &value) in StyleColor::VARIANTS.iter().enumerate() {
|
||||
assert_eq!(idx, value as usize);
|
||||
}
|
||||
}
|
879
plugins/libimhex-rust/imgui-rs/src/tables.rs
Normal file
879
plugins/libimhex-rust/imgui-rs/src/tables.rs
Normal file
@ -0,0 +1,879 @@
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::sys;
|
||||
use crate::{Id, ImColor32, Ui};
|
||||
|
||||
bitflags! {
|
||||
/// Flags passed to `begin_table` methods.
|
||||
///
|
||||
/// Important! Sizing policies have complex and subtle side effects, more so than you would expect.
|
||||
/// Read comments/demos carefully + experiment with live demos to get acquainted with them.
|
||||
/// - The DEFAULT sizing policies are:
|
||||
/// - Default to [SizingFixedFit] if [ScrollX] is on, or if host window has (WindowFlags::AlwaysAutoResize)[crate::WindowFlags::AlwaysAutoResize].
|
||||
/// - Default to [SizingStretchSame] if [ScrollX] is off.
|
||||
/// - When [ScrollX] is off:
|
||||
/// - Table defaults to [SizingStretchSame] -> all Columns defaults to [TableColumnFlags::WidthStretch] with same weight.
|
||||
/// - Columns sizing policy allowed: [Stretch] (default), [Fixed]/Auto.
|
||||
/// - [Fixed] Columns will generally obtain their requested width (unless the table cannot fit them all).
|
||||
/// - [Stretch] Columns will share the remaining width.
|
||||
/// - Mixed [Fixed]/[Stretch] columns is possible but has various side-effects on resizing behaviors.
|
||||
/// The typical use of mixing sizing policies is: any number of LEADING [Fixed] columns, followed by one or two TRAILING [Stretch] columns.
|
||||
/// (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).
|
||||
/// - When [ScrollX] is on:
|
||||
/// - Table defaults to [SizingFixedFit] -> all Columns defaults to [TableColumnFlags::WidthFixed]
|
||||
/// - Columns sizing policy allowed: [Fixed]/Auto mostly.
|
||||
/// - [Fixed] Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed.
|
||||
/// - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop.
|
||||
/// - Using [Stretch] columns OFTEN DOES NOT MAKE SENSE if [ScrollX] is on, UNLESS you have specified a value for `inner_width` in BeginTable().
|
||||
/// If you specify a value for `inner_width` then effectively the scrolling space is known and [Stretch] or mixed [Fixed]/[Stretch] columns become meaningful again.
|
||||
/// - Read on documentation at the top of imgui_tables.cpp for more details.
|
||||
#[repr(transparent)]
|
||||
pub struct TableFlags: u32 {
|
||||
// Features
|
||||
|
||||
/// Enable resizing columns.
|
||||
const RESIZABLE = sys::ImGuiTableFlags_Resizable;
|
||||
/// Enable reordering columns in header row, though you must set up a header row
|
||||
/// with `begin_table_header` or `table_setup_column`.
|
||||
const REORDERABLE =sys::ImGuiTableFlags_Reorderable;
|
||||
/// Enable hiding/disabling columns in context menu.
|
||||
const HIDEABLE = sys::ImGuiTableFlags_Hideable;
|
||||
/// Enable sorting. See `table_get_sort_specs` to object sort specs. Also see [SortMulti]
|
||||
/// and [SortTristate].
|
||||
const SORTABLE = sys::ImGuiTableFlags_Sortable;
|
||||
/// Disable persisting columns order, width, and sort settings in the .ini file.
|
||||
const NO_SAVED_SETTINGS = sys::ImGuiTableFlags_NoSavedSettings;
|
||||
/// Right-click on columns body/contents will display table context menu.
|
||||
/// By default you can only right click in a headers row.
|
||||
const CONTEXT_MENU_IN_BODY = sys::ImGuiTableFlags_ContextMenuInBody;
|
||||
|
||||
// Decorations
|
||||
|
||||
/// Set each RowBg color with [table_row_bg] or [table_row_bg_alt] (equivalent of calling
|
||||
/// `table_set_bg_color` with `ROW_BG0` on each row manually)
|
||||
const ROW_BG = sys::ImGuiTableFlags_RowBg;
|
||||
/// Draw horizontal borders between rows.
|
||||
const BORDERS_INNER_H = sys::ImGuiTableFlags_BordersInnerH;
|
||||
/// Draw horizontal borders at the top and bottom.
|
||||
const BORDERS_OUTER_H = sys::ImGuiTableFlags_BordersOuterH;
|
||||
/// Draw vertical borders between columns.
|
||||
const BORDERS_INNER_V = sys::ImGuiTableFlags_BordersInnerV;
|
||||
/// Draw vertical borders on the left and right sides.
|
||||
const BORDERS_OUTER_V = sys::ImGuiTableFlags_BordersOuterV;
|
||||
/// Draw all horizontal borders (this is just [BORDERS_INNER_H] | [BORDERS_OUTER_H]).
|
||||
const BORDERS_H = sys::ImGuiTableFlags_BordersH;
|
||||
/// Draw all vertical borders (this is just [BORDERS_INNER_V] | [BORDERS_OUTER_V]).
|
||||
const BORDERS_V = sys::ImGuiTableFlags_BordersV;
|
||||
/// Draw all inner borders (this is just [BORDERS_INNER_H] | [BORDERS_INNER_V]).
|
||||
const BORDERS_INNER = sys::ImGuiTableFlags_BordersInner;
|
||||
/// Draw all outer borders (this is just [BORDERS_OUTER_H] | [BORDERS_OUTER_V]).
|
||||
const BORDERS_OUTER = sys::ImGuiTableFlags_BordersOuter;
|
||||
/// Draw all borders (this is just [BORDERS_INNER] | [BORDERS_OUTER]).
|
||||
const BORDERS = sys::ImGuiTableFlags_Borders;
|
||||
/// **ALPHA** Disable vertical borders in columns Body (borders will always appears in Headers).
|
||||
/// May move to Style
|
||||
const NO_BORDERS_IN_BODY = sys::ImGuiTableFlags_NoBordersInBody;
|
||||
/// **ALPHA** Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers).
|
||||
/// May move to style
|
||||
const NO_BORDERS_IN_BODY_UNTIL_RESIZE = sys::ImGuiTableFlags_NoBordersInBodyUntilResize;
|
||||
|
||||
// Sizing Policy (read above for defaults)
|
||||
|
||||
/// Columns default to [WidthFixed] or [WidthAuto] (if resizable or not resizable),
|
||||
/// matching contents width.
|
||||
const SIZING_FIXED_FIT = sys::ImGuiTableFlags_SizingFixedFit;
|
||||
/// Columns default to [WidthFixed] or [WidthAuto] (if resizable or not resizable),
|
||||
/// matching the maximum contents width of all columns.
|
||||
/// Implicitly enable [NoKeepColumnsVisible].
|
||||
const SIZING_FIXED_SAME = sys::ImGuiTableFlags_SizingFixedSame;
|
||||
/// Columns default to [WidthStretch] with default weights proportional to each columns
|
||||
/// contents widths.
|
||||
const SIZING_STRETCH_PROP = sys::ImGuiTableFlags_SizingStretchProp;
|
||||
/// Columns default to [WidthStretch] with default weights all equal, unless overridden by
|
||||
/// a column's `TableHeader`.
|
||||
const SIZING_STRETCH_SAME = sys::ImGuiTableFlags_SizingStretchSame;
|
||||
|
||||
// Sizing Extra Options
|
||||
|
||||
/// Make outer width auto-fit to columns, overriding outer_size.x value. Only available when
|
||||
/// [ScrollX]/[ScrollY] are disabled and [Stretch] columns are not used.
|
||||
const NO_HOST_EXTEND_X = sys::ImGuiTableFlags_NoHostExtendX;
|
||||
/// Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).
|
||||
/// Only available when [ScrollX]/[ScrollY] are disabled.
|
||||
/// Data below the limit will be clipped and not visible.
|
||||
const NO_HOST_EXTEND_Y = sys::ImGuiTableFlags_NoHostExtendY;
|
||||
/// Disable keeping column always minimally visible when [ScrollX] is off and table
|
||||
/// gets too small. Not recommended if columns are resizable.
|
||||
const NO_KEEP_COLUMNS_VISIBLE = sys::ImGuiTableFlags_NoKeepColumnsVisible;
|
||||
/// Disable distributing remainder width to stretched columns (width allocation on a 100-wide
|
||||
/// table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33).
|
||||
/// With larger number of columns, resizing will appear to be less smooth.
|
||||
const PRECISE_WIDTHS = sys::ImGuiTableFlags_PreciseWidths;
|
||||
|
||||
// Clipping
|
||||
|
||||
/// Disable clipping rectangle for every individual columns (reduce draw command count, items will
|
||||
/// be able to overflow into other columns). Generally incompatible with [table_setup_scroll_freeze].
|
||||
const NO_CLIP = sys::ImGuiTableFlags_NoClip;
|
||||
|
||||
// Padding
|
||||
|
||||
/// Default if [BordersOuterV] is on. Enable outer-most padding. Generally desirable if you have headers.
|
||||
const PAD_OUTER_X = sys::ImGuiTableFlags_PadOuterX;
|
||||
/// Default if [BordersOuterV] is off. Disable outer-most padding.
|
||||
const NO_PAD_OUTER_X = sys::ImGuiTableFlags_NoPadOuterX;
|
||||
/// Disable inner padding between columns (double inner padding if [BordersOuterV] is on, single
|
||||
/// inner padding if BordersOuterV is off).
|
||||
const NO_PAD_INNER_X = sys::ImGuiTableFlags_NoPadInnerX;
|
||||
|
||||
// Scrolling
|
||||
|
||||
/// Enable horizontal scrolling. Require 'outer_size' parameter of [begin_table] to specify the
|
||||
/// container size. Changes default sizing policy. Because this create a child window,
|
||||
/// [ScrollY] is currently generally recommended when using [ScrollX].
|
||||
const SCROLL_X = sys::ImGuiTableFlags_ScrollX;
|
||||
/// Enable vertical scrolling. Require 'outer_size' parameter of [begin_table] to specify the
|
||||
/// container size.
|
||||
const SCROLL_Y = sys::ImGuiTableFlags_ScrollY;
|
||||
|
||||
// Sorting
|
||||
|
||||
/// Hold shift when clicking headers to sort on multiple column. [table_get_sort_specs] may return specs where `[spec_count] > 1`.
|
||||
const SORT_MULTI = sys::ImGuiTableFlags_SortMulti;
|
||||
/// Allow no sorting, disable default sorting. `table_get_sort_specs` may return specs where `[specs_count] == 0`.
|
||||
const SORT_TRISTATE = sys::ImGuiTableFlags_SortTristate;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for [table_next_row_with_flags].
|
||||
#[repr(transparent)]
|
||||
pub struct TableRowFlags: u32 {
|
||||
/// Identify header row (set default background color + width of its contents
|
||||
/// accounted different for auto column width)
|
||||
const HEADERS = sys::ImGuiTableRowFlags_Headers;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for [TableColumnSetup] and [table_setup_column_with].
|
||||
#[repr(transparent)]
|
||||
#[derive(Default)]
|
||||
pub struct TableColumnFlags: u32 {
|
||||
// Input configuration flags
|
||||
|
||||
/// Default as a hidden/disabled column.
|
||||
const DEFAULT_HIDE = sys::ImGuiTableColumnFlags_DefaultHide;
|
||||
/// Default as a sorting column.
|
||||
const DEFAULT_SORT = sys::ImGuiTableColumnFlags_DefaultSort;
|
||||
/// Column will stretch. Preferable with horizontal scrolling disabled (default
|
||||
/// if table sizing policy is [ImGuiTableFlags::SizingStretchSame] or
|
||||
/// [ImGuiTableFlags::SizingStretchProp]).
|
||||
const WIDTH_STRETCH = sys::ImGuiTableColumnFlags_WidthStretch;
|
||||
/// Column will not stretch. Preferable with horizontal scrolling enabled (default
|
||||
/// if table sizing policy is [ImGuiTableFlags::SizingFixedFit] and table is resizable).
|
||||
const WIDTH_FIXED = sys::ImGuiTableColumnFlags_WidthFixed;
|
||||
/// Disable manual resizing.
|
||||
const NO_RESIZE = sys::ImGuiTableColumnFlags_NoResize;
|
||||
/// Disable manual reordering this column, this will also prevent other columns from
|
||||
/// crossing over this column.
|
||||
const NO_REORDER = sys::ImGuiTableColumnFlags_NoReorder;
|
||||
/// Disable ability to hide/disable this column.
|
||||
const NO_HIDE = sys::ImGuiTableColumnFlags_NoHide;
|
||||
/// Disable clipping for this column (all [NO_CLIP] columns will render in a same
|
||||
/// draw command).
|
||||
const NO_CLIP = sys::ImGuiTableColumnFlags_NoClip;
|
||||
/// Disable ability to sort on this field (even if [ImGuiTableFlags::Sortable] is
|
||||
/// set on the table).
|
||||
const NO_SORT = sys::ImGuiTableColumnFlags_NoSort;
|
||||
/// Disable ability to sort in the ascending direction.
|
||||
const NO_SORT_ASCENDING = sys::ImGuiTableColumnFlags_NoSortAscending;
|
||||
/// Disable ability to sort in the descending direction.
|
||||
const NO_SORT_DESCENDING = sys::ImGuiTableColumnFlags_NoSortDescending;
|
||||
/// Disable header text width contribution to automatic column width.
|
||||
const NO_HEADER_WIDTH = sys::ImGuiTableColumnFlags_NoHeaderWidth;
|
||||
/// Make the initial sort direction Ascending when first sorting on this column (default).
|
||||
const PREFER_SORT_ASCENDING = sys::ImGuiTableColumnFlags_PreferSortAscending;
|
||||
/// Make the initial sort direction Descending when first sorting on this column.
|
||||
const PREFER_SORT_DESCENDING = sys::ImGuiTableColumnFlags_PreferSortDescending;
|
||||
/// Use current Indent value when entering cell (default for column 0).
|
||||
const INDENT_ENABLE = sys::ImGuiTableColumnFlags_IndentEnable;
|
||||
/// Ignore current Indent value when entering cell (default for columns > 0).
|
||||
/// Indentation changes _within_ the cell will still be honored.
|
||||
const INDENT_DISABLE = sys::ImGuiTableColumnFlags_IndentDisable;
|
||||
|
||||
// Output status flags, read-only via [table_get_column_flags]
|
||||
|
||||
/// Status: is enabled == not hidden by user/api (referred to as "Hide" in
|
||||
/// [DefaultHide] and [NoHide]) flags.
|
||||
const IS_ENABLED = sys::ImGuiTableColumnFlags_IsEnabled;
|
||||
/// Status: is visible == is enabled AND not clipped by scrolling.
|
||||
const IS_VISIBLE = sys::ImGuiTableColumnFlags_IsVisible;
|
||||
/// Status: is currently part of the sort specs
|
||||
const IS_SORTED = sys::ImGuiTableColumnFlags_IsSorted;
|
||||
/// Status: is hovered by mouse
|
||||
const IS_HOVERED = sys::ImGuiTableColumnFlags_IsHovered;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Enum for [table_set_bg_color].
|
||||
/// Background colors are rendering in 3 layers:
|
||||
/// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
|
||||
/// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
|
||||
/// - Layer 2: draw with CellBg color if set.
|
||||
/// The purpose of the two row/columns layers is to let you decide if a background color
|
||||
/// changes should override or blend with the existing color.
|
||||
/// When using [TableFlags::RowBg] on the table, each row has the RowBg0 color automatically
|
||||
/// set for odd/even rows.
|
||||
/// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
|
||||
/// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
|
||||
#[repr(transparent)]
|
||||
pub struct TableBgTarget: u32 {
|
||||
/// Set row background color 0 (generally used for background, automatically set when
|
||||
/// [TableFlags::RowBg] is used)
|
||||
const ROW_BG0 = sys::ImGuiTableBgTarget_RowBg0;
|
||||
/// Set row background color 1 (generally used for selection marking)
|
||||
const ROW_BG1 = sys::ImGuiTableBgTarget_RowBg1;
|
||||
/// Set cell background color (top-most color)
|
||||
const CELL_BG = sys::ImGuiTableBgTarget_CellBg;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum TableSortDirection {
|
||||
Ascending,
|
||||
Descending,
|
||||
}
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Begins a table with no flags and with standard sizing contraints.
|
||||
///
|
||||
/// This does no work on styling the headers (the top row) -- see either
|
||||
/// [begin_table_header](Self::begin_table_header) or the more complex
|
||||
/// [table_setup_column](Self::table_setup_column).
|
||||
///
|
||||
/// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
|
||||
/// If this makes a difference to you, you are probably trying to make too many columns.
|
||||
#[must_use = "if return is dropped immediately, table is ended immediately."]
|
||||
pub fn begin_table(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_count: usize,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
self.begin_table_with_flags(str_id, column_count, TableFlags::empty())
|
||||
}
|
||||
|
||||
/// Begins a table with flags and standard sizing contraints.
|
||||
///
|
||||
/// This does no work on styling the headers (the top row) -- see either
|
||||
/// [begin_table_header](Self::begin_table_header) or the more complex
|
||||
/// [table_setup_column](Self::table_setup_column).
|
||||
///
|
||||
/// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
|
||||
/// If this makes a difference to you, you are probably trying to make too many columns.
|
||||
#[must_use = "if return is dropped immediately, table is ended immediately."]
|
||||
pub fn begin_table_with_flags(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_count: usize,
|
||||
flags: TableFlags,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
self.begin_table_with_sizing(str_id, column_count, flags, [0.0, 0.0], 0.0)
|
||||
}
|
||||
|
||||
/// Begins a table with all flags and sizing contraints. This is the base method,
|
||||
/// and gives users the most flexibility.
|
||||
///
|
||||
/// This does no work on styling the headers (the top row) -- see either
|
||||
/// [begin_table_header](Self::begin_table_header) or the more complex
|
||||
/// [table_setup_column](Self::table_setup_column).
|
||||
///
|
||||
/// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
|
||||
/// If this makes a difference to you, you are probably trying to make too many columns.
|
||||
#[must_use = "if return is dropped immediately, table is ended immediately."]
|
||||
pub fn begin_table_with_sizing(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column: usize,
|
||||
flags: TableFlags,
|
||||
outer_size: [f32; 2],
|
||||
inner_width: f32,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginTable(
|
||||
self.scratch_txt(str_id),
|
||||
column as i32,
|
||||
flags.bits() as i32,
|
||||
outer_size.into(),
|
||||
inner_width,
|
||||
)
|
||||
};
|
||||
|
||||
// todo: once msrv is 1.54, convert this to .then(||)
|
||||
if should_render {
|
||||
Some(TableToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Begins a table with no flags and with standard sizing contraints.
|
||||
///
|
||||
/// Takes an array of table header information, the length of which determines
|
||||
/// how many columns will be created.
|
||||
#[cfg(feature = "min-const-generics")]
|
||||
#[must_use = "if return is dropped immediately, table is ended immediately."]
|
||||
pub fn begin_table_header<'a, Name: AsRef<str>, const N: usize>(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_data: [TableColumnSetup<'a, Name>; N],
|
||||
) -> Option<TableToken<'ui>> {
|
||||
self.begin_table_header_with_flags(str_id, column_data, TableFlags::empty())
|
||||
}
|
||||
|
||||
/// Begins a table with flags and standard sizing contraints.
|
||||
///
|
||||
/// Takes an array of table header information, the length of which determines
|
||||
/// how many columns will be created.
|
||||
#[cfg(feature = "min-const-generics")]
|
||||
#[must_use = "if return is dropped immediately, table is ended immediately."]
|
||||
pub fn begin_table_header_with_flags<'a, Name: AsRef<str>, const N: usize>(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_data: [TableColumnSetup<'a, Name>; N],
|
||||
flags: TableFlags,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
self.begin_table_header_with_sizing(str_id, column_data, flags, [0.0, 0.0], 0.0)
|
||||
}
|
||||
|
||||
/// Begins a table with all flags and sizing contraints. This is the base method,
|
||||
/// and gives users the most flexibility.
|
||||
/// Takes an array of table header information, the length of which determines
|
||||
/// how many columns will be created.
|
||||
#[cfg(feature = "min-const-generics")]
|
||||
#[must_use = "if return is dropped immediately, table is ended immediately."]
|
||||
pub fn begin_table_header_with_sizing<'a, Name: AsRef<str>, const N: usize>(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_data: [TableColumnSetup<'a, Name>; N],
|
||||
flags: TableFlags,
|
||||
outer_size: [f32; 2],
|
||||
inner_width: f32,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
self.begin_table_with_sizing(str_id, N, flags, outer_size, inner_width)
|
||||
.map(|data| {
|
||||
for value in column_data {
|
||||
self.table_setup_column_with(value);
|
||||
}
|
||||
self.table_headers_row();
|
||||
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/// Moves a table to the next row (ie, down) with no flags,
|
||||
/// and with the next row having a standard computed height.
|
||||
///
|
||||
/// If your table was made with [begin_table], this **must** be called
|
||||
/// before rendering any cells (along with [table_next_column]).
|
||||
/// If your table was made with [begin_table_header], this does not need to be called,
|
||||
/// though [table_next_column] still should be.
|
||||
///
|
||||
/// [begin_table]: Self::begin_table
|
||||
/// [begin_table_header]: Self::begin_table_header
|
||||
/// [table_next_column]: Self::table_next_column
|
||||
#[inline]
|
||||
pub fn table_next_row(&self) {
|
||||
self.table_next_row_with_flags(TableRowFlags::empty());
|
||||
}
|
||||
|
||||
/// Moves a table to the next row (ie, down), with the given flags,
|
||||
/// and with the next row having a standard computed height.
|
||||
///
|
||||
/// Setting a flag here will make the next row a "header" now, which may
|
||||
/// require setup of column data.
|
||||
///
|
||||
/// See [table_next_row](Self::table_next_row) for information on how moving rows work. To set the row
|
||||
/// with a given height, see [table_next_row_with_height](Self::table_next_row_with_height).
|
||||
#[inline]
|
||||
pub fn table_next_row_with_flags(&self, flags: TableRowFlags) {
|
||||
self.table_next_row_with_height(flags, 0.0);
|
||||
}
|
||||
|
||||
/// Moves a table to the next row (ie, down), with the given flags,
|
||||
/// and with the given minimum height.
|
||||
///
|
||||
/// See [table_next_row](Self::table_next_row) for information on how moving rows work.
|
||||
#[inline]
|
||||
pub fn table_next_row_with_height(&self, flags: TableRowFlags, min_row_height: f32) {
|
||||
unsafe {
|
||||
sys::igTableNextRow(flags.bits() as i32, min_row_height);
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves onto the next column. If at `column_count`, this will move to the next row.
|
||||
/// In this way, you can use this function as an iterator over each cell in the table.
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # let mut ctx = imgui::Context::create();
|
||||
/// # { let ui = ctx.frame();
|
||||
/// if let Some(_t) = ui.begin_table("Basic-Table", 2) {
|
||||
/// // we have to call next_row because we didn't make headers..
|
||||
/// ui.table_next_row();
|
||||
///
|
||||
/// // you always have to call this to start...
|
||||
/// // take advantage of this in loops!
|
||||
/// ui.table_next_column();
|
||||
/// ui.text("x: 0, y: 0");
|
||||
///
|
||||
/// ui.table_next_column();
|
||||
/// ui.text("x: 1, y: 0");
|
||||
///
|
||||
/// // notice that we go down a row here too.
|
||||
/// ui.table_next_column();
|
||||
/// ui.text("x: 0, y: 1");
|
||||
///
|
||||
/// ui.table_next_column();
|
||||
/// ui.text("x: 1, y: 1");
|
||||
/// }
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// This functions returns true if the given column is **visible.** It is not
|
||||
/// marked as must use, as you can still render commands into the not-visible column,
|
||||
/// though you can choose to not as an optimization.
|
||||
pub fn table_next_column(&self) -> bool {
|
||||
unsafe { sys::igTableNextColumn() }
|
||||
}
|
||||
|
||||
/// Moves onto the given column.
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # let mut ctx = imgui::Context::create();
|
||||
/// # { let ui = ctx.frame();
|
||||
/// if let Some(_t) = ui.begin_table("Basic-Table", 2) {
|
||||
/// // we have to call next_row because we didn't make headers..
|
||||
/// ui.table_next_row();
|
||||
///
|
||||
/// for i in 0..2 {
|
||||
/// ui.table_set_column_index(i);
|
||||
/// ui.text(format!("x: {}", i));
|
||||
/// }
|
||||
///
|
||||
/// // oops I just remembered, i need to add something on idx 0!
|
||||
/// ui.table_set_column_index(0);
|
||||
/// // if i uncomment this line, we'll write on top of our previous "x: 0"
|
||||
/// // line:
|
||||
/// // ui.text("hello from the future on top of the past");
|
||||
/// // so we do a .newline();
|
||||
/// ui.new_line();
|
||||
/// ui.text("hello from the future");
|
||||
///
|
||||
/// // imgui will understand this and row spacing will be adjusted automatically.
|
||||
/// }
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// This functions returns true if the given column is **visible.** It is not
|
||||
/// marked as must use, as you can still render commands into the not-visible column,
|
||||
/// though you can choose to not as an optimization.
|
||||
///
|
||||
/// # Panics
|
||||
/// If `column_index >= ui.table_columm_count`, this function will panic. In `debug` releases,
|
||||
/// we will panic on the Rust side, for a nicer error message, though in release, we will
|
||||
/// panic in C++, which will result in an ugly stack overflow.
|
||||
pub fn table_set_column_index(&self, column_index: usize) -> bool {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let size = self.table_column_count() as usize;
|
||||
if column_index >= size {
|
||||
panic!(
|
||||
"column_index >= self.table_get_column_count().\
|
||||
Requested {}, but only have {} columns.",
|
||||
column_index, size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { sys::igTableSetColumnIndex(column_index as i32) }
|
||||
}
|
||||
|
||||
/// Specify label per column, with no flags and default sizing. You can avoid calling
|
||||
/// this method entirely by using [begin_table_header](Self::begin_table_header).
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # let mut ctx = imgui::Context::create();
|
||||
/// # { let ui = ctx.frame();
|
||||
/// if let Some(_t) = ui.begin_table("My Table", 2) {
|
||||
/// ui.table_setup_column("One");
|
||||
/// ui.table_setup_column("Two");
|
||||
/// ui.table_setup_column("Three");
|
||||
/// ui.table_headers_row();
|
||||
///
|
||||
/// // call next_column/set_column_index and proceed like normal.
|
||||
/// // the above code is the equivalent of just using `begin_table_header`
|
||||
/// // but does allow for some columns to have headers and others to not
|
||||
/// }
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// Along with [table_headers_row](Self::table_headers_row), this method is used to create a header
|
||||
/// row and automatically submit a table header for each column.
|
||||
/// Headers are required to perform: reordering, sorting, and opening the context menu (though,
|
||||
/// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
|
||||
pub fn table_setup_column(&self, str_id: impl AsRef<str>) {
|
||||
self.table_setup_column_with(TableColumnSetup::new(str_id))
|
||||
}
|
||||
|
||||
/// Specify label per column, with data given in [TableColumnSetup]. You can avoid calling
|
||||
/// this method entirely by using [begin_table_header](Self::begin_table_header).
|
||||
///
|
||||
/// See [table_setup_column](Self::table_setup_column) for an example of how to setup columns
|
||||
/// yourself.
|
||||
///
|
||||
/// Along with [table_headers_row](Self::table_headers_row), this method is used to create a header
|
||||
/// row and automatically submit a table header for each column.
|
||||
/// Headers are required to perform: reordering, sorting, and opening the context menu (though,
|
||||
/// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
|
||||
pub fn table_setup_column_with<N: AsRef<str>>(&self, data: TableColumnSetup<'_, N>) {
|
||||
unsafe {
|
||||
sys::igTableSetupColumn(
|
||||
self.scratch_txt(data.name),
|
||||
data.flags.bits() as i32,
|
||||
data.init_width_or_weight,
|
||||
data.user_id.as_imgui_id(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Locks columns/rows so they stay visible when scrolled. Generally, you will be calling this
|
||||
/// so that the header column is always visible (though go wild if you want). You can avoid
|
||||
/// calling this entirely by passing `true` to [begin_table_header](Self::begin_table_header).
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # let mut ctx = imgui::Context::create();
|
||||
/// # { let ui = ctx.frame();
|
||||
/// const COLUMN_COUNT: usize = 3;
|
||||
/// if let Some(_t) = ui.begin_table("scroll-freeze-example", COLUMN_COUNT) {
|
||||
/// // locks the header row. Notice how we need to call it BEFORE `table_headers_row`.
|
||||
/// ui.table_setup_scroll_freeze(1, COLUMN_COUNT);
|
||||
/// ui.table_setup_column("One");
|
||||
/// ui.table_setup_column("Two");
|
||||
/// ui.table_setup_column("Three");
|
||||
/// ui.table_headers_row();
|
||||
/// }
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// Nb: we take `locked_columns` and `locked_rows` as a `usize`, but it will be converted
|
||||
/// with `as i32` to an i32. If this makes a difference to you, you are probably
|
||||
/// trying to make too many columns.
|
||||
pub fn table_setup_scroll_freeze(&self, locked_columns: usize, locked_rows: usize) {
|
||||
unsafe {
|
||||
sys::igTableSetupScrollFreeze(locked_columns as i32, locked_rows as i32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Along with [table_setup_column](Self::table_setup_column), this method is used
|
||||
/// to create a header row and automatically submit a table header for each column.
|
||||
///
|
||||
/// For an example of using this method, see [table_setup_column](Self::table_setup_column).
|
||||
///
|
||||
/// Headers are required to perform: reordering, sorting, and opening the context menu (though,
|
||||
/// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
|
||||
///
|
||||
/// You may manually submit headers using [table_next_column] + [table_header] calls, but this is
|
||||
/// only useful in some advanced use cases (e.g. adding custom widgets in header row).
|
||||
/// See [table_header](Self::table_header) for more information.
|
||||
///
|
||||
/// [table_next_column]: Self::table_next_column
|
||||
/// [table_header]: Self::table_header
|
||||
pub fn table_headers_row(&self) {
|
||||
unsafe {
|
||||
sys::igTableHeadersRow();
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this function to manually declare a column cell to be a header.
|
||||
///
|
||||
/// You generally should avoid using this outside of specific cases,
|
||||
/// such as custom widgets. Instead, use [table_headers_row](Self::table_headers_row)
|
||||
/// and [table_setup_column](Self::table_setup_column).
|
||||
pub fn table_header(&self, label: impl AsRef<str>) {
|
||||
unsafe {
|
||||
sys::igTableHeader(self.scratch_txt(label));
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the numbers of columns in the current table.
|
||||
pub fn table_column_count(&self) -> usize {
|
||||
unsafe { sys::igTableGetColumnCount() as usize }
|
||||
}
|
||||
|
||||
/// Gets the current column index in the current table.
|
||||
pub fn table_column_index(&self) -> usize {
|
||||
unsafe { sys::igTableGetColumnIndex() as usize }
|
||||
}
|
||||
|
||||
/// Gets the current row index in the current table.
|
||||
pub fn table_row_index(&self) -> usize {
|
||||
unsafe { sys::igTableGetRowIndex() as usize }
|
||||
}
|
||||
|
||||
/// Gets the name of the current column. If there is no currently bound name
|
||||
/// for this column, we will return an empty string.
|
||||
///
|
||||
/// Use [table_column_name_with_column](Self::table_column_name_with_column)
|
||||
/// for arbitrary indices.
|
||||
pub fn table_column_name(&mut self) -> &str {
|
||||
unsafe {
|
||||
// imgui uses utf8...though that is a continuous process there.
|
||||
CStr::from_ptr(sys::igTableGetColumnName(-1))
|
||||
.to_str()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the name of a given column. If there is no currently bound name
|
||||
/// for this column, we will return an empty string.
|
||||
///
|
||||
/// Use [table_column_name](Self::table_column_name) for the current column.
|
||||
pub fn table_column_name_with_column(&mut self, column: usize) -> &str {
|
||||
unsafe {
|
||||
// imgui uses utf8...though that is a continuous process there.
|
||||
CStr::from_ptr(sys::igTableGetColumnName(column as i32))
|
||||
.to_str()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the flags on the current column in the current table.
|
||||
pub fn table_column_flags(&self) -> TableColumnFlags {
|
||||
unsafe {
|
||||
TableColumnFlags::from_bits(sys::igTableGetColumnFlags(-1) as u32)
|
||||
.expect("bad column flags")
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the flags on the given column in the current table. To get the current column's
|
||||
/// flags without having to call [table_column_index](Self::table_column_index), use
|
||||
/// [table_column_flags](Self::table_column_flags).
|
||||
pub fn table_column_flags_with_column(&self, column_n: usize) -> TableColumnFlags {
|
||||
unsafe {
|
||||
TableColumnFlags::from_bits(sys::igTableGetColumnFlags(column_n as i32) as u32)
|
||||
.expect("bad column flags")
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the given background color for this column. See [TableBgTarget]
|
||||
/// for more information on how colors work for tables.
|
||||
///
|
||||
/// Use [table_set_bg_color_with_column](Self::table_set_bg_color_with_column) to set
|
||||
/// for arbitrary indices.
|
||||
pub fn table_set_bg_color(&self, target: TableBgTarget, color: impl Into<ImColor32>) {
|
||||
unsafe {
|
||||
sys::igTableSetBgColor(target.bits() as i32, color.into().into(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the given background color for any column. See [TableBgTarget]
|
||||
/// for more information on how colors work for tables.
|
||||
///
|
||||
/// Use [table_set_bg_color](Self::table_set_bg_color) for the current column.
|
||||
pub fn table_set_bg_color_with_column(
|
||||
&self,
|
||||
target: TableBgTarget,
|
||||
color: impl Into<ImColor32>,
|
||||
column_index: usize,
|
||||
) {
|
||||
unsafe {
|
||||
sys::igTableSetBgColor(
|
||||
target.bits() as i32,
|
||||
color.into().into(),
|
||||
column_index as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Change user accessible enabled/disabled state of the current column.
|
||||
///
|
||||
/// Set to false to hide the column. Users can use the context menu to change
|
||||
/// this themselves by right-clicking in headers, or right-clicking in columns body
|
||||
/// if [TableFlags::CONTEXT_MENU_IN_BODY].
|
||||
///
|
||||
/// Use [table_set_enabled_with_column](Self::table_set_enabled_with_column) to set
|
||||
/// for arbitrary indices.
|
||||
pub fn table_set_enabled(&self, enabled: bool) {
|
||||
unsafe { sys::igTableSetColumnEnabled(-1, enabled) }
|
||||
}
|
||||
|
||||
/// Change user accessible enabled/disabled state of the current column.
|
||||
///
|
||||
/// Set to false to hide the column. Users can use the context menu to change
|
||||
/// this themselves by right-clicking in headers, or right-clicking in columns body
|
||||
/// if [TableFlags::CONTEXT_MENU_IN_BODY].
|
||||
pub fn table_set_enabled_with_column(&self, enabled: bool, column_idx: usize) {
|
||||
unsafe { sys::igTableSetColumnEnabled(column_idx as i32, enabled) }
|
||||
}
|
||||
|
||||
/// Gets the sorting data for a table. This will be `None` when not sorting.
|
||||
///
|
||||
/// See the examples folder for how to use the sorting API.
|
||||
pub fn table_sort_specs_mut(&self) -> Option<TableSortSpecsMut<'_>> {
|
||||
unsafe {
|
||||
let value = sys::igTableGetSortSpecs();
|
||||
if value.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(TableSortSpecsMut(value, PhantomData))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct containing all the data needed to setup a table column header
|
||||
/// via [begin_table_header](Ui::begin_table_header) or [table_setup_column](Ui::table_setup_column).
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TableColumnSetup<'a, Name> {
|
||||
/// The name of column to be displayed to users.
|
||||
pub name: Name,
|
||||
/// The flags this column will have.
|
||||
pub flags: TableColumnFlags,
|
||||
/// The width or weight of the given column.
|
||||
pub init_width_or_weight: f32,
|
||||
/// A user_id, primarily used in sorting operations.
|
||||
pub user_id: Id<'a>,
|
||||
}
|
||||
|
||||
impl<'a, Name: AsRef<str>> TableColumnSetup<'a, Name> {
|
||||
pub fn new(name: Name) -> Self {
|
||||
Self {
|
||||
name,
|
||||
flags: TableColumnFlags::empty(),
|
||||
init_width_or_weight: 0.0,
|
||||
user_id: Id::Int(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around table sort specs.
|
||||
///
|
||||
/// To use this simply, use [conditional_sort] and provide a closure --
|
||||
/// if you should sort your data, then the closure will be ran and imgui
|
||||
/// will be informed that your data is sorted.
|
||||
///
|
||||
/// For manual control (such as if sorting can fail), use [should_sort] to
|
||||
/// check if you should sort your data, sort your data using [specs] for information
|
||||
/// on how to sort it, and then [set_sorted] to indicate that the data is sorted.
|
||||
///
|
||||
/// [conditional_sort]: Self::conditional_sort
|
||||
/// [should_sort]: Self::should_sort
|
||||
/// [specs]: Self::specs
|
||||
/// [set_sorted]: Self::set_sorted
|
||||
pub struct TableSortSpecsMut<'ui>(*mut sys::ImGuiTableSortSpecs, PhantomData<Ui<'ui>>);
|
||||
|
||||
impl TableSortSpecsMut<'_> {
|
||||
/// Gets the specs for a given sort. In most scenarios, this will be a slice of 1 entry.
|
||||
pub fn specs(&self) -> Specs<'_> {
|
||||
let value =
|
||||
unsafe { std::slice::from_raw_parts((*self.0).Specs, (*self.0).SpecsCount as usize) };
|
||||
|
||||
Specs(value)
|
||||
}
|
||||
|
||||
/// Returns true if the data should be sorted.
|
||||
pub fn should_sort(&self) -> bool {
|
||||
unsafe { (*self.0).SpecsDirty }
|
||||
}
|
||||
|
||||
/// Sets the internal flag that the data has been sorted.
|
||||
pub fn set_sorted(&mut self) {
|
||||
unsafe {
|
||||
(*self.0).SpecsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide a closure, which will receive the Specs for a sort.
|
||||
///
|
||||
/// If you should sort the data, the closure will run, and ImGui will be
|
||||
/// told that the data has been sorted.
|
||||
///
|
||||
/// If you need manual control over sorting, consider using [should_sort], [specs],
|
||||
/// and [set_sorted] youself.
|
||||
///
|
||||
/// [should_sort]: Self::should_sort
|
||||
/// [specs]: Self::specs
|
||||
/// [set_sorted]: Self::set_sorted
|
||||
pub fn conditional_sort(mut self, mut f: impl FnMut(Specs<'_>)) {
|
||||
let is_dirty = self.should_sort();
|
||||
|
||||
if is_dirty {
|
||||
f(self.specs());
|
||||
}
|
||||
|
||||
self.set_sorted();
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around a slice of [TableColumnSortSpecs].
|
||||
///
|
||||
/// This slice may be 0 if [TableFlags::SORT_TRISTATE] is true, may be > 1 is [TableFlags::SORT_MULTI] is true,
|
||||
/// but is generally == 1.
|
||||
///
|
||||
/// Consume this struct as an iterator.
|
||||
pub struct Specs<'a>(&'a [sys::ImGuiTableColumnSortSpecs]);
|
||||
|
||||
impl<'a> Specs<'a> {
|
||||
pub fn iter(self) -> impl Iterator<Item = TableColumnSortSpecs<'a>> {
|
||||
self.0.iter().map(|v| TableColumnSortSpecs(v))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TableColumnSortSpecs<'a>(&'a sys::ImGuiTableColumnSortSpecs);
|
||||
impl<'a> TableColumnSortSpecs<'a> {
|
||||
/// User id of the column (if specified by a TableSetupColumn() call)
|
||||
pub fn column_user_id(&self) -> sys::ImGuiID {
|
||||
self.0.ColumnUserID
|
||||
}
|
||||
|
||||
/// Index of the column
|
||||
pub fn column_idx(&self) -> usize {
|
||||
self.0.ColumnIndex as usize
|
||||
}
|
||||
|
||||
/// Index within parent [Specs] slice where this was found -- always stored in order starting
|
||||
/// from 0, tables sorted on a single criteria will always have a 0 here.
|
||||
///
|
||||
/// Generally, you don't need to access this, as it's the same as calling `specs.iter().enumerate()`.
|
||||
pub fn sort_order(&self) -> usize {
|
||||
self.0.SortOrder as usize
|
||||
}
|
||||
|
||||
/// Gets the sort direction for the given column. This will nearly always be `Some` if you
|
||||
/// can access it.
|
||||
pub fn sort_direction(&self) -> Option<TableSortDirection> {
|
||||
match self.0.SortDirection() {
|
||||
0 => None,
|
||||
1 => Some(TableSortDirection::Ascending),
|
||||
2 => Some(TableSortDirection::Descending),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a table which can be rendered onto, ending with `.end()`
|
||||
/// or by dropping.
|
||||
pub struct TableToken<'ui>;
|
||||
|
||||
/// Ends the table.
|
||||
drop { sys::igEndTable() }
|
||||
);
|
23
plugins/libimhex-rust/imgui-rs/src/test.rs
Normal file
23
plugins/libimhex-rust/imgui-rs/src/test.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
|
||||
use std::ptr;
|
||||
|
||||
use crate::context::Context;
|
||||
|
||||
pub static TEST_MUTEX: ReentrantMutex<()> = parking_lot::const_reentrant_mutex(());
|
||||
|
||||
pub fn test_ctx() -> (ReentrantMutexGuard<'static, ()>, Context) {
|
||||
let guard = TEST_MUTEX.lock();
|
||||
let mut ctx = Context::create();
|
||||
ctx.io_mut().ini_filename = ptr::null();
|
||||
(guard, ctx)
|
||||
}
|
||||
|
||||
pub fn test_ctx_initialized() -> (ReentrantMutexGuard<'static, ()>, Context) {
|
||||
let (guard, mut ctx) = test_ctx();
|
||||
let io = ctx.io_mut();
|
||||
io.display_size = [1024.0, 768.0];
|
||||
io.delta_time = 1.0 / 60.0;
|
||||
io.mouse_pos = [0.0, 0.0];
|
||||
ctx.fonts().build_rgba32_texture();
|
||||
(guard, ctx)
|
||||
}
|
43
plugins/libimhex-rust/imgui-rs/src/tokens.rs
Normal file
43
plugins/libimhex-rust/imgui-rs/src/tokens.rs
Normal file
@ -0,0 +1,43 @@
|
||||
#[macro_export]
|
||||
/// This is a macro used internally by imgui-rs to create StackTokens
|
||||
/// representing various global state in DearImGui.
|
||||
///
|
||||
/// These tokens can either be allowed to drop or dropped manually
|
||||
/// by called `end` on them. Preventing this token from dropping,
|
||||
/// or moving this token out of the block it was made in can have
|
||||
/// unintended side effects, including failed asserts in the DearImGui C++.
|
||||
///
|
||||
/// In general, if you're looking at this, don't overthink these -- just slap
|
||||
/// a '_token` as their binding name and allow them to drop.
|
||||
macro_rules! create_token {
|
||||
(
|
||||
$(#[$struct_meta:meta])*
|
||||
$v:vis struct $token_name:ident<'ui>;
|
||||
|
||||
$(#[$end_meta:meta])*
|
||||
drop { $on_drop:expr }
|
||||
) => {
|
||||
#[must_use]
|
||||
$(#[$struct_meta])*
|
||||
pub struct $token_name<'a>($crate::__core::marker::PhantomData<crate::Ui<'a>>);
|
||||
|
||||
impl<'a> $token_name<'a> {
|
||||
/// Creates a new token type.
|
||||
pub(crate) fn new(_: &crate::Ui<'a>) -> Self {
|
||||
Self(std::marker::PhantomData)
|
||||
}
|
||||
|
||||
$(#[$end_meta])*
|
||||
#[inline]
|
||||
pub fn end(self) {
|
||||
// left empty for drop
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for $token_name<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { $on_drop }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
176
plugins/libimhex-rust/imgui-rs/src/utils.rs
Normal file
176
plugins/libimhex-rust/imgui-rs/src/utils.rs
Normal file
@ -0,0 +1,176 @@
|
||||
#![allow(clippy::float_cmp)]
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::input::mouse::MouseButton;
|
||||
use crate::style::StyleColor;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
bitflags! {
|
||||
/// Item hover check option flags
|
||||
#[repr(transparent)]
|
||||
pub struct ItemHoveredFlags: u32 {
|
||||
/// Return true even if a popup window is blocking access to this item
|
||||
const ALLOW_WHEN_BLOCKED_BY_POPUP = sys::ImGuiHoveredFlags_AllowWhenBlockedByPopup;
|
||||
/// Return true even if an active item is blocking access to this item
|
||||
const ALLOW_WHEN_BLOCKED_BY_ACTIVE_ITEM = sys::ImGuiHoveredFlags_AllowWhenBlockedByActiveItem;
|
||||
/// Return true even if the position is obstructed or overlapped by another window
|
||||
const ALLOW_WHEN_OVERLAPPED = sys::ImGuiHoveredFlags_AllowWhenOverlapped;
|
||||
/// Return true even if the item is disabled
|
||||
const ALLOW_WHEN_DISABLED = sys::ImGuiHoveredFlags_AllowWhenDisabled;
|
||||
const RECT_ONLY = sys::ImGuiHoveredFlags_RectOnly;
|
||||
}
|
||||
}
|
||||
|
||||
/// # Item/widget utilities
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns `true` if the last item is hovered
|
||||
#[doc(alias = "IsItemHovered")]
|
||||
pub fn is_item_hovered(&self) -> bool {
|
||||
unsafe { sys::igIsItemHovered(0) }
|
||||
}
|
||||
/// Returns `true` if the last item is hovered based on the given flags
|
||||
#[doc(alias = "IsItemHovered")]
|
||||
pub fn is_item_hovered_with_flags(&self, flags: ItemHoveredFlags) -> bool {
|
||||
unsafe { sys::igIsItemHovered(flags.bits() as i32) }
|
||||
}
|
||||
/// Returns `true` if the last item is active
|
||||
#[doc(alias = "IsItemActive")]
|
||||
pub fn is_item_active(&self) -> bool {
|
||||
unsafe { sys::igIsItemActive() }
|
||||
}
|
||||
#[doc(alias = "IsItemFocused")]
|
||||
/// Returns `true` if the last item is focused for keyboard/gamepad navigation
|
||||
pub fn is_item_focused(&self) -> bool {
|
||||
unsafe { sys::igIsItemFocused() }
|
||||
}
|
||||
/// Returns `true` if the last item is being clicked by `MouseButton::Left`.
|
||||
///
|
||||
/// This is the same as [is_item_clicked_with_button](Self::is_item_clicked_with_button)
|
||||
/// with `button` set to `MouseButton::Left`.
|
||||
#[doc(alias = "IsItemClicked")]
|
||||
pub fn is_item_clicked(&self) -> bool {
|
||||
self.is_item_clicked_with_button(MouseButton::Left)
|
||||
}
|
||||
|
||||
/// Returns `true` if the last item is being clicked
|
||||
#[doc(alias = "IsItemClicked")]
|
||||
pub fn is_item_clicked_with_button(&self, button: MouseButton) -> bool {
|
||||
unsafe { sys::igIsItemClicked(button as i32) }
|
||||
}
|
||||
/// Returns `true` if the last item is visible
|
||||
#[doc(alias = "IsItemVisible")]
|
||||
pub fn is_item_visible(&self) -> bool {
|
||||
unsafe { sys::igIsItemVisible() }
|
||||
}
|
||||
/// Returns `true` if the last item modified its underlying value this frame or was pressed
|
||||
#[doc(alias = "IsItemEdited")]
|
||||
pub fn is_item_edited(&self) -> bool {
|
||||
unsafe { sys::igIsItemEdited() }
|
||||
}
|
||||
/// Returns `true` if the last item was just made active
|
||||
#[doc(alias = "IsItemActivated")]
|
||||
pub fn is_item_activated(&self) -> bool {
|
||||
unsafe { sys::igIsItemActivated() }
|
||||
}
|
||||
/// Returns `true` if the last item was just made inactive
|
||||
#[doc(alias = "IsItemDeactivated")]
|
||||
pub fn is_item_deactivated(&self) -> bool {
|
||||
unsafe { sys::igIsItemDeactivated() }
|
||||
}
|
||||
/// Returns `true` if the last item was just made inactive and made a value change when it was
|
||||
#[doc(alias = "IsItemDeactivatedAfterEdit")]
|
||||
/// active
|
||||
pub fn is_item_deactivated_after_edit(&self) -> bool {
|
||||
unsafe { sys::igIsItemDeactivatedAfterEdit() }
|
||||
}
|
||||
/// Returns `true` if the last item open state was toggled
|
||||
#[doc(alias = "IsItemToggledOpen")]
|
||||
pub fn is_item_toggled_open(&self) -> bool {
|
||||
unsafe { sys::igIsItemToggledOpen() }
|
||||
}
|
||||
/// Returns `true` if any item is hovered
|
||||
#[doc(alias = "IsAnyItemHovered")]
|
||||
pub fn is_any_item_hovered(&self) -> bool {
|
||||
unsafe { sys::igIsAnyItemHovered() }
|
||||
}
|
||||
/// Returns `true` if any item is active
|
||||
#[doc(alias = "IsAnyItemActive")]
|
||||
pub fn is_any_item_active(&self) -> bool {
|
||||
unsafe { sys::igIsAnyItemActive() }
|
||||
}
|
||||
/// Returns `true` if any item is focused
|
||||
#[doc(alias = "IsAnyItemFocused")]
|
||||
pub fn is_any_item_focused(&self) -> bool {
|
||||
unsafe { sys::igIsAnyItemFocused() }
|
||||
}
|
||||
/// Returns the upper-left bounding rectangle of the last item (in screen coordinates)
|
||||
#[doc(alias = "GetItemRectMin")]
|
||||
pub fn item_rect_min(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetItemRectMin(&mut out) }
|
||||
out.into()
|
||||
}
|
||||
/// Returns the lower-right bounding rectangle of the last item (in screen coordinates)
|
||||
#[doc(alias = "GetItemRectMax")]
|
||||
pub fn item_rect_max(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetItemRectMax(&mut out) }
|
||||
out.into()
|
||||
}
|
||||
/// Returns the size of the last item
|
||||
#[doc(alias = "GetItemRectSize")]
|
||||
pub fn item_rect_size(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetItemRectSize(&mut out) }
|
||||
out.into()
|
||||
}
|
||||
/// Allows the last item to be overlapped by a subsequent item.
|
||||
///
|
||||
/// Both may be activated during the same frame before the later one takes priority.
|
||||
#[doc(alias = "SetItemAllowOverlap")]
|
||||
pub fn set_item_allow_overlap(&self) {
|
||||
unsafe { sys::igSetItemAllowOverlap() };
|
||||
}
|
||||
/// Makes the last item the default focused item of the window
|
||||
#[doc(alias = "SetItemDefaultFocus")]
|
||||
pub fn set_item_default_focus(&self) {
|
||||
unsafe { sys::igSetItemDefaultFocus() };
|
||||
}
|
||||
}
|
||||
|
||||
/// # Miscellaneous utilities
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns `true` if the rectangle (of given size, starting from cursor position) is visible
|
||||
#[doc(alias = "IsRectVisibleNil")]
|
||||
pub fn is_cursor_rect_visible(&self, size: [f32; 2]) -> bool {
|
||||
unsafe { sys::igIsRectVisible_Nil(size.into()) }
|
||||
}
|
||||
/// Returns `true` if the rectangle (in screen coordinates) is visible
|
||||
#[doc(alias = "IsRectVisibleNilVec2")]
|
||||
pub fn is_rect_visible(&self, rect_min: [f32; 2], rect_max: [f32; 2]) -> bool {
|
||||
unsafe { sys::igIsRectVisible_Vec2(rect_min.into(), rect_max.into()) }
|
||||
}
|
||||
/// Returns the global imgui-rs time.
|
||||
///
|
||||
/// Incremented by Io::delta_time every frame.
|
||||
#[doc(alias = "GetTime")]
|
||||
pub fn time(&self) -> f64 {
|
||||
unsafe { sys::igGetTime() }
|
||||
}
|
||||
/// Returns the global imgui-rs frame count.
|
||||
///
|
||||
/// Incremented by 1 every frame.
|
||||
#[doc(alias = "GetFrameCount")]
|
||||
pub fn frame_count(&self) -> i32 {
|
||||
unsafe { sys::igGetFrameCount() }
|
||||
}
|
||||
/// Returns a single style color from the user interface style.
|
||||
///
|
||||
/// Use this function if you need to access the colors, but don't want to clone the entire
|
||||
/// style object.
|
||||
#[doc(alias = "GetStyle")]
|
||||
pub fn style_color(&self, style_color: StyleColor) -> [f32; 4] {
|
||||
self.ctx.style()[style_color]
|
||||
}
|
||||
}
|
647
plugins/libimhex-rust/imgui-rs/src/widget/color_editors.rs
Normal file
647
plugins/libimhex-rust/imgui-rs/src/widget/color_editors.rs
Normal file
@ -0,0 +1,647 @@
|
||||
use bitflags::bitflags;
|
||||
use std::ptr;
|
||||
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// Mutable reference to an editable color value.
|
||||
#[derive(Debug)]
|
||||
pub enum EditableColor<'a> {
|
||||
/// Color value with three float components (e.g. RGB).
|
||||
Float3(&'a mut [f32; 3]),
|
||||
/// Color value with four float components (e.g. RGBA).
|
||||
Float4(&'a mut [f32; 4]),
|
||||
}
|
||||
|
||||
impl<'a> EditableColor<'a> {
|
||||
/// Returns an unsafe mutable pointer to the color slice's buffer.
|
||||
fn as_mut_ptr(&mut self) -> *mut f32 {
|
||||
match *self {
|
||||
EditableColor::Float3(ref mut value) => value.as_mut_ptr(),
|
||||
EditableColor::Float4(ref mut value) => value.as_mut_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut [f32; 3]> for EditableColor<'a> {
|
||||
#[inline]
|
||||
fn from(value: &'a mut [f32; 3]) -> EditableColor<'a> {
|
||||
EditableColor::Float3(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut [f32; 4]> for EditableColor<'a> {
|
||||
#[inline]
|
||||
fn from(value: &'a mut [f32; 4]) -> EditableColor<'a> {
|
||||
EditableColor::Float4(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Color editor input mode.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ColorEditInputMode {
|
||||
/// Edit as RGB(A).
|
||||
Rgb,
|
||||
/// Edit as HSV(A).
|
||||
Hsv,
|
||||
}
|
||||
|
||||
impl ColorEditInputMode {
|
||||
// Note: Probably no point in deprecating these since they're ~0 maintance burden.
|
||||
/// Edit as RGB(A). Alias for [`Self::Rgb`] for backwards-compatibility.
|
||||
pub const RGB: Self = Self::Rgb;
|
||||
/// Edit as HSV(A). Alias for [`Self::Hsv`] for backwards-compatibility.
|
||||
pub const HSV: Self = Self::Hsv;
|
||||
}
|
||||
|
||||
/// Color editor display mode.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ColorEditDisplayMode {
|
||||
/// Display as RGB(A).
|
||||
Rgb,
|
||||
/// Display as HSV(A).
|
||||
Hsv,
|
||||
/// Display as hex (e.g. `#AABBCC(DD)`)
|
||||
Hex,
|
||||
}
|
||||
|
||||
impl ColorEditDisplayMode {
|
||||
// Note: Probably no point in deprecating these since they're ~0 maintance burden.
|
||||
/// Display as RGB(A). Alias for [`Self::Rgb`] for backwards-compatibility.
|
||||
pub const RGB: Self = Self::Rgb;
|
||||
/// Display as HSV(A). Alias for [`Self::Hsv`] for backwards-compatibility.
|
||||
pub const HSV: Self = Self::Hsv;
|
||||
/// Display as hex. Alias for [`Self::Hex`] for backwards-compatibility.
|
||||
pub const HEX: Self = Self::Hex;
|
||||
}
|
||||
|
||||
/// Color picker hue/saturation/value editor mode
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ColorPickerMode {
|
||||
/// Use a bar for hue, rectangle for saturation/value.
|
||||
HueBar,
|
||||
/// Use a wheel for hue, triangle for saturation/value.
|
||||
HueWheel,
|
||||
}
|
||||
|
||||
/// Color component formatting
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ColorFormat {
|
||||
/// Display values formatted as 0..255.
|
||||
U8,
|
||||
/// Display values formatted as 0.0..1.0.
|
||||
Float,
|
||||
}
|
||||
|
||||
/// Color editor preview style
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ColorPreview {
|
||||
/// Don't show the alpha component.
|
||||
Opaque,
|
||||
/// Half of the preview area shows the alpha component using a checkerboard pattern.
|
||||
HalfAlpha,
|
||||
/// Show the alpha component using a checkerboard pattern.
|
||||
Alpha,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Color edit flags
|
||||
#[repr(transparent)]
|
||||
pub struct ColorEditFlags: u32 {
|
||||
/// ColorEdit, ColorPicker, ColorButton: ignore Alpha component (read only 3 components of
|
||||
/// the value).
|
||||
const NO_ALPHA = sys::ImGuiColorEditFlags_NoAlpha;
|
||||
/// ColorEdit: disable picker when clicking on colored square.
|
||||
const NO_PICKER = sys::ImGuiColorEditFlags_NoPicker;
|
||||
/// ColorEdit: disable toggling options menu when right-clicking on inputs/small preview.
|
||||
const NO_OPTIONS = sys::ImGuiColorEditFlags_NoOptions;
|
||||
/// ColorEdit, ColorPicker: disable colored square preview next to the inputs. (e.g. to
|
||||
/// show only the inputs)
|
||||
const NO_SMALL_PREVIEW = sys::ImGuiColorEditFlags_NoSmallPreview;
|
||||
/// ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the
|
||||
/// small preview colored square).
|
||||
const NO_INPUTS = sys::ImGuiColorEditFlags_NoInputs;
|
||||
/// ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview.
|
||||
const NO_TOOLTIP = sys::ImGuiColorEditFlags_NoTooltip;
|
||||
/// ColorEdit, ColorPicker: disable display of inline text label (the label is still
|
||||
/// forwarded to the tooltip and picker).
|
||||
const NO_LABEL = sys::ImGuiColorEditFlags_NoLabel;
|
||||
/// ColorPicker: disable bigger color preview on right side of the picker, use small
|
||||
/// colored square preview instead.
|
||||
const NO_SIDE_PREVIEW = sys::ImGuiColorEditFlags_NoSidePreview;
|
||||
/// ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source.
|
||||
const NO_DRAG_DROP = sys::ImGuiColorEditFlags_NoDragDrop;
|
||||
/// ColorButton: disable border (which is enforced by default).
|
||||
const NO_BORDER = sys::ImGuiColorEditFlags_NoBorder;
|
||||
|
||||
/// ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker.
|
||||
const ALPHA_BAR = sys::ImGuiColorEditFlags_AlphaBar;
|
||||
/// ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a
|
||||
/// checkerboard, instead of opaque.
|
||||
const ALPHA_PREVIEW = sys::ImGuiColorEditFlags_AlphaPreview;
|
||||
/// ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead
|
||||
/// of opaque.
|
||||
const ALPHA_PREVIEW_HALF = sys::ImGuiColorEditFlags_AlphaPreviewHalf;
|
||||
/// (WIP) ColorEdit: Currently onlys disable 0.0f..1.0f limits in RGBA editing (note: you
|
||||
/// probably want to use `ColorEditFlags::FLOAT` as well).
|
||||
const HDR = sys::ImGuiColorEditFlags_HDR;
|
||||
/// ColorEdit: display only as RGB. ColorPicker: Enable RGB display.
|
||||
const DISPLAY_RGB = sys::ImGuiColorEditFlags_DisplayRGB;
|
||||
/// ColorEdit: display only as HSV. ColorPicker: Enable HSV display.
|
||||
const DISPLAY_HSV = sys::ImGuiColorEditFlags_DisplayHSV;
|
||||
/// ColorEdit: display only as HEX. ColorPicker: Enable Hex display.
|
||||
const DISPLAY_HEX = sys::ImGuiColorEditFlags_DisplayHex;
|
||||
/// ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255.
|
||||
const UINT8 = sys::ImGuiColorEditFlags_Uint8;
|
||||
/// ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats
|
||||
/// instead of 0..255 integers. No round-trip of value via integers.
|
||||
const FLOAT = sys::ImGuiColorEditFlags_Float;
|
||||
/// ColorPicker: bar for Hue, rectangle for Sat/Value.
|
||||
const PICKER_HUE_BAR = sys::ImGuiColorEditFlags_PickerHueBar;
|
||||
/// ColorPicker: wheel for Hue, triangle for Sat/Value.
|
||||
const PICKER_HUE_WHEEL = sys::ImGuiColorEditFlags_PickerHueWheel;
|
||||
/// ColorEdit, ColorPicker: input and output data in RGB format.
|
||||
const INPUT_RGB = sys::ImGuiColorEditFlags_InputRGB;
|
||||
/// ColorEdit, ColorPicker: input and output data in HSV format.
|
||||
const INPUT_HSV = sys::ImGuiColorEditFlags_InputHSV;
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a color editor widget.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut imgui = Context::create();
|
||||
/// # let ui = imgui.frame();
|
||||
/// # let mut color = [0.0, 0.0, 0.0, 1.0];
|
||||
/// let ce = ColorEdit::new(im_str!("color_edit"), &mut color);
|
||||
/// if ce.build(&ui) {
|
||||
/// println!("The color was changed");
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct ColorEdit<'a, T: AsRef<str> + 'a> {
|
||||
label: T,
|
||||
value: EditableColor<'a>,
|
||||
flags: ColorEditFlags,
|
||||
}
|
||||
|
||||
impl<'a, T: AsRef<str> + 'a> ColorEdit<'a, T> {
|
||||
/// Constructs a new color editor builder.
|
||||
#[doc(alias = "ColorEdit3", alias = "ColorEdit4")]
|
||||
pub fn new(label: T, value: impl Into<EditableColor<'a>>) -> ColorEdit<'a, T> {
|
||||
ColorEdit {
|
||||
label,
|
||||
value: value.into(),
|
||||
flags: ColorEditFlags::empty(),
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags.
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: ColorEditFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Enables/disables the use of the alpha component.
|
||||
#[inline]
|
||||
pub fn alpha(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_ALPHA, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the picker that appears when clicking on colored square.
|
||||
#[inline]
|
||||
pub fn picker(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_PICKER, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables toggling of the options menu when right-clicking on inputs or the small
|
||||
/// preview.
|
||||
#[inline]
|
||||
pub fn options(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_OPTIONS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the colored square preview next to the inputs.
|
||||
#[inline]
|
||||
pub fn small_preview(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_SMALL_PREVIEW, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the input sliders/text widgets.
|
||||
#[inline]
|
||||
pub fn inputs(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_INPUTS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the tooltip that appears when hovering the preview.
|
||||
#[inline]
|
||||
pub fn tooltip(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_TOOLTIP, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables display of the inline text label (the label is in any case forwarded to
|
||||
/// the tooltip and picker).
|
||||
#[inline]
|
||||
pub fn label(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_LABEL, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the vertical alpha bar/gradient in the color picker.
|
||||
#[inline]
|
||||
pub fn alpha_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::ALPHA_BAR, value);
|
||||
self
|
||||
}
|
||||
/// Sets the preview style.
|
||||
#[inline]
|
||||
pub fn preview(mut self, preview: ColorPreview) -> Self {
|
||||
self.flags.set(
|
||||
ColorEditFlags::ALPHA_PREVIEW_HALF,
|
||||
preview == ColorPreview::HalfAlpha,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::ALPHA_PREVIEW,
|
||||
preview == ColorPreview::Alpha,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// (WIP) Currently only disables 0.0..1.0 limits in RGBA edition.
|
||||
///
|
||||
/// Note: you probably want to use ColorFormat::Float as well.
|
||||
#[inline]
|
||||
pub fn hdr(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::HDR, value);
|
||||
self
|
||||
}
|
||||
/// Sets the data format for input and output data.
|
||||
#[inline]
|
||||
pub fn input_mode(mut self, input_mode: ColorEditInputMode) -> Self {
|
||||
self.flags.set(
|
||||
ColorEditFlags::INPUT_RGB,
|
||||
input_mode == ColorEditInputMode::RGB,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::INPUT_HSV,
|
||||
input_mode == ColorEditInputMode::HSV,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Sets the color editor display mode.
|
||||
#[inline]
|
||||
pub fn display_mode(mut self, mode: ColorEditDisplayMode) -> Self {
|
||||
self.flags.set(
|
||||
ColorEditFlags::DISPLAY_RGB,
|
||||
mode == ColorEditDisplayMode::RGB,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::DISPLAY_HSV,
|
||||
mode == ColorEditDisplayMode::HSV,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::DISPLAY_HEX,
|
||||
mode == ColorEditDisplayMode::HEX,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Sets the formatting style of color components.
|
||||
#[inline]
|
||||
pub fn format(mut self, format: ColorFormat) -> Self {
|
||||
self.flags
|
||||
.set(ColorEditFlags::UINT8, format == ColorFormat::U8);
|
||||
self.flags
|
||||
.set(ColorEditFlags::FLOAT, format == ColorFormat::Float);
|
||||
self
|
||||
}
|
||||
/// Builds the color editor.
|
||||
///
|
||||
/// Returns true if the color value was changed.
|
||||
pub fn build(mut self, ui: &Ui<'_>) -> bool {
|
||||
if let EditableColor::Float3(_) = self.value {
|
||||
self.flags.insert(ColorEditFlags::NO_ALPHA);
|
||||
}
|
||||
match self.value {
|
||||
EditableColor::Float3(value) => unsafe {
|
||||
sys::igColorEdit3(
|
||||
ui.scratch_txt(self.label),
|
||||
value.as_mut_ptr(),
|
||||
self.flags.bits() as _,
|
||||
)
|
||||
},
|
||||
EditableColor::Float4(value) => unsafe {
|
||||
sys::igColorEdit4(
|
||||
ui.scratch_txt(self.label),
|
||||
value.as_mut_ptr(),
|
||||
self.flags.bits() as _,
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a color picker widget.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut imgui = Context::create();
|
||||
/// # let ui = imgui.frame();
|
||||
/// # let mut color = [0.0, 0.0, 0.0, 1.0];
|
||||
/// let cp = ColorPicker::new(im_str!("color_picker"), &mut color);
|
||||
/// if cp.build(&ui) {
|
||||
/// println!("A color was picked");
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct ColorPicker<'a, T: AsRef<str> + 'a> {
|
||||
label: T,
|
||||
value: EditableColor<'a>,
|
||||
flags: ColorEditFlags,
|
||||
ref_color: Option<&'a [f32; 4]>,
|
||||
}
|
||||
|
||||
impl<'a, T: AsRef<str>> ColorPicker<'a, T> {
|
||||
/// Constructs a new color picker builder.
|
||||
#[doc(alias = "ColorButton")]
|
||||
pub fn new(label: T, value: impl Into<EditableColor<'a>>) -> Self {
|
||||
ColorPicker {
|
||||
label,
|
||||
value: value.into(),
|
||||
flags: ColorEditFlags::empty(),
|
||||
ref_color: None,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags.
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: ColorEditFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Enables/disables the use of the alpha component.
|
||||
#[inline]
|
||||
pub fn alpha(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_ALPHA, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables toggling of the options menu when right-clicking on inputs or the small
|
||||
/// preview.
|
||||
#[inline]
|
||||
pub fn options(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_OPTIONS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the colored square preview next to the inputs.
|
||||
#[inline]
|
||||
pub fn small_preview(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_SMALL_PREVIEW, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the input sliders/text widgets.
|
||||
#[inline]
|
||||
pub fn inputs(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_INPUTS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the tooltip that appears when hovering the preview.
|
||||
#[inline]
|
||||
pub fn tooltip(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_TOOLTIP, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables display of the inline text label (the label is in any case forwarded to
|
||||
/// the tooltip and picker).
|
||||
#[inline]
|
||||
pub fn label(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_LABEL, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the bigger color preview on the right side of the picker.
|
||||
#[inline]
|
||||
pub fn side_preview(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_SIDE_PREVIEW, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the vertical alpha bar/gradient in the color picker.
|
||||
#[inline]
|
||||
pub fn alpha_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::ALPHA_BAR, value);
|
||||
self
|
||||
}
|
||||
/// Sets the preview style.
|
||||
#[inline]
|
||||
pub fn preview(mut self, preview: ColorPreview) -> Self {
|
||||
self.flags.set(
|
||||
ColorEditFlags::ALPHA_PREVIEW_HALF,
|
||||
preview == ColorPreview::HalfAlpha,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::ALPHA_PREVIEW,
|
||||
preview == ColorPreview::Alpha,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Sets the data format for input and output data.
|
||||
#[inline]
|
||||
pub fn input_mode(mut self, input_mode: ColorEditInputMode) -> Self {
|
||||
self.flags.set(
|
||||
ColorEditFlags::INPUT_RGB,
|
||||
input_mode == ColorEditInputMode::RGB,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::INPUT_HSV,
|
||||
input_mode == ColorEditInputMode::HSV,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Enables/disables displaying the value as RGB.
|
||||
#[inline]
|
||||
pub fn display_rgb(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::DISPLAY_RGB, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables displaying the value as HSV.
|
||||
#[inline]
|
||||
pub fn display_hsv(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::DISPLAY_HSV, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables displaying the value as hex.
|
||||
#[inline]
|
||||
pub fn display_hex(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::DISPLAY_HEX, value);
|
||||
self
|
||||
}
|
||||
/// Sets the hue/saturation/value editor mode.
|
||||
#[inline]
|
||||
pub fn mode(mut self, mode: ColorPickerMode) -> Self {
|
||||
self.flags.set(
|
||||
ColorEditFlags::PICKER_HUE_BAR,
|
||||
mode == ColorPickerMode::HueBar,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::PICKER_HUE_WHEEL,
|
||||
mode == ColorPickerMode::HueWheel,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Sets the formatting style of color components.
|
||||
#[inline]
|
||||
pub fn format(mut self, format: ColorFormat) -> Self {
|
||||
self.flags
|
||||
.set(ColorEditFlags::UINT8, format == ColorFormat::U8);
|
||||
self.flags
|
||||
.set(ColorEditFlags::FLOAT, format == ColorFormat::Float);
|
||||
self
|
||||
}
|
||||
/// Sets the shown reference color.
|
||||
#[inline]
|
||||
pub fn reference_color(mut self, ref_color: &'a [f32; 4]) -> Self {
|
||||
self.ref_color = Some(ref_color);
|
||||
self
|
||||
}
|
||||
/// Builds the color picker.
|
||||
///
|
||||
/// Returns true if the color value was changed.
|
||||
pub fn build(mut self, ui: &Ui<'_>) -> bool {
|
||||
if let EditableColor::Float3(_) = self.value {
|
||||
self.flags.insert(ColorEditFlags::NO_ALPHA);
|
||||
}
|
||||
let ref_color = self.ref_color.map(|c| c.as_ptr()).unwrap_or(ptr::null());
|
||||
unsafe {
|
||||
sys::igColorPicker4(
|
||||
ui.scratch_txt(self.label),
|
||||
self.value.as_mut_ptr(),
|
||||
self.flags.bits() as _,
|
||||
ref_color,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a color button widget.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut imgui = Context::create();
|
||||
/// # let ui = imgui.frame();
|
||||
/// ColorButton::new(im_str!("color_button"), [1.0, 0.0, 0.0, 1.0])
|
||||
/// .build(&ui);
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ColorButton<T> {
|
||||
desc_id: T,
|
||||
color: [f32; 4],
|
||||
flags: ColorEditFlags,
|
||||
size: [f32; 2],
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ColorButton<T> {
|
||||
/// Constructs a new color button builder.
|
||||
pub fn new(desc_id: T, color: [f32; 4]) -> Self {
|
||||
ColorButton {
|
||||
desc_id,
|
||||
color,
|
||||
flags: ColorEditFlags::empty(),
|
||||
size: [0.0, 0.0],
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags.
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: ColorEditFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Enables/disables the use of the alpha component.
|
||||
#[inline]
|
||||
pub fn alpha(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_ALPHA, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the tooltip that appears when hovering the preview.
|
||||
#[inline]
|
||||
pub fn tooltip(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_TOOLTIP, !value);
|
||||
self
|
||||
}
|
||||
/// Sets the preview style.
|
||||
#[inline]
|
||||
pub fn preview(mut self, preview: ColorPreview) -> Self {
|
||||
self.flags.set(
|
||||
ColorEditFlags::ALPHA_PREVIEW_HALF,
|
||||
preview == ColorPreview::HalfAlpha,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::ALPHA_PREVIEW,
|
||||
preview == ColorPreview::Alpha,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Sets the data format for input data.
|
||||
#[inline]
|
||||
pub fn input_mode(mut self, input_mode: ColorEditInputMode) -> Self {
|
||||
self.flags.set(
|
||||
ColorEditFlags::INPUT_RGB,
|
||||
input_mode == ColorEditInputMode::RGB,
|
||||
);
|
||||
self.flags.set(
|
||||
ColorEditFlags::INPUT_HSV,
|
||||
input_mode == ColorEditInputMode::HSV,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Enables/disables using the button as drag&drop source.
|
||||
///
|
||||
/// Enabled by default.
|
||||
pub fn drag_drop(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_DRAG_DROP, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the button border.
|
||||
///
|
||||
/// Enabled by default.
|
||||
pub fn border(mut self, value: bool) -> Self {
|
||||
self.flags.set(ColorEditFlags::NO_BORDER, !value);
|
||||
self
|
||||
}
|
||||
/// Sets the button size.
|
||||
///
|
||||
/// Use 0.0 for width and/or height to use the default size.
|
||||
#[inline]
|
||||
pub fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
/// Builds the color button.
|
||||
///
|
||||
/// Returns true if this color button was clicked.
|
||||
pub fn build(self, ui: &Ui<'_>) -> bool {
|
||||
unsafe {
|
||||
sys::igColorButton(
|
||||
ui.scratch_txt(self.desc_id),
|
||||
self.color.into(),
|
||||
self.flags.bits() as _,
|
||||
self.size.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Widgets: Color Editor/Picker
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Initializes current color editor/picker options (generally on application startup) if you
|
||||
/// want to select a default format, picker type, etc. Users will be able to change many
|
||||
/// settings, unless you use .options(false) in your widget builders.
|
||||
#[doc(alias = "SetColorEditOptions")]
|
||||
pub fn set_color_edit_options(&self, flags: ColorEditFlags) {
|
||||
unsafe {
|
||||
sys::igSetColorEditOptions(flags.bits() as i32);
|
||||
}
|
||||
}
|
||||
}
|
317
plugins/libimhex-rust/imgui-rs/src/widget/combo_box.rs
Normal file
317
plugins/libimhex-rust/imgui-rs/src/widget/combo_box.rs
Normal file
@ -0,0 +1,317 @@
|
||||
use bitflags::bitflags;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
// TODO: support size constraints
|
||||
|
||||
/// Combo box height mode.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ComboBoxHeight {
|
||||
/// Max ~4 items visible.
|
||||
Small,
|
||||
/// Max ~8 items visible.
|
||||
Regular,
|
||||
/// Max ~20 items visible.
|
||||
Large,
|
||||
/// As many fitting items as possible visible.
|
||||
Largest,
|
||||
}
|
||||
|
||||
/// Combo box preview mode.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ComboBoxPreviewMode {
|
||||
/// Show only a box with the preview value
|
||||
Label,
|
||||
/// Show only an arrow button
|
||||
ArrowButton,
|
||||
/// Show a box with the preview value and an arrow button
|
||||
Full,
|
||||
}
|
||||
|
||||
bitflags!(
|
||||
/// Flags for combo boxes
|
||||
#[repr(transparent)]
|
||||
pub struct ComboBoxFlags: u32 {
|
||||
/// Align the popup toward the left by default
|
||||
const POPUP_ALIGN_LEFT = sys::ImGuiComboFlags_PopupAlignLeft;
|
||||
/// Max ~4 items visible.
|
||||
const HEIGHT_SMALL = sys::ImGuiComboFlags_HeightSmall;
|
||||
/// Max ~8 items visible (default)
|
||||
const HEIGHT_REGULAR = sys::ImGuiComboFlags_HeightRegular;
|
||||
/// Max ~20 items visible
|
||||
const HEIGHT_LARGE = sys::ImGuiComboFlags_HeightLarge;
|
||||
/// As many fitting items as possible
|
||||
const HEIGHT_LARGEST = sys::ImGuiComboFlags_HeightLargest;
|
||||
/// Display on the preview box without the square arrow button
|
||||
const NO_ARROW_BUTTON = sys::ImGuiComboFlags_NoArrowButton;
|
||||
/// Display only a square arrow button
|
||||
const NO_PREVIEW = sys::ImGuiComboFlags_NoPreview;
|
||||
}
|
||||
);
|
||||
|
||||
/// Builder for a combo box widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ComboBox<Label, Preview = &'static str> {
|
||||
label: Label,
|
||||
preview_value: Option<Preview>,
|
||||
flags: ComboBoxFlags,
|
||||
}
|
||||
|
||||
impl<Label: AsRef<str>> ComboBox<Label> {
|
||||
/// Constructs a new combo box builder.
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn new(label: Label) -> Self {
|
||||
ComboBox {
|
||||
label,
|
||||
preview_value: None,
|
||||
flags: ComboBoxFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>, Preview: AsRef<str>> ComboBox<T, Preview> {
|
||||
pub fn preview_value<Preview2: AsRef<str>>(
|
||||
self,
|
||||
preview_value: Preview2,
|
||||
) -> ComboBox<T, Preview2> {
|
||||
ComboBox {
|
||||
label: self.label,
|
||||
preview_value: Some(preview_value),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces all current settings with the given flags.
|
||||
pub fn flags(mut self, flags: ComboBoxFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables aligning the combo box popup toward the left.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn popup_align_left(mut self, popup_align_left: bool) -> Self {
|
||||
self.flags
|
||||
.set(ComboBoxFlags::POPUP_ALIGN_LEFT, popup_align_left);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the combo box height.
|
||||
///
|
||||
/// Default: `ComboBoxHeight::Regular`
|
||||
#[inline]
|
||||
pub fn height(mut self, height: ComboBoxHeight) -> Self {
|
||||
self.flags
|
||||
.set(ComboBoxFlags::HEIGHT_SMALL, height == ComboBoxHeight::Small);
|
||||
self.flags.set(
|
||||
ComboBoxFlags::HEIGHT_REGULAR,
|
||||
height == ComboBoxHeight::Regular,
|
||||
);
|
||||
self.flags
|
||||
.set(ComboBoxFlags::HEIGHT_LARGE, height == ComboBoxHeight::Large);
|
||||
self.flags.set(
|
||||
ComboBoxFlags::HEIGHT_LARGEST,
|
||||
height == ComboBoxHeight::Largest,
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the combo box preview mode.
|
||||
///
|
||||
/// Default: `ComboBoxPreviewMode::Full`
|
||||
#[inline]
|
||||
pub fn preview_mode(mut self, preview_mode: ComboBoxPreviewMode) -> Self {
|
||||
self.flags.set(
|
||||
ComboBoxFlags::NO_ARROW_BUTTON,
|
||||
preview_mode == ComboBoxPreviewMode::Label,
|
||||
);
|
||||
self.flags.set(
|
||||
ComboBoxFlags::NO_PREVIEW,
|
||||
preview_mode == ComboBoxPreviewMode::ArrowButton,
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates a combo box and starts appending to it.
|
||||
///
|
||||
/// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ComboBoxToken<'ui>> {
|
||||
let should_render = unsafe {
|
||||
if let Some(preview_value) = self.preview_value {
|
||||
let (ptr_one, ptr_two) = ui.scratch_txt_two(self.label, preview_value);
|
||||
sys::igBeginCombo(ptr_one, ptr_two, self.flags.bits() as i32)
|
||||
} else {
|
||||
let ptr_one = ui.scratch_txt(self.label);
|
||||
sys::igBeginCombo(ptr_one, std::ptr::null(), self.flags.bits() as i32)
|
||||
}
|
||||
};
|
||||
if should_render {
|
||||
Some(ComboBoxToken::new(ui))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a combo box and runs a closure to construct the popup contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if the combo box is not open.
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_combo| f())
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a combo box that can be ended by calling `.end()`
|
||||
/// or by dropping.
|
||||
pub struct ComboBoxToken<'ui>;
|
||||
|
||||
/// Ends a combo box
|
||||
drop { sys::igEndCombo() }
|
||||
);
|
||||
|
||||
/// # Convenience functions
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Creates a combo box which can be appended to with `Selectable::new`.
|
||||
///
|
||||
/// If you do not want to provide a preview, use [`begin_combo_no_preview`]. If you want
|
||||
/// to pass flags, use [`begin_combo_with_flags`].
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
///
|
||||
/// [`begin_combo_no_preview`]: Ui::begin_combo_no_preview
|
||||
/// [`begin_combo_with_flags`]: Ui::begin_combo_with_flags
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn begin_combo(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
preview_value: impl AsRef<str>,
|
||||
) -> Option<ComboBoxToken<'ui>> {
|
||||
self.begin_combo_with_flags(label, preview_value, ComboBoxFlags::empty())
|
||||
}
|
||||
|
||||
/// Creates a combo box which can be appended to with `Selectable::new`.
|
||||
///
|
||||
/// If you do not want to provide a preview, use [begin_combo_no_preview].
|
||||
/// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
///
|
||||
/// [begin_combo_no_preview]: Ui::begin_combo_no_preview
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn begin_combo_with_flags(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
preview_value: impl AsRef<str>,
|
||||
flags: ComboBoxFlags,
|
||||
) -> Option<ComboBoxToken<'ui>> {
|
||||
self._begin_combo(label, Some(preview_value), flags)
|
||||
}
|
||||
|
||||
/// Creates a combo box which can be appended to with `Selectable::new`.
|
||||
///
|
||||
/// If you want to provide a preview, use [begin_combo]. If you want
|
||||
/// to pass flags, use [begin_combo_no_preview_with_flags].
|
||||
///
|
||||
/// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
///
|
||||
/// [begin_combo]: Ui::begin_combo
|
||||
/// [begin_combo_no_preview_with_flags]: Ui::begin_combo_no_preview_with_flags
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn begin_combo_no_preview(&self, label: impl AsRef<str>) -> Option<ComboBoxToken<'ui>> {
|
||||
self.begin_combo_no_preview_with_flags(label, ComboBoxFlags::empty())
|
||||
}
|
||||
|
||||
/// Creates a combo box which can be appended to with `Selectable::new`.
|
||||
///
|
||||
/// If you do not want to provide a preview, use [begin_combo_no_preview].
|
||||
/// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
///
|
||||
/// [begin_combo_no_preview]: Ui::begin_combo_no_preview
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginCombo")]
|
||||
pub fn begin_combo_no_preview_with_flags(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
flags: ComboBoxFlags,
|
||||
) -> Option<ComboBoxToken<'ui>> {
|
||||
self._begin_combo(label, Option::<&'static str>::None, flags)
|
||||
}
|
||||
|
||||
/// This is the internal begin combo method that they all...eventually call.
|
||||
fn _begin_combo(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
preview_value: Option<impl AsRef<str>>,
|
||||
flags: ComboBoxFlags,
|
||||
) -> Option<ComboBoxToken<'ui>> {
|
||||
let should_render = unsafe {
|
||||
let (ptr_one, ptr_two) = self.scratch_txt_with_opt(label, preview_value);
|
||||
sys::igBeginCombo(ptr_one, ptr_two, flags.bits() as i32)
|
||||
};
|
||||
if should_render {
|
||||
Some(ComboBoxToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Builds a simple combo box for choosing from a slice of values
|
||||
#[doc(alias = "Combo")]
|
||||
pub fn combo<V, L>(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
current_item: &mut usize,
|
||||
items: &[V],
|
||||
label_fn: L,
|
||||
) -> bool
|
||||
where
|
||||
for<'b> L: Fn(&'b V) -> Cow<'b, str>,
|
||||
{
|
||||
use crate::widget::selectable::Selectable;
|
||||
let label_fn = &label_fn;
|
||||
let mut result = false;
|
||||
let preview_value = items.get(*current_item).map(label_fn);
|
||||
|
||||
if let Some(_cb) = self._begin_combo(label, preview_value, ComboBoxFlags::empty()) {
|
||||
for (idx, item) in items.iter().enumerate() {
|
||||
let text = label_fn(item);
|
||||
let selected = idx == *current_item;
|
||||
if Selectable::new(&text).selected(selected).build(self) {
|
||||
*current_item = idx;
|
||||
result = true;
|
||||
}
|
||||
if selected {
|
||||
self.set_item_default_focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Builds a simple combo box for choosing from a slice of values
|
||||
#[doc(alias = "Combo")]
|
||||
pub fn combo_simple_string(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
current_item: &mut usize,
|
||||
items: &[impl AsRef<str>],
|
||||
) -> bool {
|
||||
self.combo(label, current_item, items, |s| Cow::Borrowed(s.as_ref()))
|
||||
}
|
||||
}
|
285
plugins/libimhex-rust/imgui-rs/src/widget/drag.rs
Normal file
285
plugins/libimhex-rust/imgui-rs/src/widget/drag.rs
Normal file
@ -0,0 +1,285 @@
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use crate::internal::DataTypeKind;
|
||||
use crate::sys;
|
||||
use crate::widget::slider::SliderFlags;
|
||||
use crate::Ui;
|
||||
|
||||
/// Builder for a drag slider widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct Drag<T, L, F = &'static str> {
|
||||
label: L,
|
||||
speed: f32,
|
||||
min: Option<T>,
|
||||
max: Option<T>,
|
||||
display_format: Option<F>,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<L: AsRef<str>, T: DataTypeKind> Drag<T, L> {
|
||||
/// Constructs a new drag slider builder.
|
||||
#[doc(alias = "DragScalar", alias = "DragScalarN")]
|
||||
pub fn new(label: L) -> Self {
|
||||
Drag {
|
||||
label,
|
||||
speed: 1.0,
|
||||
min: None,
|
||||
max: None,
|
||||
display_format: None,
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: AsRef<str>, T: DataTypeKind, F: AsRef<str>> Drag<T, L, F> {
|
||||
/// Sets the range (inclusive)
|
||||
pub fn range(mut self, min: T, max: T) -> Self {
|
||||
self.min = Some(min);
|
||||
self.max = Some(max);
|
||||
self
|
||||
}
|
||||
/// Sets the value increment for a movement of one pixel.
|
||||
///
|
||||
/// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1
|
||||
pub fn speed(mut self, speed: f32) -> Self {
|
||||
self.speed = speed;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> Drag<T, L, F2> {
|
||||
Drag {
|
||||
label: self.label,
|
||||
speed: self.speed,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: Some(display_format),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
pub fn flags(mut self, flags: SliderFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Builds a drag slider that is bound to the given value.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
pub fn build(self, ui: &Ui<'_>, value: &mut T) -> bool {
|
||||
unsafe {
|
||||
let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igDragScalar(
|
||||
one,
|
||||
T::KIND as i32,
|
||||
value as *mut T as *mut c_void,
|
||||
self.speed,
|
||||
self.min
|
||||
.as_ref()
|
||||
.map(|min| min as *const T)
|
||||
.unwrap_or(ptr::null()) as *const c_void,
|
||||
self.max
|
||||
.as_ref()
|
||||
.map(|max| max as *const T)
|
||||
.unwrap_or(ptr::null()) as *const c_void,
|
||||
two,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
/// Builds a horizontal array of multiple drag sliders attached to the given slice.
|
||||
///
|
||||
/// Returns true if any slider value was changed.
|
||||
pub fn build_array(self, ui: &Ui<'_>, values: &mut [T]) -> bool {
|
||||
unsafe {
|
||||
let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igDragScalarN(
|
||||
one,
|
||||
T::KIND as i32,
|
||||
values.as_mut_ptr() as *mut c_void,
|
||||
values.len() as i32,
|
||||
self.speed,
|
||||
self.min
|
||||
.as_ref()
|
||||
.map(|min| min as *const T)
|
||||
.unwrap_or(ptr::null()) as *const c_void,
|
||||
self.max
|
||||
.as_ref()
|
||||
.map(|max| max as *const T)
|
||||
.unwrap_or(ptr::null()) as *const c_void,
|
||||
two,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a drag slider widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct DragRange<T, L, F = &'static str, M = &'static str> {
|
||||
label: L,
|
||||
speed: f32,
|
||||
min: Option<T>,
|
||||
max: Option<T>,
|
||||
display_format: Option<F>,
|
||||
max_display_format: Option<M>,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<T: DataTypeKind, L: AsRef<str>> DragRange<T, L> {
|
||||
/// Constructs a new drag slider builder.
|
||||
#[doc(alias = "DragIntRange2", alias = "DragFloatRange2")]
|
||||
pub fn new(label: L) -> DragRange<T, L> {
|
||||
DragRange {
|
||||
label,
|
||||
speed: 1.0,
|
||||
min: None,
|
||||
max: None,
|
||||
display_format: None,
|
||||
max_display_format: None,
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, L, F, M> DragRange<T, L, F, M>
|
||||
where
|
||||
T: DataTypeKind,
|
||||
L: AsRef<str>,
|
||||
F: AsRef<str>,
|
||||
M: AsRef<str>,
|
||||
{
|
||||
pub fn range(mut self, min: T, max: T) -> Self {
|
||||
self.min = Some(min);
|
||||
self.max = Some(max);
|
||||
self
|
||||
}
|
||||
/// Sets the value increment for a movement of one pixel.
|
||||
///
|
||||
/// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1
|
||||
pub fn speed(mut self, speed: f32) -> Self {
|
||||
self.speed = speed;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> DragRange<T, L, F2, M> {
|
||||
DragRange {
|
||||
label: self.label,
|
||||
speed: self.speed,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: Some(display_format),
|
||||
max_display_format: self.max_display_format,
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Sets the display format for the max value using *a C-style printf string*
|
||||
pub fn max_display_format<M2: AsRef<str>>(
|
||||
self,
|
||||
max_display_format: M2,
|
||||
) -> DragRange<T, L, F, M2> {
|
||||
DragRange {
|
||||
label: self.label,
|
||||
speed: self.speed,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: self.display_format,
|
||||
max_display_format: Some(max_display_format),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
pub fn flags(mut self, flags: SliderFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, F, M> DragRange<f32, L, F, M>
|
||||
where
|
||||
L: AsRef<str>,
|
||||
F: AsRef<str>,
|
||||
M: AsRef<str>,
|
||||
{
|
||||
/// Builds a drag range slider that is bound to the given min/max values.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
#[doc(alias = "DragFloatRange2")]
|
||||
pub fn build(self, ui: &Ui<'_>, min: &mut f32, max: &mut f32) -> bool {
|
||||
let label;
|
||||
let mut display_format = std::ptr::null();
|
||||
let mut max_display_format = std::ptr::null();
|
||||
|
||||
// we do this ourselves the long way...
|
||||
unsafe {
|
||||
let buffer = &mut *ui.buffer.get();
|
||||
buffer.refresh_buffer();
|
||||
|
||||
label = buffer.push(self.label);
|
||||
if let Some(v) = self.display_format {
|
||||
display_format = buffer.push(v);
|
||||
}
|
||||
if let Some(v) = self.max_display_format {
|
||||
max_display_format = buffer.push(v);
|
||||
}
|
||||
|
||||
sys::igDragFloatRange2(
|
||||
label,
|
||||
min as *mut f32,
|
||||
max as *mut f32,
|
||||
self.speed,
|
||||
self.min.unwrap_or(0.0),
|
||||
self.max.unwrap_or(0.0),
|
||||
display_format,
|
||||
max_display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, F, M> DragRange<i32, L, F, M>
|
||||
where
|
||||
L: AsRef<str>,
|
||||
F: AsRef<str>,
|
||||
M: AsRef<str>,
|
||||
{
|
||||
/// Builds a drag range slider that is bound to the given min/max values.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
#[doc(alias = "DragIntRange2")]
|
||||
pub fn build(self, ui: &Ui<'_>, min: &mut i32, max: &mut i32) -> bool {
|
||||
unsafe {
|
||||
let label;
|
||||
let mut display_format = std::ptr::null();
|
||||
let mut max_display_format = std::ptr::null();
|
||||
|
||||
// we do this ourselves the long way...
|
||||
let buffer = &mut *ui.buffer.get();
|
||||
buffer.refresh_buffer();
|
||||
|
||||
label = buffer.push(self.label);
|
||||
if let Some(v) = self.display_format {
|
||||
display_format = buffer.push(v);
|
||||
}
|
||||
if let Some(v) = self.max_display_format {
|
||||
max_display_format = buffer.push(v);
|
||||
}
|
||||
|
||||
sys::igDragIntRange2(
|
||||
label,
|
||||
min as *mut i32,
|
||||
max as *mut i32,
|
||||
self.speed,
|
||||
self.min.unwrap_or(0),
|
||||
self.max.unwrap_or(0),
|
||||
display_format,
|
||||
max_display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
147
plugins/libimhex-rust/imgui-rs/src/widget/image.rs
Normal file
147
plugins/libimhex-rust/imgui-rs/src/widget/image.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::render::renderer::TextureId;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// Builder for an image widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct Image {
|
||||
texture_id: TextureId,
|
||||
size: [f32; 2],
|
||||
uv0: [f32; 2],
|
||||
uv1: [f32; 2],
|
||||
tint_col: [f32; 4],
|
||||
border_col: [f32; 4],
|
||||
}
|
||||
|
||||
impl Image {
|
||||
/// Creates a new image builder with the given texture and size
|
||||
#[doc(alias = "Image")]
|
||||
pub const fn new(texture_id: TextureId, size: [f32; 2]) -> Image {
|
||||
Image {
|
||||
texture_id,
|
||||
size,
|
||||
uv0: [0.0, 0.0],
|
||||
uv1: [1.0, 1.0],
|
||||
tint_col: [1.0, 1.0, 1.0, 1.0],
|
||||
border_col: [0.0, 0.0, 0.0, 0.0],
|
||||
}
|
||||
}
|
||||
/// Sets the image size
|
||||
pub const fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
/// Sets uv0 (default `[0.0, 0.0]`)
|
||||
pub const fn uv0(mut self, uv0: [f32; 2]) -> Self {
|
||||
self.uv0 = uv0;
|
||||
self
|
||||
}
|
||||
/// Sets uv1 (default `[1.0, 1.0]`)
|
||||
pub const fn uv1(mut self, uv1: [f32; 2]) -> Self {
|
||||
self.uv1 = uv1;
|
||||
self
|
||||
}
|
||||
/// Sets the tint color (default: no tint color)
|
||||
pub const fn tint_col(mut self, tint_col: [f32; 4]) -> Self {
|
||||
self.tint_col = tint_col;
|
||||
self
|
||||
}
|
||||
/// Sets the border color (default: no border)
|
||||
pub const fn border_col(mut self, border_col: [f32; 4]) -> Self {
|
||||
self.border_col = border_col;
|
||||
self
|
||||
}
|
||||
/// Builds the image
|
||||
pub fn build(self, _: &Ui<'_>) {
|
||||
unsafe {
|
||||
sys::igImage(
|
||||
self.texture_id.id() as *mut c_void,
|
||||
self.size.into(),
|
||||
self.uv0.into(),
|
||||
self.uv1.into(),
|
||||
self.tint_col.into(),
|
||||
self.border_col.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for an image button widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ImageButton {
|
||||
texture_id: TextureId,
|
||||
size: [f32; 2],
|
||||
uv0: [f32; 2],
|
||||
uv1: [f32; 2],
|
||||
frame_padding: i32,
|
||||
bg_col: [f32; 4],
|
||||
tint_col: [f32; 4],
|
||||
}
|
||||
|
||||
impl ImageButton {
|
||||
/// Creates a new image button builder with the given texture and size
|
||||
#[doc(alias = "ImageButton")]
|
||||
pub fn new(texture_id: TextureId, size: [f32; 2]) -> ImageButton {
|
||||
ImageButton {
|
||||
texture_id,
|
||||
size,
|
||||
uv0: [0.0, 0.0],
|
||||
uv1: [1.0, 1.0],
|
||||
frame_padding: -1,
|
||||
bg_col: [0.0, 0.0, 0.0, 0.0],
|
||||
tint_col: [1.0, 1.0, 1.0, 1.0],
|
||||
}
|
||||
}
|
||||
/// Sets the image button size
|
||||
pub fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
/// Sets uv0 (default `[0.0, 0.0]`)
|
||||
pub fn uv0(mut self, uv0: [f32; 2]) -> Self {
|
||||
self.uv0 = uv0;
|
||||
self
|
||||
}
|
||||
/// Sets uv1 (default `[1.0, 1.0]`)
|
||||
pub fn uv1(mut self, uv1: [f32; 2]) -> Self {
|
||||
self.uv1 = uv1;
|
||||
self
|
||||
}
|
||||
/// Sets the frame padding (default: uses frame padding from style).
|
||||
///
|
||||
/// - `< 0`: uses frame padding from style (default)
|
||||
/// - `= 0`: no framing
|
||||
/// - `> 0`: set framing size
|
||||
pub fn frame_padding(mut self, frame_padding: i32) -> Self {
|
||||
self.frame_padding = frame_padding;
|
||||
self
|
||||
}
|
||||
/// Sets the background color (default: no background color)
|
||||
pub fn background_col(mut self, bg_col: [f32; 4]) -> Self {
|
||||
self.bg_col = bg_col;
|
||||
self
|
||||
}
|
||||
/// Sets the tint color (default: no tint color)
|
||||
pub fn tint_col(mut self, tint_col: [f32; 4]) -> Self {
|
||||
self.tint_col = tint_col;
|
||||
self
|
||||
}
|
||||
/// Builds the image button
|
||||
pub fn build(self, _: &Ui<'_>) -> bool {
|
||||
unsafe {
|
||||
sys::igImageButton(
|
||||
self.texture_id.id() as *mut c_void,
|
||||
self.size.into(),
|
||||
self.uv0.into(),
|
||||
self.uv1.into(),
|
||||
self.frame_padding,
|
||||
self.bg_col.into(),
|
||||
self.tint_col.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
96
plugins/libimhex-rust/imgui-rs/src/widget/list_box.rs
Normal file
96
plugins/libimhex-rust/imgui-rs/src/widget/list_box.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// Builder for a list box widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ListBox<T> {
|
||||
label: T,
|
||||
size: sys::ImVec2,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ListBox<T> {
|
||||
/// Constructs a new list box builder.
|
||||
#[doc(alias = "ListBoxHeaderVec2", alias = "ListBoxHeaderInt")]
|
||||
pub fn new(label: T) -> ListBox<T> {
|
||||
ListBox {
|
||||
label,
|
||||
size: sys::ImVec2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the list box size based on the given width and height
|
||||
/// If width or height are 0 or smaller, a default value is calculated
|
||||
/// Helper to calculate the size of a listbox and display a label on the right.
|
||||
/// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an non-visible label e.g. "##empty"
|
||||
///
|
||||
/// Default: [0.0, 0.0], in which case the combobox calculates a sensible width and height
|
||||
#[inline]
|
||||
pub fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = sys::ImVec2::new(size[0], size[1]);
|
||||
self
|
||||
}
|
||||
/// Creates a list box and starts appending to it.
|
||||
///
|
||||
/// Returns `Some(ListBoxToken)` if the list box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the list box is not open and no content should be rendered.
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ListBoxToken<'ui>> {
|
||||
let should_render = unsafe { sys::igBeginListBox(ui.scratch_txt(self.label), self.size) };
|
||||
if should_render {
|
||||
Some(ListBoxToken::new(ui))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a list box and runs a closure to construct the list contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if the list box is not open.
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_list| f())
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a list box that can be ended by calling `.end()`
|
||||
/// or by dropping
|
||||
pub struct ListBoxToken<'ui>;
|
||||
|
||||
/// Ends a list box
|
||||
drop { sys::igEndListBox() }
|
||||
);
|
||||
|
||||
/// # Convenience functions
|
||||
impl<T: AsRef<str>> ListBox<T> {
|
||||
/// Builds a simple list box for choosing from a slice of values
|
||||
pub fn build_simple<V, L>(
|
||||
self,
|
||||
ui: &Ui<'_>,
|
||||
current_item: &mut usize,
|
||||
items: &[V],
|
||||
label_fn: &L,
|
||||
) -> bool
|
||||
where
|
||||
for<'b> L: Fn(&'b V) -> Cow<'b, str>,
|
||||
{
|
||||
use crate::widget::selectable::Selectable;
|
||||
let mut result = false;
|
||||
let lb = self;
|
||||
if let Some(_cb) = lb.begin(ui) {
|
||||
for (idx, item) in items.iter().enumerate() {
|
||||
let text = label_fn(item);
|
||||
let selected = idx == *current_item;
|
||||
if Selectable::new(&text).selected(selected).build(ui) {
|
||||
*current_item = idx;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
214
plugins/libimhex-rust/imgui-rs/src/widget/menu.rs
Normal file
214
plugins/libimhex-rust/imgui-rs/src/widget/menu.rs
Normal file
@ -0,0 +1,214 @@
|
||||
// use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// # Widgets: Menus
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Creates and starts appending to a full-screen menu bar.
|
||||
///
|
||||
/// Returns `Some(MainMenuBarToken)` if the menu bar is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the menu bar is not visible and no content should be rendered.
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginMainMenuBar")]
|
||||
pub fn begin_main_menu_bar(&self) -> Option<MainMenuBarToken<'ui>> {
|
||||
if unsafe { sys::igBeginMainMenuBar() } {
|
||||
Some(MainMenuBarToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a full-screen main menu bar and runs a closure to construct the contents.
|
||||
///
|
||||
/// Note: the closure is not called if the menu bar is not visible.
|
||||
#[doc(alias = "BeginMenuBar")]
|
||||
pub fn main_menu_bar<F: FnOnce()>(&self, f: F) {
|
||||
if let Some(_menu_bar) = self.begin_main_menu_bar() {
|
||||
f();
|
||||
}
|
||||
}
|
||||
/// Creates and starts appending to the menu bar of the current window.
|
||||
///
|
||||
/// Returns `Some(MenuBarToken)` if the menu bar is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the menu bar is not visible and no content should be rendered.
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginMenuBar")]
|
||||
pub fn begin_menu_bar(&self) -> Option<MenuBarToken<'_>> {
|
||||
if unsafe { sys::igBeginMenuBar() } {
|
||||
Some(MenuBarToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a menu bar in the current window and runs a closure to construct the contents.
|
||||
///
|
||||
/// Note: the closure is not called if the menu bar is not visible.
|
||||
#[doc(alias = "BeginMenuBar")]
|
||||
pub fn menu_bar<F: FnOnce()>(&self, f: F) {
|
||||
if let Some(_menu_bar) = self.begin_menu_bar() {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and starts appending to a sub-menu entry.
|
||||
///
|
||||
/// Returns `Some(MenuToken)` if the menu is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the menu is not visible and no content should be rendered.
|
||||
///
|
||||
/// This is the equivalent of [begin_menu_with_enabled](Self::begin_menu_with_enabled)
|
||||
/// with `enabled` set to `true`.
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginMenu")]
|
||||
pub fn begin_menu(&self, label: impl AsRef<str>) -> Option<MenuToken<'_>> {
|
||||
self.begin_menu_with_enabled(label, true)
|
||||
}
|
||||
|
||||
/// Creates and starts appending to a sub-menu entry.
|
||||
///
|
||||
/// Returns `Some(MenuToken)` if the menu is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the menu is not visible and no content should be rendered.
|
||||
#[must_use]
|
||||
#[doc(alias = "BeginMenu")]
|
||||
pub fn begin_menu_with_enabled(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
enabled: bool,
|
||||
) -> Option<MenuToken<'_>> {
|
||||
if unsafe { sys::igBeginMenu(self.scratch_txt(label), enabled) } {
|
||||
Some(MenuToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a menu and runs a closure to construct the contents.
|
||||
///
|
||||
/// Note: the closure is not called if the menu is not visible.
|
||||
///
|
||||
/// This is the equivalent of [menu_with_enabled](Self::menu_with_enabled)
|
||||
/// with `enabled` set to `true`.
|
||||
#[doc(alias = "BeginMenu")]
|
||||
pub fn menu<F: FnOnce()>(&self, label: impl AsRef<str>, f: F) {
|
||||
self.menu_with_enabled(label, true, f);
|
||||
}
|
||||
|
||||
/// Creates a menu and runs a closure to construct the contents.
|
||||
///
|
||||
/// Note: the closure is not called if the menu is not visible.
|
||||
#[doc(alias = "BeginMenu")]
|
||||
pub fn menu_with_enabled<F: FnOnce()>(&self, label: impl AsRef<str>, enabled: bool, f: F) {
|
||||
if let Some(_menu) = self.begin_menu_with_enabled(label, enabled) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a menu item.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct MenuItem<Label, Shortcut = &'static str> {
|
||||
label: Label,
|
||||
shortcut: Option<Shortcut>,
|
||||
selected: bool,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl<Label: AsRef<str>> MenuItem<Label> {
|
||||
/// Construct a new menu item builder.
|
||||
pub fn new(label: Label) -> Self {
|
||||
MenuItem {
|
||||
label,
|
||||
shortcut: None,
|
||||
selected: false,
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Label: AsRef<str>, Shortcut: AsRef<str>> MenuItem<Label, Shortcut> {
|
||||
/// Sets the menu item shortcut.
|
||||
///
|
||||
/// Shortcuts are displayed for convenience only and are not automatically handled.
|
||||
#[inline]
|
||||
pub fn shortcut<Shortcut2: AsRef<str>>(
|
||||
self,
|
||||
shortcut: Shortcut2,
|
||||
) -> MenuItem<Label, Shortcut2> {
|
||||
MenuItem {
|
||||
label: self.label,
|
||||
shortcut: Some(shortcut),
|
||||
selected: self.selected,
|
||||
enabled: self.enabled,
|
||||
}
|
||||
}
|
||||
/// Sets the selected state of the menu item.
|
||||
///
|
||||
/// Default: false
|
||||
#[inline]
|
||||
pub fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
/// Enables/disables the menu item.
|
||||
///
|
||||
/// Default: enabled
|
||||
#[inline]
|
||||
pub fn enabled(mut self, enabled: bool) -> Self {
|
||||
self.enabled = enabled;
|
||||
self
|
||||
}
|
||||
/// Builds the menu item.
|
||||
///
|
||||
/// Returns true if the menu item is activated.
|
||||
#[doc(alias = "MenuItemBool")]
|
||||
pub fn build(self, ui: &Ui<'_>) -> bool {
|
||||
unsafe {
|
||||
let (label, shortcut) = ui.scratch_txt_with_opt(self.label, self.shortcut);
|
||||
sys::igMenuItem_Bool(label, shortcut, self.selected, self.enabled)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "MenuItemBool")]
|
||||
/// Builds the menu item using a mutable reference to selected state.
|
||||
pub fn build_with_ref(self, ui: &Ui<'_>, selected: &mut bool) -> bool {
|
||||
if self.selected(*selected).build(ui) {
|
||||
*selected = !*selected;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a main menu bar that can be ended by calling `.end()`
|
||||
/// or by dropping
|
||||
pub struct MainMenuBarToken<'ui>;
|
||||
|
||||
/// Ends a main menu bar
|
||||
drop { sys::igEndMainMenuBar() }
|
||||
);
|
||||
|
||||
create_token!(
|
||||
/// Tracks a menu bar that can be ended by calling `.end()`
|
||||
/// or by dropping
|
||||
pub struct MenuBarToken<'ui>;
|
||||
|
||||
/// Ends a menu bar
|
||||
drop { sys::igEndMenuBar() }
|
||||
);
|
||||
|
||||
create_token!(
|
||||
/// Tracks a menu that can be ended by calling `.end()`
|
||||
/// or by dropping
|
||||
pub struct MenuToken<'ui>;
|
||||
|
||||
/// Ends a menu
|
||||
drop { sys::igEndMenu() }
|
||||
);
|
129
plugins/libimhex-rust/imgui-rs/src/widget/misc.rs
Normal file
129
plugins/libimhex-rust/imgui-rs/src/widget/misc.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use bitflags::bitflags;
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Not};
|
||||
|
||||
use crate::sys;
|
||||
use crate::{Direction, Ui};
|
||||
|
||||
bitflags!(
|
||||
/// Flags for invisible buttons
|
||||
#[repr(transparent)]
|
||||
pub struct ButtonFlags: u32 {
|
||||
/// React on left mouse button
|
||||
const MOUSE_BUTTON_LEFT = sys::ImGuiButtonFlags_MouseButtonLeft;
|
||||
/// React on right mouse button
|
||||
const MOUSE_BUTTON_RIGHT = sys::ImGuiButtonFlags_MouseButtonRight;
|
||||
/// React on middle mouse button
|
||||
const MOUSE_BUTTON_MIDDLE = sys::ImGuiButtonFlags_MouseButtonMiddle;
|
||||
}
|
||||
);
|
||||
|
||||
/// # Widgets: Miscellaneous
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Renders a clickable button.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
///
|
||||
/// This is the equivalent of [button_with_size](Self::button_with_size)
|
||||
/// with `size` set to `[0.0, 0.0]`, which will size the button to the
|
||||
/// label's width in the current style.
|
||||
/// the current style.
|
||||
#[doc(alias = "Button")]
|
||||
pub fn button(&self, label: impl AsRef<str>) -> bool {
|
||||
self.button_with_size(label, [0.0, 0.0])
|
||||
}
|
||||
|
||||
/// Renders a clickable button.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
///
|
||||
/// Setting `size` as `[0.0, 0.0]` will size the button to the label's width in
|
||||
/// the current style.
|
||||
#[doc(alias = "Button")]
|
||||
pub fn button_with_size(&self, label: impl AsRef<str>, size: [f32; 2]) -> bool {
|
||||
unsafe { sys::igButton(self.scratch_txt(label), size.into()) }
|
||||
}
|
||||
/// Renders a small clickable button that is easy to embed in text.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
#[doc(alias = "SmallButton")]
|
||||
pub fn small_button(&self, label: impl AsRef<str>) -> bool {
|
||||
unsafe { sys::igSmallButton(self.scratch_txt(label)) }
|
||||
}
|
||||
/// Renders a widget with button behaviour without the visual look.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
#[doc(alias = "InvisibleButton")]
|
||||
pub fn invisible_button(&self, id: impl AsRef<str>, size: [f32; 2]) -> bool {
|
||||
unsafe { sys::igInvisibleButton(self.scratch_txt(id), size.into(), 0) }
|
||||
}
|
||||
/// Renders a widget with button behaviour without the visual look.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
#[doc(alias = "InvisibleButton")]
|
||||
pub fn invisible_button_flags(
|
||||
&self,
|
||||
id: impl AsRef<str>,
|
||||
size: [f32; 2],
|
||||
flags: ButtonFlags,
|
||||
) -> bool {
|
||||
unsafe { sys::igInvisibleButton(self.scratch_txt(id), size.into(), flags.bits() as i32) }
|
||||
}
|
||||
/// Renders a square button with an arrow shape.
|
||||
///
|
||||
/// Returns true if this button was clicked.
|
||||
#[doc(alias = "ArrowButton")]
|
||||
pub fn arrow_button(&self, id: impl AsRef<str>, direction: Direction) -> bool {
|
||||
unsafe { sys::igArrowButton(self.scratch_txt(id), direction as i32) }
|
||||
}
|
||||
/// Renders a simple checkbox.
|
||||
///
|
||||
/// Returns true if this checkbox was clicked.
|
||||
#[doc(alias = "Checkbox")]
|
||||
pub fn checkbox(&self, label: impl AsRef<str>, value: &mut bool) -> bool {
|
||||
unsafe { sys::igCheckbox(self.scratch_txt(label), value as *mut bool) }
|
||||
}
|
||||
/// Renders a checkbox suitable for toggling bit flags using a mask.
|
||||
///
|
||||
/// Returns true if this checkbox was clicked.
|
||||
pub fn checkbox_flags<T>(&self, label: impl AsRef<str>, flags: &mut T, mask: T) -> bool
|
||||
where
|
||||
T: Copy + PartialEq + BitOrAssign + BitAndAssign + BitAnd<Output = T> + Not<Output = T>,
|
||||
{
|
||||
let mut value = *flags & mask == mask;
|
||||
let pressed = self.checkbox(label, &mut value);
|
||||
if pressed {
|
||||
if value {
|
||||
*flags |= mask;
|
||||
} else {
|
||||
*flags &= !mask;
|
||||
}
|
||||
}
|
||||
pressed
|
||||
}
|
||||
/// Renders a simple radio button.
|
||||
///
|
||||
/// Returns true if this radio button was clicked.
|
||||
#[doc(alias = "RadioButtonBool")]
|
||||
pub fn radio_button_bool(&self, label: impl AsRef<str>, active: bool) -> bool {
|
||||
unsafe { sys::igRadioButton_Bool(self.scratch_txt(label), active) }
|
||||
}
|
||||
/// Renders a radio button suitable for choosing an arbitrary value.
|
||||
///
|
||||
/// Returns true if this radio button was clicked.
|
||||
#[doc(alias = "RadioButtonBool")]
|
||||
pub fn radio_button<T>(&self, label: impl AsRef<str>, value: &mut T, button_value: T) -> bool
|
||||
where
|
||||
T: Copy + PartialEq,
|
||||
{
|
||||
let pressed = self.radio_button_bool(label, *value == button_value);
|
||||
if pressed {
|
||||
*value = button_value;
|
||||
}
|
||||
pressed
|
||||
}
|
||||
/// Renders a small circle and keeps the cursor on the same line
|
||||
#[doc(alias = "Bullet")]
|
||||
pub fn bullet(&self) {
|
||||
unsafe { sys::igBullet() };
|
||||
}
|
||||
}
|
13
plugins/libimhex-rust/imgui-rs/src/widget/mod.rs
Normal file
13
plugins/libimhex-rust/imgui-rs/src/widget/mod.rs
Normal file
@ -0,0 +1,13 @@
|
||||
pub mod color_editors;
|
||||
pub mod combo_box;
|
||||
pub mod drag;
|
||||
pub mod image;
|
||||
pub mod list_box;
|
||||
pub mod menu;
|
||||
pub mod misc;
|
||||
pub mod progress_bar;
|
||||
pub mod selectable;
|
||||
pub mod slider;
|
||||
pub mod tab;
|
||||
pub mod text;
|
||||
pub mod tree;
|
73
plugins/libimhex-rust/imgui-rs/src/widget/progress_bar.rs
Normal file
73
plugins/libimhex-rust/imgui-rs/src/widget/progress_bar.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use crate::sys;
|
||||
// use crate::ImStr;
|
||||
use crate::Ui;
|
||||
|
||||
/// Builder for a progress bar widget.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut imgui = Context::create();
|
||||
/// # let ui = imgui.frame();
|
||||
/// ProgressBar::new(0.6)
|
||||
/// .size([100.0, 12.0])
|
||||
/// .overlay_text("Progress!")
|
||||
/// .build(&ui);
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ProgressBar<T = &'static str> {
|
||||
fraction: f32,
|
||||
size: [f32; 2],
|
||||
overlay_text: Option<T>,
|
||||
}
|
||||
|
||||
impl ProgressBar {
|
||||
/// Creates a progress bar with a given fraction showing
|
||||
/// the progress (0.0 = 0%, 1.0 = 100%).
|
||||
///
|
||||
/// The progress bar will be automatically sized to fill the entire width of the window if no
|
||||
/// custom size is specified.
|
||||
#[inline]
|
||||
#[doc(alias = "ProgressBar")]
|
||||
pub fn new(fraction: f32) -> Self {
|
||||
ProgressBar {
|
||||
fraction,
|
||||
size: [-1.0, 0.0],
|
||||
overlay_text: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ProgressBar<T> {
|
||||
/// Sets an optional text that will be drawn over the progress bar.
|
||||
pub fn overlay_text<T2: AsRef<str>>(self, overlay_text: T2) -> ProgressBar<T2> {
|
||||
ProgressBar {
|
||||
fraction: self.fraction,
|
||||
size: self.size,
|
||||
overlay_text: Some(overlay_text),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the size of the progress bar.
|
||||
///
|
||||
/// Negative values will automatically align to the end of the axis, zero will let the progress
|
||||
/// bar choose a size, and positive values will use the given size.
|
||||
#[inline]
|
||||
pub fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the progress bar
|
||||
pub fn build(self, ui: &Ui<'_>) {
|
||||
unsafe {
|
||||
sys::igProgressBar(
|
||||
self.fraction,
|
||||
self.size.into(),
|
||||
ui.scratch_txt_opt(self.overlay_text),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
130
plugins/libimhex-rust/imgui-rs/src/widget/selectable.rs
Normal file
130
plugins/libimhex-rust/imgui-rs/src/widget/selectable.rs
Normal file
@ -0,0 +1,130 @@
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
bitflags!(
|
||||
/// Flags for selectables
|
||||
#[repr(transparent)]
|
||||
pub struct SelectableFlags: u32 {
|
||||
/// Clicking this don't close parent popup window
|
||||
const DONT_CLOSE_POPUPS = sys::ImGuiSelectableFlags_DontClosePopups;
|
||||
/// Selectable frame can span all columns (text will still fit in current column)
|
||||
const SPAN_ALL_COLUMNS = sys::ImGuiSelectableFlags_SpanAllColumns;
|
||||
/// Generate press events on double clicks too
|
||||
const ALLOW_DOUBLE_CLICK = sys::ImGuiSelectableFlags_AllowDoubleClick;
|
||||
/// Cannot be selected, display greyed out text
|
||||
const DISABLED = sys::ImGuiSelectableFlags_Disabled;
|
||||
/// (WIP) Hit testing to allow subsequent willdgets to overlap this one
|
||||
const ALLOW_ITEM_OVERLAP = sys::ImGuiSelectableFlags_AllowItemOverlap;
|
||||
}
|
||||
);
|
||||
|
||||
/// Builder for a selectable widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct Selectable<T> {
|
||||
label: T,
|
||||
selected: bool,
|
||||
flags: SelectableFlags,
|
||||
size: [f32; 2],
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Selectable<T> {
|
||||
/// Constructs a new selectable builder.
|
||||
#[inline]
|
||||
#[doc(alias = "Selectable")]
|
||||
pub fn new(label: T) -> Self {
|
||||
Selectable {
|
||||
label,
|
||||
selected: false,
|
||||
flags: SelectableFlags::empty(),
|
||||
size: [0.0, 0.0],
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: SelectableFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Sets the selected state of the selectable
|
||||
#[inline]
|
||||
pub fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
/// Enables/disables closing parent popup window on click.
|
||||
///
|
||||
/// Default: enabled
|
||||
#[inline]
|
||||
pub fn close_popups(mut self, value: bool) -> Self {
|
||||
self.flags.set(SelectableFlags::DONT_CLOSE_POPUPS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables full column span (text will still fit in the current column).
|
||||
///
|
||||
/// Default: disabled
|
||||
#[inline]
|
||||
pub fn span_all_columns(mut self, value: bool) -> Self {
|
||||
self.flags.set(SelectableFlags::SPAN_ALL_COLUMNS, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables click event generation on double clicks.
|
||||
///
|
||||
/// Default: disabled
|
||||
#[inline]
|
||||
pub fn allow_double_click(mut self, value: bool) -> Self {
|
||||
self.flags.set(SelectableFlags::ALLOW_DOUBLE_CLICK, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the selectable.
|
||||
///
|
||||
/// When disabled, it cannot be selected and the text uses the disabled text color.
|
||||
///
|
||||
/// Default: disabled
|
||||
#[inline]
|
||||
pub fn disabled(mut self, value: bool) -> Self {
|
||||
self.flags.set(SelectableFlags::DISABLED, value);
|
||||
self
|
||||
}
|
||||
/// Sets the size of the selectable.
|
||||
///
|
||||
/// For the X axis:
|
||||
///
|
||||
/// - `> 0.0`: use given width
|
||||
/// - `= 0.0`: use remaining width
|
||||
///
|
||||
/// For the Y axis:
|
||||
///
|
||||
/// - `> 0.0`: use given height
|
||||
/// - `= 0.0`: use label height
|
||||
#[inline]
|
||||
pub fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
/// Builds the selectable.
|
||||
///
|
||||
/// Returns true if the selectable was clicked.
|
||||
pub fn build(self, ui: &Ui<'_>) -> bool {
|
||||
unsafe {
|
||||
sys::igSelectable_Bool(
|
||||
ui.scratch_txt(self.label),
|
||||
self.selected,
|
||||
self.flags.bits() as i32,
|
||||
self.size.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the selectable using a mutable reference to selected state.
|
||||
pub fn build_with_ref(self, ui: &Ui<'_>, selected: &mut bool) -> bool {
|
||||
if self.selected(*selected).build(ui) {
|
||||
*selected = !*selected;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
342
plugins/libimhex-rust/imgui-rs/src/widget/slider.rs
Normal file
342
plugins/libimhex-rust/imgui-rs/src/widget/slider.rs
Normal file
@ -0,0 +1,342 @@
|
||||
use bitflags::bitflags;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::internal::DataTypeKind;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
bitflags!(
|
||||
/// Flags for sliders
|
||||
#[repr(transparent)]
|
||||
pub struct SliderFlags: u32 {
|
||||
/// Clamp value to min/max bounds when input manually with CTRL+Click.
|
||||
///
|
||||
/// By default CTRL+click allows going out of bounds.
|
||||
const ALWAYS_CLAMP = sys::ImGuiSliderFlags_AlwaysClamp;
|
||||
/// Make the widget logarithmic instead of linear
|
||||
const LOGARITHMIC = sys::ImGuiSliderFlags_Logarithmic;
|
||||
/// Disable rounding underlying value to match precision of the display format string
|
||||
const NO_ROUND_TO_FORMAT = sys::ImGuiSliderFlags_NoRoundToFormat;
|
||||
/// Disable CTRL+Click or Enter key allowing to input text directly into the widget
|
||||
const NO_INPUT = sys::ImGuiSliderFlags_NoInput;
|
||||
}
|
||||
);
|
||||
|
||||
/// Builder for a slider widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct Slider<Label, Data, Format = &'static str> {
|
||||
label: Label,
|
||||
min: Data,
|
||||
max: Data,
|
||||
display_format: Option<Format>,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>, K: DataTypeKind> Slider<T, K> {
|
||||
/// Constructs a new slider builder with the given range.
|
||||
#[doc(alias = "SliderScalar", alias = "SliderScalarN")]
|
||||
pub fn new(label: T, min: K, max: K) -> Self {
|
||||
Slider {
|
||||
label,
|
||||
min,
|
||||
max,
|
||||
display_format: None,
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Label, Data, Format> Slider<Label, Data, Format>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
Data: DataTypeKind,
|
||||
Format: AsRef<str>,
|
||||
{
|
||||
/// Sets the range inclusively, such that both values given
|
||||
/// are valid values which the slider can be dragged to.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use imgui::im_str;
|
||||
/// imgui::Slider::new(im_str!("Example"), i8::MIN, i8::MAX)
|
||||
/// .range(4, 8)
|
||||
/// // Remember to call .build(&ui)
|
||||
/// ;
|
||||
/// ```
|
||||
///
|
||||
/// It is safe, though up to C++ Dear ImGui, on how to handle when
|
||||
/// `min > max`.
|
||||
#[inline]
|
||||
pub fn range(mut self, min: Data, max: Data) -> Self {
|
||||
self.min = min;
|
||||
self.max = max;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn display_format<Format2: AsRef<str>>(
|
||||
self,
|
||||
display_format: Format2,
|
||||
) -> Slider<Label, Data, Format2> {
|
||||
Slider {
|
||||
label: self.label,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: Some(display_format),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: SliderFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Builds a slider that is bound to the given value.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
pub fn build(self, ui: &Ui<'_>, value: &mut Data) -> bool {
|
||||
unsafe {
|
||||
let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igSliderScalar(
|
||||
label,
|
||||
Data::KIND as i32,
|
||||
value as *mut Data as *mut c_void,
|
||||
&self.min as *const Data as *const c_void,
|
||||
&self.max as *const Data as *const c_void,
|
||||
display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
/// Builds a horizontal array of multiple sliders attached to the given slice.
|
||||
///
|
||||
/// Returns true if any slider value was changed.
|
||||
pub fn build_array(self, ui: &Ui<'_>, values: &mut [Data]) -> bool {
|
||||
unsafe {
|
||||
let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igSliderScalarN(
|
||||
label,
|
||||
Data::KIND as i32,
|
||||
values.as_mut_ptr() as *mut c_void,
|
||||
values.len() as i32,
|
||||
&self.min as *const Data as *const c_void,
|
||||
&self.max as *const Data as *const c_void,
|
||||
display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a vertical slider widget.
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct VerticalSlider<Label, Data, Format = &'static str> {
|
||||
label: Label,
|
||||
size: [f32; 2],
|
||||
min: Data,
|
||||
max: Data,
|
||||
display_format: Option<Format>,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<Label, Data> VerticalSlider<Label, Data>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
Data: DataTypeKind,
|
||||
{
|
||||
/// Constructs a new vertical slider builder with the given size and range.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use imgui::im_str;
|
||||
/// imgui::VerticalSlider::new(im_str!("Example"), [20.0, 20.0], i8::MIN, i8::MAX)
|
||||
/// .range(4, 8)
|
||||
/// // Remember to call .build(&ui)
|
||||
/// ;
|
||||
/// ```
|
||||
///
|
||||
/// It is safe, though up to C++ Dear ImGui, on how to handle when
|
||||
/// `min > max`.
|
||||
#[doc(alias = "VSliderScalar")]
|
||||
pub fn new(label: Label, size: [f32; 2], min: Data, max: Data) -> Self {
|
||||
VerticalSlider {
|
||||
label,
|
||||
size,
|
||||
min,
|
||||
max,
|
||||
display_format: None,
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Label, Data, Format> VerticalSlider<Label, Data, Format>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
Data: DataTypeKind,
|
||||
Format: AsRef<str>,
|
||||
{
|
||||
/// Sets the range for the vertical slider.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use imgui::im_str;
|
||||
/// imgui::VerticalSlider::new(im_str!("Example"), [20.0, 20.0], i8::MIN, i8::MAX)
|
||||
/// .range(4, 8)
|
||||
/// // Remember to call .build(&ui)
|
||||
/// ;
|
||||
/// ```
|
||||
///
|
||||
/// It is safe, though up to C++ Dear ImGui, on how to handle when
|
||||
/// `min > max`.
|
||||
#[inline]
|
||||
pub fn range(mut self, min: Data, max: Data) -> Self {
|
||||
self.min = min;
|
||||
self.max = max;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn display_format<Format2: AsRef<str>>(
|
||||
self,
|
||||
display_format: Format2,
|
||||
) -> VerticalSlider<Label, Data, Format2> {
|
||||
VerticalSlider {
|
||||
label: self.label,
|
||||
size: self.size,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
display_format: Some(display_format),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: SliderFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Builds a vertical slider that is bound to the given value.
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
pub fn build(self, ui: &Ui<'_>, value: &mut Data) -> bool {
|
||||
unsafe {
|
||||
let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
|
||||
|
||||
sys::igVSliderScalar(
|
||||
label,
|
||||
self.size.into(),
|
||||
Data::KIND as i32,
|
||||
value as *mut Data as *mut c_void,
|
||||
&self.min as *const Data as *const c_void,
|
||||
&self.max as *const Data as *const c_void,
|
||||
display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for an angle slider widget.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct AngleSlider<Label, Format = &'static str> {
|
||||
label: Label,
|
||||
min_degrees: f32,
|
||||
max_degrees: f32,
|
||||
display_format: Format,
|
||||
flags: SliderFlags,
|
||||
}
|
||||
|
||||
impl<Label> AngleSlider<Label>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
{
|
||||
/// Constructs a new angle slider builder, where its minimum defaults to -360.0 and
|
||||
/// maximum defaults to 360.0
|
||||
#[doc(alias = "SliderAngle")]
|
||||
pub fn new(label: Label) -> Self {
|
||||
AngleSlider {
|
||||
label,
|
||||
min_degrees: -360.0,
|
||||
max_degrees: 360.0,
|
||||
display_format: "%.0f deg",
|
||||
flags: SliderFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Label, Format> AngleSlider<Label, Format>
|
||||
where
|
||||
Label: AsRef<str>,
|
||||
Format: AsRef<str>,
|
||||
{
|
||||
/// Sets the range in degrees (inclusive)
|
||||
/// ```rust
|
||||
/// # use imgui::im_str;
|
||||
/// imgui::AngleSlider::new(im_str!("Example"))
|
||||
/// .range_degrees(-20.0, 20.0)
|
||||
/// // Remember to call .build(&ui)
|
||||
/// ;
|
||||
/// ```
|
||||
///
|
||||
/// It is safe, though up to C++ Dear ImGui, on how to handle when
|
||||
/// `min > max`.
|
||||
#[inline]
|
||||
pub fn range_degrees(mut self, min_degrees: f32, max_degrees: f32) -> Self {
|
||||
self.min_degrees = min_degrees;
|
||||
self.max_degrees = max_degrees;
|
||||
self
|
||||
}
|
||||
/// Sets the minimum value (in degrees)
|
||||
#[inline]
|
||||
pub fn min_degrees(mut self, min_degrees: f32) -> Self {
|
||||
self.min_degrees = min_degrees;
|
||||
self
|
||||
}
|
||||
/// Sets the maximum value (in degrees)
|
||||
#[inline]
|
||||
pub fn max_degrees(mut self, max_degrees: f32) -> Self {
|
||||
self.max_degrees = max_degrees;
|
||||
self
|
||||
}
|
||||
/// Sets the display format using *a C-style printf string*
|
||||
#[inline]
|
||||
pub fn display_format<Format2: AsRef<str>>(
|
||||
self,
|
||||
display_format: Format2,
|
||||
) -> AngleSlider<Label, Format2> {
|
||||
AngleSlider {
|
||||
label: self.label,
|
||||
min_degrees: self.min_degrees,
|
||||
max_degrees: self.max_degrees,
|
||||
display_format,
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: SliderFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Builds an angle slider that is bound to the given value (in radians).
|
||||
///
|
||||
/// Returns true if the slider value was changed.
|
||||
pub fn build(self, ui: &Ui<'_>, value_rad: &mut f32) -> bool {
|
||||
unsafe {
|
||||
let (label, display_format) = ui.scratch_txt_two(self.label, self.display_format);
|
||||
|
||||
sys::igSliderAngle(
|
||||
label,
|
||||
value_rad as *mut _,
|
||||
self.min_degrees,
|
||||
self.max_degrees,
|
||||
display_format,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
238
plugins/libimhex-rust/imgui-rs/src/widget/tab.rs
Normal file
238
plugins/libimhex-rust/imgui-rs/src/widget/tab.rs
Normal file
@ -0,0 +1,238 @@
|
||||
//! # Examples
|
||||
//
|
||||
//! ```no_run
|
||||
//! # use imgui::*;
|
||||
//! # let mut ctx = Context::create();
|
||||
//! # let ui = ctx.frame();
|
||||
//!
|
||||
//! // During UI construction
|
||||
//! TabBar::new(im_str!("tabbar")).build(&ui, || {
|
||||
//! TabItem::new(im_str!("a tab")).build(&ui, || {
|
||||
//! ui.text(im_str!("tab content 1"));
|
||||
//! });
|
||||
//! TabItem::new(im_str!("2tab")).build(&ui, || {
|
||||
//! ui.text(im_str!("tab content 2"));
|
||||
//! });
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! See `test_window_impl.rs` for a more complicated example.
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
use bitflags::bitflags;
|
||||
use std::ptr;
|
||||
|
||||
bitflags! {
|
||||
#[repr(transparent)]
|
||||
pub struct TabBarFlags: u32 {
|
||||
const REORDERABLE = sys::ImGuiTabBarFlags_Reorderable;
|
||||
const AUTO_SELECT_NEW_TABS = sys::ImGuiTabBarFlags_AutoSelectNewTabs;
|
||||
const TAB_LIST_POPUP_BUTTON = sys::ImGuiTabBarFlags_TabListPopupButton;
|
||||
const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabBarFlags_NoCloseWithMiddleMouseButton;
|
||||
const NO_TAB_LIST_SCROLLING_BUTTONS = sys::ImGuiTabBarFlags_NoTabListScrollingButtons;
|
||||
const NO_TOOLTIP = sys::ImGuiTabBarFlags_NoTooltip;
|
||||
const FITTING_POLICY_RESIZE_DOWN = sys::ImGuiTabBarFlags_FittingPolicyResizeDown;
|
||||
const FITTING_POLICY_SCROLL = sys::ImGuiTabBarFlags_FittingPolicyScroll;
|
||||
const FITTING_POLICY_MASK = sys::ImGuiTabBarFlags_FittingPolicyMask_;
|
||||
const FITTING_POLICY_DEFAULT = sys::ImGuiTabBarFlags_FittingPolicyDefault_;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(transparent)]
|
||||
pub struct TabItemFlags: u32 {
|
||||
const UNSAVED_DOCUMENT = sys::ImGuiTabItemFlags_UnsavedDocument;
|
||||
const SET_SELECTED = sys::ImGuiTabItemFlags_SetSelected;
|
||||
const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
|
||||
const NO_PUSH_ID = sys::ImGuiTabItemFlags_NoPushId;
|
||||
const NO_TOOLTIP = sys::ImGuiTabItemFlags_NoTooltip;
|
||||
const NO_REORDER = sys::ImGuiTabItemFlags_NoReorder;
|
||||
const LEADING = sys::ImGuiTabItemFlags_Leading;
|
||||
const TRAILING = sys::ImGuiTabItemFlags_Trailing;
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a tab bar.
|
||||
pub struct TabBar<T> {
|
||||
id: T,
|
||||
flags: TabBarFlags,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> TabBar<T> {
|
||||
#[inline]
|
||||
#[doc(alias = "BeginTabBar")]
|
||||
pub fn new(id: T) -> Self {
|
||||
Self {
|
||||
id,
|
||||
flags: TabBarFlags::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/Disable the reorderable property
|
||||
///
|
||||
/// Disabled by default
|
||||
#[inline]
|
||||
pub fn reorderable(mut self, value: bool) -> Self {
|
||||
self.flags.set(TabBarFlags::REORDERABLE, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the flags of the tab bar.
|
||||
///
|
||||
/// Flags are empty by default
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: TabBarFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &'ui Ui<'_>) -> Option<TabBarToken<'ui>> {
|
||||
ui.tab_bar_with_flags(self.id, self.flags)
|
||||
}
|
||||
|
||||
/// Creates a tab bar and runs a closure to construct the contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if no tabbar content is visible
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_tab| f())
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a window that can be ended by calling `.end()`
|
||||
/// or by dropping
|
||||
pub struct TabBarToken<'ui>;
|
||||
|
||||
/// Ends a tab bar.
|
||||
drop { sys::igEndTabBar() }
|
||||
);
|
||||
|
||||
pub struct TabItem<'a, T> {
|
||||
label: T,
|
||||
opened: Option<&'a mut bool>,
|
||||
flags: TabItemFlags,
|
||||
}
|
||||
|
||||
impl<'a, T: AsRef<str>> TabItem<'a, T> {
|
||||
#[doc(alias = "BeginTabItem")]
|
||||
pub fn new(name: T) -> Self {
|
||||
Self {
|
||||
label: name,
|
||||
opened: None,
|
||||
flags: TabItemFlags::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Will open or close the tab.
|
||||
///
|
||||
/// True to display the tab. Tab item is visible by default.
|
||||
#[inline]
|
||||
pub fn opened(mut self, opened: &'a mut bool) -> Self {
|
||||
self.opened = Some(opened);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the flags of the tab item.
|
||||
///
|
||||
/// Flags are empty by default
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: TabItemFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &'ui Ui<'_>) -> Option<TabItemToken<'ui>> {
|
||||
ui.tab_item_with_flags(self.label, self.opened, self.flags)
|
||||
}
|
||||
|
||||
/// Creates a tab item and runs a closure to construct the contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if the tab item is not selected
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_tab| f())
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a tab bar item that can be ended by calling `.end()`
|
||||
/// or by dropping
|
||||
pub struct TabItemToken<'ui>;
|
||||
|
||||
/// Ends a tab bar item.
|
||||
drop { sys::igEndTabItem() }
|
||||
);
|
||||
|
||||
impl Ui<'_> {
|
||||
/// Creates a tab bar and returns a tab bar token, allowing you to append
|
||||
/// Tab items afterwards. This passes no flags. To pass flags explicitly,
|
||||
/// use [tab_bar_with_flags](Self::tab_bar_with_flags).
|
||||
pub fn tab_bar(&self, id: impl AsRef<str>) -> Option<TabBarToken<'_>> {
|
||||
self.tab_bar_with_flags(id, TabBarFlags::empty())
|
||||
}
|
||||
//
|
||||
/// Creates a tab bar and returns a tab bar token, allowing you to append
|
||||
/// Tab items afterwards.
|
||||
pub fn tab_bar_with_flags(
|
||||
&self,
|
||||
id: impl AsRef<str>,
|
||||
flags: TabBarFlags,
|
||||
) -> Option<TabBarToken<'_>> {
|
||||
let should_render =
|
||||
unsafe { sys::igBeginTabBar(self.scratch_txt(id), flags.bits() as i32) };
|
||||
|
||||
if should_render {
|
||||
Some(TabBarToken::new(self))
|
||||
} else {
|
||||
unsafe { sys::igEndTabBar() };
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new tab item and returns a token if its contents are visible.
|
||||
///
|
||||
/// By default, this doesn't pass an opened bool nor any flags. See [tab_item_with_opened]
|
||||
/// and [tab_item_with_flags] for more.
|
||||
///
|
||||
/// [tab_item_with_opened]: Self::tab_item_with_opened
|
||||
/// [tab_item_with_flags]: Self::tab_item_with_flags
|
||||
pub fn tab_item(&self, label: impl AsRef<str>) -> Option<TabItemToken<'_>> {
|
||||
self.tab_item_with_flags(label, None, TabItemFlags::empty())
|
||||
}
|
||||
|
||||
/// Creates a new tab item and returns a token if its contents are visible.
|
||||
///
|
||||
/// By default, this doesn't pass any flags. See `[tab_item_with_flags]` for more.
|
||||
pub fn tab_item_with_opened(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
opened: &mut bool,
|
||||
) -> Option<TabItemToken<'_>> {
|
||||
self.tab_item_with_flags(label, Some(opened), TabItemFlags::empty())
|
||||
}
|
||||
|
||||
/// Creates a new tab item and returns a token if its contents are visible.
|
||||
pub fn tab_item_with_flags(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
opened: Option<&mut bool>,
|
||||
flags: TabItemFlags,
|
||||
) -> Option<TabItemToken<'_>> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginTabItem(
|
||||
self.scratch_txt(label),
|
||||
opened.map(|x| x as *mut bool).unwrap_or(ptr::null_mut()),
|
||||
flags.bits() as i32,
|
||||
)
|
||||
};
|
||||
|
||||
if should_render {
|
||||
Some(TabItemToken::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
55
plugins/libimhex-rust/imgui-rs/src/widget/text.rs
Normal file
55
plugins/libimhex-rust/imgui-rs/src/widget/text.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use std::os::raw::c_char;
|
||||
|
||||
// use crate::string::ImStr;
|
||||
use crate::style::StyleColor;
|
||||
use crate::Ui;
|
||||
|
||||
static FMT: &[u8] = b"%s\0";
|
||||
|
||||
#[inline]
|
||||
fn fmt_ptr() -> *const c_char {
|
||||
FMT.as_ptr() as *const c_char
|
||||
}
|
||||
|
||||
/// # Widgets: Text
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Renders simple text
|
||||
#[doc(alias = "TextUnformatted")]
|
||||
pub fn text<T: AsRef<str>>(&self, text: T) {
|
||||
let s = text.as_ref();
|
||||
unsafe {
|
||||
let start = s.as_ptr();
|
||||
let end = start.add(s.len());
|
||||
sys::igTextUnformatted(start as *const c_char, end as *const c_char);
|
||||
}
|
||||
}
|
||||
/// Renders simple text using the given text color
|
||||
pub fn text_colored<T: AsRef<str>>(&self, color: [f32; 4], text: T) {
|
||||
let style = self.push_style_color(StyleColor::Text, color);
|
||||
self.text(text);
|
||||
style.end();
|
||||
}
|
||||
/// Renders simple text using `StyleColor::TextDisabled` color
|
||||
pub fn text_disabled<T: AsRef<str>>(&self, text: T) {
|
||||
let color = self.style_color(StyleColor::TextDisabled);
|
||||
let style = self.push_style_color(StyleColor::Text, color);
|
||||
self.text(text);
|
||||
style.end();
|
||||
}
|
||||
/// Renders text wrapped to the end of window (or column)
|
||||
#[doc(alias = "TextWrapperd")]
|
||||
pub fn text_wrapped(&self, text: impl AsRef<str>) {
|
||||
unsafe { sys::igTextWrapped(fmt_ptr(), self.scratch_txt(text)) }
|
||||
}
|
||||
/// Render a text + label combination aligned the same way as value+label widgets
|
||||
#[doc(alias = "LabelText")]
|
||||
pub fn label_text(&self, label: impl AsRef<str>, text: impl AsRef<str>) {
|
||||
let (ptr_one, ptr_two) = self.scratch_txt_two(label, text);
|
||||
unsafe { sys::igLabelText(ptr_one, fmt_ptr(), ptr_two) }
|
||||
}
|
||||
/// Renders text with a little bullet aligned to the typical tree node
|
||||
#[doc(alias = "BulletText")]
|
||||
pub fn bullet_text(&self, text: impl AsRef<str>) {
|
||||
unsafe { sys::igBulletText(fmt_ptr(), self.scratch_txt(text)) }
|
||||
}
|
||||
}
|
475
plugins/libimhex-rust/imgui-rs/src/widget/tree.rs
Normal file
475
plugins/libimhex-rust/imgui-rs/src/widget/tree.rs
Normal file
@ -0,0 +1,475 @@
|
||||
use bitflags::bitflags;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
|
||||
// use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::{Condition, Ui};
|
||||
|
||||
bitflags!(
|
||||
/// Flags for tree nodes
|
||||
#[repr(transparent)]
|
||||
pub struct TreeNodeFlags: u32 {
|
||||
/// Draw as selected
|
||||
const SELECTED = sys::ImGuiTreeNodeFlags_Selected;
|
||||
/// Full colored frame (e.g. for CollapsingHeader)
|
||||
const FRAMED = sys::ImGuiTreeNodeFlags_Framed;
|
||||
/// Hit testing to allow subsequent widgets to overlap this one
|
||||
const ALLOW_ITEM_OVERLAP = sys::ImGuiTreeNodeFlags_AllowItemOverlap;
|
||||
/// Don't push a tree node when open (e.g. for CollapsingHeader) = no extra indent nor
|
||||
/// pushing on ID stack
|
||||
const NO_TREE_PUSH_ON_OPEN = sys::ImGuiTreeNodeFlags_NoTreePushOnOpen;
|
||||
/// Don't automatically and temporarily open node when Logging is active (by default
|
||||
/// logging will automatically open tree nodes)
|
||||
const NO_AUTO_OPEN_ON_LOG = sys::ImGuiTreeNodeFlags_NoAutoOpenOnLog;
|
||||
/// Default node to be open
|
||||
const DEFAULT_OPEN = sys::ImGuiTreeNodeFlags_DefaultOpen;
|
||||
/// Need double-click to open node
|
||||
const OPEN_ON_DOUBLE_CLICK = sys::ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
||||
/// Only open when clicking on the arrow part.
|
||||
///
|
||||
/// If `TreeNodeFlags::OPEN_ON_DOUBLE_CLICK` is also set, single-click arrow or
|
||||
/// double-click all box to open.
|
||||
const OPEN_ON_ARROW = sys::ImGuiTreeNodeFlags_OpenOnArrow;
|
||||
/// No collapsing, no arrow (use as a convenience for leaf nodes)
|
||||
const LEAF = sys::ImGuiTreeNodeFlags_Leaf;
|
||||
/// Display a bullet instead of arrow
|
||||
const BULLET = sys::ImGuiTreeNodeFlags_Bullet;
|
||||
/// Use `Style::frame_padding` (even for an unframed text node) to vertically align text
|
||||
/// baseline to regular widget height.
|
||||
///
|
||||
/// Equivalent to calling `Ui::align_text_to_frame_padding`.
|
||||
const FRAME_PADDING = sys::ImGuiTreeNodeFlags_FramePadding;
|
||||
/// Extend hit box to the right-most edge, even if not framed.
|
||||
///
|
||||
/// This is not the default in order to allow adding other items on the same line. In the
|
||||
/// future we may refactor the hit system to be front-to-back, allowing natural overlaps
|
||||
/// and then this can become the default.
|
||||
const SPAN_AVAIL_WIDTH = sys::ImGuiTreeNodeFlags_SpanAvailWidth;
|
||||
/// Extend hit box to the left-most and right-most edges (bypass the indented area)
|
||||
const SPAN_FULL_WIDTH = sys::ImGuiTreeNodeFlags_SpanFullWidth;
|
||||
/// (WIP) Nav: left direction may move to this tree node from any of its child
|
||||
const NAV_LEFT_JUMPS_BACK_HERE = sys::ImGuiTreeNodeFlags_NavLeftJumpsBackHere;
|
||||
}
|
||||
);
|
||||
|
||||
static FMT: &[u8] = b"%s\0";
|
||||
|
||||
#[inline]
|
||||
fn fmt_ptr() -> *const c_char {
|
||||
FMT.as_ptr() as *const c_char
|
||||
}
|
||||
|
||||
/// Unique ID used by tree nodes
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TreeNodeId<T> {
|
||||
Str(T),
|
||||
Ptr(*const c_void),
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> From<T> for TreeNodeId<T> {
|
||||
fn from(s: T) -> Self {
|
||||
TreeNodeId::Str(s)
|
||||
}
|
||||
}
|
||||
|
||||
// this is a bit wonky here using the T param...
|
||||
impl<T> From<*const T> for TreeNodeId<T> {
|
||||
fn from(p: *const T) -> Self {
|
||||
TreeNodeId::Ptr(p as *const c_void)
|
||||
}
|
||||
}
|
||||
|
||||
// this is a bit wonky here using the T param...
|
||||
impl<T> From<*mut T> for TreeNodeId<T> {
|
||||
fn from(p: *mut T) -> Self {
|
||||
TreeNodeId::Ptr(p as *const T as *const c_void)
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a tree node widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct TreeNode<T, L = &'static str> {
|
||||
id: TreeNodeId<T>,
|
||||
label: Option<L>,
|
||||
opened: bool,
|
||||
opened_cond: Condition,
|
||||
flags: TreeNodeFlags,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> TreeNode<T, &'static str> {
|
||||
/// Constructs a new tree node builder
|
||||
pub fn new<I: Into<TreeNodeId<T>>>(id: I) -> TreeNode<T, &'static str> {
|
||||
TreeNode {
|
||||
id: id.into(),
|
||||
label: None,
|
||||
opened: false,
|
||||
opened_cond: Condition::Never,
|
||||
flags: TreeNodeFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>, L: AsRef<str>> TreeNode<T, L> {
|
||||
/// Sets the tree node label
|
||||
pub fn label<I: Into<TreeNodeId<L2>>, L2: AsRef<str>>(self, label: L2) -> TreeNode<T, L2> {
|
||||
TreeNode {
|
||||
label: Some(label),
|
||||
id: self.id,
|
||||
opened: self.opened,
|
||||
opened_cond: self.opened_cond,
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the opened state of the tree node, which is applied based on the given condition value
|
||||
pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
|
||||
self.opened = opened;
|
||||
self.opened_cond = cond;
|
||||
self
|
||||
}
|
||||
|
||||
/// Replaces all current settings with the given flags.
|
||||
pub fn flags(mut self, flags: TreeNodeFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables drawing the tree node in selected state.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn selected(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::SELECTED, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables full-colored frame.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn framed(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::FRAMED, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables allowing the tree node to overlap subsequent widgets.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn allow_item_overlap(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables automatic tree push when the tree node is open (= adds extra indentation
|
||||
/// and pushes to the ID stack).
|
||||
///
|
||||
/// Enabled by default.
|
||||
pub fn tree_push_on_open(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, !value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables/disables automatic opening of the tree node when logging is active.
|
||||
///
|
||||
/// By default, logging will automatically open all tree nodes.
|
||||
///
|
||||
/// Enabled by default.
|
||||
pub fn auto_open_on_log(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, !value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the default open state for the tree node.
|
||||
///
|
||||
/// Tree nodes are closed by default.
|
||||
pub fn default_open(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::DEFAULT_OPEN, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Only open when the tree node is double-clicked.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn open_on_double_click(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Only open when clicking the arrow part of the tree node.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn open_on_arrow(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable/disables leaf mode (no collapsing, no arrow).
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn leaf(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::LEAF, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Display a bullet instead of arrow.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn bullet(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::BULLET, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Use `frame_padding` to vertically align text baseline to regular widget height.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn frame_padding(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::FRAME_PADDING, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Left direction may move to this tree node from any of its child.
|
||||
///
|
||||
/// Disabled by default.
|
||||
pub fn nav_left_jumps_back_here(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Pushes a tree node and starts appending to it.
|
||||
///
|
||||
/// Returns `Some(TreeNodeToken)` if the tree node is open. After content has been
|
||||
/// rendered, the token can be popped by calling `.pop()`.
|
||||
///
|
||||
/// Returns `None` if the tree node is not open and no content should be rendered.
|
||||
pub fn push<'ui>(self, ui: &Ui<'ui>) -> Option<TreeNodeToken<'ui>> {
|
||||
let open = unsafe {
|
||||
if self.opened_cond != Condition::Never {
|
||||
sys::igSetNextItemOpen(self.opened, self.opened_cond as i32);
|
||||
}
|
||||
match self.id {
|
||||
TreeNodeId::Str(id) => {
|
||||
let (id, label) = match self.label {
|
||||
Some(label) => ui.scratch_txt_two(id, label),
|
||||
None => {
|
||||
let v = ui.scratch_txt(id);
|
||||
(v, v)
|
||||
}
|
||||
};
|
||||
|
||||
sys::igTreeNodeEx_StrStr(id, self.flags.bits() as i32, fmt_ptr(), label)
|
||||
}
|
||||
TreeNodeId::Ptr(id) => sys::igTreeNodeEx_Ptr(
|
||||
id,
|
||||
self.flags.bits() as i32,
|
||||
fmt_ptr(),
|
||||
match self.label {
|
||||
Some(v) => ui.scratch_txt(v),
|
||||
None => ui.scratch_txt(""),
|
||||
},
|
||||
),
|
||||
}
|
||||
};
|
||||
if open {
|
||||
Some(TreeNodeToken::new(
|
||||
ui,
|
||||
!self.flags.contains(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a tree node and runs a closure to construct the contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if the tree node is not open.
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.push(ui).map(|_node| f())
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks a tree node that can be popped by calling `.pop()`, `end()`, or by dropping.
|
||||
///
|
||||
/// If `TreeNodeFlags::NO_TREE_PUSH_ON_OPEN` was used when this token was created, calling `.pop()`
|
||||
/// is not mandatory and is a no-op.
|
||||
#[must_use]
|
||||
pub struct TreeNodeToken<'a>(core::marker::PhantomData<crate::Ui<'a>>, bool);
|
||||
|
||||
impl<'a> TreeNodeToken<'a> {
|
||||
/// Creates a new token type. This takes a bool for the no-op variant on NO_TREE_PUSH_ON_OPEN.
|
||||
pub(crate) fn new(_: &crate::Ui<'a>, execute_drop: bool) -> Self {
|
||||
Self(std::marker::PhantomData, execute_drop)
|
||||
}
|
||||
|
||||
/// Pops a tree node
|
||||
#[inline]
|
||||
pub fn end(self) {
|
||||
// left empty for drop
|
||||
}
|
||||
|
||||
/// Pops a tree node
|
||||
#[inline]
|
||||
pub fn pop(self) {
|
||||
self.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TreeNodeToken<'_> {
|
||||
#[doc(alias = "TreePop")]
|
||||
fn drop(&mut self) {
|
||||
if self.1 {
|
||||
unsafe { sys::igTreePop() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a collapsing header widget
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct CollapsingHeader<T> {
|
||||
label: T,
|
||||
flags: TreeNodeFlags,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> CollapsingHeader<T> {
|
||||
/// Constructs a new collapsing header builder
|
||||
#[doc(alias = "CollapsingHeader")]
|
||||
pub fn new(label: T) -> CollapsingHeader<T> {
|
||||
CollapsingHeader {
|
||||
label,
|
||||
flags: TreeNodeFlags::empty(),
|
||||
}
|
||||
}
|
||||
/// Replaces all current settings with the given flags.
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: TreeNodeFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Enables/disables allowing the collapsing header to overlap subsequent widgets.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn allow_item_overlap(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, value);
|
||||
self
|
||||
}
|
||||
/// Sets the default open state for the collapsing header.
|
||||
///
|
||||
/// Collapsing headers are closed by default.
|
||||
#[inline]
|
||||
pub fn default_open(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::DEFAULT_OPEN, value);
|
||||
self
|
||||
}
|
||||
/// Only open when the collapsing header is double-clicked.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn open_on_double_click(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, value);
|
||||
self
|
||||
}
|
||||
/// Only open when clicking the arrow part of the collapsing header.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn open_on_arrow(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, value);
|
||||
self
|
||||
}
|
||||
/// Enable/disables leaf mode (no collapsing, no arrow).
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn leaf(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::LEAF, value);
|
||||
self
|
||||
}
|
||||
/// Display a bullet instead of arrow.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn bullet(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::BULLET, value);
|
||||
self
|
||||
}
|
||||
/// Use `frame_padding` to vertically align text baseline to regular widget height.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn frame_padding(mut self, value: bool) -> Self {
|
||||
self.flags.set(TreeNodeFlags::FRAME_PADDING, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Begins the collapsing header.
|
||||
///
|
||||
/// Returns true if the collapsing header is open and content should be rendered.
|
||||
///
|
||||
/// This is the same as [build](Self::build) but is provided for consistent naming.
|
||||
#[must_use]
|
||||
pub fn begin(self, ui: &Ui<'_>) -> bool {
|
||||
self.build(ui)
|
||||
}
|
||||
|
||||
/// Begins the collapsing header.
|
||||
///
|
||||
/// Returns true if the collapsing header is open and content should be rendered.
|
||||
///
|
||||
/// This is the same as [build_with_close_button](Self::build_with_close_button)
|
||||
/// but is provided for consistent naming.
|
||||
#[must_use]
|
||||
pub fn begin_with_close_button(self, ui: &Ui<'_>, opened: &mut bool) -> bool {
|
||||
self.build_with_close_button(ui, opened)
|
||||
}
|
||||
|
||||
/// Builds the collapsing header.
|
||||
///
|
||||
/// Returns true if the collapsing header is open and content should be rendered.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn build(self, ui: &Ui<'_>) -> bool {
|
||||
unsafe {
|
||||
sys::igCollapsingHeader_TreeNodeFlags(
|
||||
ui.scratch_txt(self.label),
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
/// Builds the collapsing header, and adds an additional close button that changes the value of
|
||||
/// the given mutable reference when clicked.
|
||||
///
|
||||
/// Returns true if the collapsing header is open and content should be rendered.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn build_with_close_button(self, ui: &Ui<'_>, opened: &mut bool) -> bool {
|
||||
unsafe {
|
||||
sys::igCollapsingHeader_BoolPtr(
|
||||
ui.scratch_txt(self.label),
|
||||
opened as *mut bool,
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ui<'_> {
|
||||
/// Constructs a new collapsing header
|
||||
#[doc(alias = "CollapsingHeader")]
|
||||
pub fn collapsing_header(&self, label: impl AsRef<str>, flags: TreeNodeFlags) -> bool {
|
||||
CollapsingHeader::new(label).flags(flags).build(self)
|
||||
}
|
||||
|
||||
/// Constructs a new collapsing header
|
||||
#[doc(alias = "CollapsingHeader")]
|
||||
pub fn collapsing_header_with_close_button(
|
||||
&self,
|
||||
label: impl AsRef<str>,
|
||||
flags: TreeNodeFlags,
|
||||
opened: &mut bool,
|
||||
) -> bool {
|
||||
CollapsingHeader::new(label)
|
||||
.flags(flags)
|
||||
.build_with_close_button(self, opened)
|
||||
}
|
||||
}
|
296
plugins/libimhex-rust/imgui-rs/src/window/child_window.rs
Normal file
296
plugins/libimhex-rust/imgui-rs/src/window/child_window.rs
Normal file
@ -0,0 +1,296 @@
|
||||
use std::f32;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
|
||||
use crate::sys;
|
||||
use crate::window::WindowFlags;
|
||||
use crate::{Id, Ui};
|
||||
|
||||
/// Builder for a child window
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ChildWindow<'a> {
|
||||
id: Id<'a>,
|
||||
flags: WindowFlags,
|
||||
size: [f32; 2],
|
||||
content_size: [f32; 2],
|
||||
focused: bool,
|
||||
bg_alpha: f32,
|
||||
border: bool,
|
||||
}
|
||||
|
||||
impl<'a> ChildWindow<'a> {
|
||||
/// Creates a new child window builder with the given ID
|
||||
#[doc(alias = "BeginChildID")]
|
||||
pub fn new<T: Into<Id<'a>>>(id: T) -> ChildWindow<'a> {
|
||||
ChildWindow {
|
||||
id: id.into(),
|
||||
flags: WindowFlags::empty(),
|
||||
size: [0.0, 0.0],
|
||||
content_size: [0.0, 0.0],
|
||||
focused: false,
|
||||
bg_alpha: f32::NAN,
|
||||
border: false,
|
||||
}
|
||||
}
|
||||
/// Replace current window flags with the given value
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: WindowFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Sets the child window size.
|
||||
///
|
||||
/// For each independent axis of size:
|
||||
///
|
||||
/// - `> 0.0`: fixed size
|
||||
/// - `= 0.0`: use remaining host window size
|
||||
/// - `< 0.0`: use remaining host window size minus abs(size)
|
||||
#[inline]
|
||||
pub fn size(mut self, size: [f32; 2]) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
/// Sets the window content size, which can be used to enforce scrollbars.
|
||||
///
|
||||
/// Does not include window decorations (title bar, menu bar, etc.). Set one of the values to
|
||||
/// 0.0 to leave the size automatic.
|
||||
#[inline]
|
||||
#[doc(alias = "SetNextWindowContentSize")]
|
||||
pub fn content_size(mut self, size: [f32; 2]) -> Self {
|
||||
self.content_size = size;
|
||||
self
|
||||
}
|
||||
/// Sets the window focused state, which can be used to bring the window to front
|
||||
#[inline]
|
||||
#[doc(alias = "SetNextWindwowFocus")]
|
||||
pub fn focused(mut self, focused: bool) -> Self {
|
||||
self.focused = focused;
|
||||
self
|
||||
}
|
||||
/// Sets the background color alpha value.
|
||||
///
|
||||
/// See also `draw_background`
|
||||
#[inline]
|
||||
#[doc(alias = "SetNextWindowContentBgAlpha")]
|
||||
pub fn bg_alpha(mut self, bg_alpha: f32) -> Self {
|
||||
self.bg_alpha = bg_alpha;
|
||||
self
|
||||
}
|
||||
/// Enables/disables the child window border.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn border(mut self, border: bool) -> Self {
|
||||
self.border = border;
|
||||
self
|
||||
}
|
||||
/// Enables/disables moving the window when child window is dragged.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn movable(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_MOVE, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables scrollbars (scrolling is still possible with the mouse or
|
||||
/// programmatically).
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn scroll_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_SCROLLBAR, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables vertical scrolling with the mouse wheel.
|
||||
///
|
||||
/// Enabled by default.
|
||||
/// When enabled, child windows forward the mouse wheel to the parent unless `NO_SCROLLBAR`
|
||||
/// is also set.
|
||||
#[inline]
|
||||
pub fn scrollable(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_SCROLL_WITH_MOUSE, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables resizing the window to its content on every frame.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn always_auto_resize(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::ALWAYS_AUTO_RESIZE, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables drawing of background color and outside border.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn draw_background(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_BACKGROUND, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables catching mouse input.
|
||||
///
|
||||
/// Enabled by default.
|
||||
/// Note: Hovering test will pass through when disabled
|
||||
#[inline]
|
||||
pub fn mouse_inputs(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_MOUSE_INPUTS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the menu bar.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn menu_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::MENU_BAR, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the horizontal scrollbar.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn horizontal_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::HORIZONTAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables taking focus when transitioning from hidden to visible state.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn focus_on_appearing(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_FOCUS_ON_APPEARING, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables bringing the window to front when taking focus (e.g. clicking it or
|
||||
/// programmatically giving it focus).
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn bring_to_front_on_focus(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::NO_BRING_TO_FRONT_ON_FOCUS, !value);
|
||||
self
|
||||
}
|
||||
/// When enabled, forces the vertical scrollbar to render regardless of the content size.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn always_vertical_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_VERTICAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
/// When enabled, forces the horizontal scrollbar to render regardless of the content size.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn always_horizontal_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_HORIZONTAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
/// When enabled, ensures child windows without border use `style.window_padding`.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn always_use_window_padding(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_USE_WINDOW_PADDING, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables gamepad/keyboard navigation within the window.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn nav_inputs(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_NAV_INPUTS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables focusing toward this window with gamepad/keyboard navigation (e.g.
|
||||
/// CTRL+TAB).
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn nav_focus(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_NAV_FOCUS, !value);
|
||||
self
|
||||
}
|
||||
/// Disable gamepad/keyboard navigation and focusing.
|
||||
///
|
||||
/// Shorthand for
|
||||
/// ```text
|
||||
/// .nav_inputs(false)
|
||||
/// .nav_focus(false)
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn no_nav(mut self) -> Self {
|
||||
self.flags |= WindowFlags::NO_NAV;
|
||||
self
|
||||
}
|
||||
/// Don't handle input.
|
||||
///
|
||||
/// Shorthand for
|
||||
/// ```text
|
||||
/// .mouse_inputs(false)
|
||||
/// .nav_inputs(false)
|
||||
/// .nav_focus(false)
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn no_inputs(mut self) -> Self {
|
||||
self.flags |= WindowFlags::NO_INPUTS;
|
||||
self
|
||||
}
|
||||
/// Creates a child window and starts append to it.
|
||||
///
|
||||
/// Returns `Some(ChildWindowToken)` if the window is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the window is not visible and no content should be rendered.
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ChildWindowToken<'ui>> {
|
||||
if self.content_size[0] != 0.0 || self.content_size[1] != 0.0 {
|
||||
unsafe { sys::igSetNextWindowContentSize(self.content_size.into()) };
|
||||
}
|
||||
if self.focused {
|
||||
unsafe { sys::igSetNextWindowFocus() };
|
||||
}
|
||||
if self.bg_alpha.is_finite() {
|
||||
unsafe { sys::igSetNextWindowBgAlpha(self.bg_alpha) };
|
||||
}
|
||||
let id = unsafe {
|
||||
match self.id {
|
||||
Id::Int(i) => sys::igGetID_Ptr(i as *const c_void),
|
||||
Id::Ptr(p) => sys::igGetID_Ptr(p),
|
||||
Id::Str(s) => {
|
||||
let start = s.as_ptr() as *const c_char;
|
||||
let end = start.add(s.len());
|
||||
sys::igGetID_StrStr(start, end)
|
||||
}
|
||||
}
|
||||
};
|
||||
let should_render = unsafe {
|
||||
sys::igBeginChild_ID(id, self.size.into(), self.border, self.flags.bits() as i32)
|
||||
};
|
||||
if should_render {
|
||||
Some(ChildWindowToken::new(ui))
|
||||
} else {
|
||||
unsafe { sys::igEndChild() };
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a child window and runs a closure to construct the contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if no window content is visible (e.g. window is collapsed
|
||||
/// or fully clipped).
|
||||
pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
|
||||
self.begin(ui).map(|_window| f())
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a child window that can be ended by calling `.end()`
|
||||
/// or by dropping
|
||||
pub struct ChildWindowToken<'ui>;
|
||||
|
||||
/// Ends a window
|
||||
drop { sys::igEndChild() }
|
||||
);
|
42
plugins/libimhex-rust/imgui-rs/src/window/content_region.rs
Normal file
42
plugins/libimhex-rust/imgui-rs/src/window/content_region.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// # Content region
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns the current content boundaries (in *window coordinates*)
|
||||
#[doc(alias = "GetContentRegionMax")]
|
||||
pub fn content_region_max(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetContentRegionMax(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
/// Equal to `ui.content_region_max()` - `ui.cursor_pos()`
|
||||
#[doc(alias = "GetContentRegionAvail")]
|
||||
pub fn content_region_avail(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetContentRegionAvail(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
/// Content boundaries min (in *window coordinates*).
|
||||
///
|
||||
/// Roughly equal to [0.0, 0.0] - scroll.
|
||||
#[doc(alias = "GetContentRegionMin")]
|
||||
pub fn window_content_region_min(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetWindowContentRegionMin(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
/// Content boundaries max (in *window coordinates*).
|
||||
///
|
||||
/// Roughly equal to [0.0, 0.0] + size - scroll.
|
||||
#[doc(alias = "GetContentRegionMax")]
|
||||
pub fn window_content_region_max(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetWindowContentRegionMax(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
#[doc(alias = "GetContentRegionWidth")]
|
||||
pub fn window_content_region_width(&self) -> f32 {
|
||||
unsafe { sys::igGetWindowContentRegionWidth() }
|
||||
}
|
||||
}
|
559
plugins/libimhex-rust/imgui-rs/src/window/mod.rs
Normal file
559
plugins/libimhex-rust/imgui-rs/src/window/mod.rs
Normal file
@ -0,0 +1,559 @@
|
||||
use bitflags::bitflags;
|
||||
use std::f32;
|
||||
use std::ptr;
|
||||
|
||||
use crate::sys;
|
||||
use crate::{Condition, Ui};
|
||||
|
||||
pub(crate) mod child_window;
|
||||
pub(crate) mod content_region;
|
||||
pub(crate) mod scroll;
|
||||
|
||||
bitflags! {
|
||||
/// Window hover check option flags
|
||||
#[repr(transparent)]
|
||||
pub struct WindowHoveredFlags: u32 {
|
||||
/// Return true if any child of the window is hovered
|
||||
const CHILD_WINDOWS = sys::ImGuiHoveredFlags_ChildWindows;
|
||||
/// Test from root window (top-most parent of the current hierarchy)
|
||||
const ROOT_WINDOW = sys::ImGuiHoveredFlags_RootWindow;
|
||||
/// Return true if any window is hovered
|
||||
const ANY_WINDOW = sys::ImGuiHoveredFlags_AnyWindow;
|
||||
/// Return true even if a popup window is blocking access to this window
|
||||
const ALLOW_WHEN_BLOCKED_BY_POPUP = sys::ImGuiHoveredFlags_AllowWhenBlockedByPopup;
|
||||
/// Return true even if an active item is blocking access to this window
|
||||
const ALLOW_WHEN_BLOCKED_BY_ACTIVE_ITEM = sys::ImGuiHoveredFlags_AllowWhenBlockedByActiveItem;
|
||||
/// Test from root window, and return true if any child is hovered
|
||||
const ROOT_AND_CHILD_WINDOWS = Self::ROOT_WINDOW.bits | Self::CHILD_WINDOWS.bits;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Window focus check option flags
|
||||
#[repr(transparent)]
|
||||
pub struct WindowFocusedFlags: u32 {
|
||||
/// Return true if any child of the window is focused
|
||||
const CHILD_WINDOWS = sys::ImGuiFocusedFlags_ChildWindows;
|
||||
/// Test from root window (top-most parent of the current hierarchy)
|
||||
const ROOT_WINDOW = sys::ImGuiFocusedFlags_RootWindow;
|
||||
/// Return true if any window is focused
|
||||
const ANY_WINDOW = sys::ImGuiFocusedFlags_AnyWindow;
|
||||
/// Test from root window, and return true if any child is focused
|
||||
const ROOT_AND_CHILD_WINDOWS = Self::ROOT_WINDOW.bits | Self::CHILD_WINDOWS.bits;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Configuration flags for windows
|
||||
#[repr(transparent)]
|
||||
pub struct WindowFlags: u32 {
|
||||
/// Disable the title bar
|
||||
const NO_TITLE_BAR = sys::ImGuiWindowFlags_NoTitleBar;
|
||||
/// Disable resizing with the lower-right grip
|
||||
const NO_RESIZE = sys::ImGuiWindowFlags_NoResize;
|
||||
/// Disable moving the window
|
||||
const NO_MOVE = sys::ImGuiWindowFlags_NoMove;
|
||||
/// Disable scrollbars (scrolling is still possible with the mouse or programmatically)
|
||||
const NO_SCROLLBAR = sys::ImGuiWindowFlags_NoScrollbar;
|
||||
/// Disable vertical scrolling with the mouse wheel.
|
||||
///
|
||||
/// On child window, the mouse wheel will be forwarded to the parent unless `NO_SCROLLBAR`
|
||||
/// is also set.
|
||||
const NO_SCROLL_WITH_MOUSE = sys::ImGuiWindowFlags_NoScrollWithMouse;
|
||||
/// Disable collapsing the window by double-clicking it
|
||||
const NO_COLLAPSE = sys::ImGuiWindowFlags_NoCollapse;
|
||||
/// Resize the window to its content on every frame
|
||||
const ALWAYS_AUTO_RESIZE = sys::ImGuiWindowFlags_AlwaysAutoResize;
|
||||
/// Disable drawing of background color and outside border
|
||||
const NO_BACKGROUND = sys::ImGuiWindowFlags_NoBackground;
|
||||
/// Never load/save settings
|
||||
const NO_SAVED_SETTINGS = sys::ImGuiWindowFlags_NoSavedSettings;
|
||||
/// Disable catching mouse input. Hovering test will pass through
|
||||
const NO_MOUSE_INPUTS = sys::ImGuiWindowFlags_NoMouseInputs;
|
||||
/// Show a menu bar
|
||||
const MENU_BAR = sys::ImGuiWindowFlags_MenuBar;
|
||||
/// Allow horizontal scrollbar to appear
|
||||
const HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_HorizontalScrollbar;
|
||||
/// Disable taking focus when transitioning from hidden to visible state
|
||||
const NO_FOCUS_ON_APPEARING = sys::ImGuiWindowFlags_NoFocusOnAppearing;
|
||||
/// Disable bringing window to front when taking focus (e.g. clicking it or
|
||||
/// programmatically giving it focus)
|
||||
const NO_BRING_TO_FRONT_ON_FOCUS = sys::ImGuiWindowFlags_NoBringToFrontOnFocus;
|
||||
/// Always show vertical scrollbar
|
||||
const ALWAYS_VERTICAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysVerticalScrollbar;
|
||||
/// Always show horizontal scrollbar
|
||||
const ALWAYS_HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysHorizontalScrollbar;
|
||||
/// Ensure child windows without border use `style.window_padding`
|
||||
const ALWAYS_USE_WINDOW_PADDING = sys::ImGuiWindowFlags_AlwaysUseWindowPadding;
|
||||
/// Disable gamepad/keyboard navigation within the window
|
||||
const NO_NAV_INPUTS = sys::ImGuiWindowFlags_NoNavInputs;
|
||||
/// No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by
|
||||
/// CTRL+TAB)
|
||||
const NO_NAV_FOCUS = sys::ImGuiWindowFlags_NoNavFocus;
|
||||
/// Append '*' to title without affecting the ID, as a convenience
|
||||
const UNSAVED_DOCUMENT = sys::ImGuiWindowFlags_UnsavedDocument;
|
||||
/// Disable gamepad/keyboard navigation and focusing.
|
||||
///
|
||||
/// Shorthand for `WindowFlags::NO_NAV_INPUTS | WindowFlags::NO_NAV_FOCUS`.
|
||||
const NO_NAV = sys::ImGuiWindowFlags_NoNav;
|
||||
/// Disable all window decorations.
|
||||
///
|
||||
/// Shorthand for `WindowFlags::NO_TITLE_BAR | WindowFlags::NO_RESIZE |
|
||||
/// WindowFlags::NO_SCROLLBAR | WindowFlags::NO_COLLAPSE`.
|
||||
const NO_DECORATION = sys::ImGuiWindowFlags_NoDecoration;
|
||||
/// Don't handle input.
|
||||
///
|
||||
/// Shorthand for `WindowFlags::NO_MOUSE_INPUTS | WindowFlags::NO_NAV_INPUTS |
|
||||
/// WindowFlags::NO_NAV_FOCUS`.
|
||||
const NO_INPUTS = sys::ImGuiWindowFlags_NoInputs;
|
||||
}
|
||||
}
|
||||
|
||||
/// # Window utilities
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns true if the current window appeared during this frame
|
||||
#[doc(alias = "IsWindowAppearing")]
|
||||
pub fn is_window_appearing(&self) -> bool {
|
||||
unsafe { sys::igIsWindowAppearing() }
|
||||
}
|
||||
/// Returns true if the current window is in collapsed state (= only the title bar is visible)
|
||||
#[doc(alias = "IsWindowCollapsed")]
|
||||
pub fn is_window_collapsed(&self) -> bool {
|
||||
unsafe { sys::igIsWindowCollapsed() }
|
||||
}
|
||||
/// Returns true if the current window is focused
|
||||
#[doc(alias = "IsWindowFocused")]
|
||||
pub fn is_window_focused(&self) -> bool {
|
||||
unsafe { sys::igIsWindowFocused(0) }
|
||||
}
|
||||
/// Returns true if the current window is focused based on the given flags
|
||||
#[doc(alias = "IsWindowFocused")]
|
||||
pub fn is_window_focused_with_flags(&self, flags: WindowFocusedFlags) -> bool {
|
||||
unsafe { sys::igIsWindowFocused(flags.bits() as i32) }
|
||||
}
|
||||
/// Returns true if the current window is hovered
|
||||
#[doc(alias = "IsWindowHovered")]
|
||||
pub fn is_window_hovered(&self) -> bool {
|
||||
unsafe { sys::igIsWindowHovered(0) }
|
||||
}
|
||||
/// Returns true if the current window is hovered based on the given flags
|
||||
#[doc(alias = "IsWindowHovered")]
|
||||
pub fn is_window_hovered_with_flags(&self, flags: WindowHoveredFlags) -> bool {
|
||||
unsafe { sys::igIsWindowHovered(flags.bits() as i32) }
|
||||
}
|
||||
/// Returns the position of the current window (in screen space)
|
||||
#[doc(alias = "GetWindowPos")]
|
||||
pub fn window_pos(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetWindowPos(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
/// Returns the size of the current window
|
||||
#[doc(alias = "GetWindowPos")]
|
||||
pub fn window_size(&self) -> [f32; 2] {
|
||||
let mut out = sys::ImVec2::zero();
|
||||
unsafe { sys::igGetWindowSize(&mut out) };
|
||||
out.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a window
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct Window<'a, T> {
|
||||
name: T,
|
||||
opened: Option<&'a mut bool>,
|
||||
flags: WindowFlags,
|
||||
pos: [f32; 2],
|
||||
pos_cond: Condition,
|
||||
pos_pivot: [f32; 2],
|
||||
size: [f32; 2],
|
||||
size_cond: Condition,
|
||||
size_constraints: Option<([f32; 2], [f32; 2])>,
|
||||
content_size: [f32; 2],
|
||||
collapsed: bool,
|
||||
collapsed_cond: Condition,
|
||||
focused: bool,
|
||||
bg_alpha: f32,
|
||||
}
|
||||
|
||||
impl<'a, T: AsRef<str>> Window<'a, T> {
|
||||
/// Creates a new window builder with the given name
|
||||
pub fn new(name: T) -> Self {
|
||||
Window {
|
||||
name,
|
||||
opened: None,
|
||||
flags: WindowFlags::empty(),
|
||||
pos: [0.0, 0.0],
|
||||
pos_cond: Condition::Never,
|
||||
pos_pivot: [0.0, 0.0],
|
||||
size: [0.0, 0.0],
|
||||
size_cond: Condition::Never,
|
||||
size_constraints: None,
|
||||
content_size: [0.0, 0.0],
|
||||
collapsed: false,
|
||||
collapsed_cond: Condition::Never,
|
||||
focused: false,
|
||||
bg_alpha: f32::NAN,
|
||||
}
|
||||
}
|
||||
/// Enables the window close button, which sets the passed boolean to false when clicked
|
||||
#[inline]
|
||||
pub fn opened(mut self, opened: &'a mut bool) -> Self {
|
||||
self.opened = Some(opened);
|
||||
self
|
||||
}
|
||||
/// Replace current window flags with the given value
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: WindowFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Sets the window position, which is applied based on the given condition value
|
||||
#[inline]
|
||||
pub fn position(mut self, position: [f32; 2], condition: Condition) -> Self {
|
||||
self.pos = position;
|
||||
self.pos_cond = condition;
|
||||
self
|
||||
}
|
||||
/// Sets the window position pivot, which can be used to adjust the alignment of the window
|
||||
/// relative to the position.
|
||||
///
|
||||
/// For example, pass [0.5, 0.5] to center the window on the position.
|
||||
/// Does nothing if window position is not also set with `position()`.
|
||||
#[inline]
|
||||
pub fn position_pivot(mut self, pivot: [f32; 2]) -> Self {
|
||||
self.pos_pivot = pivot;
|
||||
self
|
||||
}
|
||||
/// Sets the window size, which is applied based on the given condition value
|
||||
#[inline]
|
||||
pub fn size(mut self, size: [f32; 2], condition: Condition) -> Self {
|
||||
self.size = size;
|
||||
self.size_cond = condition;
|
||||
self
|
||||
}
|
||||
/// Sets window size constraints.
|
||||
///
|
||||
/// Use -1.0, -1.0 on either X or Y axis to preserve current size.
|
||||
#[inline]
|
||||
pub fn size_constraints(mut self, size_min: [f32; 2], size_max: [f32; 2]) -> Self {
|
||||
self.size_constraints = Some((size_min, size_max));
|
||||
self
|
||||
}
|
||||
/// Sets the window content size, which can be used to enforce scrollbars.
|
||||
///
|
||||
/// Does not include window decorations (title bar, menu bar, etc.). Set one of the values to
|
||||
/// 0.0 to leave the size automatic.
|
||||
#[inline]
|
||||
pub fn content_size(mut self, size: [f32; 2]) -> Self {
|
||||
self.content_size = size;
|
||||
self
|
||||
}
|
||||
/// Sets the window collapse state, which is applied based on the given condition value
|
||||
#[inline]
|
||||
pub fn collapsed(mut self, collapsed: bool, condition: Condition) -> Self {
|
||||
self.collapsed = collapsed;
|
||||
self.collapsed_cond = condition;
|
||||
self
|
||||
}
|
||||
/// Sets the window focused state, which can be used to bring the window to front
|
||||
#[inline]
|
||||
pub fn focused(mut self, focused: bool) -> Self {
|
||||
self.focused = focused;
|
||||
self
|
||||
}
|
||||
/// Sets the background color alpha value.
|
||||
///
|
||||
/// See also `draw_background`
|
||||
#[inline]
|
||||
pub fn bg_alpha(mut self, bg_alpha: f32) -> Self {
|
||||
self.bg_alpha = bg_alpha;
|
||||
self
|
||||
}
|
||||
/// Enables/disables the title bar.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn title_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_TITLE_BAR, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables resizing with the lower-right grip.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn resizable(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_RESIZE, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables moving the window.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn movable(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_MOVE, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables scrollbars (scrolling is still possible with the mouse or
|
||||
/// programmatically).
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn scroll_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_SCROLLBAR, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables vertical scrolling with the mouse wheel.
|
||||
///
|
||||
/// Enabled by default.
|
||||
/// When enabled, child windows forward the mouse wheel to the parent unless `NO_SCROLLBAR`
|
||||
/// is also set.
|
||||
#[inline]
|
||||
pub fn scrollable(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_SCROLL_WITH_MOUSE, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables collapsing the window by double-clicking it.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn collapsible(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_COLLAPSE, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables resizing the window to its content on every frame.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn always_auto_resize(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::ALWAYS_AUTO_RESIZE, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables drawing of background color and outside border.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn draw_background(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_BACKGROUND, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables loading and saving of settings (e.g. from/to an .ini file).
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn save_settings(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_SAVED_SETTINGS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables catching mouse input.
|
||||
///
|
||||
/// Enabled by default.
|
||||
/// Note: Hovering test will pass through when disabled
|
||||
#[inline]
|
||||
pub fn mouse_inputs(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_MOUSE_INPUTS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the menu bar.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn menu_bar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::MENU_BAR, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables the horizontal scrollbar.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn horizontal_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::HORIZONTAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables taking focus when transitioning from hidden to visible state.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn focus_on_appearing(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_FOCUS_ON_APPEARING, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables bringing the window to front when taking focus (e.g. clicking it or
|
||||
/// programmatically giving it focus).
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn bring_to_front_on_focus(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::NO_BRING_TO_FRONT_ON_FOCUS, !value);
|
||||
self
|
||||
}
|
||||
/// When enabled, forces the vertical scrollbar to render regardless of the content size.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn always_vertical_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_VERTICAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
/// When enabled, forces the horizontal scrollbar to render regardless of the content size.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn always_horizontal_scrollbar(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_HORIZONTAL_SCROLLBAR, value);
|
||||
self
|
||||
}
|
||||
/// When enabled, ensures child windows without border use `style.window_padding`.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn always_use_window_padding(mut self, value: bool) -> Self {
|
||||
self.flags
|
||||
.set(WindowFlags::ALWAYS_USE_WINDOW_PADDING, value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables gamepad/keyboard navigation within the window.
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn nav_inputs(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_NAV_INPUTS, !value);
|
||||
self
|
||||
}
|
||||
/// Enables/disables focusing toward this window with gamepad/keyboard navigation (e.g.
|
||||
/// CTRL+TAB).
|
||||
///
|
||||
/// Enabled by default.
|
||||
#[inline]
|
||||
pub fn nav_focus(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::NO_NAV_FOCUS, !value);
|
||||
self
|
||||
}
|
||||
/// When enabled, appends '*' to title without affecting the ID, as a convenience.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn unsaved_document(mut self, value: bool) -> Self {
|
||||
self.flags.set(WindowFlags::UNSAVED_DOCUMENT, value);
|
||||
self
|
||||
}
|
||||
/// Disable gamepad/keyboard navigation and focusing.
|
||||
///
|
||||
/// Shorthand for
|
||||
/// ```text
|
||||
/// .nav_inputs(false)
|
||||
/// .nav_focus(false)
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn no_nav(mut self) -> Self {
|
||||
self.flags |= WindowFlags::NO_NAV;
|
||||
self
|
||||
}
|
||||
/// Disable all window decorations.
|
||||
///
|
||||
/// Shorthand for
|
||||
/// ```text
|
||||
/// .title_bar(false)
|
||||
/// .resizable(false)
|
||||
/// .scroll_bar(false)
|
||||
/// .collapsible(false)
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn no_decoration(mut self) -> Self {
|
||||
self.flags |= WindowFlags::NO_DECORATION;
|
||||
self
|
||||
}
|
||||
/// Don't handle input.
|
||||
///
|
||||
/// Shorthand for
|
||||
/// ```text
|
||||
/// .mouse_inputs(false)
|
||||
/// .nav_inputs(false)
|
||||
/// .nav_focus(false)
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn no_inputs(mut self) -> Self {
|
||||
self.flags |= WindowFlags::NO_INPUTS;
|
||||
self
|
||||
}
|
||||
/// Creates a window and starts appending to it.
|
||||
///
|
||||
/// Returns `Some(WindowToken)` if the window is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the window is not visible and no content should be rendered.
|
||||
#[must_use]
|
||||
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<WindowToken<'ui>> {
|
||||
if self.pos_cond != Condition::Never {
|
||||
unsafe {
|
||||
sys::igSetNextWindowPos(
|
||||
self.pos.into(),
|
||||
self.pos_cond as i32,
|
||||
self.pos_pivot.into(),
|
||||
)
|
||||
};
|
||||
}
|
||||
if self.size_cond != Condition::Never {
|
||||
unsafe { sys::igSetNextWindowSize(self.size.into(), self.size_cond as i32) };
|
||||
}
|
||||
if let Some((size_min, size_max)) = self.size_constraints {
|
||||
// TODO: callback support
|
||||
unsafe {
|
||||
sys::igSetNextWindowSizeConstraints(
|
||||
size_min.into(),
|
||||
size_max.into(),
|
||||
None,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
}
|
||||
if self.content_size[0] != 0.0 || self.content_size[1] != 0.0 {
|
||||
unsafe { sys::igSetNextWindowContentSize(self.content_size.into()) };
|
||||
}
|
||||
if self.collapsed_cond != Condition::Never {
|
||||
unsafe { sys::igSetNextWindowCollapsed(self.collapsed, self.collapsed_cond as i32) };
|
||||
}
|
||||
if self.focused {
|
||||
unsafe { sys::igSetNextWindowFocus() };
|
||||
}
|
||||
if self.bg_alpha.is_finite() {
|
||||
unsafe { sys::igSetNextWindowBgAlpha(self.bg_alpha) };
|
||||
}
|
||||
let should_render = unsafe {
|
||||
sys::igBegin(
|
||||
ui.scratch_txt(self.name),
|
||||
self.opened
|
||||
.map(|x| x as *mut bool)
|
||||
.unwrap_or(ptr::null_mut()),
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
};
|
||||
if should_render {
|
||||
Some(WindowToken::new(ui))
|
||||
} else {
|
||||
unsafe { sys::igEnd() };
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a window and runs a closure to construct the contents.
|
||||
/// Returns the result of the closure, if it is called.
|
||||
///
|
||||
/// Note: the closure is not called if no window content is visible (e.g. window is collapsed
|
||||
/// or fully clipped).
|
||||
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
|
||||
self.begin(ui).map(|_window| f())
|
||||
}
|
||||
}
|
||||
|
||||
create_token!(
|
||||
/// Tracks a window that can be ended by calling `.end()`
|
||||
/// or by dropping.
|
||||
pub struct WindowToken<'ui>;
|
||||
|
||||
/// Ends a window
|
||||
drop { sys::igEnd() }
|
||||
);
|
118
plugins/libimhex-rust/imgui-rs/src/window/scroll.rs
Normal file
118
plugins/libimhex-rust/imgui-rs/src/window/scroll.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// # Window scrolling
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Returns the horizontal scrolling position.
|
||||
///
|
||||
/// Value is between 0.0 and self.scroll_max_x().
|
||||
#[doc(alias = "GetScrollX")]
|
||||
pub fn scroll_x(&self) -> f32 {
|
||||
unsafe { sys::igGetScrollX() }
|
||||
}
|
||||
/// Returns the vertical scrolling position.
|
||||
///
|
||||
/// Value is between 0.0 and self.scroll_max_y().
|
||||
#[doc(alias = "GetScrollY")]
|
||||
pub fn scroll_y(&self) -> f32 {
|
||||
unsafe { sys::igGetScrollY() }
|
||||
}
|
||||
/// Returns the maximum horizontal scrolling position.
|
||||
///
|
||||
/// Roughly equal to content size X - window size X.
|
||||
#[doc(alias = "GetScrollMaxX")]
|
||||
pub fn scroll_max_x(&self) -> f32 {
|
||||
unsafe { sys::igGetScrollMaxX() }
|
||||
}
|
||||
/// Returns the maximum vertical scrolling position.
|
||||
///
|
||||
/// Roughly equal to content size Y - window size Y.
|
||||
#[doc(alias = "GetScrollMaxY")]
|
||||
pub fn scroll_max_y(&self) -> f32 {
|
||||
unsafe { sys::igGetScrollMaxY() }
|
||||
}
|
||||
/// Sets the horizontal scrolling position
|
||||
#[doc(alias = "SetScrollX")]
|
||||
pub fn set_scroll_x(&self, scroll_x: f32) {
|
||||
unsafe { sys::igSetScrollX(scroll_x) };
|
||||
}
|
||||
/// Sets the vertical scroll position
|
||||
#[doc(alias = "SetScrollY")]
|
||||
pub fn set_scroll_y(&self, scroll_y: f32) {
|
||||
unsafe { sys::igSetScrollY(scroll_y) };
|
||||
}
|
||||
/// Adjusts the horizontal scroll position to make the current cursor position visible.
|
||||
///
|
||||
/// This is the same as [set_scroll_here_x_with_ratio](Self::set_scroll_here_x_with_ratio) but with `ratio` at 0.5.
|
||||
#[doc(alias = "SetScrollHereX")]
|
||||
pub fn set_scroll_here_x(&self) {
|
||||
self.set_scroll_here_x_with_ratio(0.5);
|
||||
}
|
||||
/// Adjusts the horizontal scroll position to make the current cursor position visible.
|
||||
///
|
||||
/// center_x_ratio:
|
||||
///
|
||||
/// - `0.0`: left
|
||||
/// - `0.5`: center
|
||||
/// - `1.0`: right
|
||||
#[doc(alias = "SetScrollHereX")]
|
||||
pub fn set_scroll_here_x_with_ratio(&self, center_x_ratio: f32) {
|
||||
unsafe { sys::igSetScrollHereX(center_x_ratio) };
|
||||
}
|
||||
/// Adjusts the vertical scroll position to make the current cursor position visible
|
||||
///
|
||||
/// This is the same as [set_scroll_here_y_with_ratio](Self::set_scroll_here_y_with_ratio) but with `ratio` at 0.5.
|
||||
#[doc(alias = "SetScrollHereY")]
|
||||
pub fn set_scroll_here_y(&self) {
|
||||
self.set_scroll_here_y_with_ratio(0.5);
|
||||
}
|
||||
/// Adjusts the vertical scroll position to make the current cursor position visible.
|
||||
///
|
||||
/// center_y_ratio:
|
||||
///
|
||||
/// - `0.0`: top
|
||||
/// - `0.5`: center
|
||||
/// - `1.0`: bottom
|
||||
#[doc(alias = "SetScrollHereY")]
|
||||
pub fn set_scroll_here_y_with_ratio(&self, center_y_ratio: f32) {
|
||||
unsafe { sys::igSetScrollHereY(center_y_ratio) };
|
||||
}
|
||||
#[doc(alias = "SetScrollFromPosX")]
|
||||
/// Adjusts the horizontal scroll position to make the given position visible
|
||||
///
|
||||
/// This is the same as [set_scroll_from_pos_x_with_ratio](Self::set_scroll_from_pos_x_with_ratio)
|
||||
/// but with `ratio` at 0.5.
|
||||
pub fn set_scroll_from_pos_x(&self, local_x: f32) {
|
||||
self.set_scroll_from_pos_x_with_ratio(local_x, 0.5);
|
||||
}
|
||||
/// Adjusts the horizontal scroll position to make the given position visible.
|
||||
///
|
||||
/// center_x_ratio:
|
||||
///
|
||||
/// - `0.0`: left
|
||||
/// - `0.5`: center
|
||||
/// - `1.0`: right
|
||||
#[doc(alias = "SetScrollFromPosX")]
|
||||
pub fn set_scroll_from_pos_x_with_ratio(&self, local_x: f32, center_x_ratio: f32) {
|
||||
unsafe { sys::igSetScrollFromPosX(local_x, center_x_ratio) };
|
||||
}
|
||||
/// Adjusts the vertical scroll position to make the given position visible
|
||||
///
|
||||
/// This is the same as [set_scroll_from_pos_y_with_ratio](Self::set_scroll_from_pos_y_with_ratio)
|
||||
/// but with `ratio` at 0.5.
|
||||
#[doc(alias = "SetScrollFromPosY")]
|
||||
pub fn set_scroll_from_pos_y(&self, local_y: f32) {
|
||||
self.set_scroll_from_pos_y_with_ratio(local_y, 0.5);
|
||||
}
|
||||
/// Adjusts the vertical scroll position to make the given position visible.
|
||||
///
|
||||
/// center_y_ratio:
|
||||
///
|
||||
/// - `0.0`: top
|
||||
/// - `0.5`: center
|
||||
/// - `1.0`: bottom
|
||||
#[doc(alias = "SetScrollFromPosY")]
|
||||
pub fn set_scroll_from_pos_y_with_ratio(&self, local_y: f32, center_y_ratio: f32) {
|
||||
unsafe { sys::igSetScrollFromPosY(local_y, center_y_ratio) };
|
||||
}
|
||||
}
|
30
plugins/libimhex-rust/imgui-sys/Cargo.lock
generated
Normal file
30
plugins/libimhex-rust/imgui-sys/Cargo.lock
generated
Normal file
@ -0,0 +1,30 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
||||
|
||||
[[package]]
|
||||
name = "chlorine"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd650552110e39b7c5058986cf177decd3365841836578ac50a286094eac0be6"
|
||||
|
||||
[[package]]
|
||||
name = "imgui-sys"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"chlorine",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
|
26
plugins/libimhex-rust/imgui-sys/Cargo.toml
Normal file
26
plugins/libimhex-rust/imgui-sys/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "imgui-sys"
|
||||
version = "0.8.0"
|
||||
edition = "2018"
|
||||
authors = ["The imgui-rs Developers"]
|
||||
description = "Raw FFI bindings to dear imgui"
|
||||
homepage = "https://github.com/imgui-rs/imgui-rs"
|
||||
repository = "https://github.com/imgui-rs/imgui-rs"
|
||||
license = "MIT/Apache-2.0"
|
||||
categories = ["gui", "external-ffi-bindings"]
|
||||
build = "build.rs"
|
||||
links = "imgui"
|
||||
# exclude json, lua, and the imgui subdirs (imgui/examples, imgui/docs, etc)
|
||||
exclude = ["third-party/*.json", "third-party/*.lua", "third-party/imgui/*/"]
|
||||
|
||||
[dependencies]
|
||||
chlorine = "1.0.7"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
pkg-config = { version="0.3", optional=true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
wasm = []
|
||||
freetype = ["pkg-config"]
|
202
plugins/libimhex-rust/imgui-sys/LICENSE-APACHE
Normal file
202
plugins/libimhex-rust/imgui-sys/LICENSE-APACHE
Normal file
@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
19
plugins/libimhex-rust/imgui-sys/LICENSE-MIT
Normal file
19
plugins/libimhex-rust/imgui-sys/LICENSE-MIT
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015-2020 The imgui-rs Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
10
plugins/libimhex-rust/imgui-sys/README.markdown
Normal file
10
plugins/libimhex-rust/imgui-sys/README.markdown
Normal file
@ -0,0 +1,10 @@
|
||||
# imgui-sys: Low level bindings
|
||||
|
||||
This crate contains the raw FFI bindings to the Dear ImGui C++
|
||||
library, by using the [cimgui](https://github.com/cimgui/cimgui) (a C
|
||||
API wrapper project for Dear ImGui), then creating Rust bindings using
|
||||
[bindgen](https://github.com/rust-lang/rust-bindgen).
|
||||
|
||||
These low level, mostly `unsafe` bindings are then used by `imgui-rs`
|
||||
which wraps them in a nice to use, mostly safe API. Therefore most
|
||||
users should not need to interact with this crate directly.
|
8
plugins/libimhex-rust/imgui-sys/build.rs
Normal file
8
plugins/libimhex-rust/imgui-sys/build.rs
Normal file
@ -0,0 +1,8 @@
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=dylib=imhex");
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
env!("LIBIMHEX_OUTPUT_DIRECTORY")
|
||||
);
|
||||
}
|
||||
|
16
plugins/libimhex-rust/imgui-sys/include_all_imgui.cpp
Normal file
16
plugins/libimhex-rust/imgui-sys/include_all_imgui.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
// This improves build speed by only compiling a single file, and performance by
|
||||
// allowing the optimizer to inline across separate object files (note that even
|
||||
// when rust is built with LTO, unless the steps are taken to allow cross-lang
|
||||
// LTO (tricky), the C/C++ code won't be LTOed).
|
||||
#include "./third-party/imgui/imgui.cpp"
|
||||
#include "./third-party/imgui/imgui_demo.cpp"
|
||||
#include "./third-party/imgui/imgui_draw.cpp"
|
||||
#include "./third-party/imgui/imgui_widgets.cpp"
|
||||
#include "./third-party/imgui/imgui_tables.cpp"
|
||||
#include "./third-party/cimgui.cpp"
|
||||
|
||||
#ifdef IMGUI_ENABLE_FREETYPE
|
||||
#include "./third-party/imgui/misc/freetype/imgui_freetype.cpp"
|
||||
#endif
|
||||
|
||||
|
4359
plugins/libimhex-rust/imgui-sys/src/bindings.rs
Normal file
4359
plugins/libimhex-rust/imgui-sys/src/bindings.rs
Normal file
File diff suppressed because it is too large
Load Diff
142
plugins/libimhex-rust/imgui-sys/src/lib.rs
Normal file
142
plugins/libimhex-rust/imgui-sys/src/lib.rs
Normal file
@ -0,0 +1,142 @@
|
||||
#![no_std]
|
||||
// We use `chlorine` over (the more well known) `cty` right now since `cty`
|
||||
// doesn't fully match std::os::raw (leading to issues like
|
||||
// https://github.com/japaric/cty/issues/18). Chlorine *does* match std::os::raw
|
||||
// (and libc), but has a longer and more confusing name, so we just alias it.
|
||||
// Also, this makes it easier to switch to something else/back easier, if we
|
||||
// decide to.
|
||||
//
|
||||
// Note that with the exception of bugs like the above, which crate we use (cty,
|
||||
// chlorine, libc, std::os::raw, ...) shouldn't matter to end user code¹, since
|
||||
// these are type aliases that should all be equivalent. This means that we're
|
||||
// free to switch back iff the bug is fixed, and users are free to use whichever
|
||||
// they prefer regardless of what we chose.
|
||||
//
|
||||
// (TODO: using extern crate for this is a hack, we probably should replace this
|
||||
// with `use chlorine as cty` in the binding files eventually, but lets punt on
|
||||
// it for a bit)
|
||||
//
|
||||
// ¹ The exception to this is that `std::os::raw` isn't there for `no_std`, and
|
||||
// `libc` has potentially undesirable linking impacts on windows.
|
||||
pub extern crate chlorine as cty;
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
mod wasm_bindings;
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
pub use crate::wasm_bindings::*;
|
||||
|
||||
#[cfg(not(feature = "wasm"))]
|
||||
mod bindings;
|
||||
|
||||
#[cfg(not(feature = "wasm"))]
|
||||
pub use crate::bindings::*;
|
||||
|
||||
impl ImVec2 {
|
||||
#[inline]
|
||||
pub const fn new(x: f32, y: f32) -> ImVec2 {
|
||||
ImVec2 { x, y }
|
||||
}
|
||||
#[inline]
|
||||
pub const fn zero() -> ImVec2 {
|
||||
ImVec2 { x: 0.0, y: 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 2]> for ImVec2 {
|
||||
#[inline]
|
||||
fn from(array: [f32; 2]) -> ImVec2 {
|
||||
ImVec2::new(array[0], array[1])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f32, f32)> for ImVec2 {
|
||||
#[inline]
|
||||
fn from((x, y): (f32, f32)) -> ImVec2 {
|
||||
ImVec2::new(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImVec2> for [f32; 2] {
|
||||
#[inline]
|
||||
fn from(v: ImVec2) -> [f32; 2] {
|
||||
[v.x, v.y]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImVec2> for (f32, f32) {
|
||||
#[inline]
|
||||
fn from(v: ImVec2) -> (f32, f32) {
|
||||
(v.x, v.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl ImVec4 {
|
||||
#[inline]
|
||||
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> ImVec4 {
|
||||
ImVec4 { x, y, z, w }
|
||||
}
|
||||
#[inline]
|
||||
pub const fn zero() -> ImVec4 {
|
||||
ImVec4 {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
w: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 4]> for ImVec4 {
|
||||
#[inline]
|
||||
fn from(array: [f32; 4]) -> ImVec4 {
|
||||
ImVec4::new(array[0], array[1], array[2], array[3])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f32, f32, f32, f32)> for ImVec4 {
|
||||
#[inline]
|
||||
fn from((x, y, z, w): (f32, f32, f32, f32)) -> ImVec4 {
|
||||
ImVec4::new(x, y, z, w)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImVec4> for [f32; 4] {
|
||||
#[inline]
|
||||
fn from(v: ImVec4) -> [f32; 4] {
|
||||
[v.x, v.y, v.z, v.w]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImVec4> for (f32, f32, f32, f32) {
|
||||
#[inline]
|
||||
fn from(v: ImVec4) -> (f32, f32, f32, f32) {
|
||||
(v.x, v.y, v.z, v.w)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imvec2_memory_layout() {
|
||||
use core::mem;
|
||||
assert_eq!(mem::size_of::<ImVec2>(), mem::size_of::<[f32; 2]>());
|
||||
assert_eq!(mem::align_of::<ImVec2>(), mem::align_of::<[f32; 2]>());
|
||||
let test = ImVec2::new(1.0, 2.0);
|
||||
let ref_a: &ImVec2 = &test;
|
||||
let ref_b: &[f32; 2] = unsafe { &*(&test as *const _ as *const [f32; 2]) };
|
||||
assert_eq!(&ref_a.x as *const _, &ref_b[0] as *const _);
|
||||
assert_eq!(&ref_a.y as *const _, &ref_b[1] as *const _);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imvec4_memory_layout() {
|
||||
use core::mem;
|
||||
assert_eq!(mem::size_of::<ImVec4>(), mem::size_of::<[f32; 4]>());
|
||||
assert_eq!(mem::align_of::<ImVec4>(), mem::align_of::<[f32; 4]>());
|
||||
let test = ImVec4::new(1.0, 2.0, 3.0, 4.0);
|
||||
let ref_a: &ImVec4 = &test;
|
||||
let ref_b: &[f32; 4] = unsafe { &*(&test as *const _ as *const [f32; 4]) };
|
||||
assert_eq!(&ref_a.x as *const _, &ref_b[0] as *const _);
|
||||
assert_eq!(&ref_a.y as *const _, &ref_b[1] as *const _);
|
||||
assert_eq!(&ref_a.z as *const _, &ref_b[2] as *const _);
|
||||
assert_eq!(&ref_a.w as *const _, &ref_b[3] as *const _);
|
||||
}
|
4954
plugins/libimhex-rust/imgui-sys/src/wasm_bindings.rs
Normal file
4954
plugins/libimhex-rust/imgui-sys/src/wasm_bindings.rs
Normal file
File diff suppressed because it is too large
Load Diff
32
plugins/libimhex-rust/proc_macros/CMakeLists.txt
Normal file
32
plugins/libimhex-rust/proc_macros/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Change this to the name of your plugin #
|
||||
project(proc_macros)
|
||||
|
||||
# ---- No need to change anything from here downwards unless you know what you're doing ---- #
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CARGO_CMD cargo build)
|
||||
set(TARGET_DIR "debug")
|
||||
else ()
|
||||
set(CARGO_CMD cargo build --release)
|
||||
set(TARGET_DIR "release")
|
||||
endif ()
|
||||
|
||||
add_custom_target(${PROJECT_NAME} ALL
|
||||
COMMENT "Compiling client module"
|
||||
COMMAND ${CMAKE_COMMAND} -E env CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} ${CARGO_CMD}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES LOCATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE Rust)
|
||||
|
||||
if (WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--allow-multiple-definition -fvisibility=hidden")
|
||||
endif()
|
||||
|
||||
add_compile_definitions(IMHEX_PLUGIN_NAME=${PROJECT_NAME})
|
||||
|
||||
if (NOT TARGET libimhex)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libimhex ${CMAKE_CURRENT_BINARY_DIR}/plugins/libimhex)
|
||||
endif()
|
46
plugins/libimhex-rust/proc_macros/Cargo.lock
generated
Normal file
46
plugins/libimhex-rust/proc_macros/Cargo.lock
generated
Normal file
@ -0,0 +1,46 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
11
plugins/libimhex-rust/proc_macros/Cargo.toml
Normal file
11
plugins/libimhex-rust/proc_macros/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1", features = ["full"] }
|
||||
quote = "1"
|
70
plugins/libimhex-rust/proc_macros/src/lib.rs
Normal file
70
plugins/libimhex-rust/proc_macros/src/lib.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::LitStr;
|
||||
use syn::Token;
|
||||
|
||||
struct AttrList(Punctuated<LitStr, Token![,]>);
|
||||
|
||||
impl Parse for AttrList {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Punctuated::parse_terminated(input).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
fn symbol(name: &str) -> String {
|
||||
let pkg_name = std::env::var("CARGO_PKG_NAME").unwrap();
|
||||
format!(
|
||||
"_ZN3hex6plugin{}{}8internal{}{}Ev",
|
||||
pkg_name.len(),
|
||||
pkg_name,
|
||||
name.len(),
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn plugin_setup(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = syn::parse_macro_input!(attr as AttrList)
|
||||
.0
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
let plugin_name = &args[0];
|
||||
let author_name = &args[1];
|
||||
let description = &args[2];
|
||||
|
||||
let function = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
|
||||
let plugin_name_export = symbol("getPluginName");
|
||||
let plugin_author_export = symbol("getPluginAuthor");
|
||||
let plugin_desc_export = symbol("getPluginDescription");
|
||||
let plugin_init_export = symbol("initializePlugin");
|
||||
let plugin_set_imgui_ctxt_export = symbol("setImGuiContext");
|
||||
|
||||
quote!(
|
||||
#[export_name = #plugin_name_export]
|
||||
pub extern "C" fn plugin_name() -> *const u8 {
|
||||
concat!(#plugin_name, "\0").as_ptr()
|
||||
}
|
||||
|
||||
#[export_name = #plugin_author_export]
|
||||
pub extern "C" fn plugin_author() -> *const u8 {
|
||||
concat!(#author_name, "\0").as_ptr()
|
||||
}
|
||||
|
||||
#[export_name = #plugin_desc_export]
|
||||
pub extern "C" fn plugin_description() -> *const u8 {
|
||||
concat!(#description, "\0").as_ptr()
|
||||
}
|
||||
|
||||
#[export_name = #plugin_set_imgui_ctxt_export]
|
||||
pub unsafe extern "C" fn set_imgui_context(context: *mut ::hex::imgui::sys::ImGuiContext) {
|
||||
::hex::imgui::sys::igSetCurrentContext(context);
|
||||
}
|
||||
|
||||
#[export_name = #plugin_init_export]
|
||||
pub extern "C" #function
|
||||
)
|
||||
.into()
|
||||
}
|
99
plugins/libimhex-rust/src/imhex_api.rs
Normal file
99
plugins/libimhex-rust/src/imhex_api.rs
Normal file
@ -0,0 +1,99 @@
|
||||
pub mod ffi {
|
||||
|
||||
pub mod ImHexApi {
|
||||
|
||||
#[cxx::bridge]
|
||||
pub mod Common {
|
||||
|
||||
#[namespace = "hex::ImHexApi::Common"]
|
||||
extern "C++" {
|
||||
include!("hex/api/imhex_api.hpp");
|
||||
|
||||
pub unsafe fn closeImHex(no_questions: bool);
|
||||
pub unsafe fn restartImHex();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cxx::bridge]
|
||||
pub mod Bookmarks {
|
||||
|
||||
#[namespace = "hex::ImHexApi::Bookmarks"]
|
||||
extern "C++" {
|
||||
include!("hex/api/imhex_api.hpp");
|
||||
|
||||
pub unsafe fn add(addr : u64, size : usize, name : &CxxString, comment : &CxxString, color : u32);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Region {
|
||||
pub address : u64,
|
||||
pub size : usize
|
||||
}
|
||||
|
||||
pub struct Color {
|
||||
pub a : u8,
|
||||
pub g : u8,
|
||||
pub b : u8,
|
||||
pub r : u8
|
||||
}
|
||||
|
||||
impl Region {
|
||||
|
||||
pub fn new(address : u64, size : usize) -> Self {
|
||||
Region { address, size }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Color {
|
||||
|
||||
pub fn new(r : u8, g : u8, b : u8, a : u8) -> Self {
|
||||
Color { a, g, b, r }
|
||||
}
|
||||
|
||||
pub fn rgba(self) -> u32 {
|
||||
(self.a as u32) << 24 | (self.b as u32) << 16 | (self.g as u32) << 8 | (self.r as u32) << 0
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ImHexApi {
|
||||
|
||||
pub mod Common {
|
||||
|
||||
pub fn closeImHex() {
|
||||
|
||||
unsafe {
|
||||
crate::imhex_api::ffi::ImHexApi::Common::closeImHex(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn restartImmHex() {
|
||||
|
||||
unsafe {
|
||||
crate::imhex_api::ffi::ImHexApi::Common::restartImHex();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub mod Bookmarks {
|
||||
|
||||
pub fn add(region : crate::Region, name : &str, comment : &str, color : Option<crate::Color>) {
|
||||
cxx::let_cxx_string!(cpp_name = name);
|
||||
cxx::let_cxx_string!(cpp_comment = comment);
|
||||
|
||||
unsafe {
|
||||
crate::imhex_api::ffi::ImHexApi::Bookmarks::add(region.address, region.size, &cpp_name, &cpp_comment, color.unwrap_or(crate::Color::new(0, 0, 0, 0)).rgba());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
9
plugins/libimhex-rust/src/lib.rs
Normal file
9
plugins/libimhex-rust/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
mod imhex_api;
|
||||
|
||||
pub use macros::plugin_setup;
|
||||
pub use imhex_api::ImHexApi;
|
||||
pub use imhex_api::Region;
|
||||
pub use imhex_api::Color;
|
||||
pub use imgui;
|
@ -54,6 +54,10 @@ endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
|
||||
if (WIN32)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols")
|
||||
endif()
|
||||
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
|
||||
set(LIBIMHEX_SOURCES
|
||||
|
@ -24,13 +24,10 @@ namespace hex {
|
||||
It allows you to add/register new content that will be picked up and used by the ImHex core or by other
|
||||
plugins when needed.
|
||||
*/
|
||||
struct ContentRegistry {
|
||||
ContentRegistry() = delete;
|
||||
namespace ContentRegistry {
|
||||
|
||||
/* Settings Registry. Allows adding of new entries into the ImHex preferences window. */
|
||||
struct Settings {
|
||||
Settings() = delete;
|
||||
|
||||
namespace Settings {
|
||||
using Callback = std::function<bool(const std::string&, nlohmann::json&)>;
|
||||
|
||||
struct Entry {
|
||||
@ -38,28 +35,27 @@ namespace hex {
|
||||
Callback callback;
|
||||
};
|
||||
|
||||
static void load();
|
||||
static void store();
|
||||
void load();
|
||||
void store();
|
||||
|
||||
static void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, s64 defaultValue, const Callback &callback);
|
||||
static void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback);
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, s64 defaultValue, const Callback &callback);
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback);
|
||||
|
||||
static void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, s64 value);
|
||||
static void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &value);
|
||||
static void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string>& value);
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, s64 value);
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &value);
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string>& value);
|
||||
|
||||
static s64 read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, s64 defaultValue);
|
||||
static std::string read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue);
|
||||
static std::vector<std::string> read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string>& defaultValue = { });
|
||||
s64 read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, s64 defaultValue);
|
||||
std::string read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue);
|
||||
std::vector<std::string> read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string>& defaultValue = { });
|
||||
|
||||
static std::map<std::string, std::vector<Entry>>& getEntries();
|
||||
static nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName);
|
||||
static nlohmann::json& getSettingsData();
|
||||
std::map<std::string, std::vector<Entry>>& getEntries();
|
||||
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName);
|
||||
nlohmann::json& getSettingsData();
|
||||
};
|
||||
|
||||
/* Command Palette Command Registry. Allows adding of new commands to the command palette */
|
||||
struct CommandPaletteCommands {
|
||||
CommandPaletteCommands() = delete;
|
||||
namespace CommandPaletteCommands {
|
||||
|
||||
enum class Type : u32 {
|
||||
SymbolCommand,
|
||||
@ -77,13 +73,12 @@ namespace hex {
|
||||
ExecuteCallback executeCallback;
|
||||
};
|
||||
|
||||
static void add(Type type, const std::string &command, const std::string &unlocalizedDescription, const DisplayCallback &displayCallback, const ExecuteCallback &executeCallback = [](auto){});
|
||||
static std::vector<Entry>& getEntries();
|
||||
void add(Type type, const std::string &command, const std::string &unlocalizedDescription, const DisplayCallback &displayCallback, const ExecuteCallback &executeCallback = [](auto){});
|
||||
std::vector<Entry>& getEntries();
|
||||
};
|
||||
|
||||
/* Pattern Language Function Registry. Allows adding of new functions that may be used inside the pattern language */
|
||||
struct PatternLanguageFunctions {
|
||||
PatternLanguageFunctions() = delete;
|
||||
namespace PatternLanguageFunctions {
|
||||
|
||||
constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF;
|
||||
constexpr static u32 MoreParametersThan = 0x8000'0000;
|
||||
@ -98,31 +93,25 @@ namespace hex {
|
||||
Callback func;
|
||||
};
|
||||
|
||||
static void add(const Namespace &ns, const std::string &name, u32 parameterCount, const Callback &func);
|
||||
static std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function>& getEntries();
|
||||
void add(const Namespace &ns, const std::string &name, u32 parameterCount, const Callback &func);
|
||||
std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function>& getEntries();
|
||||
};
|
||||
|
||||
/* View Registry. Allows adding of new windows */
|
||||
struct Views {
|
||||
Views() = delete;
|
||||
namespace Views {
|
||||
void add(View *view);
|
||||
|
||||
template<hex::derived_from<View> T, typename ... Args>
|
||||
static void add(Args&& ... args) {
|
||||
void add(Args&& ... args) {
|
||||
return add(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
static std::vector<View*>& getEntries();
|
||||
|
||||
private:
|
||||
static void add(View *view);
|
||||
|
||||
std::vector<View*>& getEntries();
|
||||
|
||||
};
|
||||
|
||||
/* Tools Registry. Allows adding new entries to the tools window */
|
||||
struct Tools {
|
||||
Tools() = delete;
|
||||
|
||||
namespace Tools {
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
struct Entry {
|
||||
@ -130,14 +119,13 @@ namespace hex {
|
||||
Callback function;
|
||||
};
|
||||
|
||||
static void add(const std::string &unlocalizedName, const Callback &function);
|
||||
void add(const std::string &unlocalizedName, const Callback &function);
|
||||
|
||||
static std::vector<Entry>& getEntries();
|
||||
std::vector<Entry>& getEntries();
|
||||
};
|
||||
|
||||
/* Data Inspector Registry. Allows adding of new types to the data inspector */
|
||||
struct DataInspector {
|
||||
DataInspector() = delete;
|
||||
namespace DataInspector {
|
||||
|
||||
enum class NumberDisplayStyle {
|
||||
Decimal,
|
||||
@ -154,13 +142,14 @@ namespace hex {
|
||||
GeneratorFunction generatorFunction;
|
||||
};
|
||||
|
||||
static void add(const std::string &unlocalizedName, size_t requiredSize, GeneratorFunction function);
|
||||
void add(const std::string &unlocalizedName, size_t requiredSize, GeneratorFunction function);
|
||||
|
||||
static std::vector<Entry>& getEntries();
|
||||
std::vector<Entry>& getEntries();
|
||||
};
|
||||
|
||||
/* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */
|
||||
struct DataProcessorNode {
|
||||
namespace DataProcessorNode {
|
||||
|
||||
using CreatorFunction = std::function<dp::Node*()>;
|
||||
struct Entry {
|
||||
std::string category;
|
||||
@ -168,8 +157,10 @@ namespace hex {
|
||||
CreatorFunction creatorFunction;
|
||||
};
|
||||
|
||||
void add(const Entry &entry);
|
||||
|
||||
template<hex::derived_from<dp::Node> T, typename ... Args>
|
||||
static void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, Args&& ... args) {
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, Args&& ... args) {
|
||||
add(Entry{ unlocalizedCategory.c_str(), unlocalizedName.c_str(),
|
||||
[=]{
|
||||
auto node = new T(std::forward<Args>(args)...);
|
||||
@ -179,33 +170,32 @@ namespace hex {
|
||||
});
|
||||
}
|
||||
|
||||
static void addSeparator();
|
||||
void addSeparator();
|
||||
|
||||
std::vector<Entry>& getEntries();
|
||||
|
||||
static std::vector<Entry>& getEntries();
|
||||
private:
|
||||
static void add(const Entry &entry);
|
||||
};
|
||||
|
||||
/* Language Registry. Allows together with the LangEntry class and the _lang user defined literal to add new languages */
|
||||
struct Language {
|
||||
static void registerLanguage(const std::string &name, const std::string &languageCode);
|
||||
static void addLocalizations(const std::string &languageCode, const LanguageDefinition &definition);
|
||||
namespace Language {
|
||||
void registerLanguage(const std::string &name, const std::string &languageCode);
|
||||
void addLocalizations(const std::string &languageCode, const LanguageDefinition &definition);
|
||||
|
||||
static std::map<std::string, std::string>& getLanguages();
|
||||
static std::map<std::string, std::vector<LanguageDefinition>>& getLanguageDefinitions();
|
||||
std::map<std::string, std::string>& getLanguages();
|
||||
std::map<std::string, std::vector<LanguageDefinition>>& getLanguageDefinitions();
|
||||
};
|
||||
|
||||
/* Interface Registry. Allows adding new items to various interfaces */
|
||||
struct Interface {
|
||||
namespace Interface {
|
||||
using DrawCallback = std::function<void()>;
|
||||
|
||||
static void addWelcomeScreenEntry(const DrawCallback &function);
|
||||
static void addFooterItem(const DrawCallback &function);
|
||||
static void addToolbarItem(const DrawCallback &function);
|
||||
void addWelcomeScreenEntry(const DrawCallback &function);
|
||||
void addFooterItem(const DrawCallback &function);
|
||||
void addToolbarItem(const DrawCallback &function);
|
||||
|
||||
static std::vector<DrawCallback>& getWelcomeScreenEntries();
|
||||
static std::vector<DrawCallback>& getFooterItems();
|
||||
static std::vector<DrawCallback>& getToolbarItems();
|
||||
std::vector<DrawCallback>& getWelcomeScreenEntries();
|
||||
std::vector<DrawCallback>& getFooterItems();
|
||||
std::vector<DrawCallback>& getToolbarItems();
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -12,19 +12,16 @@ namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
struct ImHexApi {
|
||||
ImHexApi() = delete;
|
||||
namespace ImHexApi {
|
||||
namespace Common {
|
||||
|
||||
struct Common {
|
||||
|
||||
static void closeImHex(bool noQuestions = false);
|
||||
static void restartImHex();
|
||||
void sayHello();
|
||||
void closeImHex(bool noQuestions = false);
|
||||
void restartImHex();
|
||||
|
||||
};
|
||||
|
||||
struct Bookmarks {
|
||||
Bookmarks() = delete;
|
||||
|
||||
namespace Bookmarks {
|
||||
struct Entry {
|
||||
Region region;
|
||||
|
||||
@ -34,28 +31,28 @@ namespace hex {
|
||||
bool locked;
|
||||
};
|
||||
|
||||
static void add(Region region, const std::string &name, const std::string &comment, u32 color = 0x00000000);
|
||||
static void add(u64 addr, size_t size, const std::string &name, const std::string &comment, u32 color = 0x00000000);
|
||||
void add(Region region, const std::string &name, const std::string &comment, u32 color = 0x00000000);
|
||||
void add(u64 addr, size_t size, const std::string &name, const std::string &comment, u32 color = 0x00000000);
|
||||
|
||||
static std::list<Entry>& getEntries();
|
||||
std::list<Entry>& getEntries();
|
||||
};
|
||||
|
||||
struct Provider {
|
||||
namespace Provider {
|
||||
|
||||
static prv::Provider* get();
|
||||
static const std::vector<prv::Provider*>& getProviders();
|
||||
prv::Provider* get();
|
||||
const std::vector<prv::Provider*>& getProviders();
|
||||
|
||||
static bool isValid();
|
||||
bool isValid();
|
||||
|
||||
void add(prv::Provider *provider);
|
||||
|
||||
template<hex::derived_from<prv::Provider> T>
|
||||
static void add(auto&& ... args) {
|
||||
void add(auto&& ... args) {
|
||||
add(new T(std::forward<decltype(args)>(args)...));
|
||||
}
|
||||
|
||||
static void remove(prv::Provider *provider);
|
||||
void remove(prv::Provider *provider);
|
||||
|
||||
private:
|
||||
static void add(prv::Provider *provider);
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ namespace hex::log {
|
||||
}
|
||||
|
||||
void warn(const std::string &fmt, auto ... args) {
|
||||
fmt::print(fg(fmt::color::light_golden_rod_yellow) | fmt::emphasis::bold, "[WARN] ");
|
||||
fmt::print(fg(fmt::color::orange) | fmt::emphasis::bold, "[WARN] ");
|
||||
fmt::print(fmt::runtime(fmt), args...);
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
@ -5,8 +5,14 @@
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
void ImHexApi::Common::sayHello() {
|
||||
log::warn("Hello!");
|
||||
}
|
||||
|
||||
void ImHexApi::Common::closeImHex(bool noQuestions) {
|
||||
EventManager::post<RequestCloseImHex>(noQuestions);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "helpers/project_file_handler.hpp"
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@ -42,7 +44,9 @@ namespace hex {
|
||||
ProjectFile::s_dataProcessorContent = projectFileData["dataProcessor"];
|
||||
|
||||
for (auto &element : projectFileData["bookmarks"].items()) {
|
||||
ProjectFile::s_bookmarks.push_back(element.value().get<ImHexApi::Bookmarks::Entry>());
|
||||
ImHexApi::Bookmarks::Entry entry;
|
||||
from_json(element.value(), entry);
|
||||
ProjectFile::s_bookmarks.push_back(entry);
|
||||
}
|
||||
|
||||
} catch (json::exception &e) {
|
||||
@ -73,7 +77,7 @@ namespace hex {
|
||||
projectFileData["dataProcessor"] = ProjectFile::s_dataProcessorContent;
|
||||
|
||||
for (auto &bookmark : ProjectFile::s_bookmarks) {
|
||||
projectFileData["bookmarks"].push_back(bookmark);
|
||||
to_json(projectFileData["bookmarks"].emplace_back(), bookmark);
|
||||
}
|
||||
|
||||
std::ofstream projectFile(filePath.c_str(), std::fstream::trunc);
|
||||
|
Loading…
x
Reference in New Issue
Block a user