diff --git a/.gitmodules b/.gitmodules index f3ef8bbd..c78fee42 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "extern/adpcm"] path = extern/adpcm url = https://github.com/superctr/adpcm +[submodule "extern/portaudio"] + path = extern/portaudio + url = https://github.com/PortAudio/portaudio.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 63053c78..3d9234f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ include(TestBigEndian) if (ANDROID) set(USE_RTMIDI_DEFAULT OFF) + set(WITH_PORTAUDIO_DEFAULT OFF) set(USE_BACKWARD_DEFAULT OFF) find_library(TERMUX rt) if (TERMUX) @@ -34,6 +35,7 @@ if (ANDROID) endif() else() set(USE_RTMIDI_DEFAULT ON) + set(WITH_PORTAUDIO_DEFAULT ON) if (WIN32 OR APPLE) set(USE_BACKWARD_DEFAULT ON) else() @@ -78,6 +80,7 @@ option(USE_SDL2 "Build with SDL2. Required to build with GUI." ${USE_SDL2_DEFAUL option(USE_SNDFILE "Build with libsndfile. Required in order to work with audio files." ${USE_SNDFILE_DEFAULT}) option(USE_BACKWARD "Use backward-cpp to print a backtrace on crash/abort." ${USE_BACKWARD_DEFAULT}) option(WITH_JACK "Whether to build with JACK support. Auto-detects if JACK is available" ${WITH_JACK_DEFAULT}) +option(WITH_PORTAUDIO "Whether to build with PortAudio for audio output." ${WITH_PORTAUDIO_DEFAULT}) option(WITH_RENDER_SDL "Whether to build with the SDL_Renderer render backend." ${WITH_RENDER_SDL_DEFAULT}) option(WITH_RENDER_OPENGL "Whether to build with the OpenGL render backend." ${WITH_RENDER_OPENGL_DEFAULT}) option(WITH_RENDER_DX11 "Whether to build with the DirectX 11 render backend." ${WITH_RENDER_DX11_DEFAULT}) @@ -85,6 +88,7 @@ option(USE_GLES "Use OpenGL ES for the OpenGL render backend." ${USE_GLES_DEFAUL option(SYSTEM_FFTW "Use a system-installed version of FFTW instead of the vendored one" OFF) option(SYSTEM_FMT "Use a system-installed version of fmt instead of the vendored one" OFF) option(SYSTEM_LIBSNDFILE "Use a system-installed version of libsndfile instead of the vendored one" OFF) +option(SYSTEM_PORTAUDIO "Use a system-installed version of PortAudio instead of the vendored one" OFF) option(SYSTEM_RTMIDI "Use a system-installed version of RtMidi instead of the vendored one" OFF) option(SYSTEM_ZLIB "Use a system-installed version of zlib instead of the vendored one" OFF) option(SYSTEM_SDL2 "Use a system-installed version of SDL2 instead of the vendored one" ${SYSTEM_SDL2_DEFAULT}) @@ -204,6 +208,27 @@ else() message(STATUS "Not using libsndfile") endif() +if (WITH_PORTAUDIO) + if (SYSTEM_PORTAUDIO) + find_package(PkgConfig REQUIRED) + pkg_check_modules(PORTAUDIO REQUIRED portaudio) + list(APPEND DEPENDENCIES_INCLUDE_DIRS ${PORTAUDIO_INCLUDE_DIRS}) + list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${PORTAUDIO_CFLAGS_OTHER}) + list(APPEND DEPENDENCIES_LIBRARIES ${PORTAUDIO_LIBRARIES}) + list(APPEND DEPENDENCIES_LIBRARY_DIRS ${PORTAUDIO_LIBRARY_DIRS}) + list(APPEND DEPENDENCIES_LINK_OPTIONS ${PORTAUDIO_LDFLAGS_OTHER}) + list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${PORTAUDIO_LDFLAGS}) + message(STATUS "Using system-installed PortAudio") + else() + set(PA_BUILD_SHARED_LIBS OFF CACHE BOOL "Build dynamic library" FORCE) + # don't - Furnace has its own implementation + set(PA_USE_JACK OFF CACHE BOOL "Enable support for JACK Audio Connection Kit" FORCE) + add_subdirectory(extern/portaudio EXCLUDE_FROM_ALL) + list(APPEND DEPENDENCIES_LIBRARIES PortAudio) + message(STATUS "Using vendored PortAudio") + endif() +endif() + if (USE_RTMIDI) if (SYSTEM_RTMIDI) find_package(PkgConfig REQUIRED) @@ -345,6 +370,14 @@ else() message(STATUS "Building without JACK support") endif() +if (WITH_PORTAUDIO) + list(APPEND AUDIO_SOURCES src/audio/pa.cpp) + message(STATUS "Building with PortAudio") + list(APPEND DEPENDENCIES_DEFINES HAVE_PA) +else() + message(STATUS "Building without PortAudio") +endif() + if (USE_RTMIDI) list(APPEND AUDIO_SOURCES src/audio/rtmidi.cpp) message(STATUS "Building with RtMidi") diff --git a/README.md b/README.md index 08ccfc85..37549dbd 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,7 @@ Available options: | `USE_SNDFILE` | `ON` | Build with libsndfile (required in order to work with audio files) | | `USE_BACKWARD` | `ON` | Use backward-cpp to print a backtrace on crash/abort | | `WITH_JACK` | `ON` if system-installed JACK detected, otherwise `OFF` | Whether to build with JACK support. Auto-detects if JACK is available | +| `WITH_PORTAUDIO` | `ON` | Whether to build with PortAudio. | | `SYSTEM_FFTW` | `OFF` | Use a system-installed version of FFTW instead of the vendored one | | `SYSTEM_FMT` | `OFF` | Use a system-installed version of fmt instead of the vendored one | | `SYSTEM_LIBSNDFILE` | `OFF` | Use a system-installed version of libsndfile instead of the vendored one | diff --git a/doc/2-interface/keyboard.md b/doc/2-interface/keyboard.md index 7fe7f8f5..2255670e 100644 --- a/doc/2-interface/keyboard.md +++ b/doc/2-interface/keyboard.md @@ -1,15 +1,256 @@ -# global keyboard shortcuts +# keyboard -this is a list of available shortcuts that may be used at any time, regardless of currently focused window. +everything on this list can be configured in the "Keyboard" tab of the Settings dialog: +- click on a keybind then enter a key or key combination to change it. +- right-click to clear the keybind. -key | action ------------|----------------------- -Ctrl-O | open file -Ctrl-S | save file -Enter | play/stop song -F5/F6 | " -Shift-Enter| play from cursor position -F7 | " -Ctrl-Enter | play one row -NumPad `/` | decrease octave -NumPad `*` | increase octave +the keys in the "Global hotkeys" section can be used in any window, though not within text boxes. + +| action | default keybind | +|--------------------------------------------------------|------------------| +| **Global hotkeys** | | +| New | `Ctrl-N` | +| Clear song data | — | +| Open file | `Ctrl-O` | +| Restore backup | — | +| Save file | `Ctrl-S` | +| Save as | `Ctrl-Shift-S` | +| Undo | `Ctrl-Z` | +| Redo | `Ctrl-Y` | +| Play/Stop (toggle) | `Return` | +| Play | — | +| Stop | — | +| Play (from beginning) | `F5` | +| Play (repeat pattern) | — | +| Play from cursor | `Shift-Return` | +| Step row | `Ctrl-Return` | +| Octave up | `Keypad *` | +| Octave down | `Keypad /` | +| Previous instrument | `Shift-Keypad /` | +| Next instrument | `Shift-Keypad *` | +| Increase edit step | `Ctrl-Keypad *` | +| Decrease edit step | `Ctrl-Keypad /` | +| Toggle edit mode | `Space` | +| Metronome | `Ctrl-M` | +| Toggle repeat pattern | — | +| Follow orders | — | +| Follow pattern | — | +| Toggle full-screen | `F11` | +| Request voice from TX81Z | — | +| Panic | `F12` | +| | | +| **Window activation** | | +| Find/Replace | Ctrl-F | +| Settings | — | +| Song Information | — | +| Subsongs | — | +| Speed | — | +| Instrument List | — | +| Wavetable List | — | +| Sample List | — | +| Orders | — | +| Pattern | — | +| Mixer | — | +| Grooves | — | +| Channels | — | +| Pattern Manager | — | +| Chip Manager | — | +| Compatibility Flags | — | +| Song Comments | — | +| Instrument Editor | — | +| Wavetable Editor | — | +| Sample Editor | — | +| Edit Controls | — | +| Piano | — | +| Oscilloscope (master) | — | +| Oscilloscope (per-channel) | — | +| Volume Meter | — | +| Clock | — | +| Register View | — | +| Log Viewer | — | +| Statistics | — | +| Effect List | — | +| Debug Menu | `Ctrl-Shift-D` | +| About | — | +| Collapse/expand current window | — | +| Close current window | `Shift-Escape` | +| | | +| **Note input** | | +| _see "note input" section after table_ | | +| | | +| **Pattern** | | +| Transpose (+1) | `Ctrl-F2` | +| Transpose (-1) | `Ctrl-F1` | +| Transpose (+1 octave) | `Ctrl-F4` | +| Transpose (-1 octave) | `Ctrl-F3` | +| Increase values (+1) | `Ctrl-Shift-F2` | +| Increase values (-1) | `Ctrl-Shift-F1` | +| Increase values (+16) | `Ctrl-Shift-F4` | +| Increase values (-16) | `Ctrl-Shift-F3` | +| Select all | `Ctrl-A` | +| Cut | `Ctrl-X` | +| Copy | `Ctrl-C` | +| Paste | `Ctrl-V` | +| Paste Mix (foreground) | `Ctrl-Shift-V` | +| Paste Mix (background) | — | +| Paste Flood | — | +| Paste Overflow | — | +| Move cursor up | `Up` | +| Move cursor down | `Down` | +| Move cursor left | `Left` | +| Move cursor right | `Right` | +| Move cursor up by one (override Edit Step) | `Shift-Home` | +| Move cursor down by one (override Edit Step) | `Shift-End` | +| Move cursor to previous channel | — | +| Move cursor to next channel | — | +| Move cursor to next channel (overflow) | — | +| Move cursor to previous channel (overflow) | — | +| Move cursor to beginning of pattern | `Home` | +| Move cursor to end of pattern | `End` | +| Move cursor up (coarse) | `PageUp` | +| Move cursor down (coarse) | `PageDown` | +| Expand selection upwards | `Shift-Up` | +| Expand selection downwards | `Shift-Down` | +| Expand selection to the left | `Shift-Left` | +| Expand selection to the right | `Shift-Right` | +| Expand selection upwards by one (override Edit Step) | — | +| Expand selection downwards by one (override Edit Step) | — | +| Expand selection to beginning of pattern | — | +| Expand selection to end of pattern | — | +| Expand selection upwards (coarse) | `Shift-PageUp` | +| Expand selection downwards (coarse) | `Shift-PageDown` | +| Delete | `Delete` | +| Pull delete | `Backspace` | +| Insert | `Insert` | +| Mute channel at cursor | `Alt-F9` | +| Solo channel at cursor | `Alt-F10` | +| Unmute all channels | `Alt-Shift-F9` | +| Go to next order | — | +| Go to previous order | — | +| Collapse channel at cursor | — | +| Increase effect columns | — | +| Decrease effect columns | — | +| Interpolate | — | +| Fade | — | +| Invert values | — | +| Flip selection | — | +| Collapse rows | — | +| Expand rows | — | +| Collapse pattern | — | +| Expand pattern | — | +| Collapse song | — | +| Expand song | — | +| Set note input latch | — | +| Clear note input latch | — | +| | | +| **Instrument list** | | +| Add | `Insert` | +| Duplicate | `Ctrl-D` | +| Open | — | +| Open (replace current) | — | +| Save | — | +| Save (.dmp) | — | +| Move up | `Shift-Up` | +| Move down | `Shift-Down` | +| Delete | — | +| Edit | `Shift-Return` | +| Cursor up | `Up` | +| Cursor down | `Down` | +| Toggle folders/standard view | `Ctrl-V` | +| | | +| **Wavetable list** | | +| Add | `Insert` | +| Duplicate | `Ctrl-D` | +| Open | — | +| Open (replace current) | — | +| Save | — | +| Save (.dmw) | — | +| Save (raw) | — | +| Move up | `Shift-Up` | +| Move down | `Shift-Down` | +| Delete | — | +| Edit | `Shift-Return` | +| Cursor up | `Up` | +| Cursor down | `Down` | +| Toggle folders/standard view | `Ctrl-V` | +| | | +| **Sample list** | | +| Add | `Insert` | +| Duplicate | `Ctrl-D` | +| Create wavetable from selection | `Ctrl-W` | +| Open | — | +| Open (replace current) | — | +| Import raw data | — | +| Import raw data (replace current) | — | +| Save | — | +| Save (raw) | — | +| Move up | `Shift-Up` | +| Move down | `Shift-Down` | +| Delete | — | +| Edit | `Shift-Return` | +| Cursor up | `Up` | +| Cursor down | `Down` | +| Preview | — | +| Stop preview | — | +| Toggle folders/standard view | `Ctrl-V` | +| | | +| **Orders** | | +| Previous order | `Up` | +| Next order | `Down` | +| Cursor left | `Left` | +| Cursor right | `Right` | +| Increase value | — | +| Decrease value | — | +| Switch edit mode | — | +| Toggle alter entire row | `Ctrl-L` | +| Add | `Insert` | +| Duplicate | `Ctrl-D` | +| Deep clone | `Ctrl-Shift-D` | +| Duplicate to end of song | `Ctrl-E` | +| Deep clone to end of song | `Ctrl-Shift-E` | +| Remove | `Delete` | +| Move up | `Shift-Up` | +| Move down | `Shift-Down` | +| Replay | — | +| | | +| **Sample editor** | | +| Edit mode: Select | `Shift-I` | +| Edit mode: Draw | `Shift-D` | +| Cut | `Ctrl-X` | +| Copy | `Ctrl-C` | +| Paste | `Ctrl-V` | +| Paste replace | `Ctrl-Shift-V` | +| Paste mix | `Ctrl-Alt-V` | +| Select all | `Ctrl-A` | +| Resize | `Ctrl-R` | +| Resample | `Ctrl-E` | +| Amplify | `Ctrl-B` | +| Normalize | `Ctrl-N` | +| Fade in | `Ctrl-I` | +| Fade out | `Ctrl-O` | +| Insert silence | `Insert` | +| Apply silence | `Shift-Delete` | +| Delete | `Delete` | +| Trim | `Ctrl-Delete` | +| Reverse | `Ctrl-T` | +| Invert | `Ctrl-Shift-T` | +| Signed/unsigned exchange | `Ctrl-U` | +| Apply filter | `Ctrl-F` | +| Preview sample | — | +| Stop sample preview | — | +| Zoom in | `Ctrl-=` | +| Zoom out | `Ctrl--` | +| Toggle auto-zoom | `Ctrl-0` | +| Create instrument from sample | — | +| Set loop to selection | `Ctrl-l` | + +## note input + +the settings for note input keybinds operate differently. each entry in the list of keybinds is made of the following: +- **Key**: key assignment. +- **Type**: type of note input. left-click cycles through "Note", "Note off", "Note release", and "Macro release". + - _note:_ the list is sorted by type. on changing a key's type, it will instantly move to its new sorting position! +- **Value**: number of semitones above C at the current octave. only appears for note type binds. +- **Remove**: removes the keybind from the list. + +below all the binds, select a key from the dropdown list to add it. it will appear at or near the top of the list as a note with value 0. diff --git a/extern/imgui_patched/MODIFIED.md b/extern/imgui_patched/MODIFIED.md index 5dd3ea11..d6e59db1 100644 --- a/extern/imgui_patched/MODIFIED.md +++ b/extern/imgui_patched/MODIFIED.md @@ -1,5 +1,10 @@ # modified version -this is a modified version of Dear ImGui to fix UI scaling on macOS, which works in a really weird (but logical) way. +this is a modified version of Dear ImGui (docking branch) to suit Furnace. +the following changes have been made: -further modifications may be made to suit Furnace. +- fix UI scaling on macOS, Wayland and any other platform where HiDPI is implemented through logical pixels +- gradients on frames +- improved touch support (inertial scrolling in particular) +- disable text input undo/redo by default +- add ability to lock dockspace diff --git a/extern/imgui_patched/backends/imgui_impl_allegro5.cpp b/extern/imgui_patched/backends/imgui_impl_allegro5.cpp index ae91443c..01f4bce6 100644 --- a/extern/imgui_patched/backends/imgui_impl_allegro5.cpp +++ b/extern/imgui_patched/backends/imgui_impl_allegro5.cpp @@ -47,10 +47,11 @@ // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_allegro5.h" #include // uint64_t #include // memcpy -#include "imgui.h" -#include "imgui_impl_allegro5.h" // Allegro #include @@ -603,3 +604,7 @@ void ImGui_ImplAllegro5_NewFrame() // Setup mouse cursor shape ImGui_ImplAllegro5_UpdateMouseCursor(); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_allegro5.h b/extern/imgui_patched/backends/imgui_impl_allegro5.h index 7e97969e..12679a08 100644 --- a/extern/imgui_patched/backends/imgui_impl_allegro5.h +++ b/extern/imgui_patched/backends/imgui_impl_allegro5.h @@ -17,6 +17,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct ALLEGRO_DISPLAY; union ALLEGRO_EVENT; @@ -30,3 +31,5 @@ IMGUI_IMPL_API bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* event); // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API bool ImGui_ImplAllegro5_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplAllegro5_InvalidateDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_android.cpp b/extern/imgui_patched/backends/imgui_impl_android.cpp index 48828ec2..3d705c63 100644 --- a/extern/imgui_patched/backends/imgui_impl_android.cpp +++ b/extern/imgui_patched/backends/imgui_impl_android.cpp @@ -27,6 +27,7 @@ // 2021-03-04: Initial version. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_android.h" #include #include @@ -294,3 +295,7 @@ void ImGui_ImplAndroid_NewFrame() io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); g_Time = current_time; } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_android.h b/extern/imgui_patched/backends/imgui_impl_android.h index eb97c4c8..17bff2c5 100644 --- a/extern/imgui_patched/backends/imgui_impl_android.h +++ b/extern/imgui_patched/backends/imgui_impl_android.h @@ -19,6 +19,8 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs #pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct ANativeWindow; struct AInputEvent; @@ -27,3 +29,5 @@ IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window); IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event); IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown(); IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_dx10.cpp b/extern/imgui_patched/backends/imgui_impl_dx10.cpp index 05b106cf..4f7a81a0 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx10.cpp +++ b/extern/imgui_patched/backends/imgui_impl_dx10.cpp @@ -32,6 +32,7 @@ // 2016-05-07: DirectX10: Disabling depth-write. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_dx10.h" // DirectX @@ -713,3 +714,6 @@ void ImGui_ImplDX10_ShutdownPlatformInterface() ImGui::DestroyPlatformWindows(); } +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_dx10.h b/extern/imgui_patched/backends/imgui_impl_dx10.h index c2c0f936..7c954234 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx10.h +++ b/extern/imgui_patched/backends/imgui_impl_dx10.h @@ -13,6 +13,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct ID3D10Device; @@ -24,3 +25,5 @@ IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_dx11.cpp b/extern/imgui_patched/backends/imgui_impl_dx11.cpp index 00ecb8e6..26288d0c 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx11.cpp +++ b/extern/imgui_patched/backends/imgui_impl_dx11.cpp @@ -35,6 +35,7 @@ // DISCLAIMER: modified with d3dcompiler patch (see https://github.com/ocornut/imgui/pull/638). #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_dx11.h" // DirectX @@ -747,3 +748,7 @@ static void ImGui_ImplDX11_ShutdownPlatformInterface() { ImGui::DestroyPlatformWindows(); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_dx11.h b/extern/imgui_patched/backends/imgui_impl_dx11.h index 5f4dd7f9..3fe5ecc9 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx11.h +++ b/extern/imgui_patched/backends/imgui_impl_dx11.h @@ -13,6 +13,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct ID3D11Device; struct ID3D11DeviceContext; @@ -25,3 +26,5 @@ IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_dx12.cpp b/extern/imgui_patched/backends/imgui_impl_dx12.cpp index 36b12ec7..71661bc8 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx12.cpp +++ b/extern/imgui_patched/backends/imgui_impl_dx12.cpp @@ -42,6 +42,7 @@ // 2018-02-22: Merged into master with all Win32 code synchronized to other examples. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_dx12.h" // DirectX @@ -1074,3 +1075,7 @@ void ImGui_ImplDX12_ShutdownPlatformInterface() { ImGui::DestroyPlatformWindows(); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_dx12.h b/extern/imgui_patched/backends/imgui_impl_dx12.h index a4d02aa8..e77793d2 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx12.h +++ b/extern/imgui_patched/backends/imgui_impl_dx12.h @@ -16,6 +16,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE #include // DXGI_FORMAT struct ID3D12Device; @@ -37,3 +38,5 @@ IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3 // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_dx9.cpp b/extern/imgui_patched/backends/imgui_impl_dx9.cpp index 7274f299..7ee02b5b 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx9.cpp +++ b/extern/imgui_patched/backends/imgui_impl_dx9.cpp @@ -34,6 +34,7 @@ // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_dx9.h" // DirectX @@ -540,3 +541,7 @@ static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows() if (platform_io.Viewports[i]->RendererUserData) ImGui_ImplDX9_DestroyWindow(platform_io.Viewports[i]); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_dx9.h b/extern/imgui_patched/backends/imgui_impl_dx9.h index 3e7c1736..ffcd4682 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx9.h +++ b/extern/imgui_patched/backends/imgui_impl_dx9.h @@ -13,6 +13,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct IDirect3DDevice9; @@ -24,3 +25,5 @@ IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_glfw.cpp b/extern/imgui_patched/backends/imgui_impl_glfw.cpp index 87ea528b..e5f0e648 100644 --- a/extern/imgui_patched/backends/imgui_impl_glfw.cpp +++ b/extern/imgui_patched/backends/imgui_impl_glfw.cpp @@ -22,6 +22,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2023-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) +// 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491) // 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702) // 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034) // 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240) @@ -32,7 +34,7 @@ // 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). -// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position. +// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position *EDIT* Reverted 2023-07-18. // 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX. // 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11. // 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend. @@ -69,6 +71,7 @@ // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_glfw.h" // Clang warnings with -Weverything @@ -418,8 +421,6 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) bd->PrevUserCallbackCursorPos(window, x, y); - if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) - return; ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) @@ -440,8 +441,6 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) bd->PrevUserCallbackCursorEnter(window, entered); - if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) - return; ImGuiIO& io = ImGui::GetIO(); if (entered) @@ -727,11 +726,6 @@ static void ImGui_ImplGlfw_UpdateMouseData() ImGuiIO& io = ImGui::GetIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - if (glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) - { - io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); - return; - } ImGuiID mouse_viewport_id = 0; const ImVec2 mouse_pos_prev = io.MousePos; @@ -937,7 +931,10 @@ void ImGui_ImplGlfw_NewFrame() ImGui_ImplGlfw_UpdateMonitors(); // Setup time step + // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) double current_time = glfwGetTime(); + if (current_time <= bd->Time) + current_time = bd->Time + 0.00001f; io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; @@ -1285,6 +1282,10 @@ static void ImGui_ImplGlfw_ShutdownPlatformInterface() ImGui::DestroyPlatformWindows(); } +//----------------------------------------------------------------------------- + #if defined(__clang__) #pragma clang diagnostic pop #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_glfw.h b/extern/imgui_patched/backends/imgui_impl_glfw.h index 2e3b8bf2..241a9710 100644 --- a/extern/imgui_patched/backends/imgui_impl_glfw.h +++ b/extern/imgui_patched/backends/imgui_impl_glfw.h @@ -21,6 +21,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct GLFWwindow; struct GLFWmonitor; @@ -50,3 +51,5 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_glut.cpp b/extern/imgui_patched/backends/imgui_impl_glut.cpp index 3f911a13..36050e0e 100644 --- a/extern/imgui_patched/backends/imgui_impl_glut.cpp +++ b/extern/imgui_patched/backends/imgui_impl_glut.cpp @@ -32,6 +32,7 @@ // 2018-03-22: Added GLUT Platform binding. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_glut.h" #define GL_SILENCE_DEPRECATION #ifdef __APPLE__ @@ -298,3 +299,7 @@ void ImGui_ImplGLUT_MotionFunc(int x, int y) ImGuiIO& io = ImGui::GetIO(); io.AddMousePosEvent((float)x, (float)y); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_glut.h b/extern/imgui_patched/backends/imgui_impl_glut.h index 545cd8dd..9f141679 100644 --- a/extern/imgui_patched/backends/imgui_impl_glut.h +++ b/extern/imgui_patched/backends/imgui_impl_glut.h @@ -20,6 +20,7 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs #pragma once +#ifndef IMGUI_DISABLE #include "imgui.h" // IMGUI_IMPL_API IMGUI_IMPL_API bool ImGui_ImplGLUT_Init(); @@ -29,7 +30,7 @@ IMGUI_IMPL_API void ImGui_ImplGLUT_NewFrame(); // You can call ImGui_ImplGLUT_InstallFuncs() to get all those functions installed automatically, // or call them yourself from your own GLUT handlers. We are using the same weird names as GLUT for consistency.. -//---------------------------------------- GLUT name --------------------------------------------- Decent Name --------- +//------------------------------------ GLUT name ---------------------------------------------- Decent Name --------- IMGUI_IMPL_API void ImGui_ImplGLUT_ReshapeFunc(int w, int h); // ~ ResizeFunc IMGUI_IMPL_API void ImGui_ImplGLUT_MotionFunc(int x, int y); // ~ MouseMoveFunc IMGUI_IMPL_API void ImGui_ImplGLUT_MouseFunc(int button, int state, int x, int y); // ~ MouseButtonFunc @@ -38,3 +39,5 @@ IMGUI_IMPL_API void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int IMGUI_IMPL_API void ImGui_ImplGLUT_KeyboardUpFunc(unsigned char c, int x, int y); // ~ CharReleasedFunc IMGUI_IMPL_API void ImGui_ImplGLUT_SpecialFunc(int key, int x, int y); // ~ KeyPressedFunc IMGUI_IMPL_API void ImGui_ImplGLUT_SpecialUpFunc(int key, int x, int y); // ~ KeyReleasedFunc + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_metal.h b/extern/imgui_patched/backends/imgui_impl_metal.h index a281f6a2..83b3ee8d 100644 --- a/extern/imgui_patched/backends/imgui_impl_metal.h +++ b/extern/imgui_patched/backends/imgui_impl_metal.h @@ -12,6 +12,7 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE //----------------------------------------------------------------------------- // ObjC API @@ -63,3 +64,7 @@ IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects(); #endif #endif + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_metal.mm b/extern/imgui_patched/backends/imgui_impl_metal.mm index 521a01ac..b29b4f26 100644 --- a/extern/imgui_patched/backends/imgui_impl_metal.mm +++ b/extern/imgui_patched/backends/imgui_impl_metal.mm @@ -32,6 +32,7 @@ // 2018-07-05: Metal: Added new Metal backend implementation. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_metal.h" #import #import @@ -740,3 +741,7 @@ static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows() } @end + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_opengl2.cpp b/extern/imgui_patched/backends/imgui_impl_opengl2.cpp index 47e7aef9..7e76cb5a 100644 --- a/extern/imgui_patched/backends/imgui_impl_opengl2.cpp +++ b/extern/imgui_patched/backends/imgui_impl_opengl2.cpp @@ -39,12 +39,9 @@ // 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_opengl2.h" -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif // Clang/GCC warnings with -Weverything #if defined(__clang__) @@ -305,6 +302,7 @@ void ImGui_ImplOpenGL2_DestroyDeviceObjects() ImGui_ImplOpenGL2_DestroyFontsTexture(); } + //-------------------------------------------------------------------------------------------------------- // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT // This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. @@ -333,6 +331,10 @@ static void ImGui_ImplOpenGL2_ShutdownPlatformInterface() ImGui::DestroyPlatformWindows(); } +//----------------------------------------------------------------------------- + #if defined(__clang__) #pragma clang diagnostic pop #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_opengl2.h b/extern/imgui_patched/backends/imgui_impl_opengl2.h index 91b3b1da..c6d67f65 100644 --- a/extern/imgui_patched/backends/imgui_impl_opengl2.h +++ b/extern/imgui_patched/backends/imgui_impl_opengl2.h @@ -5,7 +5,7 @@ // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs @@ -20,6 +20,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE IMGUI_IMPL_API bool ImGui_ImplOpenGL2_Init(); IMGUI_IMPL_API void ImGui_ImplOpenGL2_Shutdown(); @@ -31,3 +32,5 @@ IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture(); IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_opengl3.cpp b/extern/imgui_patched/backends/imgui_impl_opengl3.cpp index d5ef359d..78461d9f 100644 --- a/extern/imgui_patched/backends/imgui_impl_opengl3.cpp +++ b/extern/imgui_patched/backends/imgui_impl_opengl3.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2023-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333) // 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375) // 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333) // 2023-03-23: OpenGL: Properly restoring "no shader program bound" if it was the case prior to running the rendering function. (#6267, #6220, #6224) @@ -105,13 +106,10 @@ #endif #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_opengl3.h" #include -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif #if defined(__APPLE__) #include #endif @@ -292,7 +290,12 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) io.BackendRendererName = "imgui_impl_opengl3"; // Query for GL version (e.g. 320 for GL 3.2) -#if !defined(IMGUI_IMPL_OPENGL_ES2) +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GLES 2 + bd->GlVersion = 200; + bd->GlProfileIsES2 = true; +#else + // Desktop or GLES 3 GLint major = 0; GLint minor = 0; glGetIntegerv(GL_MAJOR_VERSION, &major); @@ -305,10 +308,15 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) } bd->GlVersion = (GLuint)(major * 100 + minor * 10); #if defined(GL_CONTEXT_PROFILE_MASK) - glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask); + if (bd->GlVersion >= 320) + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask); bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; #endif +#if defined(IMGUI_IMPL_OPENGL_ES3) + bd->GlProfileIsES3 = true; +#endif + bd->UseBufferSubData = false; /* // Query vendor to enable glBufferSubData kludge @@ -318,16 +326,10 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) bd->UseBufferSubData = true; #endif */ -#elif defined(IMGUI_IMPL_OPENGL_ES2) - bd->GlVersion = 200; // GLES 2 - bd->GlProfileIsES2 = true; -#elif defined(IMGUI_IMPL_OPENGL_ES3) - bd->GlVersion = 200; // Don't raise version as it is intended as a desktop version check for now. - bd->GlProfileIsES3 = true; #endif #ifdef IMGUI_IMPL_OPENGL_DEBUG - logD("\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] + printf("GlVersion = %d\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2 = %d, GlProfileIsES3 = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET @@ -979,9 +981,13 @@ static void ImGui_ImplOpenGL3_ShutdownPlatformInterface() ImGui::DestroyPlatformWindows(); } +//----------------------------------------------------------------------------- + #if defined(__GNUC__) #pragma GCC diagnostic pop #endif #if defined(__clang__) #pragma clang diagnostic pop #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_opengl3.h b/extern/imgui_patched/backends/imgui_impl_opengl3.h index 328a145d..044997ad 100644 --- a/extern/imgui_patched/backends/imgui_impl_opengl3.h +++ b/extern/imgui_patched/backends/imgui_impl_opengl3.h @@ -25,6 +25,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE // Backend API IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr); @@ -59,3 +60,5 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); #endif #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_osx.h b/extern/imgui_patched/backends/imgui_impl_osx.h index 08b6ab74..41731318 100644 --- a/extern/imgui_patched/backends/imgui_impl_osx.h +++ b/extern/imgui_patched/backends/imgui_impl_osx.h @@ -18,6 +18,7 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE #ifdef __OBJC__ @@ -44,3 +45,5 @@ IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(void* _Nullable view); #endif #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_osx.mm b/extern/imgui_patched/backends/imgui_impl_osx.mm index bfd0dba9..18732b03 100644 --- a/extern/imgui_patched/backends/imgui_impl_osx.mm +++ b/extern/imgui_patched/backends/imgui_impl_osx.mm @@ -18,6 +18,7 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs #import "imgui.h" +#ifndef IMGUI_DISABLE #import "imgui_impl_osx.h" #import #import @@ -1109,3 +1110,7 @@ static void ImGui_ImplOSX_ShutdownPlatformInterface() main_viewport->PlatformUserData = nullptr; ImGui::DestroyPlatformWindows(); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_sdl2.cpp b/extern/imgui_patched/backends/imgui_impl_sdl2.cpp index 5dc65568..ddbad7be 100644 --- a/extern/imgui_patched/backends/imgui_impl_sdl2.cpp +++ b/extern/imgui_patched/backends/imgui_impl_sdl2.cpp @@ -77,6 +77,7 @@ // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_sdl2.h" #include #include @@ -321,14 +322,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) mouse_pos.y += window_y; } // Fix for high DPI mac/idevice/wayland - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - if (!platform_io.Monitors.empty() && platform_io.Monitors[0].DpiScale > 1.0f) - { - // The Framebuffer is scaled by an integer ceiling of the actual ratio, so 2.0 not 1.685 on Mac! - //printf("multiply by %f\n",platform_io.Monitors[0].DpiScale); - mouse_pos.x *= std::ceil(platform_io.Monitors[0].DpiScale); - mouse_pos.y *= std::ceil(platform_io.Monitors[0].DpiScale); - } + mouse_pos.x *= io.InputScale; + mouse_pos.y *= io.InputScale; io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); return true; @@ -626,13 +621,8 @@ static void ImGui_ImplSDL2_UpdateMouseData() mouse_y -= window_y; } // Fix for high DPI mac/idevice/wayland - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - if (!platform_io.Monitors.empty() && platform_io.Monitors[0].DpiScale > 1.0f) - { - // The Framebuffer is scaled by an integer ceiling of the actual ratio, so 2.0 not 1.685 on Mac! - mouse_x *= std::ceil(platform_io.Monitors[0].DpiScale); - mouse_y *= std::ceil(platform_io.Monitors[0].DpiScale); - } + mouse_x *= io.InputScale; + mouse_y *= io.InputScale; io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } } @@ -792,7 +782,7 @@ void ImGui_ImplSDL2_NewFrame() io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); io.DisplaySize = ImVec2((float)display_w, (float)display_h); //printf("write %d/%d to DpiScale\n",display_w,w); - platform_io.Monitors[0].DpiScale=(float)display_w/(float)w; + io.InputScale=(float)display_w/(float)w; } // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) @@ -1072,6 +1062,10 @@ static void ImGui_ImplSDL2_ShutdownPlatformInterface() ImGui::DestroyPlatformWindows(); } +//----------------------------------------------------------------------------- + #if defined(__clang__) #pragma clang diagnostic pop #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_sdl2.h b/extern/imgui_patched/backends/imgui_impl_sdl2.h index 1ca91b3a..8d50eb80 100644 --- a/extern/imgui_patched/backends/imgui_impl_sdl2.h +++ b/extern/imgui_patched/backends/imgui_impl_sdl2.h @@ -20,6 +20,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct SDL_Window; struct SDL_Renderer; @@ -37,3 +38,5 @@ IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS static inline void ImGui_ImplSDL2_NewFrame(SDL_Window*) { ImGui_ImplSDL2_NewFrame(); } // 1.84: removed unnecessary parameter #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_sdl3.cpp b/extern/imgui_patched/backends/imgui_impl_sdl3.cpp index 47fe49f2..9455412b 100644 --- a/extern/imgui_patched/backends/imgui_impl_sdl3.cpp +++ b/extern/imgui_patched/backends/imgui_impl_sdl3.cpp @@ -29,6 +29,7 @@ // 2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_sdl3.h" // Clang warnings with -Weverything @@ -752,7 +753,7 @@ static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport) sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; #if !defined(_WIN32) // See SDL hack in ImGui_ImplSDL3_ShowWindow(). - sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_SKIP_TASKBAR : 0; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_UTILITY : 0; #endif sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); @@ -801,7 +802,7 @@ static void ImGui_ImplSDL3_ShowWindow(ImGuiViewport* viewport) HWND hwnd = (HWND)viewport->PlatformHandleRaw; // SDL hack: Hide icon from task bar - // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. + // Note: SDL 3.0.0+ has a SDL_WINDOW_UTILITY flag which is supported under Windows but the way it create the window breaks our seamless transition. if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); @@ -944,6 +945,10 @@ static void ImGui_ImplSDL3_ShutdownPlatformInterface() ImGui::DestroyPlatformWindows(); } +//----------------------------------------------------------------------------- + #if defined(__clang__) #pragma clang diagnostic pop #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_sdl3.h b/extern/imgui_patched/backends/imgui_impl_sdl3.h index e2d9f90f..56b404e5 100644 --- a/extern/imgui_patched/backends/imgui_impl_sdl3.h +++ b/extern/imgui_patched/backends/imgui_impl_sdl3.h @@ -21,6 +21,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct SDL_Window; struct SDL_Renderer; @@ -34,3 +35,5 @@ IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SD IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame(); IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_sdlrenderer2.cpp b/extern/imgui_patched/backends/imgui_impl_sdlrenderer2.cpp index fd5a0747..de18d49e 100644 --- a/extern/imgui_patched/backends/imgui_impl_sdlrenderer2.cpp +++ b/extern/imgui_patched/backends/imgui_impl_sdlrenderer2.cpp @@ -26,12 +26,9 @@ // 2021-09-21: Initial version. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_sdlrenderer2.h" -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif // Clang warnings with -Weverything #if defined(__clang__) @@ -260,6 +257,10 @@ void ImGui_ImplSDLRenderer2_DestroyDeviceObjects() ImGui_ImplSDLRenderer2_DestroyFontsTexture(); } +//----------------------------------------------------------------------------- + #if defined(__clang__) #pragma clang diagnostic pop #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_sdlrenderer2.h b/extern/imgui_patched/backends/imgui_impl_sdlrenderer2.h index 0f1b2b6c..bf4fd0e7 100644 --- a/extern/imgui_patched/backends/imgui_impl_sdlrenderer2.h +++ b/extern/imgui_patched/backends/imgui_impl_sdlrenderer2.h @@ -14,6 +14,7 @@ // [ ] Renderer: Multi-viewport support (multiple windows). #pragma once +#ifndef IMGUI_DISABLE #include "imgui.h" // IMGUI_IMPL_API struct SDL_Renderer; @@ -28,3 +29,5 @@ IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_CreateFontsTexture(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_DestroyDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_sdlrenderer3.cpp b/extern/imgui_patched/backends/imgui_impl_sdlrenderer3.cpp index 7c1b3bf1..d7b7fd18 100644 --- a/extern/imgui_patched/backends/imgui_impl_sdlrenderer3.cpp +++ b/extern/imgui_patched/backends/imgui_impl_sdlrenderer3.cpp @@ -21,12 +21,9 @@ // 2023-05-30: Initial version. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_sdlrenderer3.h" -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif // Clang warnings with -Weverything #if defined(__clang__) @@ -253,6 +250,10 @@ void ImGui_ImplSDLRenderer3_DestroyDeviceObjects() ImGui_ImplSDLRenderer3_DestroyFontsTexture(); } +//----------------------------------------------------------------------------- + #if defined(__clang__) #pragma clang diagnostic pop #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_sdlrenderer3.h b/extern/imgui_patched/backends/imgui_impl_sdlrenderer3.h index 71eec439..6a59de9a 100644 --- a/extern/imgui_patched/backends/imgui_impl_sdlrenderer3.h +++ b/extern/imgui_patched/backends/imgui_impl_sdlrenderer3.h @@ -15,6 +15,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct SDL_Renderer; @@ -28,3 +29,5 @@ IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateFontsTexture(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_vulkan.cpp b/extern/imgui_patched/backends/imgui_impl_vulkan.cpp index 59bb6d65..77929100 100644 --- a/extern/imgui_patched/backends/imgui_impl_vulkan.cpp +++ b/extern/imgui_patched/backends/imgui_impl_vulkan.cpp @@ -32,6 +32,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2023-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2023-07-04: Vulkan: Added optional support for VK_KHR_dynamic_rendering. User needs to set init_info->UseDynamicRendering = true and init_info->ColorAttachmentFormat. // 2023-01-02: Vulkan: Fixed sampler passed to ImGui_ImplVulkan_AddTexture() not being honored + removed a bunch of duplicate code. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-10-04: Vulkan: Added experimental ImGui_ImplVulkan_RemoveTexture() for api symetry. (#914, #5738). @@ -67,6 +68,8 @@ // 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources. // 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active. +#include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_vulkan.h" #include @@ -238,6 +241,12 @@ IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_DEF) #undef IMGUI_VULKAN_FUNC_DEF #endif // VK_NO_PROTOTYPES +#if defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering) +#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +static PFN_vkCmdBeginRenderingKHR ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR; +static PFN_vkCmdEndRenderingKHR ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR; +#endif + //----------------------------------------------------------------------------- // SHADERS //----------------------------------------------------------------------------- @@ -872,6 +881,19 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC info.layout = bd->PipelineLayout; info.renderPass = renderPass; info.subpass = subpass; + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + VkPipelineRenderingCreateInfoKHR pipelineRenderingCreateInfo = {}; + pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR; + pipelineRenderingCreateInfo.colorAttachmentCount = 1; + pipelineRenderingCreateInfo.pColorAttachmentFormats = &bd->VulkanInitInfo.ColorAttachmentFormat; + if (bd->VulkanInitInfo.UseDynamicRendering) + { + info.pNext = &pipelineRenderingCreateInfo; + info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr. + } +#endif + VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); check_vk_result(err); } @@ -984,10 +1006,17 @@ bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const ch return false; IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_LOAD) #undef IMGUI_VULKAN_FUNC_LOAD + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + // Manually load those two (see #5446) + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRenderingKHR", user_data)); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRenderingKHR", user_data)); +#endif #else IM_UNUSED(loader_func); IM_UNUSED(user_data); #endif + g_FunctionsLoaded = true; return true; } @@ -996,6 +1025,20 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend { IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + if (info->UseDynamicRendering) + { +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +#ifndef VK_NO_PROTOTYPES + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdBeginRenderingKHR")); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdEndRenderingKHR")); +#endif + IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR != nullptr); + IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR != nullptr); +#else + IM_ASSERT(0 && "Can't use dynamic rendering when neither VK_VERSION_1_3 or VK_KHR_dynamic_rendering is defined."); +#endif + } + ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); @@ -1013,7 +1056,8 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend IM_ASSERT(info->DescriptorPool != VK_NULL_HANDLE); IM_ASSERT(info->MinImageCount >= 2); IM_ASSERT(info->ImageCount >= info->MinImageCount); - IM_ASSERT(render_pass != VK_NULL_HANDLE); + if (info->UseDynamicRendering == false) + IM_ASSERT(render_pass != VK_NULL_HANDLE); bd->VulkanInitInfo = *info; bd->RenderPass = render_pass; @@ -1350,6 +1394,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V vkDestroySwapchainKHR(device, old_swapchain, allocator); // Create the Render Pass + if (wd->UseDynamicRendering == false) { VkAttachmentDescription attachment = {}; attachment.format = wd->SurfaceFormat.format; @@ -1412,6 +1457,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V } // Create Framebuffer + if (wd->UseDynamicRendering == false) { VkImageView attachment[1]; VkFramebufferCreateInfo info = {}; @@ -1553,6 +1599,7 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) // Create SwapChain, RenderPass, Framebuffer, etc. wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; + wd->UseDynamicRendering = v->UseDynamicRendering; ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); vd->WindowOwned = true; } @@ -1618,7 +1665,45 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) { ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); + } +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + if (v->UseDynamicRendering) + { + // Transition swapchain image to a layout suitable for drawing. + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.image = fd->Backbuffer; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(fd->CommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + VkRenderingAttachmentInfo attachmentInfo = {}; + attachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR; + attachmentInfo.imageView = fd->BackbufferView; + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE; + attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentInfo.clearValue = wd->ClearValue; + + VkRenderingInfo renderingInfo = {}; + renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR; + renderingInfo.renderArea.extent.width = wd->Width; + renderingInfo.renderArea.extent.height = wd->Height; + renderingInfo.layerCount = 1; + renderingInfo.viewMask = 0; + renderingInfo.colorAttachmentCount = 1; + renderingInfo.pColorAttachments = &attachmentInfo; + + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR(fd->CommandBuffer, &renderingInfo); + } + else +#endif + { VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = wd->RenderPass; @@ -1634,7 +1719,28 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, wd->Pipeline); { - vkCmdEndRenderPass(fd->CommandBuffer); +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + if (v->UseDynamicRendering) + { + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR(fd->CommandBuffer); + + // Transition image to a layout suitable for presentation + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + barrier.image = fd->Backbuffer; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(fd->CommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + else +#endif + { + vkCmdEndRenderPass(fd->CommandBuffer); + } { VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; @@ -1701,3 +1807,7 @@ void ImGui_ImplVulkan_ShutdownPlatformInterface() { ImGui::DestroyPlatformWindows(); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_vulkan.h b/extern/imgui_patched/backends/imgui_impl_vulkan.h index 044437ad..3c5bd6cb 100644 --- a/extern/imgui_patched/backends/imgui_impl_vulkan.h +++ b/extern/imgui_patched/backends/imgui_impl_vulkan.h @@ -25,6 +25,7 @@ // Read comments in imgui_impl_vulkan.h. #pragma once +#ifndef IMGUI_DISABLE #include "imgui.h" // IMGUI_IMPL_API // [Configuration] in order to use a custom Vulkan function loader: @@ -60,6 +61,12 @@ struct ImGui_ImplVulkan_InitInfo uint32_t MinImageCount; // >= 2 uint32_t ImageCount; // >= MinImageCount VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT (0 -> default to VK_SAMPLE_COUNT_1_BIT) + + // Dynamic Rendering (Optional) + bool UseDynamicRendering; // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3. + VkFormat ColorAttachmentFormat; // Required for dynamic rendering + + // Allocation, Debugging const VkAllocationCallbacks* Allocator; void (*CheckVkResultFn)(VkResult err); }; @@ -140,6 +147,7 @@ struct ImGui_ImplVulkanH_Window VkPresentModeKHR PresentMode; VkRenderPass RenderPass; VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo + bool UseDynamicRendering; bool ClearEnable; VkClearValue ClearValue; uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) @@ -156,3 +164,4 @@ struct ImGui_ImplVulkanH_Window } }; +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_wgpu.cpp b/extern/imgui_patched/backends/imgui_impl_wgpu.cpp index 8da8e457..e21ef81d 100644 --- a/extern/imgui_patched/backends/imgui_impl_wgpu.cpp +++ b/extern/imgui_patched/backends/imgui_impl_wgpu.cpp @@ -13,6 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-07-13: Use WGPUShaderModuleWGSLDescriptor's code instead of source. use WGPUMipmapFilterMode_Linear instead of WGPUFilterMode_Linear. (#6602) // 2023-04-11: Align buffer sizes. Use WGSL shaders instead of precompiled SPIR-V. // 2023-04-11: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2023-01-25: Revert automatic pipeline layout generation (see https://github.com/gpuweb/gpuweb/issues/2470) @@ -28,6 +29,7 @@ // 2021-01-28: Initial version. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_wgpu.h" #include #include @@ -230,7 +232,7 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; - wgsl_desc.source = wgsl_source; + wgsl_desc.code = wgsl_source; WGPUShaderModuleDescriptor desc = {}; desc.nextInChain = reinterpret_cast(&wgsl_desc); @@ -511,7 +513,7 @@ static void ImGui_ImplWGPU_CreateFontsTexture() WGPUSamplerDescriptor sampler_desc = {}; sampler_desc.minFilter = WGPUFilterMode_Linear; sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUFilterMode_Linear; + sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; sampler_desc.addressModeU = WGPUAddressMode_Repeat; sampler_desc.addressModeV = WGPUAddressMode_Repeat; sampler_desc.addressModeW = WGPUAddressMode_Repeat; @@ -761,3 +763,7 @@ void ImGui_ImplWGPU_NewFrame() if (!bd->pipelineState) ImGui_ImplWGPU_CreateDeviceObjects(); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_wgpu.h b/extern/imgui_patched/backends/imgui_impl_wgpu.h index 09142078..00a54158 100644 --- a/extern/imgui_patched/backends/imgui_impl_wgpu.h +++ b/extern/imgui_patched/backends/imgui_impl_wgpu.h @@ -13,6 +13,8 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + #include IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format = WGPUTextureFormat_Undefined); @@ -23,3 +25,5 @@ IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURen // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_win32.cpp b/extern/imgui_patched/backends/imgui_impl_win32.cpp index d24c655d..927caea2 100644 --- a/extern/imgui_patched/backends/imgui_impl_win32.cpp +++ b/extern/imgui_patched/backends/imgui_impl_win32.cpp @@ -15,6 +15,7 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_win32.h" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN @@ -950,11 +951,12 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) struct ImGui_ImplWin32_ViewportData { HWND Hwnd; + HWND HwndParent; bool HwndOwned; DWORD DwStyle; DWORD DwExStyle; - ImGui_ImplWin32_ViewportData() { Hwnd = nullptr; HwndOwned = false; DwStyle = DwExStyle = 0; } + ImGui_ImplWin32_ViewportData() { Hwnd = HwndParent = nullptr; HwndOwned = false; DwStyle = DwExStyle = 0; } ~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == nullptr); } }; @@ -974,6 +976,14 @@ static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags fl *out_ex_style |= WS_EX_TOPMOST; } +static HWND ImGui_ImplWin32_GetHwndFromViewportID(ImGuiID viewport_id) +{ + if (viewport_id != 0) + if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id)) + return (HWND)viewport->PlatformHandle; + return nullptr; +} + static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); @@ -981,10 +991,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) // Select style and parent window ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &vd->DwStyle, &vd->DwExStyle); - HWND parent_window = nullptr; - if (viewport->ParentViewportId != 0) - if (ImGuiViewport* parent_viewport = ImGui::FindViewportByID(viewport->ParentViewportId)) - parent_window = (HWND)parent_viewport->PlatformHandle; + vd->HwndParent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId); // Create window RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; @@ -992,7 +999,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) vd->Hwnd = ::CreateWindowEx( vd->DwExStyle, _T("ImGui Platform"), _T("Untitled"), vd->DwStyle, // Style, class name, window name rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area - parent_window, nullptr, ::GetModuleHandle(nullptr), nullptr); // Parent window, Menu, Instance, Param + vd->HwndParent, nullptr, ::GetModuleHandle(nullptr), nullptr); // Owner window, Menu, Instance, Param vd->HwndOwned = true; viewport->PlatformRequestResize = false; viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; @@ -1029,10 +1036,26 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport) { - // (Optional) Update Win32 style if it changed _after_ creation. - // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; IM_ASSERT(vd->Hwnd != 0); + + // Update Win32 parent if it changed _after_ creation + // Unlike style settings derived from configuration flags, this is more likely to change for advanced apps that are manipulating ParentViewportID manually. + HWND new_parent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId); + if (new_parent != vd->HwndParent) + { + // Win32 windows can either have a "Parent" (for WS_CHILD window) or an "Owner" (which among other thing keeps window above its owner). + // Our Dear Imgui-side concept of parenting only mostly care about what Win32 call "Owner". + // The parent parameter of CreateWindowEx() sets up Parent OR Owner depending on WS_CHILD flag. In our case an Owner as we never use WS_CHILD. + // Calling ::SetParent() here would be incorrect: it will create a full child relation, alter coordinate system and clipping. + // Calling ::SetWindowLongPtr() with GWLP_HWNDPARENT seems correct although poorly documented. + // https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613 + vd->HwndParent = new_parent; + ::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent); + } + + // (Optional) Update Win32 style if it changed _after_ creation. + // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. DWORD new_style; DWORD new_ex_style; ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &new_style, &new_ex_style); @@ -1261,3 +1284,5 @@ static void ImGui_ImplWin32_ShutdownPlatformInterface() } //--------------------------------------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/backends/imgui_impl_win32.h b/extern/imgui_patched/backends/imgui_impl_win32.h index 5f720cda..232ddc13 100644 --- a/extern/imgui_patched/backends/imgui_impl_win32.h +++ b/extern/imgui_patched/backends/imgui_impl_win32.h @@ -16,6 +16,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); @@ -45,3 +46,5 @@ IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // // - Use to enable alpha compositing transparency with the desktop. // - Use together with e.g. clearing your framebuffer with zero-alpha. IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/docs/CHANGELOG.txt b/extern/imgui_patched/docs/CHANGELOG.txt index 03d290e4..5ef06ddc 100644 --- a/extern/imgui_patched/docs/CHANGELOG.txt +++ b/extern/imgui_patched/docs/CHANGELOG.txt @@ -5,25 +5,30 @@ This document holds the user-facing changelog that we also use in release notes. We generally fold multiple commits pertaining to the same topic as a single entry. Changes to backends are also included within the individual .cpp files of each backend. -RELEASE NOTES: https://github.com/ocornut/imgui/releases -REPORT ISSUES: https://github.com/ocornut/imgui/issues -DISCUSS, ASK QUESTIONS: https://github.com/ocornut/imgui/discussions -WIKI https://github.com/ocornut/imgui/wiki FAQ https://www.dearimgui.com/faq/ +RELEASE NOTES: https://github.com/ocornut/imgui/releases +WIKI https://github.com/ocornut/imgui/wiki +GETTING STARTED https://github.com/ocornut/imgui/wiki/Getting-Started +GLOSSARY https://github.com/ocornut/imgui/wiki/Glossary +ISSUES & SUPPORT https://github.com/ocornut/imgui/issues WHEN TO UPDATE? - Keeping your copy of Dear ImGui updated regularly is recommended. -- It is generally safe to sync to the latest commit in master or docking branches - The library is fairly stable and regressions tends to be fixed fast when reported. +- It is generally safe and recommended to sync to the latest commit in 'master' or 'docking' + branches. The library is fairly stable and regressions tends to be fixed fast when reported. HOW TO UPDATE? -- Overwrite every file except imconfig.h (if you have modified it). -- You may also locally branch to modify imconfig.h and merge latest into your branch. +- Update submodule or copy/overwrite every file. +- About imconfig.h: + - You may modify your copy of imconfig.h, in this case don't overwrite it. + - or you may locally branch to modify imconfig.h and merge/rebase latest. + - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to + specify a custom path for your imconfig.h file and instead not have to modify the default one. - Read the `Breaking Changes` section (in imgui.cpp or here in the Changelog). - If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it. -- If you are dropping this repository in your codebase, please leave the demo and text files in there, they will be useful. +- If you are copying this repository in your codebase, please leave the demo and documentations files in there, they will be useful. - You may diff your previous Changelog with the one you just copied and read that diff. - You may enable `IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in imconfig.h to forcefully disable legacy names and symbols. Doing it every once in a while is a good way to make sure you are not using obsolete symbols. Dear ImGui is in active development, @@ -32,70 +37,193 @@ HOW TO UPDATE? ----------------------------------------------------------------------- - DOCKING+MULTI-VIEWPORT BRANCH (In Progress) + VERSION 1.89.8 (Released 2023-08-01) ----------------------------------------------------------------------- -DOCKING FEATURES -(see https://github.com/ocornut/imgui/wiki/Docking for quick intro) +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.89.8 -- Added Docking system: [BETA] (#2109, #351) - - Added ImGuiConfigFlags_DockingEnable flag to enable Docking. - Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`. - - Added DockSpace(), DockSpaceOverViewport() API. - - Added ImGuiDockNodeFlags flags for DockSpace(). - - Added SetNextWindowDockID(), SetNextWindowClass() API. - - Added GetWindowDockID(), IsWindowDocked() API. - - Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked. - Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. - - Added ImGuiWindowClass to specify advanced docking/viewport related flags via SetNextWindowClass(). - - Added io.ConfigDockingNoSplit option. - - Added io.ConfigDockingWithShift option. - - Added io.ConfigDockingAlwaysTabBar option. - - Added io.ConfigDockingTransparentPayload option. - - Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. - - Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. +Breaking changes: -MULTI-VIEWPORT FEATURES -(see https://github.com/ocornut/imgui/wiki/Multi-Viewports for quick intro) - -Breaking Changes: - -- IMPORTANT: When multi-viewports are enabled (with io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable), - all coordinates/positions will be in your natural OS coordinates space. It means that: - - Reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are _probably_ not what you want anymore. - Use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos). - - Likewise io.MousePos and GetMousePos() will use OS coordinates. - If you query mouse positions to interact with non-imgui coordinates you will need to offset them. - e.g. subtract GetWindowViewport()->Pos. -- IO: Removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (they were marked obsoleted, used to clip within the (0,0)..(DisplaySize) range). +- IO: Obsoleted io.ClearInputCharacters() (added in 1.47) as it now ambiguous + and often incorrect/misleading considering the existence of a higher-level + input queue. This is automatically cleared by io.ClearInputsKeys(). (#4921) +- ImDrawData: CmdLists[] array is now owned, changed from 'ImDrawList**' to + 'ImVector'. Majority of users shouldn't be affected, but you + cannot compare to NULL nor reassign manually anymore. + Instead use AddDrawList(). Allocation count are identical. (#6406, #4879, #1878) Other changes: -(FIXME: This need a fuller explanation!) -- Added ImGuiPlatformIO structure and GetPlatformIO(). - - Similarly to ImGuiIO and GetIO(), this structure is the main point of communication for backends supporting multi-viewports. - - Backend sets functions in ImGuiPlatformIO to manipulate platform windows. - - ImGuiPlatformIO::Monitors is a list of platform monitors (input from backend) - - ImGuiPlatformIO::Viewports is a list of viewports (output from dear imgui) -- Added ImGuiPlatformMonitor to feed OS monitor information in the ImGuiPlatformIO::Monitors. -- Added GetWindowViewport(), SetNextWindowViewport(). -- Added GetWindowDpiScale(). -- Added GetOverlayDrawList(ImGuiViewport* viewport). - The no-parameter version of GetOverlayDrawList() return the overlay for the current window's viewport. -- Added UpdatePlatformWindows(), RenderPlatformWindowsDefault(), DestroyPlatformWindows() for usage in application setup. -- Added FindViewportByID(), FindViewportByPlatformHandle() for usage by backends. -- Added ImGuiConfigFlags_ViewportsEnable configuration flag and other viewport options. -- Added io.ConfigViewportsNoAutoMerge option. -- Added io.ConfigViewportsNoTaskBarIcon option. -- Added io.ConfigViewportsNoDecoration option. -- Added io.ConfigViewportsNoDefaultParent option. -- Added ImGuiBackendFlags_PlatformHasViewports, ImGuiBackendFlags_RendererHasViewports, ImGuiBackendFlags_HasMouseHoveredViewport backend flags. -- Added io.AddMouseViewportEvent() (optional _even_ for multi-viewport support, tied to ImGuiBackendFlags_HasMouseHoveredViewport flag). -- Expanded ImGuiViewport structure, ImGuiViewportFlags flags. -- Added ImGuiWindowClass and SetNextWindowClass() for passing viewport related hints to the OS/platform back-end. -- Examples: Renderer: OpenGL2, OpenGL3, DirectX9, DirectX10, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. -- Examples: Platforms: Win32, GLFW, SDL2: Added support for multi-viewports. - Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +- Fonts: ImFontConfig::OversampleH now defaults to 2 instead of 3, since the + quality increase is largely minimal. +- Fonts, imgui_freetype: Added support to render OpenType SVG fonts using lunasvg. + Requires enabling IMGUI_ENABLE_FREETYPE_LUNASVG along with IMGUI_ENABLE_FREETYPE, + and providing headers/libraries for lunasvg. (#6591, #6607) [@sakiodre] +- ImDrawData: CmdLists[] array is now an ImVector<> owned by ImDrawData rather + than a pointer to internal state. + - This makes it easier for user to create their own or append to an existing draw data. + Added a ImDrawData::AddDrawList() helper function to do that. (#6406, #4879, #1878) + - This makes it easier to perform a deep-swap instead of a deep-copy, as array + ownership is now clear. (#6597, #6475, #6167, #5776, #5109, #4763, #3515, #1860) + - Syntax and allocation count are otherwise identical. +- Fixed CTRL+Tab dimming background assert when target window has a callback + in the last ImDrawCmd. (#4857, #5937) +- IsItemHovered: Fixed ImGuiHoveredFlags_ForTooltip for Keyboard/Gamepad navigation, + got broken prior to 1.89.7 due to an unrelated change making flags conflict. (#6622, #1485) +- InputText: Fixed a case where deactivation frame would write to underlying + buffer or call CallbackResize although unnecessary, in a frame where the + return value was false. +- Tables: fixed GetContentRegionAvail().y report not taking account of lower cell + padding or of using ImGuiTableFlags_NoHostExtendY. Not taking it into account + would make the idiom of creating vertically bottom-aligned content (e.g. a child + window) inside a table make the parent window erroneously have a scrollbar. (#6619) +- Tables: fixed calculation of multi-instance shared decoration/scrollbar width of + scrolling tables, to avoid flickering width variation when resizing down a table + hosting a child window. (#5920, #6619) +- Scrollbar: layout needs to take account of window border size, so a border size + will slightly reduce scrollbar size. Generally we tried to make it that window + border size has no incidence on layout but this can't work with thick borders. (#2522) +- IO: Added io.ClearEventsQueue() to clear incoming inputs events. (#4921) + May be useful in conjunction with io.ClearInputsKeys() if you need to clear + both current inputs state and queued events (e.g. when using blocking native + dialogs such as Windows's ::MessageBox() or ::GetOpenFileName()). +- IO: Changed io.ClearInputsKeys() specs to also clear current frame character buffer + (what now obsoleted io.ClearInputCharacters() did), as this is effectively the + desirable behavior. +- Misc: Added IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION config macro to disable + stb_sprintf implementation when using IMGUI_USE_STB_SPRINTF. (#6626) [@septag] +- Misc: Avoid stb_textedit.h reincluding string.h while in a namespace, which + messes up with building with Clang Modules. (#6653, #4791) [@JohelEGP] +- Demo: Better showcase use of SetNextItemAllowOverlap(). (#6574, #6512, #3909, #517) +- Demo: Showcase a few more InputText() flags. +- Backends: Made all backends sources files support global IMGUI_DISABLE. (#6601) +- Backends: GLFW: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used + differently. User may set ImGuiConfigFlags_NoMouse if desired. (#5625, #6609) [@scorpion-26] +- Backends: WebGPU: Update for changes in Dawn. (#6602, #6188) [@williamhCode] +- Examples: Vulkan: Creating minimal descriptor pools to fit only what is needed by + example. (#6642) [@SaschaWillem] + +Docking+Viewports Branch: + +- Docking, Style: resizing separators use same colors as window borders (ImGuiCol_Border) + for consistency. With default styles it doesn't make a big difference. (#2522) [@rmitton] + In the future if we promote using thick value for inner/outer docking padding we may + need to introduce new colors for it. +- Docking: added style.DockingSeparatorSize, ImGuiStyleVar_DockingSeparatorSize. Now + also scaled by style.ScaleAllSizes(). (#3481, #4721, #2522) [@PossiblyAShrub, @wobbier] +- Docking: fixed rendering of docked-window scrollbar above outer border. (#2522) + + +----------------------------------------------------------------------- + VERSION 1.89.7 (Released 2023-07-04) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.89.7 + +Breaking changes: + +- Moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. + As the fields were added in 1.89 and expected to be left unchanged by most users, or only + tweaked once during app initialisation, we are exceptionally accepting the breakage. + Majority of users should not even notice. +- Overlapping items: (#6512, #3909, #517) + - Added 'SetNextItemAllowOverlap()' (called before an item) as a replacement for using + 'SetItemAllowOverlap()' (called after an item). This is roughly equivalent to using the + legacy 'SetItemAllowOverlap()' call (public API) + ImGuiButtonFlags_AllowOverlap (internal). + - Obsoleted 'SetItemAllowOverlap()': it didn't and couldn't work reliably since 1.89 (2022-11-15), + and relied on ambiguously defined design. Use 'SetNextItemAllowOverlap()' before item instead. + - Selectable, TreeNode: When using ImGuiSelectableFlags_AllowOverlap/ImGuiTreeNodeFlags_AllowOverlap + and holding item held, overlapping widgets won't appear as hovered. (#6512, #3909) + While this fixes a common small visual issue, it also means that calling IsItemHovered() + after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't + use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610) + - Renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap'. + - Renamed 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap' + - Kept redirecting enums (will obsolete). + +Other changes: + +- Tooltips/IsItemHovered() related changes: + - Tooltips: Added SetItemTooltip() and BeginItemTooltip() functions. + They are shortcuts for the common idiom of using IsItemHovered(). + - SetItemTooltip("Hello") == if (IsItemHovered(ImGuiHoveredFlags_Tooltip)) { SetTooltip("Hello"); } + - BeginItemTooltip() == IsItemHovered(ImGuiHoveredFlags_Tooltip) && BeginTooltip() + The newly added ImGuiHoveredFlags_Tooltip is meant to facilitate standardizing + mouse hovering delays and rules for a given application. + The previously common idiom of using 'if (IsItemHovered()) { SetTooltip(...); }' + won't use delay or stationary test. + - IsItemHovered: Added ImGuiHoveredFlags_Stationary to require mouse being + stationary when hovering a new item. Added style.HoverStationaryDelay (~0.15 sec). + Once the mouse has been stationary once the state is preserved for same item. (#1485) + - IsItemHovered: Added ImGuiHoveredFlags_ForTooltip as a shortcut for pulling flags + from style.HoverFlagsForTooltipMouse or style.HoverFlagsForTooltipNav depending + on active inputs (#1485) + - style.HoverFlagsForTooltipMouse defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort' + - style.HoverFlagsForTooltipNav defaults to 'ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal'. + - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items + isn't covered as much. (Match offset for drag and drop tooltips) + - IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40, + Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) + - IsItemHovered: Added ImGuiHoveredFlags_AllowWhenOverlappedByWindow to ignore window-overlap only. + Option ImGuiHoveredFlags_AllowWhenOverlapped now expand into a combination of both + _AllowWhenOverlappedByWindow + _AllowWhenOverlappedByItem, matching old behavior. +- Overlapping items: (#6512, #3909, #517) + - Most item types should now work with SetNextItemAllowOverlap(). (#6512, #3909, #517) + - Fixed first frame of an overlap highlighting underlying item if previous frame didn't hover anything. + - IsItemHovered: Changed to return false when querying an item using AllowOverlap mode which + is being overlapped. Added ImGuiHoveredFlags_AllowWhenOverlappedByItem to opt-out. (#6512, #3909, #517) +- IsWindowHovered: Added support for ImGuiHoveredFlags_Stationary. +- IsWindowHovered, IsItemHovered: Assert when passed any unsupported flags. +- Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either + ScrollX or ScrollY flags from being impossible to resize. (#6503) +- CollapsingHeader/TreeNode: Fixed text padding when using _Framed+_Leaf flags. (#6549) [@BobbyAnguelov] +- InputText: Fixed not returning true when buffer is cleared while using the + ImGuiInputTextFlags_EscapeClearsAll flag. (#5688, #2620) +- InputText: Fixed a crash on deactivating a ReadOnly buffer. (#6570, #6292, #4714) +- InputText: ImGuiInputTextCallbackData::InsertChars() accept (NULL,NULL) range, in order to conform + to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#6565, #6566, #3615) +- Combo: Made simple/legacy Combo() function not returns true when picking already selected item. + This is consistent with other widgets. If you need something else, you can use BeginCombo(). (#1182) +- Clipper: Rework inner logic to allow functioning with a zero-clear constructor. + This is order to facilitate usage for language bindings (e.g cimgui or dear_binding) + where user may not be calling a constructor manually. (#5856) +- Drag and Drop: Apply default behavior of drag source not reporting itself as hovered + at lower-level, so DragXXX, SliderXXX, InputXXX, Plot widgets are fulfilling it. + (Behavior doesn't apply when ImGuiDragDropFlags_SourceNoDisableHover is set). +- Modals: In the case of nested modal, made sure that focused or appearing windows are + moved below the lowest blocking modal (rather than the highest one). (#4317) +- GetKeyName(): Fixed assert with ImGuiMod_XXX values when IMGUI_DISABLE_OBSOLETE_KEYIO is set. +- Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra + comments. Currently mainly for inspecting Docking .ini data, but makes saving slower. +- Demo: Added more developed "Widgets->Tooltips" section. (#1485) +- Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] +- Backends: OpenGL3: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts + lower than 3.2. (#6539, #6333) [@krumelmonster] +- Backends: Vulkan: Added optional support for VK_KHR_dynamic_rendering (Vulkan 1.3+) in the + backend for applications using it. User needs to set 'init_info->UseDynamicRendering = true' + and 'init_info->ColorAttachmentFormat'. RenderPass becomes unused. (#5446, #5037) [@spnda, @cmarcelo] +- Backends: GLFW: Accept glfwGetTime() not returning a monotonically increasing value. + This seems to happens on some Windows setup when peripherals disconnect, and is likely + to also happen on browser+Emscripten. Matches similar 1.89.4 fix in SDL backend. (#6491) +- Examples: Win32+OpenGL3: Changed DefWindowProc() to DefWindowProcW() to match other examples + and support the example app being compiled without UNICODE. (#6516, #5725, #5961, #5975) [@yenixing] + +Docking+Viewports Branch: + +- Viewports+Docking: Fixed extraneous viewport+platform-window recreation in various + combination of showing or hiding windows, docking with/without split, undocking. + While with some backends and without OS decorations, some extraneous window recreation + were visibly not noticeable, they would typically become noticeable when enabling + OS decorations on those windows (e.g. Windows title bar fade-in/animation). +- Viewports: Closing a viewport via OS/platform means (e.g. OS close button or task-bar menu), + mark all windows in this viewport as closed. +- Docking: Fixed one-frame flickering on reappearing windows binding to a dock node + where a later-submitted window was already bound. +- Docking: Fixed dragging from title-bar empty space (regression from 1.88 related to + keeping ID alive when calling low-level ButtonBehavior() directly). (#5181, #2645) +- Docking: [Internal] DockBuilderDockWindow() API calls don't clear docking order + if the target node is same as existing one. +- Backends: Win32: Added support for changing ParentViewportID after viewport creation. ----------------------------------------------------------------------- @@ -643,7 +771,7 @@ Other Changes: - ColorEdit3: fixed id collision leading to an assertion. (#5707) - IsItemHovered: Added ImGuiHoveredFlags_DelayNormal and ImGuiHoveredFlags_DelayShort flags, allowing to introduce a shared delay for tooltip idioms. The delays are respectively - io.HoverDelayNormal (default to 0.30f) and io.HoverDelayFast (default to 0.10f). (#1485) + io.HoverDelayNormal (default to 0.30f) and io.HoverDelayShort (default to 0.10f). (#1485) - IsItemHovered: Added ImGuiHoveredFlags_NoSharedDelay to disable sharing delays between items, so moving from one item to a nearby one will requires delay to elapse again. (#1485) - Tables: activating an ID (e.g. clicking button inside) column doesn't prevent columns diff --git a/extern/imgui_patched/docs/CONTRIBUTING.md b/extern/imgui_patched/docs/CONTRIBUTING.md index 81a6f0e3..26e82f51 100644 --- a/extern/imgui_patched/docs/CONTRIBUTING.md +++ b/extern/imgui_patched/docs/CONTRIBUTING.md @@ -11,7 +11,7 @@ ## Getting Started & General Advice - Article: [How To Ask Good Questions](https://bit.ly/3nwRnx1). -- Please browse the [Wiki](https://github.com/ocornut/imgui/wiki) to find code snippets, links and other resources (e.g. [Useful extensions](https://github.com/ocornut/imgui/wiki/Useful-Extensions)). +- Please browse the [Wiki](https://github.com/ocornut/imgui/wiki) to find code snippets, links and other resources (e.g. [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started), [Useful extensions](https://github.com/ocornut/imgui/wiki/Useful-Extensions)). - Please read [docs/FAQ.md](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md). - Please read [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) if your question relates to fonts or text. - Please read one of the [examples/](https://github.com/ocornut/imgui/tree/master/examples) application if your question relates to setting up Dear ImGui. diff --git a/extern/imgui_patched/docs/FAQ.md b/extern/imgui_patched/docs/FAQ.md index 2a2c9ebf..d0e75799 100644 --- a/extern/imgui_patched/docs/FAQ.md +++ b/extern/imgui_patched/docs/FAQ.md @@ -50,7 +50,8 @@ or view this file with any Markdown viewer. **This library is poorly documented at the moment and expects the user to be acquainted with C/C++.** - The [Wiki](https://github.com/ocornut/imgui/wiki) is a hub to many resources and links. -- Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the [examples/](https://github.com/ocornut/imgui/blob/master/examples/) folder to explain how to integrate Dear ImGui with your own engine/application. You can run those applications and explore them. +- Handy [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide to integrate Dear ImGui in an existing application. +- 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the [examples/](https://github.com/ocornut/imgui/blob/master/examples/) folder to explain how to integrate Dear ImGui with your own engine/application. You can run those applications and explore them. - See demo code in [imgui_demo.cpp](https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp) and particularly the `ImGui::ShowDemoWindow()` function. The demo covers most features of Dear ImGui, so you can read the code and see its output. - See documentation: [Backends](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md), [Examples](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md), [Fonts](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md). - See documentation and comments at the top of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp) + general API comments in [imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h). @@ -90,6 +91,7 @@ Many projects are using this branch and it is kept in sync with master regularly ### Q: How to get started? +Read [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started).
Read [EXAMPLES.md](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md).
Read [BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md).
Read `PROGRAMMER GUIDE` section of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp).
@@ -163,8 +165,8 @@ Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-lik --- ### Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... -Your renderer is not using the font texture correctly or it hasn't been uploaded to the GPU. -- If this happens using the standard backends: A) have you modified the font atlas after `ImGui_ImplXXX_NewFrame()`? B) maybe the texture failed to upload, which could happens if for some reason your texture is too big. Also see [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md). +Your renderer backend is not using the font texture correctly or it hasn't been uploaded to the GPU. +- If this happens using the standard backends: A) have you modified the font atlas after `ImGui_ImplXXX_NewFrame()`? B) maybe the texture failed to upload, which **can if your texture atlas is too big**. Also see [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md). - If this happens with a custom backend: make sure you have uploaded the font texture to the GPU, that all shaders are rendering states are setup properly (e.g. texture is bound). Compare your code to existing backends and use a graphics debugger such as [RenderDoc](https://renderdoc.org) to debug your rendering states. ##### [Return to Index](#index) @@ -528,7 +530,7 @@ This approach is relatively easy and functional but comes with two issues: - Style override may be lost during the `Begin()` call crossing monitor boundaries. You may need to do some custom scaling mumbo-jumbo if you want your `OnChangedViewport()` handler to preserve style overrides. Please note that if you are not using multi-viewports with multi-monitors using different DPI scales, you can ignore that and use the simpler technique recommended at the top. - + On Windows, in addition to scaling the font size (make sure to round to an integer) and using `style.ScaleAllSizes()`, you will need to inform Windows that your application is DPI aware. If this is not done, Windows will scale the application window and the UI text will be blurry. Potential solutions to indicate DPI awareness on Windows are: - For SDL: the flag `SDL_WINDOW_ALLOW_HIGHDPI` needs to be passed to `SDL_CreateWindow()``. @@ -568,44 +570,15 @@ io.Fonts->AddFontFromFileTTF("MyFolder/MyFont.ttf", size); // ALSO CORRECT ### Q: How can I easily use icons in my application? The most convenient and practical way is to merge an icon font such as FontAwesome inside your main font. Then you can refer to icons within your strings. -You may want to see `ImFontConfig::GlyphMinAdvanceX` to make your icon look monospace to facilitate alignment. -(Read the [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) file for more details about icons font loading.) -With some extra effort, you may use colorful icons by registering custom rectangle space inside the font atlas, -and copying your own graphics data into it. See docs/FONTS.md about using the AddCustomRectFontGlyph API. +Read the [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) file for more details about icons font loading. ##### [Return to Index](#index) --- ### Q: How can I load multiple fonts? -Use the font atlas to pack them into a single texture: -(Read the [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) file and the code in ImFontAtlas for more details.) -```cpp -ImGuiIO& io = ImGui::GetIO(); -ImFont* font0 = io.Fonts->AddFontDefault(); -ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); -ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels); -io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() -// the first loaded font gets used by default -// use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime - -// Options -ImFontConfig config; -config.OversampleH = 2; -config.OversampleV = 1; -config.GlyphOffset.y -= 1.0f; // Move everything by 1 pixel up -config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters -io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config); - -// Combine multiple fonts into one (e.g. for icon fonts) -static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; -ImFontConfig config; -config.MergeMode = true; -io.Fonts->AddFontDefault(); -io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font -io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, nullptr, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs -``` +Use the font atlas to pack them into a single texture. Read [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) for more details. ##### [Return to Index](#index) diff --git a/extern/imgui_patched/docs/FONTS.md b/extern/imgui_patched/docs/FONTS.md index 6049fb48..fa68dad2 100644 --- a/extern/imgui_patched/docs/FONTS.md +++ b/extern/imgui_patched/docs/FONTS.md @@ -11,10 +11,7 @@ In the [misc/fonts/](https://github.com/ocornut/imgui/tree/master/misc/fonts) fo **Also read the FAQ:** https://www.dearimgui.com/faq (there is a Fonts section!) ## Index -- [Readme First](#readme-first) -- [About Filenames](#about-filenames) -- [About UTF-8 Encoding](#about-utf-8-encoding) -- [Debug Tools](#debug-tools) +- [Troubleshooting](#troubleshooting) - [How should I handle DPI in my application?](#how-should-i-handle-dpi-in-my-application) - [Fonts Loading Instructions](#fonts-loading-instructions) - [Using Icon Fonts](#using-icon-fonts) @@ -23,103 +20,44 @@ In the [misc/fonts/](https://github.com/ocornut/imgui/tree/master/misc/fonts) fo - [Using Custom Glyph Ranges](#using-custom-glyph-ranges) - [Using Custom Colorful Icons](#using-custom-colorful-icons) - [Using Font Data Embedded In Source Code](#using-font-data-embedded-in-source-code) +- [About Filenames](#about-filenames) +- [About UTF-8 Encoding](#about-utf-8-encoding) +- [Debug Tools](#debug-tools) - [Credits/Licenses For Fonts Included In Repository](#creditslicenses-for-fonts-included-in-repository) - [Font Links](#font-links) --------------------------------------- -## Readme First +## Troubleshooting -**A vast majority of font and text related issues encountered comes from 3 things:** -- Invalid filename due to use of `\` or unexpected working directory. See [About Filenames](#about-filenames). AddFontXXX functions should assert if the filename is incorrect. -- Invalid UTF-8 encoding of your non-ASCII strings. See [About UTF-8 Encoding](#about-utf-8-encoding). Use the encoding viewer to confirm yours is correct. -- You need to load a font with explicit glyph ranges if you want to use non-ASCII characters. See [Fonts Loading Instructions](#fonts-loading-instructions). Use Metrics/Debugger->Fonts to confirm loaded fonts and loaded glyph ranges. +**A vast majority of font and text related issues encountered comes from 4 things:** -The third point is a current constraint of Dear ImGui (which we will lift in the future): when loading a font you need to specify which characters glyphs to load. -All loaded fonts glyphs are rendered into a single texture atlas ahead of time. Calling either of `io.Fonts->GetTexDataAsAlpha8()`, `io.Fonts->GetTexDataAsRGBA32()` or `io.Fonts->Build()` will build the atlas. This is generally called by the Renderer backend, e.g. `ImGui_ImplDX11_NewFrame()` calls it. +### (1) Invalid filename due to use of `\` or unexpected working directory. -**If you use custom glyphs ranges, make sure the array is persistent** and available during the calls to `GetTexDataAsAlpha8()/GetTexDataAsRGBA32()/Build()`. +See [About Filenames](#about-filenames). AddFontXXX functions should assert if the filename is incorrect. -##### [Return to Index](#index) +### (2) Invalid UTF-8 encoding of your non-ASCII strings. -## About Filenames +See [About UTF-8 Encoding](#about-utf-8-encoding). Use the encoding viewer to confirm encoding of string literal in your source code is correct. -**Please note that many new C/C++ users have issues loading their files _because the filename they provide is wrong_ due to incorrect assumption of what is the current directory.** +### (3) Missing glyph ranges. -Two things to watch for: +You need to load a font with explicit glyph ranges if you want to use non-ASCII characters. See [Fonts Loading Instructions](#fonts-loading-instructions). Use [Debug Tools](#debug-tools) confirm loaded fonts and loaded glyph ranges. -(1) In C/C++ and most programming languages if you want to use a backslash `\` within a string literal, you need to write it double backslash `\\`. At it happens, Windows uses backslashes as a path separator, so be mindful. -```cpp -io.Fonts->AddFontFromFileTTF("MyFiles\MyImage01.jpg", ...); // This is INCORRECT!! -io.Fonts->AddFontFromFileTTF("MyFiles\\MyImage01.jpg", ...); // This is CORRECT -``` -In some situations, you may also use `/` path separator under Windows. +This is a current constraint of Dear ImGui (which we will lift in the future): when loading a font you need to specify which characters glyphs to load. +All loaded fonts glyphs are rendered into a single texture atlas ahead of time. Calling either of `io.Fonts->GetTexDataAsAlpha8()`, `io.Fonts->GetTexDataAsRGBA32()` or `io.Fonts->Build()` will build the atlas. This is generally called by the Renderer backend, e.g. `ImGui_ImplDX11_NewFrame()` calls it. **If you use custom glyphs ranges, make sure the array is persistent** and available during the calls to `GetTexDataAsAlpha8()/GetTexDataAsRGBA32()/Build()`. -(2) Make sure your IDE/debugger settings starts your executable from the right working (current) directory. In Visual Studio you can change your working directory in project `Properties > General > Debugging > Working Directory`. People assume that their execution will start from the root folder of the project, where by default it often starts from the folder where object or executable files are stored. -```cpp -io.Fonts->AddFontFromFileTTF("MyImage01.jpg", ...); // Relative filename depends on your Working Directory when running your program! -io.Fonts->AddFontFromFileTTF("../MyImage01.jpg", ...); // Load from the parent folder of your Working Directory -``` -##### [Return to Index](#index) +### (4) Font atlas texture fails to upload to GPU. +This is often of byproduct of point 3. If you have large number of glyphs or multiple fonts, the texture may become too big for your graphics API. **The typical result of failing to upload a texture is if every glyph or everything appears as empty black or white rectangle.** Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours. -## About UTF-8 Encoding - -**For non-ASCII characters display, a common user issue is not passing correctly UTF-8 encoded strings.** - -(1) We provide a function `ImGui::DebugTextEncoding(const char* text)` which you can call to verify the content of your UTF-8 strings. -This is a convenient way to confirm that your encoding is correct. - -```cpp -ImGui::SeparatorText("CORRECT"); -ImGui::DebugTextEncoding(u8"こんにちは"); - -ImGui::SeparatorText("INCORRECT"); -ImGui::DebugTextEncoding("こんにちは"); -``` -![UTF-8 Encoding viewer](https://github.com/ocornut/imgui/assets/8225057/61c1696a-9a94-46c5-9627-cf91211111f0) - -You can also find this tool under `Metrics/Debuggers->Tools->UTF-8 Encoding viewer` if you want to paste from clipboard, but this won't validate the UTF-8 encoding done by your compiler. - -(2) To encode in UTF-8: - -There are also compiler-specific ways to enforce UTF-8 encoding by default: - -- Visual Studio compiler: `/utf-8` command-line flag. -- Visual Studio compiler: `#pragma execution_character_set("utf-8")` inside your code. -- Since May 2023 we have changed the Visual Studio projects of all our examples to use `/utf-8` ([see commit](https://github.com/ocornut/imgui/commit/513af1efc9080857bbd10000d98f98f2a0c96803)). - -Or, since C++11, you can use the `u8"my text"` syntax to encode literal strings as UTF-8. e.g.: -```cpp -ImGui::Text(u8"hello"); -ImGui::Text(u8"こんにちは"); // this will always be encoded as UTF-8 -ImGui::Text("こんにちは"); // the encoding of this is depending on compiler settings/flags and may be incorrect. -``` - -Since C++20, because the C++ committee hate its users, they decided to change the `u8""` syntax to not return `const char*` but a new type `const char_t*` which doesn't cast to `const char*`. -Because of type usage of `u8""` in C++20 is a little more tedious: -```cpp -ImGui::Text((const char*)u8"こんにちは"); -``` -We suggest using a macro in your codebase: -```cpp -#define U8(_S) (const char*)u8##_S -ImGui::Text(U8("こんにちは")); -``` -##### [Return to Index](#index) - - -## Debug Tools - -#### Metrics/Debugger->Fonts -You can use the `Metrics/Debugger` window (available in `Demo>Tools`) to browse your fonts and understand what's going on if you have an issue. You can also reach it in `Demo->Tools->Style Editor->Fonts`. The same information are also available in the Style Editor under Fonts. - -![Fonts debugging](https://user-images.githubusercontent.com/8225057/135429892-0e41ef8d-33c5-4991-bcf6-f997a0bcfd6b.png) - -#### UTF-8 Encoding Viewer** -You can use the `UTF-8 Encoding viewer` in `Metrics/Debugger` to verify the content of your UTF-8 strings. From C/C++ code, you can call `ImGui::DebugTextEncoding("my string");` function to verify that your UTF-8 encoding is correct. - -![UTF-8 Encoding viewer](https://user-images.githubusercontent.com/8225057/166505963-8a0d7899-8ee8-4558-abb2-1ae523dc02f9.png) +Some solutions: +- You may reduce oversampling, e.g. `font_config.OversampleH = 1`, this will half your texture size for a quality looss. + Note that while OversampleH = 2 looks visibly very close to 3 in most situations, with OversampleH = 1 the quality drop will be noticeable. Read about oversampling [here](https://github.com/nothings/stb/blob/master/tests/oversample). +- Reduce glyphs ranges by calculating them from source localization data. + You can use the `ImFontGlyphRangesBuilder` for this purpose and rebuilding your atlas between frames when new characters are needed. This will be the biggest win! +- Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two. +- Set `io.Fonts.TexDesiredWidth` to specify a texture width to reduce maximum texture height (see comment in `ImFontAtlas::Build()` function). ##### [Return to Index](#index) @@ -144,7 +82,7 @@ io.Fonts->AddFontDefault(); ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels); ``` -If you get an assert stating "Could not load font file!", your font filename is likely incorrect. Read "[About filenames](#about-filenames)" carefully. +If you get an assert stating "Could not load font file!", your font filename is likely incorrect. Read [About filenames](#about-filenames) carefully. **Load multiple fonts:** ```cpp @@ -153,8 +91,9 @@ ImGuiIO& io = ImGui::GetIO(); ImFont* font1 = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels); ImFont* font2 = io.Fonts->AddFontFromFileTTF("anotherfont.otf", size_pixels); ``` + +In your application loop, select which font to use: ```cpp -// In application loop: select font at runtime ImGui::Text("Hello"); // use the default font (which is the first loaded font) ImGui::PushFont(font2); ImGui::Text("Hello with another font"); @@ -220,22 +159,6 @@ ImGui::SliderFloat("float", &f, 0.0f, 1.0f); ![sample code output](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v160/code_sample_02_jp.png)
_(settings: Dark style (left), Light style (right) / Font: NotoSansCJKjp-Medium, 20px / Rounding: 5)_ -**Font Atlas too large?** - -- If you have very large number of glyphs or multiple fonts, the texture may become too big for your graphics API. The typical result of failing to upload a texture is if every glyph appears as a white rectangle. -- Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours. - -Some solutions: - -1. Reduce glyphs ranges by calculating them from source localization data. - You can use the `ImFontGlyphRangesBuilder` for this purpose and rebuilding your atlas between frames when new characters are needed. This will be the biggest win! -2. You may reduce oversampling, e.g. `font_config.OversampleH = 2`, this will largely reduce your texture size. - Note that while OversampleH = 2 looks visibly very close to 3 in most situations, with OversampleH = 1 the quality drop will be noticeable. -3. Set `io.Fonts.TexDesiredWidth` to specify a texture width to minimize texture height (see comment in `ImFontAtlas::Build()` function). -4. Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two. -5. Read about oversampling [here](https://github.com/nothings/stb/blob/master/tests/oversample). -6. To support the extended range of unicode beyond 0xFFFF (e.g. emoticons, dingbats, symbols, shapes, ancient languages, etc...) add `#define IMGUI_USE_WCHAR32`in your `imconfig.h`. - ##### [Return to Index](#index) ## Using Icon Fonts @@ -270,6 +193,12 @@ ImGui::Button(ICON_FA_SEARCH " Search"); ``` See Links below for other icons fonts and related tools. +**Monospace Icons?** + +To make your icon look more monospace and facilitate alignment, you may want to set the ImFontConfig::GlyphMinAdvanceX value when loading an icon font. + +**Screenshot** + Here's an application using icons ("Avoyd", https://www.avoyd.com): ![avoyd](https://user-images.githubusercontent.com/8225057/81696852-c15d9e80-9464-11ea-9cab-2a4d4fc84396.jpg) @@ -287,7 +216,7 @@ Here's an application using icons ("Avoyd", https://www.avoyd.com): ## Using Colorful Glyphs/Emojis -- Rendering of colored emojis is only supported by imgui_freetype with FreeType 2.10+. +- Rendering of colored emojis is supported by imgui_freetype with FreeType 2.10+. - You will need to load fonts with the `ImGuiFreeTypeBuilderFlags_LoadColor` flag. - Emojis are frequently encoded in upper Unicode layers (character codes >0x10000) and will need dear imgui compiled with `IMGUI_USE_WCHAR32`. - Not all types of color fonts are supported by FreeType at the moment. @@ -384,6 +313,93 @@ ImFont* font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(compressed_data_ba ##### [Return to Index](#index) +-- + +## About Filenames + +**Please note that many new C/C++ users have issues loading their files _because the filename they provide is wrong_ due to incorrect assumption of what is the current directory.** + +Two things to watch for: + +(1) In C/C++ and most programming languages if you want to use a backslash `\` within a string literal, you need to write it double backslash `\\`. At it happens, Windows uses backslashes as a path separator, so be mindful. +```cpp +io.Fonts->AddFontFromFileTTF("MyFiles\MyImage01.jpg", ...); // This is INCORRECT!! +io.Fonts->AddFontFromFileTTF("MyFiles\\MyImage01.jpg", ...); // This is CORRECT +``` +In some situations, you may also use `/` path separator under Windows. + +(2) Make sure your IDE/debugger settings starts your executable from the right working (current) directory. In Visual Studio you can change your working directory in project `Properties > General > Debugging > Working Directory`. People assume that their execution will start from the root folder of the project, where by default it often starts from the folder where object or executable files are stored. +```cpp +io.Fonts->AddFontFromFileTTF("MyImage01.jpg", ...); // Relative filename depends on your Working Directory when running your program! +io.Fonts->AddFontFromFileTTF("../MyImage01.jpg", ...); // Load from the parent folder of your Working Directory +``` +##### [Return to Index](#index) + +-- + +## About UTF-8 Encoding + +**For non-ASCII characters display, a common user issue is not passing correctly UTF-8 encoded strings.** + +(1) We provide a function `ImGui::DebugTextEncoding(const char* text)` which you can call to verify the content of your UTF-8 strings. +This is a convenient way to confirm that your encoding is correct. + +```cpp +ImGui::SeparatorText("CORRECT"); +ImGui::DebugTextEncoding(u8"こんにちは"); + +ImGui::SeparatorText("INCORRECT"); +ImGui::DebugTextEncoding("こんにちは"); +``` +![UTF-8 Encoding viewer](https://github.com/ocornut/imgui/assets/8225057/61c1696a-9a94-46c5-9627-cf91211111f0) + +You can also find this tool under `Metrics/Debuggers->Tools->UTF-8 Encoding viewer` if you want to paste from clipboard, but this won't validate the UTF-8 encoding done by your compiler. + +(2) To encode in UTF-8: + +There are also compiler-specific ways to enforce UTF-8 encoding by default: + +- Visual Studio compiler: `/utf-8` command-line flag. +- Visual Studio compiler: `#pragma execution_character_set("utf-8")` inside your code. +- Since May 2023 we have changed the Visual Studio projects of all our examples to use `/utf-8` ([see commit](https://github.com/ocornut/imgui/commit/513af1efc9080857bbd10000d98f98f2a0c96803)). + +Or, since C++11, you can use the `u8"my text"` syntax to encode literal strings as UTF-8. e.g.: +```cpp +ImGui::Text(u8"hello"); +ImGui::Text(u8"こんにちは"); // this will always be encoded as UTF-8 +ImGui::Text("こんにちは"); // the encoding of this is depending on compiler settings/flags and may be incorrect. +``` + +Since C++20, because the C++ committee hate its users, they decided to change the `u8""` syntax to not return `const char*` but a new type `const char_t*` which doesn't cast to `const char*`. +Because of type usage of `u8""` in C++20 is a little more tedious: +```cpp +ImGui::Text((const char*)u8"こんにちは"); +``` +We suggest using a macro in your codebase: +```cpp +#define U8(_S) (const char*)u8##_S +ImGui::Text(U8("こんにちは")); +``` +##### [Return to Index](#index) + +-- + +## Debug Tools + +#### Metrics/Debugger->Fonts +You can use the `Metrics/Debugger` window (available in `Demo>Tools`) to browse your fonts and understand what's going on if you have an issue. You can also reach it in `Demo->Tools->Style Editor->Fonts`. The same information are also available in the Style Editor under Fonts. + +![Fonts debugging](https://user-images.githubusercontent.com/8225057/135429892-0e41ef8d-33c5-4991-bcf6-f997a0bcfd6b.png) + +#### UTF-8 Encoding Viewer** +You can use the `UTF-8 Encoding viewer` in `Metrics/Debugger` to verify the content of your UTF-8 strings. From C/C++ code, you can call `ImGui::DebugTextEncoding("my string");` function to verify that your UTF-8 encoding is correct. + +![UTF-8 Encoding viewer](https://user-images.githubusercontent.com/8225057/166505963-8a0d7899-8ee8-4558-abb2-1ae523dc02f9.png) + +##### [Return to Index](#index) + +-- + ## Credits/Licenses For Fonts Included In Repository Some fonts files are available in the `misc/fonts/` folder: diff --git a/extern/imgui_patched/docs/README.md b/extern/imgui_patched/docs/README.md index c3159d36..10e7bde7 100644 --- a/extern/imgui_patched/docs/README.md +++ b/extern/imgui_patched/docs/README.md @@ -39,7 +39,7 @@ Dear ImGui is particularly suited to integration in game engines (for tooling), ### Usage -**The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (imgui*.cpp, imgui*.h). **No specific build process is required**. You can add the .cpp files into your existing project. +**The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (imgui*.cpp, imgui*.h). **No specific build process is required**. You can add the .cpp files into your existing project. See [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started). **Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. See the [Integration](#integration) section of this document for details. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui. @@ -92,7 +92,7 @@ Dear ImGui allows you to **create elaborate tools** as well as very short-lived ### How it works -Check out the Wiki's [About the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) section if you want to understand the core principles behind the IMGUI paradigm. An IMGUI tries to minimize superfluous state duplication, state synchronization, and state retention from the user's point of view. It is less error-prone (less code and fewer bugs) than traditional retained-mode interfaces, and lends itself to creating dynamic user interfaces. +The IMGUI paradigm through its API tries to minimize superfluous state duplication, state synchronization, and state retention from the user's point of view. It is less error-prone (less code and fewer bugs) than traditional retained-mode interfaces, and lends itself to creating dynamic user interfaces. Check out the Wiki's [About the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) section for more details. Dear ImGui outputs vertex buffers and command lists that you can easily render in your application. The number of draw calls and state changes required to render them is fairly small. Because Dear ImGui doesn't know or touch graphics state directly, you can call its functions anywhere in your code (e.g. in the middle of a running algorithm, or in the middle of your own rendering process). Refer to the sample applications in the examples/ folder for instructions on how to integrate Dear ImGui with your existing codebase. @@ -108,7 +108,7 @@ Reading the changelogs is a good way to keep up to date with the things Dear ImG Calling the `ImGui::ShowDemoWindow()` function will create a demo window showcasing a variety of features and examples. The code is always available for reference in `imgui_demo.cpp`. [Here's how the demo looks](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v167/v167-misc.png). You should be able to build the examples from sources. If you don't, let us know! If you want to have a quick look at some Dear ImGui features, you can download Windows binaries of the demo app here: -- [imgui-demo-binaries-20220504.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20220504.zip) (Windows, 1.88 WIP, built 2022/05/04, master) or [older binaries](https://www.dearimgui.com/binaries). +- [imgui-demo-binaries-20230704.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20230704.zip) (Windows, 1.89.7, built 2023/07/04, master) or [older binaries](https://www.dearimgui.com/binaries). The demo applications are not DPI aware so expect some blurriness on a 4K screen. For DPI awareness in your application, you can load/reload your font at a different scale and scale your style with `style.ScaleAllSizes()` (see [FAQ](https://www.dearimgui.com/faq)). @@ -116,7 +116,7 @@ The demo applications are not DPI aware so expect some blurriness on a 4K screen On most platforms and when using C++, **you should be able to use a combination of the [imgui_impl_xxxx](https://github.com/ocornut/imgui/tree/master/backends) backends without modification** (e.g. `imgui_impl_win32.cpp` + `imgui_impl_dx11.cpp`). If your engine supports multiple platforms, consider using more imgui_impl_xxxx files instead of rewriting them: this will be less work for you, and you can get Dear ImGui running immediately. You can _later_ decide to rewrite a custom backend using your custom engine functions if you wish so. -Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading a texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that. If you are an experienced programmer at ease with those concepts, it should take you less than two hours to integrate Dear ImGui into your custom engine. **Make sure to spend time reading the [FAQ](https://www.dearimgui.com/faq), comments, and the examples applications!** +See [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started). Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading a texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles, which is essentially what Backends are doing. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that: setting up a window and using backends. If you follow [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) it should in theory takes you less than an hour to integrate Dear ImGui. **Make sure to spend time reading the [FAQ](https://www.dearimgui.com/faq), comments, and the examples applications!** Officially maintained backends/bindings (in repository): - Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_Renderer, Vulkan, WebGPU. @@ -148,7 +148,7 @@ For a list of third-party widgets and extensions, check out the [Useful Extensio See: [Frequently Asked Questions (FAQ)](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md) where common questions are answered. -See: [Wiki](https://github.com/ocornut/imgui/wiki) for many links, references, articles. +See: [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) and [Wiki](https://github.com/ocornut/imgui/wiki) for many links, references, articles. See: [Articles about the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) to read/learn about the Immediate Mode GUI paradigm. @@ -162,7 +162,7 @@ Private support is available for paying business customers (E-mail: _contact @ d **Which version should I get?** -We occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to master/latest. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/issues/1542) and [Docking](https://github.com/ocornut/imgui/issues/2109) features. This branch is kept in sync with master regularly. +We occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to latest `master` or `docking` branch. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/issues/1542) and [Docking](https://github.com/ocornut/imgui/issues/2109) features. This branch is kept in sync with master regularly. **Who uses Dear ImGui?** diff --git a/extern/imgui_patched/docs/TODO.txt b/extern/imgui_patched/docs/TODO.txt index 2c67c26b..0174cb0a 100644 --- a/extern/imgui_patched/docs/TODO.txt +++ b/extern/imgui_patched/docs/TODO.txt @@ -2,12 +2,12 @@ dear imgui ISSUES & TODO LIST Issue numbers (#) refer to GitHub issues listed at https://github.com/ocornut/imgui/issues/XXXX -This list is not well maintained, most of the work happens on GitHub nowadays. +THIS LIST IS NOT WELL MAINTAINED. MOST OF THE WORK HAPPENS ON GITHUB NOWADAYS. The list below consist mostly of ideas noted down before they are requested/discussed by users (at which point they usually exist on the github issue tracker). It's mostly a bunch of personal notes, probably incomplete. Feel free to query if you have any questions. - - doc/test: add a proper documentation+regression testing system (#435) - - doc/test: checklist app to verify backends/integration of imgui (test inputs, rendering, callback, etc.). + - doc: add a proper documentation system (maybe relying on automation? #435) + - doc: checklist app to verify backends/integration of imgui (test inputs, rendering, callback, etc.). - doc/tips: tips of the day: website? applet in imgui_club? - doc/wiki: work on the wiki https://github.com/ocornut/imgui/wiki @@ -18,7 +18,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - window: begin with *p_open == false could return false. - window: get size/pos helpers given names (see discussion in #249) - window: when window is very small, prioritize resize button over close button. - - window: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? + - window: double-clicking on title bar to minimize isn't consistent interaction, perhaps move to single-click on left-most collapse icon? - window: expose contents size. (#1045) - window: using SetWindowPos() inside Begin() and moving the window with the mouse reacts a very ugly glitch. We should just defer the SetWindowPos() call. - window: GetWindowSize() returns (0,0) when not calculated? (#1045) @@ -31,29 +31,26 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - window/child: allow resizing of child windows (possibly given min/max for each axis?.) - window/child: allow SetNextWindowContentSize() to work on child windows. - window/clipping: some form of clipping when DisplaySize (or corresponding viewport) is zero. - - window/tabbing: add a way to signify that a window or docked window requires attention (e.g. blinking title bar). - - window/id_stack: add e.g. window->GetIDFromPath() with support for leading / and ../ (#1390, #331) + - window/tabbing: add a way to signify that a window or docked window requires attention (e.g. blinking title bar, trying to click behind a modal). + - window/id_stack: add e.g. window->GetIDFromPath() with support for leading / and ../ (#1390, #331) -> model from test engine. ! scrolling: exposing horizontal scrolling with Shift+Wheel even when scrollbar is disabled expose lots of issues (#2424, #1463) - scrolling: while holding down a scrollbar, try to keep the same contents visible (at least while not moving mouse) - scrolling: allow immediately effective change of scroll after Begin() if we haven't appended items yet. - scrolling: forward mouse wheel scrolling to parent window when at the edge of scrolling limits? (useful for listbox,tables?) - - scrolling/clipping: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y). (2017-08-20: can't repro) - scrolling/style: shadows on scrollable areas to denote that there is more contents (see e.g. DaVinci Resolve ui) - drawdata: make it easy to deep-copy (or swap?) a full ImDrawData so user can easily save that data if they use threaded rendering. (e.g. #2646) ! drawlist: add CalcTextSize() func to facilitate consistent code from user pov (currently need to use ImGui or ImFont alternatives!) - drawlist: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). (WIP branch) - - drawlist: primitives/helpers to manipulate vertices post submission, so e.g. a quad/rect can be resized to fit later submitted content, _without_ using the ChannelSplit api - drawlist: make it easier to toggle AA per primitive, so we can use e.g. non-AA fill + AA borders more naturally - drawlist: non-AA strokes have gaps between points (#593, #288), glitch especially on RenderCheckmark() and ColorPicker4(). - - drawlist: rendering: provide a way for imgui to output to a single/global vertex buffer, re-order indices only at the end of the frame (ref: https://gist.github.com/floooh/10388a0afbe08fce9e617d8aefa7d302) - drawlist: callback: add an extra void* in ImDrawCallback to allow passing render-local data to the callback (would break API). - drawlist: AddRect vs AddLine position confusing (#2441) - drawlist/opt: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. (#1962) - drawlist/opt: AddRect() axis aligned pixel aligned (no-aa) could use 8 triangles instead of 16 and no normal calculation. - drawlist/opt: thick AA line could be doable in same number of triangles as 1.0 AA line by storing gradient+full color in atlas. - - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? + - items: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. (#395) - widgets: clean up widgets internal toward exposing everything and stabilizing imgui_internals.h. @@ -61,8 +58,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - widgets: start exposing PushItemFlag() and ImGuiItemFlags - widgets: alignment options in style (e.g. center Selectable, Right-Align within Button, etc.) #1260 - widgets: activate by identifier (trigger button, focus given id) - - widgets: a way to represent "mixed" values, so e.g. all values replaced with *, including check-boxes, colors, etc. with support for multi-components widgets (e.g. SliderFloat3, make only "Y" mixed) (#2644) - - widgets: checkbox: checkbox with custom glyph inside frame. + - widgets: custom glyph/shapes replacements for stock sapes. (also #6090 #2431 #2235 #6517) - widgets: coloredit: keep reporting as active when picker is on? - widgets: group/scalarn functions: expose more per-component information. e.g. store NextItemData.ComponentIdx set by scalarn function, groups can expose them back somehow. - selectable: using (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported. @@ -81,7 +77,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - input text: option to Tab after an Enter validation. - input text: add ImGuiInputTextFlags_EnterToApply? (off #218) - input text: easier ways to update buffer (from source char*) while owned. preserve some sort of cursor position for multi-line text. - - input text: add flag (e.g. ImGuiInputTextFlags_EscapeClearsBuffer) to clear instead of revert. what to do with focus? (also see #2890) - input text: add discard flag (e.g. ImGuiInputTextFlags_DiscardActiveBuffer) or make it easier to clear active focus for text replacement during edition (#725) - input text: display bug when clicking a drag/slider after an input text in a different window has all-selected text (order dependent). actually a very old bug but no one appears to have noticed it. - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position. @@ -116,20 +111,14 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - tables: see https://github.com/ocornut/imgui/issues/2957#issuecomment-569726095 - group: BeginGroup() needs a border option. (~#1496) - - group: IsHovered() after EndGroup() covers whole AABB rather than the intersection of individual items. Is that desirable? + - group: IsItemHovered() after EndGroup() covers whole AABB rather than the intersection of individual items. Is that desirable? - group: merge deactivation/activation within same group (fwd WasEdited flag). (#2550) !- color: the color conversion helpers/types are a mess and needs sorting out. - color: (api breaking) ImGui::ColorConvertXXX functions should be loose ImColorConvertXX to match imgui_internals.h - - plot: full featured plot/graph api w/ scrolling, zooming etc. all bell & whistle. why not! - - plot: PlotLines() should use the polygon-stroke facilities, less vertices (currently issues with averaging normals) - - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots) - - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value) - - plot: option/feature: draw the zero line - - plot: option/feature: draw grid, vertical markers - - plot: option/feature: draw unit - - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) + - plot: full featured plot/graph api w/ scrolling, zooming etc. --> ImPlot + - (plot: deleted all other todo lines on 2023-06-28) - clipper: ability to disable the clipping through a simple flag/bool. - clipper: ability to run without knowing full count in advance. @@ -140,7 +129,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - separator: width, thickness, centering (#1643) - splitter: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - - docking: merge docking branch (#2109) - docking: B: ordering currently held in tab bar should be implicitly held by windows themselves (also see #2304) - docking: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) - docking: B~ rework code to be able to lazily create tab bar instance in a single place. The _Unsorted tab flag could be replacing a trailing-counter in DockNode? @@ -174,16 +162,11 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - tabs: "there is currently a problem because TabItem() will try to submit their own tooltip after 0.50 second, and this will have the effect of making your tooltip flicker once." -> tooltip priority work (WIP branch) - tabs: make EndTabBar fail if users doesn't respect BeginTabBar return value, for consistency/future-proofing. - tabs: persistent order/focus in BeginTabBar() api (#261, #351) - - tabs: TabItem could honor SetNextItemWidth()? - tabs: explicit api (even if internal) to cleanly manipulate tab order. - - tabs: Mouse wheel over tab bar could scroll? (with shift?) (#2702) - image/image button: misalignment on padded/bordered button? - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that? - - image button: not taking an explicit id can be problematic. (#2464, #1390) - - slider/drag: ctrl+click when format doesn't include a % character.. disable? display underlying value in default format? (see TempInputTextScalar) - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt() - - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar). (#1946) - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate. - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign) - slider: relative dragging? + precision dragging @@ -196,7 +179,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits) - combo: use clipper. - - combo: flag for BeginCombo to not return true when unchanged (#1182) - combo: a way/helper to customize the combo preview (#1658) -> experimental BeginComboPreview() - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203) - listbox: multiple selection (WIP range-select branch) @@ -218,7 +200,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - tooltip: drag and drop with tooltip near monitor edges lose/changes its last direction instead of locking one. The drag and drop tooltip should always follow without changing direction. - tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. - - tooltip: tooltips with delay timers? or general timer policy? (instantaneous vs timed): IsItemHovered() with timer + implicit aabb-id for items with no ID. (#1485) (WIP branch) - tooltip: drag tooltip hovering over source widget with IsItemHovered/SetTooltip flickers (WIP branch) - status-bar: add a per-window status bar helper similar to what menu-bar does. generalize concept of layer0 rect in window (can make _MenuBar window flag obsolete too). @@ -227,7 +208,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - shortcuts: programmatically access shortcuts "Focus("&Save")) - menus: menu-bar: main menu-bar could affect clamping of windows position (~ akin to modifying DisplayMin) - menus: hovering from menu to menu on a menu-bar has 1 frame without any menu, which is a little annoying. ideally either 0 either longer. - - menus: could merge draw call in most cases (how about storing an optional aabb in ImDrawCmd to move the burden of merging in a single spot). - menus: would be nice if the Selectable() supported horizontal alignment (must be given the equivalent of WorkRect.Max.x matching the position of the shortcut column) - tree node: add treenode/treepush int variants? not there because (void*) cast from int warns on some platforms/settings? @@ -273,7 +253,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - drag and drop: drag source on a group object (would need e.g. an invisible button covering group in EndGroup) https://twitter.com/paniq/status/1121446364909535233 - drag and drop: have some way to know when a drag begin from BeginDragDropSource() pov. (see 2018/01/11 post in #143) - drag and drop: allow preview tooltip to be submitted from a different place than the drag source. (#1725) - - drag and drop: allow using with other mouse buttons (where activeid won't be set). (#1637) - drag and drop: make it easier and provide a demo to have tooltip both are source and target site, with a more detailed one on target site (tooltip ordering problem) - drag and drop: demo with reordering nodes (in a list, or a tree node). (#143) - drag and drop: test integrating with os drag and drop (make it easy to do a naive WM_DROPFILE integration) @@ -325,10 +304,10 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font/opt: Considering storing standalone AdvanceX table as 16-bit fixed point integer? - font/opt: Glyph currently 40 bytes (2+9*4). Consider storing UV as 16-bits integer? (->32 bytes). X0/Y0/X1/Y1 as 16 fixed-point integers? Or X0/Y0 as float and X1/Y1 as fixed8_8? + - nav: visual feedback on button press. - nav: some features such as PageUp/Down/Home/End should probably work without ImGuiConfigFlags_NavEnableKeyboard? (where do we draw the line? how about CTRL+Tab) ! nav: never clear NavId on some setup (e.g. gamepad centric) - nav: there's currently no way to completely clear focus with the keyboard. depending on patterns used by the application to dispatch inputs, it may be desirable. - - nav: code to focus child-window on restoring NavId appears to have issue: e.g. when focus change is implicit because of window closure. - nav: Home/End behavior when navigable item is not fully visible at the edge of scrolling? should be backtrack to keep item into view? - nav: NavScrollToBringItemIntoView() with item bigger than view should focus top-right? Repro: using Nav in "About Window" - nav: wrap around logic to allow e.g. grid based layout (pressing NavRight on the right-most element would go to the next row, etc.). see internal's NavMoveRequestTryWrapping(). @@ -349,7 +328,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav/menus: when using the main menu bar, even though we restore focus after, the underlying window loses its title bar highlight during menu manipulation. could we prevent it? - nav/menus: main menu bar currently cannot restore a nullptr focus. Could save NavWindow at the time of being focused, similarly to what popup do? - nav/menus: Alt,Up could open the first menu (e.g. "File") currently it tends to nav into the window/collapse menu. Do do that we would need custom transition? - - nav/windowing: configure fade-in/fade-out delay on Ctrl+Tab? - nav/windowing: when CTRL+Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering. - nav/windowing: Resizing window will currently fail with certain types of resizing constraints/callback applied - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) @@ -368,7 +346,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - platform: sdl: no refresh of monitor/display (SDL doesn't seem to have an event for it). - platform: sdl: multi-viewport + minimized window seems to break mouse wheel events (at least under Win32). - - inputs: we need an explicit flag about whether the platform window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. - inputs: support track pad style scrolling & slider edit. - inputs/io: backspace and arrows in the context of a text input could use system repeat rate. - inputs/io: clarify/standardize/expose repeat rate and repeat delays (#1808) @@ -394,7 +371,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - examples: provide a zero frame-rate/idle example. - examples: dx11/dx12: try to use new swapchain blit models (#2970) - backends: report it better when not able to create texture? - - backends: apple: example_apple should be using modern GL3. - backends: glfw: could go idle when minimized? if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { glfwWaitEvents(); continue; } // issue: DeltaTime will be super high on resume, perhaps provide a way to let impl know (#440) - backends: opengl: rename imgui_impl_opengl2 to impl_opengl_legacy and imgui_impl_opengl3 to imgui_impl_opengl? (#1900) - backends: opengl: could use a single vertex buffer and glBufferSubData for uploads? diff --git a/extern/imgui_patched/imconfig.h b/extern/imgui_patched/imconfig.h index 860c0b42..5ed81b14 100644 --- a/extern/imgui_patched/imconfig.h +++ b/extern/imgui_patched/imconfig.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// COMPILE-TIME OPTIONS FOR DEAR IMGUI +// DEAR IMGUI COMPILE-TIME OPTIONS // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. //----------------------------------------------------------------------------- @@ -9,7 +9,7 @@ // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. -// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. +// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using. //----------------------------------------------------------------------------- #pragma once @@ -26,7 +26,7 @@ //#define IMGUI_API __declspec( dllexport ) //#define IMGUI_API __declspec( dllimport ) -//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. +//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. @@ -40,7 +40,7 @@ //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) -//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). +//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. @@ -62,9 +62,10 @@ // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" -//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled +//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined. //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined. //---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) // Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h. @@ -75,6 +76,12 @@ // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. //#define IMGUI_ENABLE_FREETYPE +//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT) +// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided). +// Only works in combination with IMGUI_ENABLE_FREETYPE. +// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement) +//#define IMGUI_ENABLE_FREETYPE_LUNASVG + //---- Use stb_truetype to build and rasterize the font atlas (default) // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. //#define IMGUI_ENABLE_STB_TRUETYPE @@ -107,7 +114,7 @@ //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); //#define ImDrawCallback MyImDrawCallback -//---- Debug Tools: Macro to break in Debugger +//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase) // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) //#define IM_DEBUG_BREAK IM_ASSERT(0) //#define IM_DEBUG_BREAK __debugbreak() @@ -115,10 +122,10 @@ //---- Debug Tools: Enable slower asserts //#define IMGUI_DEBUG_PARANOID -//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. +//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files) /* namespace ImGui { - void MyFunction(const char* name, const MyMatrix44& v); + void MyFunction(const char* name, MyMatrix44* mtx); } */ diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index ceb71b32..71aee935 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.6 +// dear imgui, v1.89.8 // (main code and documentation) // Help: @@ -9,22 +9,26 @@ // Resources: // - FAQ http://dearimgui.com/faq -// - Homepage & latest https://github.com/ocornut/imgui +// - Homepage https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases // - Gallery https://github.com/ocornut/imgui/issues/6478 (please post your screenshots/video there!) // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) +// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Issues & support https://github.com/ocornut/imgui/issues +// - Tests & Automation https://github.com/ocornut/imgui_test_engine // Getting Started? -// - For first-time users having issues compiling/linking/running or issues loading fonts: +// - Read https://github.com/ocornut/imgui/wiki/Getting-Started +// - For first-time users having issues compiling/linking/running/loading fonts: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but needs your support to sustain development and maintenance. -// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com". -// Individuals: you can support continued development via donations. See docs/README or web page. +// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts. +// PLEASE reach out at contact AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Sponsors +// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without @@ -108,9 +112,10 @@ CODE - Portable, minimize dependencies, run on target (consoles, phones, etc.). - Efficient runtime and memory consumption. - Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes: + Designed primarily for developers and content-creators, not the typical end-user! + Some of the current weaknesses (which we aim to address in the future) includes: - - Doesn't look fancy, doesn't animate. + - Doesn't look fancy. - Limited layout features, intricate layouts are typically crafted in code. @@ -189,9 +194,11 @@ CODE READ FIRST ---------- - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki) - - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction or - destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs. + - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone! + The UI can be highly dynamic, there are no construction or destruction steps, less superfluous + data retention on your side, less state duplication, less state synchronization, fewer bugs. - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. + Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version. - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki. @@ -199,18 +206,38 @@ CODE For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI, where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. - - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). If you get an assert, read the messages and comments around the assert. - - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace. - - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. - See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. - However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase. - - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!). + - This codebase aims to be highly optimized: + - A typical idle frame should never call malloc/free. + - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible. + - We put particular energy in making sure performances are decent with typical "Debug" build settings as well. + Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all. + - This codebase aims to be both highly opinionated and highly flexible: + - This code works because of the things it choose to solve or not solve. + - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers, + and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now). + This is to increase compatibility, increase maintainability and facilitate use from other languages. + - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. + See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. + We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally. + - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction + (so don't use ImVector in your code or at our own risk!). + - Building: We don't use nor mandate a build system for the main library. + This is in an effort to ensure that it works in the real world aka with any esoteric build setup. + This is also because providing a build system for the main library would be of little-value. + The build problems are almost never coming from the main library but from specific backends. HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI ---------------------------------------------- + - Update submodule or copy/overwrite every file. + - About imconfig.h: + - You may modify your copy of imconfig.h, in this case don't overwrite it. + - or you may locally branch to modify imconfig.h and merge/rebase latest. + - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to + specify a custom path for your imconfig.h file and instead not have to modify the default one. + - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h) - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master". - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file. @@ -219,11 +246,12 @@ CODE from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it. Please report any issue to the GitHub page! - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file. - - Try to keep your copy of Dear ImGui reasonably up to date. + - Try to keep your copy of Dear ImGui reasonably up to date! GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE --------------------------------------------------------------- + - See https://github.com/ocornut/imgui/wiki/Getting-Started. - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder. - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system. @@ -405,6 +433,12 @@ CODE - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. + - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878) + - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15). + - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete). + - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior. + - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610) + - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage. - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3. - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago: - ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference) @@ -807,11 +841,12 @@ CODE Q: Where is the documentation? A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++. - - Run the examples/ and explore them. + - Run the examples/ applications and explore them. + - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide. - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. - The demo covers most features of Dear ImGui, so you can read the code and see its output. - See documentation and comments at the top of imgui.cpp + effectively imgui.h. - - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the + - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the examples/ folder to explain how to integrate Dear ImGui with your own engine/application. - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. @@ -827,14 +862,14 @@ CODE ================ Q: How to get started? - A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. + A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application? A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this. - Q. How can I enable keyboard controls? - Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) + Q. How can I enable keyboard or gamepad controls? + Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display) Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around... Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries... @@ -849,7 +884,7 @@ CODE - How can I have multiple widgets with the same label? - How can I have multiple windows with the same label? Q: How can I display an image? What is ImTextureID, how does it work? - Q: How can I use my own math types instead of ImVec2/ImVec4? + Q: How can I use my own math types instead of ImVec2? Q: How can I interact with standard C++ types (such as std::string and std::vector)? Q: How can I display custom shapes? (using low-level ImDrawList API) >> See https://www.dearimgui.com/faq @@ -879,10 +914,10 @@ CODE Q: How can I help? A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui! We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. - This is among the most useful thing you can do for Dear ImGui. With increased funding, we can hire more people working on this project. - - Individuals: you can support continued development via PayPal donations. See README. - - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt - and see how you want to help and can help! + This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project. + Also see https://github.com/ocornut/imgui/wiki/Sponsors + - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. + - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help! - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers. But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions. @@ -908,11 +943,7 @@ CODE // System includes #include // vsnprintf, sscanf, printf -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif #include "../../src/fileutils.h" @@ -993,7 +1024,6 @@ CODE // Debug options #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window -#define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower) // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in @@ -1004,9 +1034,11 @@ static const float WINDOWS_HOVER_PADDING = 4.0f; // Exten static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. +// Tooltip offset +static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale + // Docking static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport. -static const float DOCKING_SPLITTER_SIZE = 2.0f; //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS @@ -1017,7 +1049,6 @@ static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); // Settings @@ -1180,6 +1211,7 @@ ImGuiStyle::ImGuiStyle() SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + DockingSeparatorSize = 2.0f; // Thickness of resizing border between docked windows MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). @@ -1187,6 +1219,13 @@ ImGuiStyle::ImGuiStyle() CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. + // Behaviors + HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. + HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. + HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. + HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. + // Default theme ImGui::StyleColorsDark(this); } @@ -1216,6 +1255,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) TabRounding = ImFloor(TabRounding * scale_factor); TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; SeparatorTextPadding = ImFloor(SeparatorTextPadding * scale_factor); + DockingSeparatorSize = ImFloor(DockingSeparatorSize * scale_factor); DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); @@ -1233,18 +1273,13 @@ ImGuiIO::ImGuiIO() DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f / 60.0f; IniSavingRate = 5.0f; + InputScale = 1.0f; IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). LogFilename = "imgui_log.txt"; - MouseDoubleClickTime = 0.30f; - MouseDoubleClickMaxDist = 6.0f; #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO for (int i = 0; i < ImGuiKey_COUNT; i++) KeyMap[i] = -1; #endif - KeyRepeatDelay = 0.275f; - KeyRepeatRate = 0.050f; - HoverDelayNormal = 0.30f; - HoverDelayShort = 0.10f; UserData = NULL; Fonts = NULL; @@ -1285,6 +1320,13 @@ ImGuiIO::ImGuiIO() ConfigDebugBeginReturnValueOnce = false; ConfigDebugBeginReturnValueLoop = false; + // Inputs Behaviors + MouseDoubleClickTime = 0.30f; + MouseDoubleClickMaxDist = 6.0f; + MouseDragThreshold = 6.0f; + KeyRepeatDelay = 0.275f; + KeyRepeatRate = 0.050f; + // Platform Functions // Note: Initialize() will setup default clipboard/ime handlers. BackendPlatformName = BackendRendererName = NULL; @@ -1294,7 +1336,6 @@ ImGuiIO::ImGuiIO() MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseSource = ImGuiMouseSource_Mouse; - MouseDragThreshold = 6.0f; for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } AppAcceptingEvents = true; @@ -1369,13 +1410,15 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) } } -// FIXME: Perhaps we could clear queued events as well? -void ImGuiIO::ClearInputCharacters() +// Clear all incoming events. +void ImGuiIO::ClearEventsQueue() { - InputQueueCharacters.resize(0); + IM_ASSERT(Ctx != NULL); + ImGuiContext& g = *Ctx; + g.InputEventsQueue.clear(); } -// FIXME: Perhaps we could clear queued events as well? +// Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons. void ImGuiIO::ClearInputKeys() { #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -1396,8 +1439,18 @@ void ImGuiIO::ClearInputKeys() MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f; } MouseWheel = MouseWheelH = 0.0f; + InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters(). } +// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue. +// Current frame character buffer is now also cleared by ClearInputKeys(). +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +void ImGuiIO::ClearInputCharacters() +{ + InputQueueCharacters.resize(0); +} +#endif + static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1) { ImGuiContext& g = *ctx; @@ -1871,13 +1924,15 @@ const char* ImStrSkipBlank(const char* str) // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.) #ifdef IMGUI_USE_STB_SPRINTF +#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION #define STB_SPRINTF_IMPLEMENTATION +#endif #ifdef IMGUI_STB_SPRINTF_FILENAME #include IMGUI_STB_SPRINTF_FILENAME #else #include "stb_sprintf.h" #endif -#endif +#endif // #ifdef IMGUI_USE_STB_SPRINTF #if defined(_MSC_VER) && !defined(vsnprintf) #define vsnprintf _vsnprintf @@ -2814,9 +2869,6 @@ static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int it ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); - Ctx = ImGui::GetCurrentContext(); - IM_ASSERT(Ctx != NULL); - ItemsCount = -1; } ImGuiListClipper::~ImGuiListClipper() @@ -2826,6 +2878,9 @@ ImGuiListClipper::~ImGuiListClipper() void ImGuiListClipper::Begin(int items_count, float items_height) { + if (Ctx == NULL) + Ctx = ImGui::GetCurrentContext(); + ImGuiContext& g = *Ctx; ImGuiWindow* window = g.CurrentWindow; IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name); @@ -2851,10 +2906,10 @@ void ImGuiListClipper::Begin(int items_count, float items_height) void ImGuiListClipper::End() { - ImGuiContext& g = *Ctx; if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) { // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. + ImGuiContext& g = *Ctx; IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name); if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) ImGuiListClipper_SeekCursorForItem(this, ItemsCount); @@ -2955,7 +3010,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); if (is_nav_request) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); - if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1) + if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount)); // Add focused/active item @@ -3143,6 +3198,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] = { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextBorderSize) },// ImGuiStyleVar_SeparatorTextBorderSize { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DockingSeparatorSize) }, // ImGuiStyleVar_DockingSeparatorSize }; const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx) @@ -3588,6 +3644,7 @@ void ImGui::DestroyContext(ImGuiContext* ctx) // IMPORTANT: ###xxx suffixes must be same in ALL languages static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { + { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" }, { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" }, { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" }, { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" }, @@ -3634,6 +3691,7 @@ void ImGui::Initialize() viewport->Flags = ImGuiViewportFlags_OwnedByApp; g.Viewports.push_back(viewport); g.TempBuffer.resize(1024 * 3 + 1, 0); + g.ViewportCreatedCount++; g.PlatformIO.Viewports.push_back(g.Viewports[0]); #ifdef IMGUI_HAS_DOCK @@ -4033,6 +4091,16 @@ bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flag return true; } +static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags) +{ + ImGuiContext& g = *GImGui; + if (flags & ImGuiHoveredFlags_DelayShort) + return g.Style.HoverDelayShort; + if (flags & ImGuiHoveredFlags_DelayNormal) + return g.Style.HoverDelayNormal; + return 0.0f; +} + // This is roughly matching the behavior of internal-facing ItemHoverable() // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered() // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId @@ -4040,6 +4108,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && "Invalid flags for IsItemHovered()!"); + if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride)) { if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) @@ -4048,6 +4118,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return false; if (window->InertialScroll) return false; + + if (flags & ImGuiHoveredFlags_ForTooltip) + flags |= g.Style.HoverFlagsForTooltipNav; } else { @@ -4055,6 +4128,10 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) return false; + + if (flags & ImGuiHoveredFlags_ForTooltip) + flags |= g.Style.HoverFlagsForTooltipMouse; + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy)) == 0); // Flags not supported by this function // Done with rectangle culling so we can perform heavier checks now @@ -4064,12 +4141,13 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was // the test that has been running for a long while. if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) - if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0) return false; // Test if another item is active (e.g. being dragged) + const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap) + if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId) return false; @@ -4089,48 +4167,60 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Special handling for calling after Begin() which represent the title bar or tab. // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin) // will never be overwritten so we need to detect the case. - if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) + if (id == window->MoveId && window->WriteAccessed) return false; + + // Test if using AllowOverlap and overlapped + if ((g.LastItemData.InFlags & ImGuiItemflags_AllowOverlap) && id != 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0) + if (g.HoveredIdPreviousFrame != g.LastItemData.ID) + return false; } // Handle hover delay // (some ideas: https://www.nngroup.com/articles/timing-exposing-content) - float delay; - if (flags & ImGuiHoveredFlags_DelayNormal) - delay = g.IO.HoverDelayNormal; - else if (flags & ImGuiHoveredFlags_DelayShort) - delay = g.IO.HoverDelayShort; - else - delay = 0.0f; - if (delay > 0.0f) + const float delay = CalcDelayFromHoveredFlags(flags); + if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary)) { ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect); - if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverDelayIdPreviousFrame != hover_delay_id)) - g.HoverDelayTimer = 0.0f; - g.HoverDelayId = hover_delay_id; - return g.HoverDelayTimer >= delay; + if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id)) + g.HoverItemDelayTimer = 0.0f; + g.HoverItemDelayId = hover_delay_id; + + // When changing hovered item we requires a bit of stationary delay before activating hover timer, + // but once unlocked on a given item we also moving. + //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); } + if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id) + return false; + + if (g.HoverItemDelayTimer < delay) + return false; } return true; } // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). -bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) +// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call) +// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28. +// If you used this in your legacy/custom widgets code: +// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.InFlags'. +// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable. +bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags) { ImGuiContext& g = *GImGui; - if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) - return false; - ImGuiWindow* window = g.CurrentWindow; if (g.HoveredWindow != window) return false; - if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) - return false; if (!IsMouseHoveringRect(bb.Min, bb.Max)) return false; + if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) + return false; + if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) + return false; + // Done with rectangle culling so we can perform heavier checks now. - ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdDisabled = true; @@ -4140,14 +4230,29 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level // hover test in widgets code. We could also decide to split this function is two. if (id != 0) + { + // Drag source doesn't report as hovered + if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) + return false; + SetHoveredID(id); + // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. + // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test. + if (item_flags & ImGuiItemflags_AllowOverlap) + { + g.HoveredIdAllowOverlap = true; + if (g.HoveredIdPreviousFrame != id) + return false; + } + } + // When disabled we'll return false but still set HoveredId // Same thing if swiping if (item_flags & ImGuiItemFlags_Disabled || window->InertialScroll) { // Release active id if turning disabled - if (g.ActiveId == id) + if (g.ActiveId == id && id != 0) ClearActiveID(); g.HoveredIdDisabled = true; return false; @@ -4402,7 +4507,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; // When a window stop being submitted while being dragged, it may will its viewport until next Begin() - const bool window_disappared = ((!moving_window->WasActive && !moving_window->Active) || moving_window->Viewport == NULL); + const bool window_disappared = (!moving_window->WasActive && !moving_window->Active); if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; @@ -4410,7 +4515,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() { SetWindowPos(moving_window, pos, ImGuiCond_Always); g.InertialScrollInhibited=true; - if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. + if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. { moving_window->Viewport->Pos = pos; moving_window->Viewport->UpdateWorkRect(); @@ -4428,11 +4533,12 @@ void ImGui::UpdateMouseMovingWindowNewFrame() UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. - if (!IsDragDropPayloadBeingAccepted()) + if (moving_window->Viewport && !IsDragDropPayloadBeingAccepted()) g.MouseViewport = moving_window->Viewport; // Clear the NoInput window flag set by the Viewport system - moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL. + if (moving_window->Viewport) + moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; } g.MovingWindow = NULL; @@ -4747,21 +4853,33 @@ void ImGui::NewFrame() } #endif + // Record when we have been stationary as this state is preserved while over same item. + // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values. + // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function. + if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay) + g.HoverItemUnlockedStationaryId = g.HoverItemDelayId; + else if (g.HoverItemDelayId == 0) + g.HoverItemUnlockedStationaryId = 0; + if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay) + g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID; + else if (g.HoveredWindow == NULL) + g.HoverWindowUnlockedStationaryId = 0; + // Update hover delay for IsItemHovered() with delays and tooltips - g.HoverDelayIdPreviousFrame = g.HoverDelayId; - if (g.HoverDelayId != 0) + g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId; + if (g.HoverItemDelayId != 0) { - //if (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f) // Need design/flags - g.HoverDelayTimer += g.IO.DeltaTime; - g.HoverDelayClearTimer = 0.0f; - g.HoverDelayId = 0; + g.HoverItemDelayTimer += g.IO.DeltaTime; + g.HoverItemDelayClearTimer = 0.0f; + g.HoverItemDelayId = 0; } - else if (g.HoverDelayTimer > 0.0f) + else if (g.HoverItemDelayTimer > 0.0f) { // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps - g.HoverDelayClearTimer += g.IO.DeltaTime; - if (g.HoverDelayClearTimer >= ImMax(0.20f, g.IO.DeltaTime * 2.0f)) // ~6 frames at 30 Hz + allow for low framerate - g.HoverDelayTimer = g.HoverDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. + // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle. + g.HoverItemDelayClearTimer += g.IO.DeltaTime; + if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate + g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. } // Drag and drop @@ -4917,49 +5035,15 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im } } -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) -{ - if (draw_list->CmdBuffer.Size == 0) - return; - if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) - return; - - // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. - // May trigger for you if you are using PrimXXX functions incorrectly. - IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); - IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); - if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) - IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); - - // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) - // If this assert triggers because you are drawing lots of stuff manually: - // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. - // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. - // - If you want large meshes with more than 64K vertices, you can either: - // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. - // Most example backends already support this from 1.71. Pre-1.71 backends won't. - // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. - // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. - // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: - // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - // Your own engine or render API may use different parameters or function calls to specify index sizes. - // 2 and 4 bytes indices are generally supported by most graphics API. - // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching - // the 64K limit to split your draw commands in multiple draw lists. - if (sizeof(ImDrawIdx) == 2) - IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); - - out_list->push_back(draw_list); -} - static void AddWindowToDrawData(ImGuiWindow* window, int layer) { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = window->Viewport; + IM_ASSERT(viewport != NULL); g.IO.MetricsRenderWindows++; if (window->Flags & ImGuiWindowFlags_DockNodeHost) window->DrawList->ChannelsMerge(); - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); + ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; @@ -4979,26 +5063,35 @@ static inline void AddRootWindowToDrawData(ImGuiWindow* window) AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } -void ImDrawDataBuilder::FlattenIntoSingleLayer() +static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder) { - int n = Layers[0].Size; - int size = n; - for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) - size += Layers[i].Size; - Layers[0].resize(size); - for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) + int n = builder->Layers[0]->Size; + int full_size = n; + for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++) + full_size += builder->Layers[i]->Size; + builder->Layers[0]->resize(full_size); + for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++) { - ImVector& layer = Layers[layer_n]; - if (layer.empty()) + ImVector* layer = builder->Layers[layer_n]; + if (layer->empty()) continue; - memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); - n += layer.Size; - layer.resize(0); + memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*)); + n += layer->Size; + layer->resize(0); } } -static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists) +static void InitViewportDrawData(ImGuiViewportP* viewport) { + ImGuiIO& io = ImGui::GetIO(); + ImDrawData* draw_data = &viewport->DrawDataP; + + viewport->DrawData = draw_data; // Make publicly accessible + viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists; + viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1; + viewport->DrawDataBuilder.Layers[0]->resize(0); + viewport->DrawDataBuilder.Layers[1]->resize(0); + // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode, // and to allow applications/backends to easily skip rendering. // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure. @@ -5006,24 +5099,13 @@ static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVectorFlags & ImGuiViewportFlags_IsMinimized) != 0; - ImGuiIO& io = ImGui::GetIO(); - ImDrawData* draw_data = &viewport->DrawDataP; - viewport->DrawData = draw_data; // Make publicly accessible draw_data->Valid = true; - draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - draw_data->CmdListsCount = draw_lists->Size; + draw_data->CmdListsCount = 0; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size; draw_data->FramebufferScale = io.DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis? draw_data->OwnerViewport = viewport; - for (int n = 0; n < draw_lists->Size; n++) - { - ImDrawList* draw_list = draw_lists->Data[n]; - draw_list->_PopUnusedDrawCmd(); - draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; - draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; - } } // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. @@ -5070,14 +5152,14 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 ImDrawList* draw_list = window->RootWindowDockTree->DrawList; if (draw_list->CmdBuffer.Size == 0) draw_list->AddDrawCmd(); - draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged + draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that) draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); ImDrawCmd cmd = draw_list->CmdBuffer.back(); IM_ASSERT(cmd.ElemCount == 6); draw_list->CmdBuffer.pop_back(); draw_list->CmdBuffer.push_front(cmd); - draw_list->PopClipRect(); draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command. + draw_list->PopClipRect(); } // Draw over sibling docking nodes in a same docking tree @@ -5297,9 +5379,9 @@ void ImGui::Render() for (int n = 0; n != g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.Clear(); + InitViewportDrawData(viewport); if (viewport->DrawLists[0] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); + AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } // Add ImDrawList to render @@ -5330,14 +5412,18 @@ void ImGui::Render() for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.FlattenIntoSingleLayer(); + FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder); // Add foreground ImDrawList (for each active viewport) if (viewport->DrawLists[1] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); + AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); + + // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). + ImDrawData* draw_data = &viewport->DrawDataP; + IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount); + for (int draw_list_n = 0; draw_list_n < draw_data->CmdLists.Size; draw_list_n++) + draw_data->CmdLists[draw_list_n]->_PopUnusedDrawCmd(); - SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); - ImDrawData* draw_data = viewport->DrawData; g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; } @@ -5536,17 +5622,28 @@ bool ImGui::IsItemEdited() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0; } +// Allow next item to be overlapped by subsequent items. +// This works by requiring HoveredId to match for two subsequent frames, +// so if a following items overwrite it our interactions will naturally be disabled. +void ImGui::SetNextItemAllowOverlap() +{ + ImGuiContext& g = *GImGui; + g.NextItemData.ItemFlags |= ImGuiItemflags_AllowOverlap; +} + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. -// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework. +// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead. void ImGui::SetItemAllowOverlap() { ImGuiContext& g = *GImGui; ImGuiID id = g.LastItemData.ID; if (g.HoveredId == id) g.HoveredIdAllowOverlap = true; - if (g.ActiveId == id) + if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id. g.ActiveIdAllowOverlap = true; } +#endif // FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function. void ImGui::SetActiveIdUsingAllKeyboardKeys() @@ -5624,7 +5721,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b // Process navigation-in immediately so NavInit can run on first frame // Can enter a child if (A) it has navigatable items or (B) it can be scrolled. - const ImGuiID temp_id_for_activation = (id + 1); + const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id); if (g.ActiveId == temp_id_for_activation) ClearActiveID(); if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY)) @@ -6491,12 +6588,13 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags // When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) // should be positioned behind that modal window, unless the window was created inside the modal begin-stack. // In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. -// - Window // FindBlockingModal() returns Modal1 -// - Window // .. returns Modal1 +// - WindowA // FindBlockingModal() returns Modal1 +// - WindowB // .. returns Modal1 // - Modal1 // .. returns Modal2 -// - Window // .. returns Modal2 -// - Window // .. returns Modal2 +// - WindowC // .. returns Modal2 +// - WindowD // .. returns Modal2 // - Modal2 // .. returns Modal2 +// - WindowE // .. returns NULL // Notes: // - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL. // Only difference is here we check for ->Active/WasActive but it may be unecessary. @@ -6507,7 +6605,7 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) return NULL; // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal. - for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + for (int i = 0; i < g.OpenPopupStack.Size; i++) { ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal)) @@ -6516,11 +6614,9 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) continue; if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click. return popup_window; - if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed. - break; - for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow) - if (IsWindowWithinBeginStackOf(window, parent)) - return popup_window; // Place window above its begin stack parent. + if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal + continue; + return popup_window; // Place window right below first block modal } return NULL; } @@ -6796,7 +6892,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style. - if (flags & ImGuiWindowFlags_ChildWindow) + if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow)) window->WindowBorderSize = style.ChildBorderSize; else window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; @@ -7227,6 +7323,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // [LEGACY] Content Region // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. + // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling. // Used by: // - Mouse wheel scrolling + many other things window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1; @@ -7290,16 +7387,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (want_focus && window == g.NavWindow) NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls - // Close requested by platform window + // Close requested by platform window (apply to all windows in this viewport) if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) { - if (!window->DockIsActive || window->DockTabIsVisible) - { - window->Viewport->PlatformRequestClose = false; - g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. - IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' PlatformRequestClose\n", window->Name); - *p_open = false; - } + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' closed by PlatformRequestClose\n", window->Name); + *p_open = false; + g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. // FIXME-NAV: Try removing. } // Title bar @@ -7896,7 +7989,8 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { - IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function + IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && "Invalid flags for IsWindowHovered()!"); + ImGuiContext& g = *GImGui; ImGuiWindow* ref_window = g.HoveredWindow; ImGuiWindow* cur_window = g.CurrentWindow; @@ -7925,6 +8019,17 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; + + // When changing hovered window we requires a bit of stationary delay before activating hover timer. + // FIXME: We don't support delay other than stationary one for now, other delay would need a way + // to fullfill the possibility that multiple IsWindowHovered() with varying flag could return true + // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache. + // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow. + if (flags & ImGuiHoveredFlags_ForTooltip) + flags |= g.Style.HoverFlagsForTooltipMouse; + if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID) + return false; + return true; } @@ -8266,13 +8371,6 @@ void ImGui::SetWindowFontScale(float scale) g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); } -void ImGui::ActivateItem(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - g.NavNextActivateId = id; - g.NavNextActivateFlags = ImGuiActivateFlags_None; -} - void ImGui::PushFocusScope(ImGuiID id) { ImGuiContext& g = *GImGui; @@ -8288,13 +8386,40 @@ void ImGui::PopFocusScope() g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back() : 0; } +// Focus = move navigation cursor, set scrolling, set focus window. +void ImGui::FocusItem() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name); + if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this? + { + IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n"); + return; + } + + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSelect; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + SetNavWindow(window); + NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags); + NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); +} + +void ImGui::ActivateItemByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.NavNextActivateId = id; + g.NavNextActivateFlags = ImGuiActivateFlags_None; +} + // Note: this will likely be called ActivateItem() once we rework our Focus/Activation system! +// But ActivateItem() should function without altering scroll/focus? void ImGui::SetKeyboardFocusHere(int offset) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(offset >= -1); // -1 is allowed but not below - IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); + IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); // It makes sense in the vast majority of cases to never interrupt a drag and drop. // When we refactor this function into ActivateItem() we may want to make this an option. @@ -8302,14 +8427,15 @@ void ImGui::SetKeyboardFocusHere(int offset) // is also automatically dropped in the event g.ActiveId is stolen. if (g.DragDropActive || g.MovingWindow != NULL) { - IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere() ignored while DragDropActive!\n"); + IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n"); return; } SetNavWindow(window); + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; - NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. if (offset == -1) { NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); @@ -8572,7 +8698,7 @@ const char* ImGui::GetKeyName(ImGuiKey key) { ImGuiContext& g = *GImGui; #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT((IsNamedKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); + IM_ASSERT((IsNamedKeyOrModKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); #else if (IsLegacyKey(key)) { @@ -9244,6 +9370,13 @@ static void ImGui::UpdateMouseInputs() else io.MouseDelta = ImVec2(0.0f, 0.0f); + // Update stationary timer. + // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates. + const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework. + const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold); + g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f; + //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer); + // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) g.NavDisableMouseHover = false; @@ -10155,7 +10288,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu g.LastItemData.ID = id; g.LastItemData.Rect = bb; g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; - g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags; + g.LastItemData.InFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags; g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; // Directional navigation processing @@ -10187,6 +10320,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); } g.NextItemData.Flags = ImGuiNextItemDataFlags_None; + g.NextItemData.ItemFlags = ImGuiItemFlags_None; #ifdef IMGUI_ENABLE_TEST_ENGINE if (id != 0) @@ -10452,10 +10586,8 @@ ImVec2 ImGui::GetContentRegionMax() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImVec2 mx = window->ContentRegionRect.Max - window->Pos; - if (window->DC.CurrentColumns || g.CurrentTable) - mx.x = window->WorkRect.Max.x - window->Pos.x; - return mx; + ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max; + return mx - window->Pos; } // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features. @@ -10463,9 +10595,7 @@ ImVec2 ImGui::GetContentRegionMaxAbs() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImVec2 mx = window->ContentRegionRect.Max; - if (window->DC.CurrentColumns || g.CurrentTable) - mx.x = window->WorkRect.Max.x; + ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max; return mx; } @@ -10832,26 +10962,35 @@ bool ImGui::BeginTooltip() return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } +bool ImGui::BeginItemTooltip() +{ + if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + return false; + return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); +} + bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; if (g.DragDropWithinSource || g.DragDropWithinTarget) { - // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) - // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. - // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. + // Drag and Drop tooltips are positioning differently than other tooltips: + // - offset visibility to increase visibility around mouse. + // - never clamp within outer viewport boundary. + // We call SetNextWindowPos() to enforce position and disable clamping. + // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones). //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; - ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); + ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale; SetNextWindowPos(tooltip_pos); SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( - tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip; + tooltip_flags |= ImGuiTooltipFlags_OverridePrevious; } char window_name[16]; ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); - if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip) + if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious) if (ImGuiWindow* window = FindWindowByName(window_name)) if (window->Active) { @@ -10875,14 +11014,6 @@ void ImGui::EndTooltip() End(); } -void ImGui::SetTooltipV(const char* fmt, va_list args) -{ - if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None)) - return; - TextV(fmt, args); - EndTooltip(); -} - void ImGui::SetTooltip(const char* fmt, ...) { va_list args; @@ -10891,6 +11022,32 @@ void ImGui::SetTooltip(const char* fmt, ...) va_end(args); } +void ImGui::SetTooltipV(const char* fmt, va_list args) +{ + if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) + return; + TextV(fmt, args); + EndTooltip(); +} + +// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'. +// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse. +void ImGui::SetItemTooltip(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + SetTooltipV(fmt, args); + va_end(args); +} + +void ImGui::SetItemTooltipV(const char* fmt, va_list args) +{ + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + SetTooltipV(fmt, args); +} + + //----------------------------------------------------------------------------- // [SECTION] POPUPS //----------------------------------------------------------------------------- @@ -11424,15 +11581,20 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } if (window->Flags & ImGuiWindowFlags_Tooltip) { - // Position tooltip (always follows mouse) - float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = NavCalcPreferredRefPos(); + // Position tooltip (always follows mouse + clamp within outer boundaries) + // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position. + // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin() + IM_ASSERT(g.CurrentWindow == window); + const float scale = g.Style.MouseCursorScale; + const ImVec2 ref_pos = NavCalcPreferredRefPos(); + const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale; ImRect r_avoid; if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); + r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. + //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255)); + return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); } IM_ASSERT(0); return window->Pos; @@ -11720,7 +11882,7 @@ static void ImGui::NavProcessItem() // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0) { - const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0; + const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0; if (is_tabbing) { NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags); @@ -11826,7 +11988,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); - if (move_flags & ImGuiNavMoveFlags_Tabbing) + if (move_flags & ImGuiNavMoveFlags_IsTabbing) move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; g.NavMoveSubmitted = g.NavMoveScoringItems = true; @@ -12378,9 +12540,10 @@ void ImGui::NavUpdateCreateTabbingRequest() g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1; else g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; - NavMoveRequestSubmit(ImGuiDir_None, clip_dir, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. g.NavTabbingCounter = -1; } @@ -12397,7 +12560,7 @@ void ImGui::NavMoveRequestApplyResult() ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; // Tabbing forward wrap - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && result == NULL) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL) if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) result = &g.NavTabbingResultFirst; @@ -12405,9 +12568,9 @@ void ImGui::NavMoveRequestApplyResult() const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; if (result == NULL) { - if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) - g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; - if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) + g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight; + if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0) NavRestoreHighlightAfterMove(); NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis. IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n"); @@ -12446,9 +12609,11 @@ void ImGui::NavMoveRequestApplyResult() } if (g.ActiveId != result->ID) ClearActiveID(); - if (g.NavId != result->ID) + + // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) + // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior. + if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0) { - // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = g.NavMoveKeyMods; @@ -12461,29 +12626,28 @@ void ImGui::NavMoveRequestApplyResult() // Restore last preferred position for current axis // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..) - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) == 0) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0) { preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis]; g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel; } - // Tabbing: Activates Inputable or Focus non-Inputable - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable)) - { - g.NavNextActivateId = result->ID; - g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; - g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; - } + // Tabbing: Activates Inputable, otherwise only Focus + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0) + g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate; // Activate if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) { g.NavNextActivateId = result->ID; g.NavNextActivateFlags = ImGuiActivateFlags_None; + g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight; + if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) + g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; } // Enable nav highlight - if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0) NavRestoreHighlightAfterMove(); } @@ -12578,14 +12742,14 @@ static float ImGui::NavUpdatePageUpPageDown() nav_scoring_rect_offset_y = -page_offset_y; g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove; } else if (IsKeyPressed(ImGuiKey_PageDown, true)) { nav_scoring_rect_offset_y = +page_offset_y; g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove; } else if (home_pressed) { @@ -13049,7 +13213,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) // Rely on keeping other window->LastItemXXX fields intact. source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); KeepAliveID(source_id); - bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id); + bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.InFlags); if (is_hovered && g.IO.MouseClicked[mouse_button]) { SetActiveID(source_id, window); @@ -13820,12 +13984,13 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) { ImGuiContext& g = *GImGui; -#if !IMGUI_DEBUG_INI_SETTINGS - // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier. - if (const char* p = strstr(name, "###")) - name = p; -#endif + if (g.IO.ConfigDebugIniSettings == false) + { + // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier. + if (const char* p = strstr(name, "###")) + name = p; + } const size_t name_len = strlen(name); // Allocate chunk @@ -14477,6 +14642,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Flags = flags; UpdateViewportPlatformMonitor(viewport); g.Viewports.push_back(viewport); + g.ViewportCreatedCount++; IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Add Viewport %08X '%s'\n", id, window ? window->Name : ""); // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. @@ -14571,6 +14737,11 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window) // Code explicitly request a viewport window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId); window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. + if (window->Viewport && (window->Flags & ImGuiWindowFlags_DockNodeHost) != 0 && window->Viewport->Window != NULL) + { + window->Viewport->Window = window; + window->Viewport->ID = window->ViewportId = window->ID; // Overwrite ID (always owned by node) + } lock_viewport = true; } else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) @@ -14789,6 +14960,7 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_CreateWindow(viewport); if (g.PlatformIO.Renderer_CreateWindow != NULL) g.PlatformIO.Renderer_CreateWindow(viewport); + g.PlatformWindowsCreatedCount++; viewport->LastNameHash = 0; viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?) viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it. @@ -15347,7 +15519,7 @@ void ImGui::DockContextEndFrame(ImGuiContext* ctx) if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame) { ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size); - ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), DOCKING_SPLITTER_SIZE); + ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), g.Style.DockingSeparatorSize); node->HostWindow->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags); } @@ -15880,6 +16052,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) LastFocusedNodeId = 0; SelectedTabId = 0; WantCloseTabId = 0; + RefViewportId = 0; AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; @@ -15986,6 +16159,16 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window window->ParentWindow->DC.ChildWindows.find_erase(window); UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately + if (node->HostWindow && node->HostWindow->ViewportOwned) + { + // When undocking from a user interaction this will always run in NewFrame() and have not much effect. + // But mid-frame, if we clear viewport we need to mark window as hidden as well. + window->Viewport = NULL; + window->ViewportId = 0; + window->ViewportOwned = false; + window->Hidden = true; + } + // Remove window bool erased = false; for (int n = 0; n < node->Windows.Size; n++) @@ -16020,14 +16203,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow) { ImGuiWindow* remaining_window = node->Windows[0]; - if (node->HostWindow->ViewportOwned && node->IsRootNode()) - { - // Transfer viewport back to the remaining loose window - IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Node %08X transfer Viewport %08X=>%08X for Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, remaining_window->ID, remaining_window->Name); - IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow); - node->HostWindow->Viewport->Window = remaining_window; - node->HostWindow->Viewport->ID = remaining_window->ID; - } + // Note: we used to transport viewport ownership here. remaining_window->Collapsed = node->HostWindow->Collapsed; } @@ -16358,14 +16534,17 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) FocusWindow(single_window); if (node->HostWindow) { + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Node %08X transfer Viewport %08X->%08X to Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, single_window->ID, single_window->Name); single_window->Viewport = node->HostWindow->Viewport; single_window->ViewportId = node->HostWindow->ViewportId; if (node->HostWindow->ViewportOwned) { + single_window->Viewport->ID = single_window->ID; single_window->Viewport->Window = single_window; single_window->ViewportOwned = true; } } + node->RefViewportId = single_window->ViewportId; } DockNodeHideHostWindow(node); @@ -16455,6 +16634,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Sync Viewport if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window) SetNextWindowViewport(ref_window->ViewportId); + else if (node->AuthorityForViewport == ImGuiDataAuthority_Window && node->RefViewportId != 0) + SetNextWindowViewport(node->RefViewportId); SetNextWindowClass(&node->WindowClass); @@ -16497,6 +16678,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->WantMouseMove && node->HostWindow) DockNodeStartMouseMovingWindow(node, node->HostWindow); } + node->RefViewportId = 0; // Clear when we have a host window // Update focused node (the one whose title bar is highlight) within a node tree if (node->IsSplitNode()) @@ -16548,7 +16730,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->IsRootNode() && host_window) { DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); + PushStyleColor(ImGuiCol_Separator, g.Style.Colors[ImGuiCol_Border]); + PushStyleColor(ImGuiCol_SeparatorActive, g.Style.Colors[ImGuiCol_ResizeGripActive]); + PushStyleColor(ImGuiCol_SeparatorHovered, g.Style.Colors[ImGuiCol_ResizeGripHovered]); DockNodeTreeUpdateSplitter(node); + PopStyleColor(3); } // Draw empty node background (currently can only be the Central Node) @@ -16810,7 +16996,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused) node->LastFrameFocused = g.FrameCount; ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), DOCKING_SPLITTER_SIZE); + ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), g.Style.DockingSeparatorSize); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags); // Docking/Collapse button @@ -16937,18 +17123,18 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } // When clicking on the title bar outside of tabs, we still focus the selected tab for that node - // FIXME: TabItem use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) + // FIXME: TabItems submitted earlier use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) in order to not cover them. ImGuiID title_bar_id = host_window->GetID("#TITLEBAR"); if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id) { + // AllowItem mode required for appending into dock node tab bar, + // otherwise dragging window will steal HoveredId and amended tabs cannot get them. bool held; - ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowItemOverlap); + KeepAliveID(title_bar_id); + ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowOverlap); if (g.HoveredId == title_bar_id) { - // ImGuiButtonFlags_AllowItemOverlap + SetItemAllowOverlap() required for appending into dock node tab bar, - // otherwise dragging window will steal HoveredId and amended tabs cannot get them. g.LastItemData.ID = title_bar_id; - SetItemAllowOverlap(); } if (held) { @@ -17292,7 +17478,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock overlay_rect.Min.y += GetFrameHeight(); if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable) for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) - overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), DOCKING_SPLITTER_SIZE)); + overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), g.Style.DockingSeparatorSize)); } // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read) @@ -17409,7 +17595,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->VisibleWindow = NULL; parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode; - float size_avail = (parent_node->Size[split_axis] - DOCKING_SPLITTER_SIZE); + float size_avail = (parent_node->Size[split_axis] - g.Style.DockingSeparatorSize); size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f); IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting. child_0->SizeRef = child_1->SizeRef = parent_node->Size; @@ -17490,6 +17676,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { // During the regular dock node update we write to all nodes. // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away. + ImGuiContext& g = *GImGui; const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node; if (write_to_node) { @@ -17512,8 +17699,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible) { - ImGuiContext& g = *GImGui; - const float spacing = DOCKING_SPLITTER_SIZE; + const float spacing = g.Style.DockingSeparatorSize; const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; const float size_avail = ImMax(size[axis] - spacing, 0.0f); @@ -17963,12 +18149,16 @@ ImGuiID ImGui::DockSpaceOverViewport(const ImGuiViewport* viewport, ImGuiDockNod void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) { // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1) + ImGuiContext& g = *GImGui; IM_UNUSED(g); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderDockWindow '%s' to node 0x%08X\n", window_name, node_id); ImGuiID window_id = ImHashStr(window_name); if (ImGuiWindow* window = FindWindowByID(window_id)) { // Apply to created window + ImGuiID prev_node_id = window->DockId; SetWindowDock(window, node_id, ImGuiCond_Always); - window->DockOrder = -1; + if (window->DockId != prev_node_id) + window->DockOrder = -1; } else { @@ -17976,8 +18166,9 @@ void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) ImGuiWindowSettings* settings = FindWindowSettingsByID(window_id); if (settings == NULL) settings = CreateNewWindowSettings(window_name); + if (settings->DockId != node_id) + settings->DockOrder = -1; settings->DockId = node_id; - settings->DockOrder = -1; } } @@ -18015,22 +18206,24 @@ void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) // For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect. // - Use (id == 0) to let the system allocate a node identifier. // - Existing node with a same id will be removed. -ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) +ImGuiID ImGui::DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags) { ImGuiContext* ctx = GImGui; + ImGuiContext& g = *ctx; IM_UNUSED(g); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderAddNode 0x%08X flags=%08X\n", node_id, flags); - if (id != 0) - DockBuilderRemoveNode(id); + if (node_id != 0) + DockBuilderRemoveNode(node_id); ImGuiDockNode* node = NULL; if (flags & ImGuiDockNodeFlags_DockSpace) { - DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly); - node = DockContextFindNodeByID(ctx, id); + DockSpace(node_id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly); + node = DockContextFindNodeByID(ctx, node_id); } else { - node = DockContextAddNode(ctx, id); + node = DockContextAddNode(ctx, node_id); node->SetLocalFlags(flags); } node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame. @@ -18040,6 +18233,9 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) void ImGui::DockBuilderRemoveNode(ImGuiID node_id) { ImGuiContext* ctx = GImGui; + ImGuiContext& g = *ctx; IM_UNUSED(g); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderRemoveNode 0x%08X\n", node_id); + ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); if (node == NULL) return; @@ -18500,7 +18696,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) { if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing) window->DockIsActive = true; - if (node->Windows.Size > 1) + if (node->Windows.Size > 1 && window->Appearing) // Only hide appearing window DockNodeHideWindowDuringHostWindowCreation(window); return; } @@ -18859,24 +19055,24 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings if (node_settings->SelectedTabId) buf->appendf(" Selected=0x%08X", node_settings->SelectedTabId); -#if IMGUI_DEBUG_INI_SETTINGS - // [DEBUG] Include comments in the .ini file to ease debugging - if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) - { - buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything - if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) - buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); - // Iterate settings so we can give info about windows that didn't exist during the session. - int contains_window = 0; - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (settings->DockId == node_settings->ID) - { - if (contains_window++ == 0) - buf->appendf(" ; contains "); - buf->appendf("'%s' ", settings->GetName()); - } - } -#endif + // [DEBUG] Include comments in the .ini file to ease debugging (this makes saving slower!) + if (g.IO.ConfigDebugIniSettings) + if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) + { + buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything + if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) + buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); + // Iterate settings so we can give info about windows that didn't exist during the session. + int contains_window = 0; + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + if (settings->DockId == node_settings->ID) + { + if (contains_window++ == 0) + buf->appendf(" ; contains "); + buf->appendf("'%s' ", settings->GetName()); + } + } + buf->appendf("\n"); } buf->appendf("\n"); @@ -19226,7 +19422,7 @@ void ImGui::DebugTextEncoding(const char* str) static void MetricsHelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); @@ -19321,7 +19517,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (rect_type == WRT_InnerRect) { return window->InnerRect; } else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } else if (rect_type == WRT_WorkRect) { return window->WorkRect; } - else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } + else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); } else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; } IM_ASSERT(0); @@ -19456,7 +19652,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) // DrawLists int drawlist_count = 0; for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) - drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); + drawlist_count += g.Viewports[viewport_i]->DrawDataP.CmdLists.Size; if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) { Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); @@ -19465,14 +19661,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiViewportP* viewport = g.Viewports[viewport_i]; bool viewport_has_drawlist = false; - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - { - if (!viewport_has_drawlist) - Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID); - viewport_has_drawlist = true; - DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); - } + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataP.CmdLists.Size; draw_list_i++) + { + if (!viewport_has_drawlist) + Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID); + viewport_has_drawlist = true; + DebugNodeDrawList(NULL, viewport, viewport->DrawDataP.CmdLists[draw_list_i], "DrawList"); + } } TreePop(); } @@ -19607,11 +19802,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("\"%s\"", g.IO.IniFilename); else TextUnformatted(""); + Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) { for (int n = 0; n < g.SettingsHandlers.Size; n++) - BulletText("%s", g.SettingsHandlers[n].TypeName); + BulletText("\"%s\"", g.SettingsHandlers[n].TypeName); TreePop(); } if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) @@ -19635,7 +19831,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("In SettingsWindows:"); for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (settings->DockId != 0) - BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId); + BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings->GetName(), settings->DockId, settings->DockOrder); Text("In SettingsNodes:"); for (int n = 0; n < dc->NodesSettings.Size; n++) { @@ -19697,6 +19893,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); } Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); } Text("Mouse wheel: %.1f", io.MouseWheel); + Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer); Text("Mouse source: %s", GetMouseSourceName(io.MouseSource)); Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused Unindent(); @@ -19774,7 +19971,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame - Text("HoverDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverDelayId, g.HoverDelayTimer, g.HoverDelayClearTimer); + Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer); Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); DebugLocateItemOnHover(g.DragDropPayload.SourceId); Unindent(); @@ -20274,9 +20471,8 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : "", (flags & ImGuiViewportFlags_TopMost) ? " TopMost" : "", (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : ""); - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataP.CmdLists.Size; draw_list_i++) + DebugNodeDrawList(NULL, viewport, viewport->DrawDataP.CmdLists[draw_list_i], "DrawList"); TreePop(); } } diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 3ee53ac5..e7966cab 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.89.6 +// dear imgui, v1.89.8 // (headers) // Help: @@ -9,21 +9,24 @@ // Resources: // - FAQ http://dearimgui.com/faq -// - Homepage & latest https://github.com/ocornut/imgui +// - Homepage https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases // - Gallery https://github.com/ocornut/imgui/issues/6478 (please post your screenshots/video there!) // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) +// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Issues & support https://github.com/ocornut/imgui/issues +// - Tests & Automation https://github.com/ocornut/imgui_test_engine // Getting Started? -// - For first-time users having issues compiling/linking/running or issues loading fonts: +// - Read https://github.com/ocornut/imgui/wiki/Getting-Started +// - For first-time users having issues compiling/linking/running/loading fonts: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.89.6" -#define IMGUI_VERSION_NUM 18960 +#define IMGUI_VERSION "1.89.8" +#define IMGUI_VERSION_NUM 18980 #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch #define IMGUI_HAS_DOCK // Docking WIP branch @@ -115,10 +118,13 @@ Index of this file: #endif #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#if __has_warning("-Wunknown-warning-option") +#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind @@ -672,12 +678,21 @@ namespace ImGui IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL // Tooltips - // - Tooltip are windows following the mouse. They do not take focus away. - IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). - IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip() returns true! - IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). + // - Tooltips are windows following the mouse. They do not take focus away. + // - A tooltip window can contain items of any types. SetTooltip() is a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom. + IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. + IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true! + IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip. Often used after a ImGui::IsItemHovered() check. Override any previous call to SetTooltip(). IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + // Tooltips: helpers for showing a tooltip when hovering an item + // - BeginItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_Tooltip) && BeginTooltip())' idiom. + // - SetItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_Tooltip)) { SetTooltip(...); }' idiom. + // - Where 'ImGuiHoveredFlags_Tooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on active input type. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. + IMGUI_API bool BeginItemTooltip(); // begin/append a tooltip window if preceding item was hovered. + IMGUI_API void SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip if preceeding item was hovered. override any previous call to SetTooltip(). + IMGUI_API void SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + // Popups, Modals // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. @@ -806,9 +821,9 @@ namespace ImGui // - Drag from window menu button (upper-left button) to undock an entire node (all windows). // - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to _enable_ docking/undocking. // About dockspaces: - // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. // - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport. - // This is often used with ImGuiDockNodeFlags_PassthruCentralNode. + // This is often used with ImGuiDockNodeFlags_PassthruCentralNode to make it transparent. + // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. // - Important: Dockspaces need to be submitted _before_ any window they can host. Submit it early in your frame! // - Important: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked. // e.g. if you have multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly. @@ -864,6 +879,9 @@ namespace ImGui IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. + // Overlapping mode + IMGUI_API void SetNextItemAllowOverlap(); // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this. + // Item/Widgets Utilities and Query Functions // - Most of the functions are referring to the previous Item that has been submitted. // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions. @@ -884,7 +902,6 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectSize(); // get size of last item - IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. // Viewports // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. @@ -1073,7 +1090,7 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_None = 0, ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected ImGuiTreeNodeFlags_Framed = 1 << 1, // Draw frame with background (e.g. for CollapsingHeader) - ImGuiTreeNodeFlags_AllowItemOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one + ImGuiTreeNodeFlags_AllowOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open @@ -1087,6 +1104,10 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 14, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog, + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7 +#endif }; // Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions. @@ -1120,7 +1141,11 @@ enum ImGuiSelectableFlags_ ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column) ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text - ImGuiSelectableFlags_AllowItemOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one + ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 +#endif }; // Flags for ImGui::BeginCombo() @@ -1329,16 +1354,30 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled - ImGuiHoveredFlags_NoNavOverride = 1 << 10, // Disable using gamepad/keyboard navigation state when active, always query mouse. + ImGuiHoveredFlags_AllowWhenOverlappedByItem = 1 << 8, // IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item. + ImGuiHoveredFlags_AllowWhenOverlappedByWindow = 1 << 9, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window. + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 10, // IsItemHovered() only: Return true even if the item is disabled + ImGuiHoveredFlags_NoNavOverride = 1 << 11, // IsItemHovered() only: Disable using gamepad/keyboard navigation state when active, always query mouse + ImGuiHoveredFlags_AllowWhenOverlapped = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow, ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, - // Hovering delays (for tooltips) - ImGuiHoveredFlags_DelayNormal = 1 << 11, // Return true after io.HoverDelayNormal elapsed (~0.30 sec) - ImGuiHoveredFlags_DelayShort = 1 << 12, // Return true after io.HoverDelayShort elapsed (~0.10 sec) - ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) + // Tooltips mode + // - typically used in IsItemHovered() + SetTooltip() sequence. + // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior. + // e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. + // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often. + // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay. + ImGuiHoveredFlags_ForTooltip = 1 << 12, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence. + + // (Advanced) Mouse Hovering delays. + // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags. + // - use those if you need specific overrides. + ImGuiHoveredFlags_Stationary = 1 << 13, // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay. + ImGuiHoveredFlags_DelayNone = 1 << 14, // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this. + ImGuiHoveredFlags_DelayShort = 1 << 15, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + ImGuiHoveredFlags_DelayNormal = 1 << 16, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + ImGuiHoveredFlags_NoSharedDelay = 1 << 17, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; // Flags for ImGui::DockSpace(), shared/inherited by child nodes. @@ -1700,6 +1739,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_SeparatorTextBorderSize,// float SeparatorTextBorderSize ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign ImGuiStyleVar_SeparatorTextPadding,// ImVec2 SeparatorTextPadding + ImGuiStyleVar_DockingSeparatorSize,// float DockingSeparatorSize ImGuiStyleVar_COUNT }; @@ -1971,6 +2011,7 @@ struct ImGuiStyle ImVec2 SeparatorTextPadding; // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! + float DockingSeparatorSize; // Thickness of resizing border between docked windows float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later. bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList). @@ -1979,6 +2020,14 @@ struct ImGuiStyle float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. ImVec4 Colors[ImGuiCol_COUNT]; + // Behaviors + // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO) + float HoverStationaryDelay; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. + float HoverDelayShort; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. + float HoverDelayNormal; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. + ImGuiHoveredFlags HoverFlagsForTooltipNav; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. + IMGUI_API ImGuiStyle(); IMGUI_API void ScaleAllSizes(float scale_factor); }; @@ -2011,15 +2060,9 @@ struct ImGuiIO ImVec2 DisplaySize; // // Main display size, in pixels (generally == GetMainViewport()->Size). May change every frame. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. May change every frame. float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. + float InputScale; // = 1.0f // Used by backend in system managed scale situations. const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions. const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. - float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). - float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - float HoverDelayNormal; // = 0.30 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. - float HoverDelayShort; // = 0.10 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true. void* UserData; // = NULL // Store your own data. ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. @@ -2054,18 +2097,33 @@ struct ImGuiIO bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. + // Inputs Behaviors + // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle) + float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. + float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). + float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. + + //------------------------------------------------------------------ // Debug options - // - tools to test correct Begin/End and BeginChild/EndChild behaviors. - // - presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX() - // this is inconsistent with other BeginXXX functions and create confusion for many users. - // - we expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior. + //------------------------------------------------------------------ + + // Tools to test correct Begin/End and BeginChild/EndChild behaviors. + // Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX() + // This is inconsistent with other BeginXXX functions and create confusion for many users. + // We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior. bool ConfigDebugBeginReturnValueOnce;// = false // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows. bool ConfigDebugBeginReturnValueLoop;// = false // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running. - // - option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data. - // - backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them. - // - consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). + + // Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data. + // Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them. + // Consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys() in input processing. + // Option to audit .ini data + bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) + //------------------------------------------------------------------ // Platform Functions // (the imgui_impl_xxxx backend files are setting those up for you) @@ -2112,8 +2170,11 @@ struct ImGuiIO IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode. IMGUI_API void SetAppAcceptingEvents(bool accepting_events); // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen. - IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually - IMGUI_API void ClearInputKeys(); // [Internal] Release all keys + IMGUI_API void ClearEventsQueue(); // Clear all incoming events. + IMGUI_API void ClearInputKeys(); // Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + IMGUI_API void ClearInputCharacters(); // [Obsolete] Clear the current frame text input buffer. Now included within ClearInputKeys(). +#endif //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -2544,8 +2605,8 @@ struct ImColor constexpr ImColor() { } constexpr ImColor(float r, float g, float b, float a = 1.0f) : Value(r, g, b, a) { } constexpr ImColor(const ImVec4& col) : Value(col) {} - ImColor(int r, int g, int b, int a = 255) { float sc = 1.0f / 255.0f; Value.x = (float)r * sc; Value.y = (float)g * sc; Value.z = (float)b * sc; Value.w = (float)a * sc; } - ImColor(ImU32 rgba) { float sc = 1.0f / 255.0f; Value.x = (float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * sc; Value.y = (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * sc; Value.z = (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * sc; Value.w = (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * sc; } + constexpr ImColor(int r, int g, int b, int a = 255) : Value((float)r * (1.0f / 255.0f), (float)g * (1.0f / 255.0f), (float)b * (1.0f / 255.0f), (float)a* (1.0f / 255.0f)) {} + constexpr ImColor(ImU32 rgba) : Value((float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * (1.0f / 255.0f)) {} inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } inline operator ImVec4() const { return Value; } @@ -2820,19 +2881,20 @@ struct ImDrawList // as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList) struct ImDrawData { - bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int CmdListsCount; // Number of ImDrawList* to render - int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size - int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size - ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. - ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) - ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) - ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. - ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not). + bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. + int CmdListsCount; // Number of ImDrawList* to render + int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size + int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size + ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. + ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) + ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) + ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. + ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not). // Functions ImDrawData() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); } // The ImDrawList are owned by ImGuiContext! + IMGUI_API void Clear(); + IMGUI_API void AddDrawList(ImDrawList* draw_list); // Helper to add an external draw list into an existing ImDrawData. IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; @@ -2848,7 +2910,7 @@ struct ImFontConfig bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). int FontNo; // 0 // Index of font within TTF/OTF file float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal so you can reduce this to 2 to save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. + int OversampleH; // 2 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis. bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. @@ -3309,6 +3371,8 @@ namespace ImGui #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.89.7 (from June 2023) + IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item. // OBSOLETED in 1.89.4 (from March 2023) static inline void PushAllowKeyboardFocus(bool tab_stop) { PushTabStop(tab_stop); } static inline void PopAllowKeyboardFocus() { PopTabStop(); } diff --git a/extern/imgui_patched/imgui_demo.cpp b/extern/imgui_patched/imgui_demo.cpp index 52abc798..505be4d9 100644 --- a/extern/imgui_patched/imgui_demo.cpp +++ b/extern/imgui_patched/imgui_demo.cpp @@ -1,10 +1,12 @@ -// dear imgui, v1.89.6 +// dear imgui, v1.89.8 // (demo code) // Help: // - Read FAQ at http://dearimgui.com/faq -// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. +// - Need help integrating Dear ImGui in your codebase? +// - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started +// - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. // Read imgui.cpp for more details, documentation and comments. // Get the latest version at https://github.com/ocornut/imgui @@ -92,11 +94,7 @@ Index of this file: #include // sqrtf, powf, cosf, sinf, floorf, ceilf #include // vsnprintf, sscanf, printf #include // NULL, malloc, free, atoi -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif // Visual Studio warnings #ifdef _MSC_VER @@ -213,7 +211,7 @@ static void ShowDemoWindowInputs(); static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); @@ -525,6 +523,8 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running."); ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss); ImGui::SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data."); + ImGui::Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); + ImGui::SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)."); ImGui::TreePop(); ImGui::Spacing(); @@ -683,37 +683,8 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); ImGui::Text("%d", counter); - { - // Tooltips - IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); - //ImGui::AlignTextToFramePadding(); - ImGui::Text("Tooltips:"); - - ImGui::SameLine(); - ImGui::SmallButton("Basic"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip"); - - ImGui::SameLine(); - ImGui::SmallButton("Fancy"); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) - { - ImGui::Text("I am a fancy tooltip"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); - ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); - ImGui::EndTooltip(); - } - - ImGui::SameLine(); - ImGui::SmallButton("Delayed"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) // With a delay - ImGui::SetTooltip("I am a tooltip with a delay."); - - ImGui::SameLine(); - HelpMarker( - "Tooltip are created by using the IsItemHovered() function over any kind of item."); - } + ImGui::Button("Tooltip"); + ImGui::SetItemTooltip("I am a tooltip"); ImGui::LabelText("label", "Value"); @@ -848,6 +819,85 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tooltips"); + if (ImGui::TreeNode("Tooltips")) + { + // Tooltips are windows following the mouse. They do not take focus away. + ImGui::SeparatorText("General"); + + // Typical use cases: + // - Short-form (text only): SetItemTooltip("Hello"); + // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); } + + // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); } + // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); } + + HelpMarker( + "Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n" + "We provide a helper SetItemTooltip() function to perform the two with standards flags."); + + ImVec2 sz = ImVec2(-FLT_MIN, 0.0f); + + ImGui::Button("Basic", sz); + ImGui::SetItemTooltip("I am a tooltip"); + + ImGui::Button("Fancy", sz); + if (ImGui::BeginItemTooltip()) + { + ImGui::Text("I am a fancy tooltip"); + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); + ImGui::EndTooltip(); + } + + ImGui::SeparatorText("Always On"); + + // Showcase NOT relying on a IsItemHovered() to emit a tooltip. + // Here the tooltip is always emitted when 'always_on == true'. + static int always_on = 0; + ImGui::RadioButton("Off", &always_on, 0); + ImGui::SameLine(); + ImGui::RadioButton("Always On (Simple)", &always_on, 1); + ImGui::SameLine(); + ImGui::RadioButton("Always On (Advanced)", &always_on, 2); + if (always_on == 1) + ImGui::SetTooltip("I am following you around."); + else if (always_on == 2 && ImGui::BeginTooltip()) + { + ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f)); + ImGui::EndTooltip(); + } + + ImGui::SeparatorText("Custom"); + + // The following examples are passed for documentation purpose but may not be useful to most users. + // Passing ImGuiHoveredFlags_Tooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from + // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or gamepad/keyboard is being used. + // With default settings, ImGuiHoveredFlags_Tooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary. + ImGui::Button("Manual", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + ImGui::SetTooltip("I am a manually emitted tooltip"); + + ImGui::Button("DelayNone", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone)) + ImGui::SetTooltip("I am a tooltip with no delay."); + + ImGui::Button("DelayShort", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay)) + ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort); + + ImGui::Button("DelayLong", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) + ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec)", ImGui::GetStyle().HoverDelayNormal); + + ImGui::Button("Stationary", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary)) + ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating."); + + ImGui::TreePop(); + } + // Testing ImGuiOnceUponAFrame helper. //static ImGuiOnceUponAFrame once; //for (int i = 0; i < 5; i++) @@ -1113,7 +1163,7 @@ static void ShowDemoWindowWidgets() ImVec4 tint_col = use_text_color_for_tint ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint ImVec4 border_col = ImGui::GetStyleColorVec4(ImGuiCol_Border); ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { float region_sz = 32.0f; float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; @@ -1267,16 +1317,16 @@ static void ShowDemoWindowWidgets() IMGUI_DEMO_MARKER("Widgets/Selectables/Basic"); if (ImGui::TreeNode("Basic")) { - static bool selection[5] = { false, true, false, false, false }; + static bool selection[5] = { false, true, false, false }; ImGui::Selectable("1. I am selectable", &selection[0]); ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("(I am not selectable)"); - ImGui::Selectable("4. I am selectable", &selection[3]); - if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) + ImGui::Selectable("3. I am selectable", &selection[2]); + if (ImGui::Selectable("4. I am double clickable", selection[3], ImGuiSelectableFlags_AllowDoubleClick)) if (ImGui::IsMouseDoubleClicked(0)) - selection[4] = !selection[4]; + selection[3] = !selection[3]; ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Single Selection"); if (ImGui::TreeNode("Selection State: Single Selection")) { @@ -1308,17 +1358,18 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more text into the same line"); - if (ImGui::TreeNode("Rendering more text into the same line")) + IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line"); + if (ImGui::TreeNode("Rendering more items on the same line")) { - // Using the Selectable() override that takes "bool* p_selected" parameter, - // this function toggle your bool value automatically. + // (1) Using SetNextItemAllowOverlap() + // (2) Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically. static bool selected[3] = { false, false, false }; - ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); - ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1"); + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2"); + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3"); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/In columns"); if (ImGui::TreeNode("In columns")) { @@ -1354,6 +1405,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Grid"); if (ImGui::TreeNode("Grid")) { @@ -1452,8 +1504,8 @@ static void ShowDemoWindowWidgets() // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback) static int FilterCasingSwap(ImGuiInputTextCallbackData* data) { - if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar = data->EventChar - 'A' - 'a'; } // Lowercase becomes uppercase - else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar = data->EventChar + 'a' - 'A'; } // Uppercase becomes lowercase + if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase + else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase return 0; } @@ -1487,6 +1539,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks"); if (ImGui::TreeNode("Completion, History, Edit Callbacks")) { struct Funcs @@ -1586,6 +1639,18 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous"); + if (ImGui::TreeNode("Miscellaneous")) + { + static char buf1[16]; + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll; + ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll); + ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo); + ImGui::InputText("Hello", buf1, IM_ARRAYSIZE(buf1), flags); + ImGui::TreePop(); + } + ImGui::TreePop(); } @@ -2434,8 +2499,10 @@ static void ShowDemoWindowWidgets() if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } bool hovered_delay_none = ImGui::IsItemHovered(); + bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary); bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort); bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal); + bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary // Display the values of IsItemHovered() and other common item state functions. // Note that the ImGuiHoveredFlags_XXX flags can be combined. @@ -2447,7 +2514,8 @@ static void ShowDemoWindowWidgets() "IsItemHovered() = %d\n" "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsItemHovered(_AllowWhenOverlapped) = %d\n" + "IsItemHovered(_AllowWhenOverlappedByItem) = %d\n" + "IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n" "IsItemHovered(_AllowWhenDisabled) = %d\n" "IsItemHovered(_RectOnly) = %d\n" "IsItemActive() = %d\n" @@ -2466,7 +2534,8 @@ static void ShowDemoWindowWidgets() ImGui::IsItemHovered(), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), ImGui::IsItemActive(), @@ -2482,7 +2551,13 @@ static void ShowDemoWindowWidgets() ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y ); ImGui::BulletText( - "w/ Hovering Delay: None = %d, Fast %d, Normal = %d", hovered_delay_none, hovered_delay_short, hovered_delay_normal); + "with Hovering Delay or Stationary test:\n" + "IsItemHovered() = = %d\n" + "IsItemHovered(_Stationary) = %d\n" + "IsItemHovered(_DelayShort) = %d\n" + "IsItemHovered(_DelayNormal) = %d\n" + "IsItemHovered(_Tooltip) = %d", + hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip); if (item_disabled) ImGui::EndDisabled(); @@ -2543,7 +2618,8 @@ static void ShowDemoWindowWidgets() "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_AnyWindow) = %d\n", + "IsWindowHovered(_AnyWindow) = %d\n" + "IsWindowHovered(_Stationary) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), @@ -2557,7 +2633,8 @@ static void ShowDemoWindowWidgets() ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); + ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary)); ImGui::BeginChild("child", ImVec2(0, 50), true); ImGui::Text("This is another child window for testing the _ChildWindows flag."); @@ -2803,11 +2880,11 @@ static void ShowDemoWindowLayout() // Text IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine"); ImGui::Text("Two items: Hello"); ImGui::SameLine(); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor"); // Adjust spacing ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor"); // Button ImGui::AlignTextToFramePadding(); @@ -2858,7 +2935,7 @@ static void ShowDemoWindowLayout() ImGui::PushID(i); ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); ImGui::PopID(); - //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + //ImGui::SetItemTooltip("ListBox %d hovered", i); } ImGui::PopItemWidth(); @@ -2911,8 +2988,7 @@ static void ShowDemoWindowLayout() ImGui::SameLine(); ImGui::Button("EEE"); ImGui::EndGroup(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("First group hovered"); + ImGui::SetItemTooltip("First group hovered"); } // Capture the group size and create widgets using the same size ImVec2 size = ImGui::GetItemRectSize(); @@ -3406,6 +3482,36 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + + IMGUI_DEMO_MARKER("Layout/Overlap Mode"); + if (ImGui::TreeNode("Overlap Mode")) + { + static bool enable_allow_overlap = true; + + HelpMarker( + "Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n" + "By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state."); + ImGui::Checkbox("Enable AllowOverlap", &enable_allow_overlap); + + ImVec2 button1_pos = ImGui::GetCursorScreenPos(); + ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f); + if (enable_allow_overlap) + ImGui::SetNextItemAllowOverlap(); + ImGui::Button("Button 1", ImVec2(80, 80)); + ImGui::SetCursorScreenPos(button2_pos); + ImGui::Button("Button 2", ImVec2(80, 80)); + + // This is typically used with width-spanning items. + // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut + // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.) + if (enable_allow_overlap) + ImGui::SetNextItemAllowOverlap(); + ImGui::Selectable("Some Selectable", false); + ImGui::SameLine(); + ImGui::SmallButton("++"); + + ImGui::TreePop(); + } } static void ShowDemoWindowPopups() @@ -3473,8 +3579,7 @@ static void ShowDemoWindowPopups() ImGui::Separator(); ImGui::Text("Tooltip here"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip over a popup"); + ImGui::SetItemTooltip("I am a tooltip over a popup"); if (ImGui::Button("Stacked Popup")) ImGui::OpenPopup("another popup"); @@ -3558,8 +3663,7 @@ static void ShowDemoWindowPopups() ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Right-click to open popup"); + ImGui::SetItemTooltip("Right-click to open popup"); } } @@ -3811,7 +3915,7 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) } ImGui::SameLine(); ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); for (int m = 0; m < IM_ARRAYSIZE(policies); m++) @@ -5509,7 +5613,7 @@ static void ShowDemoWindowTables() ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow) { - ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap : ImGuiSelectableFlags_None; + ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None; if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height))) { if (ImGui::GetIO().KeyCtrl) @@ -6012,10 +6116,11 @@ void ImGui::ShowAboutWindow(bool* p_open) return; } IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); - ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); + ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); ImGui::Separator(); ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); + ImGui::Text("If your company uses this, please consider sponsoring the project!"); static bool show_config_info = false; ImGui::Checkbox("Config/Build Information", &show_config_info); @@ -6311,11 +6416,28 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); ImGui::SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%0.f"); + ImGui::SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f"); ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); + ImGui::SeparatorText("Docking"); + ImGui::SliderFloat("DockingSplitterSize", &style.DockingSeparatorSize, 0.0f, 12.0f, "%.0f"); + + ImGui::SeparatorText("Tooltips"); + for (int n = 0; n < 2; n++) + if (ImGui::TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav")) + { + ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav; + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone); + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort); + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal); + ImGui::CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary); + ImGui::CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay); + ImGui::TreePop(); + } + ImGui::SeparatorText("Misc"); ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); + ImGui::EndTabItem(); } @@ -7872,18 +7994,20 @@ static void ShowExampleAppCustomRendering(bool* p_open) // your own implicit "Debug##2" window after calling DockSpace() and leave it in the window stack for anyone to use. void ShowExampleAppDockSpace(bool* p_open) { - // If you strip some features of, this demo is pretty much equivalent to calling DockSpaceOverViewport()! - // In most cases you should be able to just call DockSpaceOverViewport() and ignore all the code below! - // In this specific demo, we are not using DockSpaceOverViewport() because: - // - we allow the host window to be floating/moveable instead of filling the viewport (when opt_fullscreen == false) - // - we allow the host window to have padding (when opt_padding == true) - // - we have a local menu bar in the host window (vs. you could use BeginMainMenuBar() + DockSpaceOverViewport() in your code!) - // TL;DR; this demo is more complicated than what you would normally use. - // If we removed all the options we are showcasing, this demo would become: + // READ THIS !!! + // TL;DR; this demo is more complicated than what most users you would normally use. + // If we remove all options we are showcasing, this demo would become: // void ShowExampleAppDockSpace() // { // ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()); // } + // In most cases you should be able to just call DockSpaceOverViewport() and ignore all the code below! + // In this specific demo, we are not using DockSpaceOverViewport() because: + // - (1) we allow the host window to be floating/moveable instead of filling the viewport (when opt_fullscreen == false) + // - (2) we allow the host window to have padding (when opt_padding == true) + // - (3) we expose many flags and need a way to have them visible. + // - (4) we have a local menu bar in the host window (vs. you could use BeginMainMenuBar() + DockSpaceOverViewport() + // in your code, but we don't here because we allow the window to be floating) static bool opt_fullscreen = true; static bool opt_padding = false; @@ -8293,8 +8417,8 @@ void ShowExampleAppDocuments(bool* p_open) for (int n = 0; n < close_queue.Size; n++) if (close_queue[n]->Dirty) ImGui::Text("%s", close_queue[n]->Name); - ImGui::EndChildFrame(); } + ImGui::EndChildFrame(); ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f); if (ImGui::Button("Yes", button_size)) diff --git a/extern/imgui_patched/imgui_draw.cpp b/extern/imgui_patched/imgui_draw.cpp index 208b2fdd..7ee0a19d 100644 --- a/extern/imgui_patched/imgui_draw.cpp +++ b/extern/imgui_patched/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.6 +// dear imgui, v1.89.8 // (drawing and font code) /* @@ -63,6 +63,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used @@ -1861,6 +1862,63 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) // [SECTION] ImDrawData //----------------------------------------------------------------------------- +void ImDrawData::Clear() +{ + Valid = false; + CmdListsCount = TotalIdxCount = TotalVtxCount = 0; + CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them. + DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f); + OwnerViewport = NULL; +} + +// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list +// as long at it is expected that the result will be later merged into draw_data->CmdLists[]. +void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list) +{ + if (draw_list->CmdBuffer.Size == 0) + return; + if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) + return; + + // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. + // May trigger for you if you are using PrimXXX functions incorrectly. + IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); + IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); + if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) + IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); + + // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) + // If this assert triggers because you are drawing lots of stuff manually: + // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. + // Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. + // - If you want large meshes with more than 64K vertices, you can either: + // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. + // Most example backends already support this from 1.71. Pre-1.71 backends won't. + // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. + // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. + // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: + // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + // Your own engine or render API may use different parameters or function calls to specify index sizes. + // 2 and 4 bytes indices are generally supported by most graphics API. + // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching + // the 64K limit to split your draw commands in multiple draw lists. + if (sizeof(ImDrawIdx) == 2) + IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); + + // Add to output list + records state in ImDrawData + out_list->push_back(draw_list); + draw_data->CmdListsCount++; + draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; + draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; +} + +void ImDrawData::AddDrawList(ImDrawList* draw_list) +{ + IM_ASSERT(CmdLists.Size == CmdListsCount); + draw_list->_PopUnusedDrawCmd(); + ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list); +} + // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! void ImDrawData::DeIndexAllBuffers() { @@ -1957,7 +2015,7 @@ ImFontConfig::ImFontConfig() { memset(this, 0, sizeof(*this)); FontDataOwnedByAtlas = true; - OversampleH = 3; // FIXME: 2 may be a better default? + OversampleH = 2; OversampleV = 1; GlyphMaxAdvanceX = FLT_MAX; RasterizerMultiply = 1.0f; diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index 6fa0dc58..2c8057bd 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.89.6 +// dear imgui, v1.89.8 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. @@ -278,6 +278,8 @@ namespace ImStb #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 #define IM_FLOOR(_VAL) ((float)(int)(_VAL)) // ImFloor() is not inlined in MSVC debug builds #define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // +#define IM_STRINGIFY_HELPER(_X) #_X +#define IM_STRINGIFY(_X) IM_STRINGIFY_HELPER(_X) // Preprocessor idiom to stringify e.g. an integer. // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall #ifdef _MSC_VER @@ -780,12 +782,10 @@ struct IMGUI_API ImDrawListSharedData struct ImDrawDataBuilder { - ImVector Layers[2]; // Global layers for: regular, tooltip + ImVector* Layers[2]; // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData. + ImVector LayerData1; - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } - void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } - int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } - IMGUI_API void FlattenIntoSingleLayer(); + ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -810,6 +810,7 @@ enum ImGuiItemFlags_ ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable() + ImGuiItemflags_AllowOverlap = 1 << 9, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. // Controlled by widget code ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. @@ -842,6 +843,14 @@ enum ImGuiItemStatusFlags_ #endif }; +// Extend ImGuiHoveredFlags_ +enum ImGuiHoveredFlagsPrivate_ +{ + ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay, + ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary, + ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_, +}; + // Extend ImGuiInputTextFlags_ enum ImGuiInputTextFlagsPrivate_ { @@ -862,7 +871,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping - ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() + ImGuiButtonFlags_AllowOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable. ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED] //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine @@ -907,6 +916,7 @@ enum ImGuiSelectableFlagsPrivate_ enum ImGuiTreeNodeFlagsPrivate_ { ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20, + ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 21,// (FIXME-WIP) Turn Down arrow into an Up arrow, but reversed trees (#6517) }; enum ImGuiSeparatorFlags_ @@ -936,7 +946,7 @@ enum ImGuiTextFlags_ enum ImGuiTooltipFlags_ { ImGuiTooltipFlags_None = 0, - ImGuiTooltipFlags_OverridePreviousTooltip = 1 << 0, // Override will clear/ignore previously submitted tooltip (defaults to append) + ImGuiTooltipFlags_OverridePrevious = 1 << 1, // Clear/ignore previously submitted tooltip (defaults to append) }; // FIXME: this is in development, not exposed/functional as a generic feature yet. @@ -1187,13 +1197,14 @@ enum ImGuiNextItemDataFlags_ struct ImGuiNextItemData { ImGuiNextItemDataFlags Flags; + ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemflags_AllowOverlap. float Width; // Set by SetNextItemWidth() ImGuiID FocusScopeId; // Set by SetNextItemMultiSelectData() (!= 0 signify value has been set, so it's an alternate version of HasSelectionData, we don't use Flags for this because they are cleared too early. This is mostly used for debugging) ImGuiCond OpenCond; bool OpenVal; // Set by SetNextItemOpen() ImGuiNextItemData() { memset(this, 0, sizeof(*this)); } - inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } // Also cleared manually by ItemAdd()! + inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! }; // Status storage for the last submitted item @@ -1501,10 +1512,12 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary ImGuiNavMoveFlags_Forwarded = 1 << 7, ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result - ImGuiNavMoveFlags_FocusApi = 1 << 9, - ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight - ImGuiNavMoveFlags_Activate = 1 << 11, - ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12, // Do not alter the visible state of keyboard vs mouse nav highlight + ImGuiNavMoveFlags_FocusApi = 1 << 9, // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details) + ImGuiNavMoveFlags_IsTabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_IsPageMove = 1 << 11, // Identify a PageDown/PageUp request. + ImGuiNavMoveFlags_Activate = 1 << 12, // Activate/select target item. + ImGuiNavMoveFlags_NoSelect = 1 << 13, // Don't trigger selection by not setting g.NavJustMovedTo + ImGuiNavMoveFlags_NoSetNavHighlight = 1 << 14, // Do not alter the visible state of keyboard vs mouse nav highlight }; enum ImGuiNavLayer @@ -1674,6 +1687,7 @@ struct IMGUI_API ImGuiDockNode ImGuiID LastFocusedNodeId; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. ImGuiID SelectedTabId; // [Leaf node only] Which of our tab/window is selected. ImGuiID WantCloseTabId; // [Leaf node only] Set when closing a specific tab/window. + ImGuiID RefViewportId; // Reference viewport ID from visible window when HostWindow == NULL. ImGuiDataAuthority AuthorityForPos :3; ImGuiDataAuthority AuthorityForSize :3; ImGuiDataAuthority AuthorityForViewport :3; @@ -1758,7 +1772,7 @@ struct ImGuiViewportP : public ImGuiViewport int DrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; - ImDrawDataBuilder DrawDataBuilder; + ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData ImVec2 LastPlatformPos; ImVec2 LastPlatformSize; ImVec2 LastRendererSize; @@ -1829,6 +1843,7 @@ struct ImGuiSettingsHandler // This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack. enum ImGuiLocKey : int { + ImGuiLocKey_VersionStr, ImGuiLocKey_TableSizeOne, ImGuiLocKey_TableSizeAllFit, ImGuiLocKey_TableSizeAllDefault, @@ -2046,6 +2061,8 @@ struct ImGuiContext ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. ImGuiID PlatformLastFocusedViewportId; ImGuiPlatformMonitor FallbackMonitor; // Virtual monitor used as fallback if backend doesn't provide monitor information. + int ViewportCreatedCount; // Unique sequential creation counter (mostly for testing/debugging) + int PlatformWindowsCreatedCount; // Unique sequential creation counter (mostly for testing/debugging) int ViewportFocusedStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter // Gamepad/keyboard Navigation @@ -2111,7 +2128,6 @@ struct ImGuiContext // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) - ImGuiMouseCursor MouseCursor; // Drag and Drop bool DragDropActive; @@ -2151,13 +2167,19 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Hover Delay system - ImGuiID HoverDelayId; - ImGuiID HoverDelayIdPreviousFrame; - float HoverDelayTimer; // Currently used IsItemHovered(), generally inferred from g.HoveredIdTimer but kept uncleared until clear timer elapse. - float HoverDelayClearTimer; // Currently used IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + ImGuiID HoverItemDelayId; + ImGuiID HoverItemDelayIdPreviousFrame; + float HoverItemDelayTimer; // Currently used by IsItemHovered() + float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + ImGuiID HoverItemUnlockedStationaryId; // Mouse has once been stationary on this item. Only reset after departing the item. + ImGuiID HoverWindowUnlockedStationaryId; // Mouse has once been stationary on this window. Only reset after departing the window. + + // Mouse state + ImGuiMouseCursor MouseCursor; + float MouseStationaryTimer; // Time the mouse has been stationary (with some loose heuristic) + ImVec2 MouseLastValidPos; // Widget state - ImVec2 MouseLastValidPos; ImGuiInputTextState InputTextState; ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFont InputTextPasswordFont; @@ -2314,6 +2336,7 @@ struct ImGuiContext CurrentViewport = NULL; MouseViewport = MouseLastHoveredViewport = NULL; PlatformLastFocusedViewportId = 0; + ViewportCreatedCount = PlatformWindowsCreatedCount = 0; ViewportFocusedStampCount = 0; NavWindow = NULL; @@ -2350,7 +2373,6 @@ struct ImGuiContext InertialScrollInhibited = false; DimBgRatio = 0.0f; - MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; DragDropSourceFlags = ImGuiDragDropFlags_None; @@ -2370,8 +2392,11 @@ struct ImGuiContext TablesTempDataStacked = 0; CurrentTabBar = NULL; - HoverDelayId = HoverDelayIdPreviousFrame = 0; - HoverDelayTimer = HoverDelayClearTimer = 0.0f; + HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; + HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; + + MouseCursor = ImGuiMouseCursor_Arrow; + MouseStationaryTimer = 0.0f; TempInputId = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; @@ -2797,8 +2822,10 @@ struct ImGuiTableInstanceData float LastOuterHeight; // Outer height from last frame float LastFirstRowHeight; // Height of first row from last frame (FIXME: this is used as "header height" and may be reworked) float LastFrozenHeight; // Height of frozen section from last frame + int HoveredRowLast; // Index of row which was hovered last frame. + int HoveredRowNext; // Index of row hovered this frame, set after encountering it. - ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastFirstRowHeight = LastFrozenHeight = 0.0f; } + ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastFirstRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; } }; // FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs, incoming RowData @@ -3022,6 +3049,7 @@ namespace ImGui IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { return GetForegroundDrawList(window->Viewport); } + IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list); // Init IMGUI_API void Initialize(); @@ -3101,7 +3129,7 @@ namespace ImGui IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min. IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); - IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); + IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags); IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); @@ -3161,10 +3189,15 @@ namespace ImGui IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); IMGUI_API void NavClearPreferredPosForAxis(ImGuiAxis axis); IMGUI_API void NavUpdateCurrentWindowIsScrollPushableX(); - IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. IMGUI_API void SetNavWindow(ImGuiWindow* window); IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); + // Focus/Activation + // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are + // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones. + IMGUI_API void FocusItem(); // Focus last item (no selection/activation). + IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. + // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } @@ -3334,7 +3367,8 @@ namespace ImGui IMGUI_API void TableOpenContextMenu(int column_n = -1); IMGUI_API void TableSetColumnWidth(int column_n, float width); IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); - IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. + IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. + IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet. IMGUI_API float TableGetHeaderRowHeight(); IMGUI_API void TablePushBackgroundChannel(); IMGUI_API void TablePopBackgroundChannel(); diff --git a/extern/imgui_patched/imgui_tables.cpp b/extern/imgui_patched/imgui_tables.cpp index 4aae7a1e..c6ab1373 100644 --- a/extern/imgui_patched/imgui_tables.cpp +++ b/extern/imgui_patched/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.6 +// dear imgui, v1.89.8 // (tables and columns code) /* @@ -198,11 +198,7 @@ Index of this file: #include "imgui_internal.h" // System includes -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif // Visual Studio warnings #ifdef _MSC_VER @@ -417,7 +413,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HasScrollbarYPrev = table->HasScrollbarYCurr; table->HasScrollbarYCurr = false; } - table->HasScrollbarYCurr |= (table->InnerWindow->ScrollMax.y > 0.0f); + table->HasScrollbarYCurr |= table->InnerWindow->ScrollbarY; } else { @@ -974,12 +970,14 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem). // - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop. ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + table_instance->HoveredRowLast = table_instance->HoveredRowNext; + table_instance->HoveredRowNext = -1; table->HoveredColumnBody = -1; table->HoveredColumnBorder = -1; const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); const ImGuiID backup_active_id = g.ActiveId; g.ActiveId = 0; - const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0); + const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None); g.ActiveId = backup_active_id; // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column @@ -1131,6 +1129,14 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f); table->BorderX2 = table->InnerClipRect.Max.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : +1.0f); + // Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call. + float window_content_max_y; + if (table->Flags & ImGuiTableFlags_NoHostExtendY) + window_content_max_y = table->OuterRect.Max.y; + else + window_content_max_y = ImMax(table->InnerWindow->ContentRegionRect.Max.y, (table->Flags & ImGuiTableFlags_ScrollY) ? 0.0f : table->OuterRect.Max.y); + table->InnerWindow->WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table->InnerWindow->WorkRect.Min.y, table->InnerWindow->WorkRect.Max.y); + // [Part 9] Allocate draw channels and setup background cliprect TableSetupDrawChannels(table); @@ -1170,8 +1176,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Process hit-testing on resizing borders. Actual size change will be applied in EndTable() // - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise. -// - Submit ahead of table contents and header, use ImGuiButtonFlags_AllowItemOverlap to prioritize -// widgets overlapping the same area. void ImGui::TableUpdateBorders(ImGuiTable* table) { ImGuiContext& g = *GImGui; @@ -1211,7 +1215,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100)); bool hovered = false, held = false; - bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); + bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); if (pressed && IsMouseDoubleClicked(0)) { TableSetColumnWidthAutoSingle(table, column_n); @@ -1552,6 +1556,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) // - TableGetCellBgRect() [Internal] // - TableGetColumnResizeID() [Internal] // - TableGetHoveredColumn() [Internal] +// - TableGetHoveredRow() [Internal] // - TableSetBgColor() //----------------------------------------------------------------------------- @@ -1656,6 +1661,19 @@ int ImGui::TableGetHoveredColumn() return (int)table->HoveredColumnBody; } +// Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row. +// *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value. +// This difference with is the reason why this is not public yet. +int ImGui::TableGetHoveredRow() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return -1; + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + return (int)table_instance->HoveredRowLast; +} + void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n) { ImGuiContext& g = *GImGui; @@ -1807,6 +1825,10 @@ void ImGui::TableEndRow(ImGuiTable* table) const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y); if (is_visible) { + // Update data for TableGetHoveredRow() + if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2) + TableGetInstanceData(table, table->InstanceCurrent)->HoveredRowNext = table->CurrentRow; + // Decide of background color for the row ImU32 bg_col0 = 0; ImU32 bg_col1 = 0; @@ -2000,6 +2022,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) window->DC.CurrLineTextBaseOffset = table->RowTextBaseline; window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent; + // Note how WorkRect.Max.y is only set once during layout window->WorkRect.Min.y = window->DC.CursorPos.y; window->WorkRect.Min.x = column->WorkMinX; window->WorkRect.Max.x = column->WorkMaxX; @@ -2964,11 +2987,9 @@ void ImGui::TableHeader(const char* label) //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] - // Using AllowItemOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. + // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowItemOverlap); - if (g.ActiveId != id) - SetItemAllowOverlap(); + bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap); if (held || hovered || selected) { const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); @@ -3039,8 +3060,8 @@ void ImGui::TableHeader(const char* label) RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); - if (text_clipped && hovered && g.ActiveId == 0 && IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - SetTooltip("%.*s", (int)(label_end - label), label); + if (text_clipped && hovered && g.ActiveId == 0) + SetItemTooltip("%.*s", (int)(label_end - label), label); // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden if (IsMouseReleased(1) && IsItemHovered()) @@ -3602,6 +3623,11 @@ void ImGui::DebugNodeTable(ImGuiTable* table) BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX); BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder); BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn); + for (int n = 0; n < table->InstanceCurrent + 1; n++) + { + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, n); + BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, table_instance->HoveredRowLast, table_instance->LastOuterHeight); + } //BulletText("BgDrawChannels: %d/%d", 0, table->BgDrawChannelUnfrozen); float sum_weights = 0.0f; for (int n = 0; n < table->ColumnsCount; n++) @@ -3960,6 +3986,7 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFl window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding; + window->WorkRect.Max.y = window->ContentRegionRect.Max.y; } void ImGui::NextColumn() diff --git a/extern/imgui_patched/imgui_widgets.cpp b/extern/imgui_patched/imgui_widgets.cpp index b99e546e..ff90e35a 100644 --- a/extern/imgui_patched/imgui_widgets.cpp +++ b/extern/imgui_patched/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.6 +// dear imgui, v1.89.8 // (widgets code) /* @@ -41,11 +41,7 @@ Index of this file: #include "imgui_internal.h" // System includes -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif //------------------------------------------------------------------------- // Warnings @@ -492,6 +488,14 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0) flags |= ImGuiButtonFlags_PressedOnDefault_; + // Default behavior inherited from item flags + // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that. + ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); + if (flags & ImGuiButtonFlags_AllowOverlap) + item_flags |= ImGuiItemflags_AllowOverlap; + if (flags & ImGuiButtonFlags_Repeat) + item_flags |= ImGuiItemFlags_ButtonRepeat; + ImGuiWindow* backup_hovered_window = g.HoveredWindow; const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree; if (flatten_hovered_children) @@ -504,11 +508,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool #endif bool pressed = false; - bool hovered = ItemHoverable(bb, id); - - // Drag source doesn't report as hovered - if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) - hovered = false; + bool hovered = ItemHoverable(bb, id, item_flags); // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) @@ -527,10 +527,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if (flatten_hovered_children) g.HoveredWindow = backup_hovered_window; - // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. - if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) - hovered = false; - // Mouse handling const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; if (hovered) @@ -579,7 +575,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { if (mouse_button_released != -1) { - const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior + const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior if (!has_repeated_at_least_once) pressed = true; if (!(flags & ImGuiButtonFlags_NoNavFocus)) @@ -590,7 +586,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. - if (g.ActiveId == id && (flags & ImGuiButtonFlags_Repeat)) + if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat)) if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat)) pressed = true; } @@ -608,7 +604,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { bool nav_activated_by_code = (g.NavActivateId == id); bool nav_activated_by_inputs = (g.NavActivatePressedId == id); - if (!nav_activated_by_inputs && (flags & ImGuiButtonFlags_Repeat)) + if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat)) { // Avoid pressing multiple keys from triggering excessive amount of repeat events const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); @@ -655,7 +651,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { // Report as pressed when releasing the mouse (this is the most common path) bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; - bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps + bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id); if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned) pressed = true; @@ -702,9 +698,6 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (!ItemAdd(bb, id)) return false; - if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -781,9 +774,6 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu if (!ItemAdd(bb, id)) return false; - if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -887,9 +877,9 @@ ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) IM_ASSERT(scrollbar_size > 0.0f); if (axis == ImGuiAxis_X) - return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x, outer_rect.Max.y); + return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); else - return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x, inner_rect.Max.y); + return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); } void ImGui::Scrollbar(ImGuiAxis axis) @@ -1174,10 +1164,8 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) if (!all_on && any_on) { ImGuiContext& g = *GImGui; - ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_MixedValue; + g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); - g.CurrentItemFlags = backup_item_flags; } else { @@ -1559,14 +1547,20 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav)) return false; + // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is + // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item. + // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item. + ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + button_flags |= ImGuiButtonFlags_AllowOverlap; +#endif + bool hovered, held; ImRect bb_interact = bb; bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); - ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); + ButtonBehavior(bb_interact, id, &hovered, &held, button_flags); if (hovered) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb - if (g.ActiveId != id) - SetItemAllowOverlap(); if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); @@ -1835,7 +1829,7 @@ bool ImGui::BeginComboPreview() if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)) return false; IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? - if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional) + if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional) return false; // FIXME: This could be contained in a PushWorkRect() api @@ -1934,7 +1928,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi const char* item_text; if (!items_getter(data, i, &item_text)) item_text = "*Unknown item*"; - if (Selectable(item_text, item_selected)) + if (Selectable(item_text, item_selected) && *current_item != i) { value_changed = true; *current_item = i; @@ -2421,7 +2415,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { @@ -3014,7 +3008,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { @@ -3182,7 +3176,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { @@ -3849,6 +3843,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const Im #define STB_TEXTEDIT_K_SHIFT 0x400000 #define STB_TEXTEDIT_IMPLEMENTATION +#define STB_TEXTEDIT_memmove memmove #include "imstb_textedit.h" // stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling @@ -3909,6 +3904,10 @@ void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) { + // Accept null ranges + if (new_text == new_text_end) + return; + const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); if (new_text_len + BufTextLen >= BufSize) @@ -4084,8 +4083,16 @@ void ImGui::InputTextDeactivateHook(ImGuiID id) if (id == 0 || state->ID != id) return; g.InputTextDeactivatedState.ID = state->ID; - g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1); - memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data ? state->TextA.Data : "", state->CurLenA + 1); + if (state->Flags & ImGuiInputTextFlags_ReadOnly) + { + g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat. + } + else + { + IM_ASSERT(state->TextA.Data != 0); + g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1); + memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->CurLenA + 1); + } } // Edit a string of text @@ -4175,7 +4182,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ return false; item_status_flags = g.LastItemData.StatusFlags; } - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); if (hovered) g.MouseCursor = ImGuiMouseCursor_TextInput; @@ -4517,7 +4524,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { if (flags & ImGuiInputTextFlags_EscapeClearsAll) { - if (state->CurLenA > 0) + if (buf[0] != 0) { revert_edit = true; } @@ -4605,8 +4612,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (flags & ImGuiInputTextFlags_EscapeClearsAll) { // Clear input + IM_ASSERT(buf[0] != 0); apply_new_text = ""; apply_new_text_length = 0; + value_changed = true; STB_TEXTEDIT_CHARTYPE empty_string; stb_textedit_replace(state, &state->Stb, &empty_string, 0); } @@ -4635,9 +4644,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL); } - // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. + // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer + // before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. - // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). + // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage + // (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object + // unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). const bool apply_edit_back_to_user_buffer = !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); if (apply_edit_back_to_user_buffer) { @@ -4738,11 +4750,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details) if (g.InputTextDeactivatedState.ID == id) { - if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly) + if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) { apply_new_text = g.InputTextDeactivatedState.TextA.Data; apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; - value_changed |= (strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0); + value_changed = true; //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); } g.InputTextDeactivatedState.ID = 0; @@ -5023,11 +5035,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)... Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); - ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; + g.NextItemData.ItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); - g.CurrentItemFlags = backup_item_flags; // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... // FIXME: This quite messy/tricky, should attempt to get rid of the child window. @@ -5861,7 +5871,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl } // Tooltip - if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) + if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; @@ -5891,7 +5901,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None)) + if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) return; const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) @@ -6226,8 +6236,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l } ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None; - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - button_flags |= ImGuiButtonFlags_AllowItemOverlap; + if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemflags_AllowOverlap)) + button_flags |= ImGuiButtonFlags_AllowOverlap; if (!is_leaf) button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; @@ -6300,8 +6310,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - SetItemAllowOverlap(); // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 @@ -6319,9 +6327,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f); else // Leaf without bullet, left-adjusted text - text_pos.x -= text_offset_x; + text_pos.x -= text_offset_x -padding.x; if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) frame_bb.Max.x -= g.FontSize + style.FramePadding.x; @@ -6341,7 +6349,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f); if (g.LogEnabled) LogSetNextTextDecoration(">", NULL); RenderText(text_pos, label, label_end, false); @@ -6445,7 +6453,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl ImGuiID id = window->GetID(label); flags |= ImGuiTreeNodeFlags_CollapsingHeader; if (p_visible) - flags |= ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_ClipLabelForTrailingButton; + flags |= ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_ClipLabelForTrailingButton; bool is_open = TreeNodeBehavior(id, flags, label); if (p_visible != NULL) { @@ -6474,7 +6482,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl // Tip: pass a non-visible label (e.g. "##hello") then you can use the space to draw other text or image. // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. -// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowItemOverlap are also frequently used flags. +// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags. // FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported. bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) { @@ -6558,7 +6566,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } - if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } + if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemflags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } const bool was_selected = selected; bool hovered, held; @@ -6587,9 +6595,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (pressed) MarkItemEdited(id); - if (flags & ImGuiSelectableFlags_AllowItemOverlap) - SetItemAllowOverlap(); - // In this branch, Selectable() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; @@ -6774,7 +6779,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, 0, &frame_bb)) return -1; - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); // Determine scale from values if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) @@ -8516,7 +8521,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } // Click to Select a tab - ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowItemOverlap); + ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; bool hovered, held; @@ -8529,10 +8534,6 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (held && docked_window && g.ActiveId == id && g.ActiveIdIsJustActivated) g.ActiveIdWindow = docked_window; - // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) - if (g.ActiveId != id) - SetItemAllowOverlap(); - // Drag and drop a single floating window node moves it ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL; const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1); @@ -8653,8 +8654,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // FIXME: We may want disabled tab to still display the tooltip? if (text_clipped && g.HoveredId == id && !held) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - if (IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button) diff --git a/extern/imgui_patched/misc/cpp/imgui_stdlib.cpp b/extern/imgui_patched/misc/cpp/imgui_stdlib.cpp index c9060e88..cf69aa89 100644 --- a/extern/imgui_patched/misc/cpp/imgui_stdlib.cpp +++ b/extern/imgui_patched/misc/cpp/imgui_stdlib.cpp @@ -10,6 +10,12 @@ #include "imgui.h" #include "imgui_stdlib.h" +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + struct InputTextCallback_UserData { std::string* Str; @@ -73,3 +79,7 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* cb_user_data.ChainCallbackUserData = user_data; return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data); } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/extern/imgui_patched/misc/freetype/README.md b/extern/imgui_patched/misc/freetype/README.md index 140ae225..275a5386 100644 --- a/extern/imgui_patched/misc/freetype/README.md +++ b/extern/imgui_patched/misc/freetype/README.md @@ -12,7 +12,7 @@ Build font atlases using FreeType instead of stb_truetype (which is the default ### About Gamma Correct Blending FreeType assumes blending in linear space rather than gamma space. -See FreeType note for [FT_Render_Glyph](https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph). +See FreeType note for [FT_Render_Glyph](https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_render_glyph). For correct results you need to be using sRGB and convert to linear space in the pixel shader output. The default Dear ImGui styles will be impacted by this change (alpha values will need tweaking). @@ -22,7 +22,7 @@ See https://gist.github.com/ocornut/b3a9ecf13502fd818799a452969649ad ### Known issues -- Oversampling settins are ignored but also not so much necessary with the higher quality rendering. +- Oversampling settings are ignored but also not so much necessary with the higher quality rendering. ### Comparison @@ -35,3 +35,10 @@ You can use the `ImGuiFreeTypeBuilderFlags_LoadColor` flag to load certain color ["Using Colorful Glyphs/Emojis"](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md#using-colorful-glyphsemojis) section of FONTS.md. ![colored glyphs](https://user-images.githubusercontent.com/8225057/106171241-9dc4ba80-6191-11eb-8a69-ca1467b206d1.png) + +### Using OpenType SVG fonts (SVGinOT) +- *SVG in Open Type* is a standard by Adobe and Mozilla for color OpenType and Open Font Format fonts. It allows font creators to embed complete SVG files within a font enabling full color and even animations. +- Popular fonts such as [twemoji](https://github.com/13rac1/twemoji-color-font) and fonts made with [scfbuild](https://github.com/13rac1/scfbuild) is SVGinOT +- Requires: [lunasvg](https://github.com/sammycage/lunasvg) v2.3.2 and above + 1. Add `#define IMGUI_ENABLE_FREETYPE_LUNASVG` in your `imconfig.h`. + 2. Get latest lunasvg binaries or build yourself. Under Windows you may use vcpkg with: `vcpkg install lunasvg --triplet=x64-windows`. diff --git a/extern/imgui_patched/misc/freetype/imgui_freetype.cpp b/extern/imgui_patched/misc/freetype/imgui_freetype.cpp index 503430a6..a11e5825 100644 --- a/extern/imgui_patched/misc/freetype/imgui_freetype.cpp +++ b/extern/imgui_patched/misc/freetype/imgui_freetype.cpp @@ -6,13 +6,13 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG' (#6591) // 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly. // 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL. // 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs. // 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format. // 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+). -// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. -// renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas(). +// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas(). // 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails. // 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!) // 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions(). @@ -33,6 +33,8 @@ // FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer). +#include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_freetype.h" #include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*, #include @@ -42,6 +44,15 @@ #include FT_GLYPH_H // #include FT_SYNTHESIS_H // +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG +#include FT_OTSVG_H // +#include FT_BBOX_H // +#include +#if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) +#error IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12 +#endif +#endif + #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) @@ -68,6 +79,14 @@ static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFre static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc; static void* GImGuiFreeTypeAllocatorUserData = nullptr; +// Lunasvg support +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG +static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state); +static void ImGuiLunasvgPortFree(FT_Pointer* state); +static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state); +static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state); +#endif + //------------------------------------------------------------------------- // Code //------------------------------------------------------------------------- @@ -242,7 +261,14 @@ namespace // Need an outline for this to work FT_GlyphSlot slot = Face->glyph; +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG + IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG); +#else +#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) + IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font"); +#endif IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP); +#endif // IMGUI_ENABLE_FREETYPE_LUNASVG // Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting) if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold) @@ -768,6 +794,14 @@ static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas) // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator. FT_Add_Default_Modules(ft_library); +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG + // Install svg hooks for FreeType + // https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks + // https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts + SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot }; + FT_Property_Set(ft_library, "ot-svg", "svg-hooks", &hooks); +#endif // IMGUI_ENABLE_FREETYPE_LUNASVG + bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags); FT_Done_Library(ft_library); @@ -788,6 +822,115 @@ void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* u GImGuiFreeTypeAllocatorUserData = user_data; } +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG +// For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c +// The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT) +struct LunasvgPortState +{ + FT_Error err = FT_Err_Ok; + lunasvg::Matrix matrix; + std::unique_ptr svg = nullptr; +}; + +static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state) +{ + *_state = IM_NEW(LunasvgPortState)(); + return FT_Err_Ok; +} + +static void ImGuiLunasvgPortFree(FT_Pointer* _state) +{ + IM_DELETE(*_state); +} + +static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state) +{ + LunasvgPortState* state = *(LunasvgPortState**)_state; + + // If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error. + if (state->err != FT_Err_Ok) + return state->err; + + // rows is height, pitch (or stride) equals to width * sizeof(int32) + lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch); + state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value + state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated + state->err = FT_Err_Ok; + return state->err; +} + +static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state) +{ + FT_SVG_Document document = (FT_SVG_Document)slot->other; + LunasvgPortState* state = *(LunasvgPortState**)_state; + FT_Size_Metrics& metrics = document->metrics; + + // This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender(). + // If it's the latter, don't do anything because it's // already done in the former. + if (cache) + return state->err; + + state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length); + if (state->svg == nullptr) + { + state->err = FT_Err_Invalid_SVG_Document; + return state->err; + } + + lunasvg::Box box = state->svg->box(); + double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h); + double xx = (double)document->transform.xx / (1 << 16); + double xy = -(double)document->transform.xy / (1 << 16); + double yx = -(double)document->transform.yx / (1 << 16); + double yy = (double)document->transform.yy / (1 << 16); + double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem; + double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem; + + // Scale and transform, we don't translate the svg yet + state->matrix.identity(); + state->matrix.scale(scale, scale); + state->matrix.transform(xx, xy, yx, yy, x0, y0); + state->svg->setMatrix(state->matrix); + + // Pre-translate the matrix for the rendering step + state->matrix.translate(-box.x, -box.y); + + // Get the box again after the transformation + box = state->svg->box(); + + // Calculate the bitmap size + slot->bitmap_left = FT_Int(box.x); + slot->bitmap_top = FT_Int(-box.y); + slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h)); + slot->bitmap.width = (unsigned int)(ImCeil((float)box.w)); + slot->bitmap.pitch = slot->bitmap.width * 4; + slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + + // Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box. + double metrics_width = box.w; + double metrics_height = box.h; + double horiBearingX = box.x; + double horiBearingY = -box.y; + double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0; + double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0; + slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0)); // Using IM_ROUND() assume width and height are positive + slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0)); + slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64); + slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64); + slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64); + slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64); + + if (slot->metrics.vertAdvance == 0) + slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0); + + state->err = FT_Err_Ok; + return state->err; +} + +#endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG + +//----------------------------------------------------------------------------- + #ifdef __GNUC__ #pragma GCC diagnostic pop #endif @@ -795,3 +938,5 @@ void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* u #ifdef _MSC_VER #pragma warning (pop) #endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/imgui_patched/misc/freetype/imgui_freetype.h b/extern/imgui_patched/misc/freetype/imgui_freetype.h index 80a1f95e..cc58ba6a 100644 --- a/extern/imgui_patched/misc/freetype/imgui_freetype.h +++ b/extern/imgui_patched/misc/freetype/imgui_freetype.h @@ -2,8 +2,8 @@ // (headers) #pragma once - #include "imgui.h" // IMGUI_API +#ifndef IMGUI_DISABLE // Forward declarations struct ImFontAtlas; @@ -48,3 +48,5 @@ namespace ImGuiFreeType static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); } #endif } + +#endif // #ifndef IMGUI_DISABLE diff --git a/extern/portaudio b/extern/portaudio new file mode 160000 index 00000000..6ee9836a --- /dev/null +++ b/extern/portaudio @@ -0,0 +1 @@ +Subproject commit 6ee9836a08d201c118b4715d4d70242816584000 diff --git a/scripts/release-win32.sh b/scripts/release-win32.sh index 206e4e6c..d189ca6d 100755 --- a/scripts/release-win32.sh +++ b/scripts/release-win32.sh @@ -15,7 +15,7 @@ fi cd win32build # TODO: potential Arch-ism? -i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2 -march=i586" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -march=i586" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=OFF -DWITH_RENDER_DX11=ON -DUSE_BACKWARD=OFF .. || exit 1 +i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2 -march=i586" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -march=i586" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=OFF -DWITH_RENDER_DX11=ON -DUSE_BACKWARD=OFF -DSDL_SSE2=OFF -DSDL_SSE3=OFF -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DENABLE_AVX=OFF -DENABLE_AVX2=OFF .. || exit 1 make -j8 || exit 1 cd .. diff --git a/scripts/release-winxp.sh b/scripts/release-winxp.sh index 59604ea3..9242bfc6 100755 --- a/scripts/release-winxp.sh +++ b/scripts/release-winxp.sh @@ -15,7 +15,7 @@ fi cd xpbuild # TODO: potential Arch-ism? -i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=OFF .. || exit 1 +i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=OFF -DSDL_SSE2=OFF -DSDL_SSE3=OFF -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DENABLE_AVX=OFF -DENABLE_AVX2=OFF .. || exit 1 make -j8 || exit 1 cd .. diff --git a/src/audio/pa.cpp b/src/audio/pa.cpp new file mode 100644 index 00000000..722d29fd --- /dev/null +++ b/src/audio/pa.cpp @@ -0,0 +1,222 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include "../ta-log.h" +#include "pa.h" + +int taPAProcess(const void* in, void* out, unsigned long nframes, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags flags, void* inst) { + TAAudioPA* instance=(TAAudioPA*)inst; + return instance->onProcess(in,out,nframes,timeInfo,flags); +} + +int TAAudioPA::onProcess(const void* in, void* out, unsigned long nframes, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags flags) { + for (int i=0; idesc.bufsize) { + delete[] inBufs[i]; + inBufs[i]=new float[nframes]; + } + } + for (int i=0; idesc.bufsize) { + delete[] outBufs[i]; + outBufs[i]=new float[nframes]; + } + } + if (nframes!=desc.bufsize) { + desc.bufsize=nframes; + } + + if (audioProcCallback!=NULL) { + if (midiIn!=NULL) midiIn->gather(); + audioProcCallback(audioProcCallbackUser,inBufs,outBufs,desc.inChans,desc.outChans,desc.bufsize); + } + float* fbuf=(float*)out; + for (size_t j=0; j TAAudioPA::listAudioDevices() { + std::vector ret; + if (!audioSysStarted) { + PaError status=Pa_Initialize(); + if (status!=paNoError) { + logE("could not initialize PortAudio to list audio devices"); + return ret; + } else { + audioSysStarted=true; + } + } + + int count=Pa_GetDeviceCount(); + if (count<0) return ret; + + for (int i=0; imaxOutputChannels<1) continue; + + if (devInfo->name!=NULL) { + ret.push_back(String(devInfo->name)); + } + } + + return ret; +} + +bool TAAudioPA::init(TAAudioDesc& request, TAAudioDesc& response) { + if (initialized) { + logE("audio already initialized"); + return false; + } + PaError status; + + if (!audioSysStarted) { + status=Pa_Initialize(); + if (status!=paNoError) { + logE("could not initialize PortAudio"); + return false; + } else { + audioSysStarted=true; + } + } + + desc=request; + desc.outFormat=TA_AUDIO_FORMAT_F32; + + const PaDeviceInfo* devInfo=NULL; + int outDeviceID=0; + + if (desc.deviceName.empty()) { + outDeviceID=Pa_GetDefaultOutputDevice(); + devInfo=Pa_GetDeviceInfo(outDeviceID); + } else { + int count=Pa_GetDeviceCount(); + bool found=false; + if (count<0) { + logE("audio device not found"); + return false; + } + + for (int i=0; imaxOutputChannels<1) continue; + + if (devInfo->name!=NULL) { + if (strcmp(devInfo->name,desc.deviceName.c_str())==0) { + outDeviceID=i; + found=true; + break; + } + } + } + if (!found) { + logE("audio device not found"); + return false; + } + } + + // check output channels and sample rate + if (devInfo!=NULL) { + if (desc.outChans>devInfo->maxOutputChannels) desc.outChans=devInfo->maxOutputChannels; + } + + PaStreamParameters outParams; + outParams.device=outDeviceID; + outParams.channelCount=desc.outChans; + outParams.sampleFormat=paFloat32; + outParams.suggestedLatency=(double)(desc.bufsize*desc.fragments)/desc.rate; + outParams.hostApiSpecificStreamInfo=NULL; + + logV("opening audio device..."); + status=Pa_OpenStream( + &ac, + NULL, + &outParams, + desc.rate, + 0, + paClipOff|paDitherOff, + taPAProcess, + this + ); + if (status!=paNoError) { + logE("could not open audio device: %s",Pa_GetErrorText(status)); + return false; + } + + desc.deviceName=devInfo->name; + desc.inChans=0; + + if (desc.outChans>0) { + outBufs=new float*[desc.outChans]; + for (int i=0; i + +class TAAudioPA: public TAAudio { + PaStream* ac; + bool audioSysStarted; + + public: + int onProcess(const void* in, void* out, unsigned long nframes, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags flags); + + void* getContext(); + bool quit(); + bool setRun(bool run); + std::vector listAudioDevices(); + bool init(TAAudioDesc& request, TAAudioDesc& response); + TAAudioPA(): + ac(NULL), + audioSysStarted(false) {} +}; diff --git a/src/engine/bsr.h b/src/engine/bsr.h new file mode 100644 index 00000000..ac3da44d --- /dev/null +++ b/src/engine/bsr.h @@ -0,0 +1,61 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if defined( _MSC_VER ) + +#include + +static inline int bsr(unsigned short v) { + unsigned long idx; + if (_BitScanReverse(&idx,(unsigned long)v)) { + return idx; + } + else { + return -1; + } +} + +#elif defined( __GNUC__ ) + +static inline int bsr(unsigned short v) +{ + if (v) { + return 32 - __builtin_clz(v); + } + else{ + return -1; + } +} + +#else + +static inline int bsr(unsigned short v) +{ + unsigned short mask = 0x8000; + for (int i = 15; i >= 0; --i) { + if (v&mask) + return (int)i; + mask>>=1; + } + + return -1; +} + +#endif + diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 7428de45..f60bdf6a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -32,6 +32,9 @@ #ifdef HAVE_JACK #include "../audio/jack.h" #endif +#ifdef HAVE_PA +#include "../audio/pa.h" +#endif #include #include #include @@ -3279,6 +3282,8 @@ bool DivEngine::initAudioBackend() { if (audioEngine==DIV_AUDIO_NULL) { if (getConfString("audioEngine","SDL")=="JACK") { audioEngine=DIV_AUDIO_JACK; + } else if (getConfString("audioEngine","SDL")=="PortAudio") { + audioEngine=DIV_AUDIO_PORTAUDIO; } else { audioEngine=DIV_AUDIO_SDL; } @@ -3322,6 +3327,21 @@ bool DivEngine::initAudioBackend() { #endif #else output=new TAAudioJACK; +#endif + break; + case DIV_AUDIO_PORTAUDIO: +#ifndef HAVE_PA + logE("Furnace was not compiled with PortAudio!"); + setConf("audioEngine","SDL"); + saveConf(); +#ifdef HAVE_SDL2 + output=new TAAudioSDL; +#else + logE("Furnace was not compiled with SDL support either!"); + output=new TAAudio; +#endif +#else + output=new TAAudioPA; #endif break; case DIV_AUDIO_SDL: diff --git a/src/engine/engine.h b/src/engine/engine.h index 8df2652d..0c755f8f 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -56,8 +56,8 @@ #define DIV_UNSTABLE -#define DIV_VERSION "dev169" -#define DIV_ENGINE_VERSION 169 +#define DIV_VERSION "dev170" +#define DIV_ENGINE_VERSION 170 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -73,6 +73,7 @@ enum DivStatusView { enum DivAudioEngines { DIV_AUDIO_JACK=0, DIV_AUDIO_SDL=1, + DIV_AUDIO_PORTAUDIO=2, DIV_AUDIO_NULL=126, DIV_AUDIO_DUMMY=127 @@ -283,6 +284,7 @@ struct DivSysDef { DivInstrumentType chanInsType[DIV_MAX_CHANS][2]; const EffectHandlerMap effectHandlers; const EffectHandlerMap postEffectHandlers; + const EffectHandlerMap preEffectHandlers; DivSysDef( const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans, bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, unsigned int formatMask, const char* desc, @@ -292,7 +294,8 @@ struct DivSysDef { std::initializer_list chInsType1, std::initializer_list chInsType2={}, const EffectHandlerMap fxHandlers_={}, - const EffectHandlerMap postFxHandlers_={}): + const EffectHandlerMap postFxHandlers_={}, + const EffectHandlerMap preFxHandlers_={}): name(sysName), nameJ(sysNameJ), description(desc), @@ -305,7 +308,8 @@ struct DivSysDef { vgmVersion(vgmVer), sampleFormatMask(formatMask), effectHandlers(fxHandlers_), - postEffectHandlers(postFxHandlers_) { + postEffectHandlers(postFxHandlers_), + preEffectHandlers(preFxHandlers_) { memset(chanNames,0,DIV_MAX_CHANS*sizeof(void*)); memset(chanShortNames,0,DIV_MAX_CHANS*sizeof(void*)); memset(chanTypes,0,DIV_MAX_CHANS*sizeof(int)); @@ -484,6 +488,7 @@ class DivEngine { // MIDI stuff std::function midiCallback=[](const TAMidiMessage&) -> int {return -2;}; + void processRowPre(int i); void processRow(int i, bool afterDelay); void nextOrder(); void nextRow(); @@ -492,6 +497,7 @@ class DivEngine { bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); bool perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal); + bool perSystemPreEffect(int ch, unsigned char effect, unsigned char effectVal); void recalcChans(); void reset(); void playSub(bool preserveDrift, int goalRow=0); diff --git a/src/engine/fileOpsSample.cpp b/src/engine/fileOpsSample.cpp index 4d4b3a0e..ac9441ad 100644 --- a/src/engine/fileOpsSample.cpp +++ b/src/engine/fileOpsSample.cpp @@ -390,11 +390,13 @@ DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth, case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: case DIV_SAMPLE_DEPTH_ADPCM_A: case DIV_SAMPLE_DEPTH_ADPCM_B: + case DIV_SAMPLE_DEPTH_ADPCM_K: case DIV_SAMPLE_DEPTH_VOX: samples=lenDivided*2; break; case DIV_SAMPLE_DEPTH_8BIT: case DIV_SAMPLE_DEPTH_MULAW: + case DIV_SAMPLE_DEPTH_C219: samples=lenDivided; break; case DIV_SAMPLE_DEPTH_BRR: @@ -487,6 +489,7 @@ DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth, case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: case DIV_SAMPLE_DEPTH_ADPCM_A: case DIV_SAMPLE_DEPTH_ADPCM_B: + case DIV_SAMPLE_DEPTH_ADPCM_K: case DIV_SAMPLE_DEPTH_VOX: // swap nibbles for (unsigned int i=0; igetCurBufLen(); i++) { diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 95a3d775..3e09dfbe 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -175,7 +175,7 @@ void DivPlatformArcade::tick(bool sysTick) { if (chan[i].std.duty.had) { if (chan[i].std.duty.val>0) { - rWrite(0x0f,0x80|(0x20-chan[i].std.duty.val)); + rWrite(0x0f,0x80|(chan[i].std.duty.val-1)); } else { rWrite(0x0f,0); } @@ -768,9 +768,9 @@ int DivPlatformArcade::dispatch(DivCommand c) { if (c.chan!=7) break; if (c.value) { if (c.value>0x1f) { - rWrite(0x0f,0x80); + rWrite(0x0f,0x80|0x1f); } else { - rWrite(0x0f,0x80|(0x1f-c.value)); + rWrite(0x0f,0x80|(c.value-1)); } } else { rWrite(0x0f,0); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 9a171176..9928372c 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -658,8 +658,6 @@ int DivPlatformAY8910::dispatch(DivCommand c) { return 15; break; case DIV_CMD_PRE_PORTA: - // TODO: FIX wtr_envelope.dmf - // the brokenPortaArp update broke it if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY)); } diff --git a/src/engine/platform/c140.cpp b/src/engine/platform/c140.cpp index 1284a78b..ed942667 100644 --- a/src/engine/platform/c140.cpp +++ b/src/engine/platform/c140.cpp @@ -23,7 +23,7 @@ #include #include -#define CHIP_FREQBASE 12582912 +#define CHIP_FREQBASE (is219?74448896:12582912) #define rWrite(a,v) {if(!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if(dumpWrites) addWrite(a,v); }} @@ -150,6 +150,15 @@ void DivPlatformC140::tick(bool sysTick) { } chan[i].freqChanged=true; } + if (is219) { + if (chan[i].std.duty.had) { + chan[i].noise=chan[i].std.duty.val&1; + chan[i].invert=chan[i].std.duty.val&2; + chan[i].surround=chan[i].std.duty.val&4; + chan[i].freqChanged=true; + chan[i].writeCtrl=true; + } + } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { chan[i].pitch2+=chan[i].std.pitch.val; @@ -193,7 +202,6 @@ void DivPlatformC140::tick(bool sysTick) { chan[i].audPos=0; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - bool writeCtrl=false; DivSample* s=parent->getSample(chan[i].sample); unsigned char ctrl=0; double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0; @@ -201,7 +209,7 @@ void DivPlatformC140::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>65535) chan[i].freq=65535; if (is219) { - ctrl|=(chan[i].active?0x80:0)|((s->isLoopable())?0x10:0)|((s->depth==DIV_SAMPLE_DEPTH_MULAW)?1:0)|(chan[i].invert?0x40:0)|(chan[i].surround?8:0)|(chan[i].noise?4:0); + ctrl|=(chan[i].active?0x80:0)|((s->isLoopable())?0x10:0)|((s->depth==DIV_SAMPLE_DEPTH_C219)?1:0)|(chan[i].invert?0x40:0)|(chan[i].surround?8:0)|(chan[i].noise?4:0); } else { ctrl|=(chan[i].active?0x80:0)|((s->isLoopable())?0x10:0)|((s->depth==DIV_SAMPLE_DEPTH_MULAW)?0x08:0); } @@ -241,7 +249,7 @@ void DivPlatformC140::tick(bool sysTick) { // shut everyone else up for (int j=0; j<4; j++) { int ch=(i&(~3))|j; - if (chan[ch].active && (i&3)!=j) { + if (chan[ch].active && !chan[ch].keyOn && (i&3)!=j) { chan[ch].sample=-1; chan[ch].active=false; chan[ch].keyOff=true; @@ -264,11 +272,11 @@ void DivPlatformC140::tick(bool sysTick) { chan[i].volChangedL=true; chan[i].volChangedR=true; } - writeCtrl=true; + chan[i].writeCtrl=true; chan[i].keyOn=false; } if (chan[i].keyOff) { - writeCtrl=true; + chan[i].writeCtrl=true; chan[i].keyOff=false; } if (chan[i].freqChanged) { @@ -276,8 +284,9 @@ void DivPlatformC140::tick(bool sysTick) { rWrite(0x03+(i<<4),chan[i].freq&0xff); chan[i].freqChanged=false; } - if (writeCtrl) { + if (chan[i].writeCtrl) { rWrite(0x05+(i<<4),ctrl); + chan[i].writeCtrl=false; } } } @@ -342,6 +351,17 @@ int DivPlatformC140::dispatch(DivCommand c) { } return chan[c.chan].outVol; break; + case DIV_CMD_STD_NOISE_MODE: + if (!is219) break; + chan[c.chan].noise=c.value; + chan[c.chan].writeCtrl=true; + break; + case DIV_CMD_SNES_INVERT: + if (!is219) break; + chan[c.chan].invert=c.value&15; + chan[c.chan].surround=c.value>>4; + chan[c.chan].writeCtrl=true; + break; case DIV_CMD_PANNING: chan[c.chan].chPanL=c.value; chan[c.chan].chPanR=c.value2; @@ -427,6 +447,12 @@ void DivPlatformC140::forceIns() { chan[i].volChangedR=true; chan[i].sample=-1; } + if (is219) { + // restore banks + for (int i=0; i<4; i++) { + rWrite(0x1f1+(((3+i)&3)<<1),groupBank[i]); + } + } } void* DivPlatformC140::getChanState(int ch) { @@ -476,8 +502,7 @@ void DivPlatformC140::notifyInsChange(int ins) { } void DivPlatformC140::notifyWaveChange(int wave) { - // TODO when wavetables are added - // TODO they probably won't be added unless the samples reside in RAM + } void DivPlatformC140::notifyInsDeletion(void* ins) { @@ -547,6 +572,7 @@ void DivPlatformC140::renderSamples(int sysID) { if ((memPos&0xfe0000)!=((memPos+length)&0xfe0000)) { memPos=((memPos+0x1ffff)&0xfe0000); } + logV("%d",length); if (memPos>=(getSampleMemCapacity())) { logW("out of C219 memory for sample %d!",i); break; @@ -555,21 +581,20 @@ void DivPlatformC140::renderSamples(int sysID) { length=getSampleMemCapacity()-memPos; logW("out of C219 memory for sample %d!",i); } - if (s->depth==DIV_SAMPLE_DEPTH_MULAW) { + if (s->depth==DIV_SAMPLE_DEPTH_C219) { for (unsigned int i=0; i=s->lengthMuLaw) { - sampleMem[i+memPos]=0; + if (i>=s->lengthC219) { + sampleMem[(memPos+i)^1]=0; } else { - unsigned char x=s->dataMuLaw[i]^0xff; - sampleMem[i+memPos]=x; + sampleMem[(memPos+i)^1]=s->dataC219[i]; } } } else { for (unsigned int i=0; i=s->length8) { - sampleMem[memPos+i]=0; + sampleMem[(memPos+i)^1]=0; } else { - sampleMem[memPos+i]=s->data8[i]; + sampleMem[(memPos+i)^1]=s->data8[i]; } } } @@ -619,10 +644,26 @@ void DivPlatformC140::set219(bool is_219) { totalChans=is219?16:24; } +int DivPlatformC140::getClockRangeMin() { + if (is219) return 1000000; + return MIN_CUSTOM_CLOCK; +} + +int DivPlatformC140::getClockRangeMax() { + if (is219) return 100000000; + return MAX_CUSTOM_CLOCK; +} + void DivPlatformC140::setFlags(const DivConfig& flags) { - chipClock=32000*256; // 8.192MHz and 12.288MHz input, verified from Assault Schematics - CHECK_CUSTOM_CLOCK; - rate=chipClock/192; + if (is219) { + chipClock=50113000; // 50.113MHz clock input in Namco NA-1/NA-2 PCB + CHECK_CUSTOM_CLOCK; + rate=chipClock/1136; // assumed as ~44100hz + } else { + chipClock=32000*256; // 8.192MHz and 12.288MHz input, verified from Assault Schematics + CHECK_CUSTOM_CLOCK; + rate=chipClock/192; + } for (int i=0; irate=rate; } diff --git a/src/engine/platform/c140.h b/src/engine/platform/c140.h index 01802de8..fb74151c 100644 --- a/src/engine/platform/c140.h +++ b/src/engine/platform/c140.h @@ -28,7 +28,7 @@ class DivPlatformC140: public DivDispatch { struct Channel: public SharedChannel { unsigned int audPos; int sample, wave; - bool setPos, invert, surround, noise, volChangedL, volChangedR; + bool setPos, invert, surround, noise, volChangedL, volChangedR, writeCtrl; int chPanL, chPanR; int chVolL, chVolR; int macroVolMul; @@ -44,6 +44,7 @@ class DivPlatformC140: public DivDispatch { noise(false), volChangedL(false), volChangedR(false), + writeCtrl(false), chPanL(255), chPanR(255), chVolL(255), @@ -105,6 +106,8 @@ class DivPlatformC140: public DivDispatch { size_t getSampleMemUsage(int index = 0); bool isSampleLoaded(int index, int sample); void renderSamples(int chipID); + int getClockRangeMin(); + int getClockRangeMax(); void set219(bool is_219); void setFlags(const DivConfig& flags); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index d5eb38a0..22791763 100644 --- a/src/engine/platform/es5506.cpp +++ b/src/engine/platform/es5506.cpp @@ -1107,8 +1107,6 @@ void DivPlatformES5506::notifyInsChange(int ins) { } void DivPlatformES5506::notifyWaveChange(int wave) { - // TODO when wavetables are added - // TODO they probably won't be added unless the samples reside in RAM } void DivPlatformES5506::notifyInsDeletion(void* ins) { diff --git a/src/engine/platform/ga20.cpp b/src/engine/platform/ga20.cpp index 901927dd..cc796bd6 100644 --- a/src/engine/platform/ga20.cpp +++ b/src/engine/platform/ga20.cpp @@ -388,8 +388,6 @@ void DivPlatformGA20::notifyInsChange(int ins) { } void DivPlatformGA20::notifyWaveChange(int wave) { - // TODO when wavetables are added - // TODO they probably won't be added unless the samples reside in RAM } void DivPlatformGA20::notifyInsDeletion(void* ins) { diff --git a/src/engine/platform/k007232.cpp b/src/engine/platform/k007232.cpp index c1e314f5..b2a09057 100644 --- a/src/engine/platform/k007232.cpp +++ b/src/engine/platform/k007232.cpp @@ -473,8 +473,6 @@ void DivPlatformK007232::notifyInsChange(int ins) { } void DivPlatformK007232::notifyWaveChange(int wave) { - // TODO when wavetables are added - // TODO they probably won't be added unless the samples reside in RAM } void DivPlatformK007232::notifyInsDeletion(void* ins) { diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp index 17ff6db4..3c9d2193 100644 --- a/src/engine/platform/k053260.cpp +++ b/src/engine/platform/k053260.cpp @@ -131,21 +131,20 @@ void DivPlatformK053260::tick(bool sysTick) { chan[i].audPos=0; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - unsigned char keyon=regPool[0x28]|(1<getSample(sample); + unsigned char keyon=regPool[0x28]|(1<isLoopable()?(1<depth==DIV_SAMPLE_DEPTH_ADPCM_K?(0x10<=0 && samplesong.sampleLen) { - DivSample* s=parent->getSample(sample); if (s->centerRate<1) { off=1.0; } else { off=8363.0/s->centerRate; } } - DivSample* s=parent->getSample(sample); chan[i].freq=0x1000-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; @@ -154,17 +153,26 @@ void DivPlatformK053260::tick(bool sysTick) { unsigned int length=0; if (sample>=0 && samplesong.sampleLen) { start=sampleOffK053260[sample]; - length=s->length8; + length=(s->depth==DIV_SAMPLE_DEPTH_ADPCM_K)?s->lengthK:s->length8; if (chan[i].reverse) { start+=length; keyon|=(16<0) { - if (chan[i].reverse) { - start=start-MIN(chan[i].audPos,s->length8); + if (s->depth==DIV_SAMPLE_DEPTH_ADPCM_K) { + chan[i].audPos>>=1; + if (chan[i].reverse) { + start=start-MIN(chan[i].audPos,s->lengthK); + } else { + start=start+MIN(chan[i].audPos,s->lengthK); + } } else { - start=start+MIN(chan[i].audPos,s->length8); + if (chan[i].reverse) { + start=start-MIN(chan[i].audPos,s->length8); + } else { + start=start+MIN(chan[i].audPos,s->length8); + } } length=MAX(1,length-chan[i].audPos); } @@ -181,10 +189,8 @@ void DivPlatformK053260::tick(bool sysTick) { chan[i].outVol=chan[i].vol; chWrite(i,7,chan[i].outVol); } + rWrite(0x2a,loopon); rWrite(0x28,keyon); - if (s->isLoopable()) { - rWrite(0x2a,loopon); - } chan[i].keyOn=false; } if (chan[i].keyOff) { @@ -403,8 +409,6 @@ void DivPlatformK053260::notifyInsChange(int ins) { } void DivPlatformK053260::notifyWaveChange(int wave) { - // TODO when wavetables are added - // TODO they probably won't be added unless the samples reside in RAM } void DivPlatformK053260::notifyInsDeletion(void* ins) { @@ -473,14 +477,28 @@ void DivPlatformK053260::renderSamples(int sysID) { continue; } - int length=MIN(65535,s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)); - int actualLength=MIN((int)(getSampleMemCapacity()-memPos-1),length); - if (actualLength>0) { - sampleOffK053260[i]=memPos-1; - for (int j=0; jdata8[j]; + int length, actualLength; + + if (s->depth==DIV_SAMPLE_DEPTH_ADPCM_K) { + length=MIN(65535,s->getEndPosition(DIV_SAMPLE_DEPTH_ADPCM_K)); + actualLength=MIN((int)(getSampleMemCapacity()-memPos-1),length); + if (actualLength>0) { + sampleOffK053260[i]=memPos-1; + for (int j=0; jdataK[j]; + } + sampleMem[memPos++]=0; // Silence for avoid popping noise + } + } else { + length=MIN(65535,s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)); + actualLength=MIN((int)(getSampleMemCapacity()-memPos-1),length); + if (actualLength>0) { + sampleOffK053260[i]=memPos-1; + for (int j=0; jdata8[j]; + } + sampleMem[memPos++]=0; // Silence for avoid popping noise } - sampleMem[memPos++]=0; // Silence for avoid popping noise } if (actualLength #define rWrite(a,v) {if (!skipRegisterWrites) {mikey->write(a,v); if (dumpWrites) {addWrite(a,v);}}} @@ -36,48 +37,6 @@ #define CHIP_DIVIDER 64 #define CHIP_FREQBASE 16000000 -#if defined( _MSC_VER ) - -#include - -static int bsr(uint16_t v) { - unsigned long idx; - if (_BitScanReverse(&idx,(unsigned long)v)) { - return idx; - } - else { - return -1; - } -} - -#elif defined( __GNUC__ ) - -static int bsr(uint16_t v) -{ - if (v) { - return 32 - __builtin_clz(v); - } - else{ - return -1; - } -} - -#else - -static int bsr(uint16_t v) -{ - uint16_t mask = 0x8000; - for (int i = 15; i >= 0; --i) { - if (v&mask) - return (int)i; - mask>>=1; - } - - return -1; -} - -#endif - static int32_t clamp(int32_t v, int32_t lo, int32_t hi) { return vhi?hi:v); diff --git a/src/engine/platform/msm5232.cpp b/src/engine/platform/msm5232.cpp index e4564134..997ab607 100644 --- a/src/engine/platform/msm5232.cpp +++ b/src/engine/platform/msm5232.cpp @@ -20,9 +20,9 @@ #define _USE_MATH_DEFINES #include "msm5232.h" #include "../engine.h" +#include "../../ta-log.h" #include -//#define rWrite(a,v) pendingWrites[a]=v; #define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } #define NOTE_LINEAR(x) ((x)<<7) @@ -55,13 +55,17 @@ void DivPlatformMSM5232::acquire(short** buf, size_t len) { } for (int i=0; i<8; i++) { - int o=( - ((regPool[12+(i>>2)]&1)?((msm->vo16[i]*partVolume[3+(i&4)])>>8):0)+ - ((regPool[12+(i>>2)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+ - ((regPool[12+(i>>2)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+ - ((regPool[12+(i>>2)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0) - )<<2; - oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(o,-32768,32767); + if (isMuted[i]) { + oscBuf[i]->data[oscBuf[i]->needle++]=0; + } else { + int o=( + ((regPool[12+(i>>2)]&1)?((msm->vo16[i]*partVolume[3+(i&4)])>>8):0)+ + ((regPool[12+(i>>2)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+ + ((regPool[12+(i>>2)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+ + ((regPool[12+(i>>2)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0) + )<<2; + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(o,-32768,32767); + } } clockDriftLFOPos+=clockDriftLFOSpeed; @@ -125,6 +129,10 @@ void DivPlatformMSM5232::tick(bool sysTick) { for (int i=0; i<2; i++) { if (updateGroup[i]) { rWrite(12+i,groupControl[i]); + // do not retrigger inactive channels + for (int j=i<<2; j<(i+1)<<2; j++) { + if (!chan[j].active) rWrite(j,0); + } updateGroup[i]=false; } if (updateGroupAR[i]) { diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index 9319de5d..c183cd50 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -355,8 +355,6 @@ void DivPlatformRF5C68::notifyInsChange(int ins) { } void DivPlatformRF5C68::notifyWaveChange(int wave) { - // TODO when wavetables are added - // TODO they probably won't be added unless the samples reside in RAM } void DivPlatformRF5C68::notifyInsDeletion(void* ins) { diff --git a/src/engine/platform/sound/c140_c219.c b/src/engine/platform/sound/c140_c219.c index 67aa6dc4..56d96ca1 100644 --- a/src/engine/platform/sound/c140_c219.c +++ b/src/engine/platform/sound/c140_c219.c @@ -171,8 +171,8 @@ void c219_voice_tick(struct c219_t *c219, const unsigned char v, const int cycle else { // fetch 8 bit sample - signed short s1 = c219->sample_mem[((unsigned int)(c219->bank[(v >> 2) & 3]) << 17) | voice->addr]; - signed short s2 = c219->sample_mem[((unsigned int)(c219->bank[(v >> 2) & 3]) << 17) | ((voice->addr + 1) & 0x1ffff)]; + signed short s1 = c219->sample_mem[((unsigned int)(c219->bank[(v >> 2) & 3]) << 17) | (voice->addr^1)]; + signed short s2 = c219->sample_mem[((unsigned int)(c219->bank[(v >> 2) & 3]) << 17) | (((voice->addr + 1) & 0x1ffff)^1)]; if (voice->compressed) { s1 = c219->mulaw[s1&0xff]; diff --git a/src/engine/platform/sound/ymfm/ymfm_opm.cpp b/src/engine/platform/sound/ymfm/ymfm_opm.cpp index 958dae57..9876c2bc 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opm.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opm.cpp @@ -176,7 +176,7 @@ int32_t opm_registers::clock_noise_and_lfo() { // base noise frequency is measured at 2x 1/2 FM frequency; this // means each tick counts as two steps against the noise counter - uint32_t freq = noise_frequency(); + uint32_t freq = noise_frequency() ^ 0x1f; for (int rep = 0; rep < 2; rep++) { // evidence seems to suggest the LFSR is clocked continually and just diff --git a/src/engine/platform/sound/ymfm/ymfm_opz.cpp b/src/engine/platform/sound/ymfm/ymfm_opz.cpp index b20bea3c..0bfce6bc 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opz.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opz.cpp @@ -327,7 +327,7 @@ int32_t opz_registers::clock_noise_and_lfo() { // base noise frequency is measured at 2x 1/2 FM frequency; this // means each tick counts as two steps against the noise counter - uint32_t freq = noise_frequency(); + uint32_t freq = noise_frequency() ^ 0x1f; for (int rep = 0; rep < 2; rep++) { // evidence seems to suggest the LFSR is clocked continually and just diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 52f71aed..540365cd 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -134,7 +134,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { if (chan[i].std.duty.had) { if (chan[i].std.duty.val>0) { - rWrite(0x0f,0x80|(0x20-chan[i].std.duty.val)); + rWrite(0x0f,0x80|(chan[i].std.duty.val-1)); } else { rWrite(0x0f,0); } @@ -865,9 +865,9 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { if (c.chan!=7) break; if (c.value) { if (c.value>0x1f) { - rWrite(0x0f,0x80); + rWrite(0x0f,0x80|0x1f); } else { - rWrite(0x0f,0x80|(0x1f-c.value)); + rWrite(0x0f,0x80|(c.value-1)); } } else { rWrite(0x0f,0); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index d1422439..7015fa51 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -947,6 +947,7 @@ void DivPlatformX1_010::setFlags(const DivConfig& flags) { CHECK_CUSTOM_CLOCK; rate=chipClock/512; stereo=flags.getBool("stereo",false); + isBanked=flags.getBool("isBanked",false); for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; } @@ -979,7 +980,7 @@ bool DivPlatformX1_010::isSampleLoaded(int index, int sample) { } void DivPlatformX1_010::renderSamples(int sysID) { - memset(sampleMem,0,getSampleMemCapacity()); + memset(sampleMem,0,16777216); memset(sampleOffX1,0,256*sizeof(unsigned int)); memset(sampleLoaded,0,256*sizeof(bool)); @@ -1018,10 +1019,6 @@ void DivPlatformX1_010::renderSamples(int sysID) { sampleMemLen=memPos+256; } -void DivPlatformX1_010::setBanked(bool banked) { - isBanked=banked; -} - int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { parent=p; dumpWrites=false; @@ -1032,7 +1029,7 @@ int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, const DivCo oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); - sampleMem=new unsigned char[getSampleMemCapacity()]; + sampleMem=new unsigned char[16777216]; sampleMemLen=0; x1_010.reset(); reset(); diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index cc698b65..632d9542 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -153,7 +153,6 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf { bool isSampleLoaded(int index, int sample); void renderSamples(int chipID); const char** getRegisterSheet(); - void setBanked(bool banked); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); DivPlatformX1_010(): diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 7483a972..2306f057 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -407,6 +407,38 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char return dispatchCmd(DivCommand(handler.dispatchCmd,ch,val,val2)); } +bool DivEngine::perSystemPreEffect(int ch, unsigned char effect, unsigned char effectVal) { + DivSysDef* sysDef=sysDefs[sysOfChan[ch]]; + if (sysDef==NULL) return false; + auto iter=sysDef->preEffectHandlers.find(effect); + if (iter==sysDef->preEffectHandlers.end()) return false; + EffectHandler handler=iter->second; + int val=0; + int val2=0; + try { + val=handler.val?handler.val(effect,effectVal):effectVal; + val2=handler.val2?handler.val2(effect,effectVal):0; + } catch (DivDoNotHandleEffect& e) { + return false; + } + // wouldn't this cause problems if it were to return 0? + return dispatchCmd(DivCommand(handler.dispatchCmd,ch,val,val2)); +} + +void DivEngine::processRowPre(int i) { + int whatOrder=curOrder; + int whatRow=curRow; + DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][whatOrder],false); + for (int j=0; jdata[whatRow][4+(j<<1)]; + short effectVal=pat->data[whatRow][5+(j<<1)]; + + if (effectVal==-1) effectVal=0; + effectVal&=255; + perSystemPreEffect(i,effect,effectVal); + } +} + void DivEngine::processRow(int i, bool afterDelay) { int whatOrder=afterDelay?chan[i].delayOrder:curOrder; int whatRow=afterDelay?chan[i].delayRow:curRow; @@ -1135,6 +1167,11 @@ void DivEngine::nextRow() { prevRow=curRow; } + for (int i=0; i>3))&8191]&(1<<(curRow&7))) { + if (!endOfSong && walked[((curOrder<<5)+(curRow>>3))&8191]&(1<<(curRow&7)) && !shallStopSched) { logV("loop reached"); endOfSong=true; memset(walked,0,8192); diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index c2ff85a6..4332503c 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -26,6 +26,7 @@ #include "sfWrapper.h" #endif #include "filter.h" +#include "bsr.h" extern "C" { #include "../../extern/adpcm/bs_codec.h" @@ -260,6 +261,9 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) { case DIV_SAMPLE_DEPTH_ADPCM_B: off=(offset+1)/2; break; + case DIV_SAMPLE_DEPTH_ADPCM_K: + off=(offset+1)/2; + break; case DIV_SAMPLE_DEPTH_8BIT: off=offset; break; @@ -272,6 +276,9 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) { case DIV_SAMPLE_DEPTH_MULAW: off=offset; break; + case DIV_SAMPLE_DEPTH_C219: + off=offset; + break; case DIV_SAMPLE_DEPTH_16BIT: off=offset*2; break; @@ -307,6 +314,10 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) { off=(offset+1)/2; len=(length+1)/2; break; + case DIV_SAMPLE_DEPTH_ADPCM_K: + off=(offset+1)/2; + len=(length+1)/2; + break; case DIV_SAMPLE_DEPTH_8BIT: off=offset; len=length; @@ -323,6 +334,10 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) { off=offset; len=length; break; + case DIV_SAMPLE_DEPTH_C219: + off=offset; + len=length; + break; case DIV_SAMPLE_DEPTH_16BIT: off=offset*2; len=length*2; @@ -363,6 +378,9 @@ int DivSample::getEndPosition(DivSampleDepth depth) { case DIV_SAMPLE_DEPTH_ADPCM_B: off=lengthB; break; + case DIV_SAMPLE_DEPTH_ADPCM_K: + off=lengthK; + break; case DIV_SAMPLE_DEPTH_8BIT: off=length8; break; @@ -375,6 +393,9 @@ int DivSample::getEndPosition(DivSampleDepth depth) { case DIV_SAMPLE_DEPTH_MULAW: off=lengthMuLaw; break; + case DIV_SAMPLE_DEPTH_C219: + off=lengthC219; + break; case DIV_SAMPLE_DEPTH_16BIT: off=length16; break; @@ -529,6 +550,12 @@ bool DivSample::initInternal(DivSampleDepth d, int count) { dataB=new unsigned char[(lengthB+255)&(~0xff)]; memset(dataB,0,(lengthB+255)&(~0xff)); break; + case DIV_SAMPLE_DEPTH_ADPCM_K: // K05 ADPCM + if (dataK!=NULL) delete[] dataK; + lengthK=(count+1)/2; + dataK=new unsigned char[(lengthK+255)&(~0xff)]; + memset(dataK,0,(lengthK+255)&(~0xff)); + break; case DIV_SAMPLE_DEPTH_8BIT: // 8-bit if (data8!=NULL) delete[] data8; length8=count; @@ -554,6 +581,12 @@ bool DivSample::initInternal(DivSampleDepth d, int count) { dataMuLaw=new unsigned char[(count+4095)&(~0xfff)]; memset(dataMuLaw,0,(count+4095)&(~0xfff)); break; + case DIV_SAMPLE_DEPTH_C219: // 8-bit C219 "μ-law" + if (dataC219!=NULL) delete[] dataC219; + lengthC219=count; + dataC219=new unsigned char[(count+4095)&(~0xfff)]; + memset(dataC219,0,(count+4095)&(~0xfff)); + break; case DIV_SAMPLE_DEPTH_16BIT: // 16-bit if (data16!=NULL) delete[] data16; length16=count*2; @@ -783,6 +816,9 @@ void DivSample::convert(DivSampleDepth newDepth) { case DIV_SAMPLE_DEPTH_ADPCM_B: // ADPCM-B setSampleCount((samples+1)&(~1)); break; + case DIV_SAMPLE_DEPTH_ADPCM_K: // K05 ADPCM + setSampleCount((samples+1)&(~1)); + break; case DIV_SAMPLE_DEPTH_BRR: // BRR setSampleCount(16*(lengthBRR/9)); break; @@ -1133,6 +1169,37 @@ union IntFloat { float f; }; +const short c219Table[256]={ + 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 480, + 512, 576, 640, 704, 768, 832, 896, 960, 1024, 1152, 1280, 1408, 1536, 1664, 1792, 1920, + 2048, 2176, 2304, 2432, 2560, 2688, 2816, 2944, 3072, 3200, 3328, 3456, 3584, 3712, 3840, 3968, + 4096, 4352, 4608, 4864, 5120, 5376, 5632, 5888, 6144, 6400, 6656, 6912, 7168, 7424, 7680, 7936, + 8192, 8448, 8704, 8960, 9216, 9472, 9728, 9984, 10240, 10496, 10752, 11008, 11264, 11520, 11776, 12032, + 12288, 12544, 12800, 13056, 13312, 13568, 13824, 14080, 14336, 14592, 14848, 15104, 15360, 15616, 15872, 16128, + 16384, 16640, 16896, 17152, 17408, 17920, 18432, 18944, 19456, 19968, 20480, 20992, 21504, 22016, 22528, 23040, + 23552, 24064, 24576, 25088, 25600, 26112, 26624, 27136, 27648, 28160, 28672, 29184, 29696, 30208, 30720, 31232, + -32, -64, -96, -128, -160, -192, -224, -256, -288, -320, -352, -384, -416, -448, -480, -512, + -544, -608, -672, -736, -800, -864, -928, -992, -1056, -1184, -1312, -1440, -1568, -1696, -1824, -1952, + -2080, -2208, -2336, -2464, -2592, -2720, -2848, -2976, -3104, -3232, -3360, -3488, -3616, -3744, -3872, -4000, + -4128, -4384, -4640, -4896, -5152, -5408, -5664, -5920, -6176, -6432, -6688, -6944, -7200, -7456, -7712, -7968, + -8224, -8480, -8736, -8992, -9248, -9504, -9760, -10016, -10272, -10528, -10784, -11040, -11296, -11552, -11808, -12064, + -12320, -12576, -12832, -13088, -13344, -13600, -13856, -14112, -14368, -14624, -14880, -15136, -15392, -15648, -15904, -16160, + -16416, -16672, -16928, -17184, -17440, -17952, -18464, -18976, -19488, -20000, -20512, -21024, -21536, -22048, -22560, -23072, + -23584, -24096, -24608, -25120, -25632, -26144, -26656, -27168, -27680, -28192, -28704, -29216, -29728, -30240, -30752, -31264 +}; + +unsigned char c219HighBitPos[16]={ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 24, 24, 48, 48, 48, 48 +}; + +unsigned char c219ShiftToVal[16]={ + 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 8, 8, 8, 8 +}; + +signed char adpcmKTable[16]={ + 0, 1, 2, 4, 8, 16, 32, 64, -128, -64, -32, -16, -8, -4, -2, -1 +}; + void DivSample::render(unsigned int formatMask) { // step 1: convert to 16-bit if needed if (depth!=DIV_SAMPLE_DEPTH_16BIT) { @@ -1165,6 +1232,20 @@ void DivSample::render(unsigned int formatMask) { case DIV_SAMPLE_DEPTH_ADPCM_B: // ADPCM-B ymb_decode(dataB,data16,samples); break; + case DIV_SAMPLE_DEPTH_ADPCM_K: { // K05 ADPCM + signed char s=0; + for (unsigned int i=0; i>1]; + if (i&1) { // TODO: is this right? + nibble>>=4; + } else { + nibble&=15; + } + s+=adpcmKTable[nibble]; + data16[i]=s<<8; + } + break; + } case DIV_SAMPLE_DEPTH_8BIT: // 8-bit PCM for (unsigned int i=0; i>8; + short delta=target-accum; + unsigned char next=0; + + if (delta!=0) { + int b=bsr((delta>=0)?delta:-delta); + if (delta>=0) { + if (b>7) b=7; + next=b&15; + + // test previous + if (next>1) { + const signed char t1=accum+adpcmKTable[next]; + const signed char t2=accum+adpcmKTable[next-1]; + const signed char d1=((t1-target)<0)?(target-t1):(t1-target); + const signed char d2=((t2-target)<0)?(target-t2):(t2-target); + + if (d28) b=8; + next=(16-b)&15; + + // test next + if (next<15) { + const signed char t1=accum+adpcmKTable[next]; + const signed char t2=accum+adpcmKTable[next+1]; + const signed char d1=((t1-target)<0)?(target-t1):(t1-target); + const signed char d2=((t2-target)<0)?(target-t2):(t2-target); + + if (d2=128 || accum+adpcmKTable[next]<-128) { + if (delta>=0) { + next--; + } else { + next++; + if (next>15) next=15; + } + }*/ + } + + out>>=4; + out|=next<<4; + accum+=adpcmKTable[next]; + + if (i&1) { + dataK[i>>1]=out; + out=0; + } + } + } if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_8BIT)) { // 8-bit PCM if (!initInternal(DIV_SAMPLE_DEPTH_8BIT,samples)) return; if (dither) { @@ -1266,13 +1412,36 @@ void DivSample::render(unsigned int formatMask) { if (!initInternal(DIV_SAMPLE_DEPTH_MULAW,samples)) return; for (unsigned int i=0; i32639.0f) s.f=32639.0f; s.f/=128.0f; s.f+=1.0f; s.i-=0x3f800000; dataMuLaw[i]=(((data16[i]<0)?0x80:0)|(s.i&0x03f80000)>>19)^0xff; } } + if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_C219)) { // C219 + if (!initInternal(DIV_SAMPLE_DEPTH_C219,samples)) return; + for (unsigned int i=0; i17152) { // 100+ + x=((s-17152)>>9)+100; + } else { + int b=bsr(s)-1; + x=((s-(c219Table[c219HighBitPos[b]]))>>c219ShiftToVal[b])+c219HighBitPos[b]; + } + if (x>127) x=127; + dataC219[i]=x|(negate?0x80:0); + } + } } void* DivSample::getCurBuf() { @@ -1289,6 +1458,8 @@ void* DivSample::getCurBuf() { return dataA; case DIV_SAMPLE_DEPTH_ADPCM_B: return dataB; + case DIV_SAMPLE_DEPTH_ADPCM_K: + return dataK; case DIV_SAMPLE_DEPTH_8BIT: return data8; case DIV_SAMPLE_DEPTH_BRR: @@ -1297,6 +1468,8 @@ void* DivSample::getCurBuf() { return dataVOX; case DIV_SAMPLE_DEPTH_MULAW: return dataMuLaw; + case DIV_SAMPLE_DEPTH_C219: + return dataC219; case DIV_SAMPLE_DEPTH_16BIT: return data16; default: @@ -1319,6 +1492,8 @@ unsigned int DivSample::getCurBufLen() { return lengthA; case DIV_SAMPLE_DEPTH_ADPCM_B: return lengthB; + case DIV_SAMPLE_DEPTH_ADPCM_K: + return lengthK; case DIV_SAMPLE_DEPTH_8BIT: return length8; case DIV_SAMPLE_DEPTH_BRR: @@ -1327,6 +1502,8 @@ unsigned int DivSample::getCurBufLen() { return lengthVOX; case DIV_SAMPLE_DEPTH_MULAW: return lengthMuLaw; + case DIV_SAMPLE_DEPTH_C219: + return lengthC219; case DIV_SAMPLE_DEPTH_16BIT: return length16; default: @@ -1434,7 +1611,9 @@ DivSample::~DivSample() { if (dataQSoundA) delete[] dataQSoundA; if (dataA) delete[] dataA; if (dataB) delete[] dataB; + if (dataK) delete[] dataK; if (dataBRR) delete[] dataBRR; if (dataVOX) delete[] dataVOX; if (dataMuLaw) delete[] dataMuLaw; + if (dataC219) delete[] dataC219; } diff --git a/src/engine/sample.h b/src/engine/sample.h index bbf90d4f..bca88165 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -40,10 +40,12 @@ enum DivSampleDepth: unsigned char { DIV_SAMPLE_DEPTH_QSOUND_ADPCM=4, DIV_SAMPLE_DEPTH_ADPCM_A=5, DIV_SAMPLE_DEPTH_ADPCM_B=6, + DIV_SAMPLE_DEPTH_ADPCM_K=7, DIV_SAMPLE_DEPTH_8BIT=8, DIV_SAMPLE_DEPTH_BRR=9, DIV_SAMPLE_DEPTH_VOX=10, DIV_SAMPLE_DEPTH_MULAW=11, + DIV_SAMPLE_DEPTH_C219=12, DIV_SAMPLE_DEPTH_16BIT=16, DIV_SAMPLE_DEPTH_MAX // boundary for sample depth }; @@ -106,10 +108,12 @@ struct DivSample { // - 4: QSound ADPCM // - 5: ADPCM-A // - 6: ADPCM-B + // - 7: K053260 4-bit simple ADPCM // - 8: 8-bit PCM // - 9: BRR (SNES) // - 10: VOX ADPCM // - 11: 8-bit µ-law PCM + // - 12: C219 "µ-law" PCM // - 16: 16-bit PCM DivSampleDepth depth; bool loop, brrEmphasis, dither; @@ -130,11 +134,13 @@ struct DivSample { unsigned char* dataQSoundA; // 4 unsigned char* dataA; // 5 unsigned char* dataB; // 6 + unsigned char* dataK; // 7 unsigned char* dataBRR; // 9 unsigned char* dataVOX; // 10 unsigned char* dataMuLaw; // 11 + unsigned char* dataC219; // 12 - unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthBRR, lengthVOX, lengthMuLaw; + unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthK, lengthBRR, lengthVOX, lengthMuLaw, lengthC219; unsigned int samples; @@ -338,9 +344,11 @@ struct DivSample { dataQSoundA(NULL), dataA(NULL), dataB(NULL), + dataK(NULL), dataBRR(NULL), dataVOX(NULL), dataMuLaw(NULL), + dataC219(NULL), length8(0), length16(0), length1(0), @@ -349,9 +357,11 @@ struct DivSample { lengthQSoundA(0), lengthA(0), lengthB(0), + lengthK(0), lengthBRR(0), lengthVOX(0), lengthMuLaw(0), + lengthC219(0), samples(0) { for (int i=0; iwriteC(0); } break; + case DIV_SYSTEM_C219: + for (int i=0; i<16; i++) { + w->writeC(0xd4); // mute + w->writeS_BE(baseAddr2S|(i<<4)|0); + w->writeC(0); + w->writeC(0xd4); + w->writeS_BE(baseAddr2S|(i<<4)|1); + w->writeC(0); + w->writeC(0xd4); // keyoff + w->writeS_BE(baseAddr2S|(i<<4)|5); + w->writeC(0); + } + break; default: break; } @@ -1058,6 +1071,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write w->writeC(write.val&0xff); break; case DIV_SYSTEM_C140: + case DIV_SYSTEM_C219: w->writeC(0xd4); w->writeS_BE(baseAddr2S|(write.addr&0x1ff)); w->writeC(write.val&0xff); @@ -1151,7 +1165,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p int hasOKIM6295=0; int hasK051649=0; int hasPCE=0; - int hasNamco=0; + int hasC140=0; + int c140Type=0; int hasK053260=0; int hasPOKEY=0; int hasQSound=0; @@ -1236,6 +1251,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p DivDispatch* writeGA20[2]={NULL,NULL}; DivDispatch* writeK053260[2]={NULL,NULL}; DivDispatch* writeC140[2]={NULL,NULL}; + DivDispatch* writeC219[2]={NULL,NULL}; DivDispatch* writeNES[2]={NULL,NULL}; int writeNESIndex[2]={0,0}; @@ -1785,18 +1801,36 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p } break; case DIV_SYSTEM_C140: - if (!hasNamco) { + if (!hasC140) { // ?!?!?! - hasNamco=disCont[i].dispatch->rate/2; + hasC140=disCont[i].dispatch->rate/2; CHIP_VOL(40,1.0); willExport[i]=true; writeC140[0]=disCont[i].dispatch; - } else if (!(hasNamco&0x40000000)) { + } else if (!(hasC140&0x40000000)) { isSecond[i]=true; CHIP_VOL_SECOND(40,1.0); willExport[i]=true; writeC140[1]=disCont[i].dispatch; - hasNamco|=0x40000000; + hasC140|=0x40000000; + howManyChips++; + } + break; + case DIV_SYSTEM_C219: + if (!hasC140) { + // ?!?!?! + hasC140=disCont[i].dispatch->rate/2; + CHIP_VOL(40,1.0); + willExport[i]=true; + writeC219[0]=disCont[i].dispatch; + c140Type=2; + } else if (!(hasC140&0x40000000)) { + isSecond[i]=true; + CHIP_VOL_SECOND(40,1.0); + willExport[i]=true; + writeC219[1]=disCont[i].dispatch; + hasC140|=0x40000000; + c140Type=2; howManyChips++; } break; @@ -1895,13 +1929,13 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeI(hasOKIM6258); w->writeC(0); // flags w->writeC(0); // K flags - w->writeC(0); // C140 chip type + w->writeC(c140Type); // C140 chip type w->writeC(0); // reserved w->writeI(hasOKIM6295); w->writeI(hasK051649); w->writeI(hasK054539); w->writeI(hasPCE); - w->writeI(hasNamco); + w->writeI(hasC140); w->writeI(hasK053260); w->writeI(hasPOKEY); w->writeI(hasQSound); @@ -2211,6 +2245,19 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeC(mem[i]>>8); } } + if (writeC219[i]!=NULL && writeC219[i]->getSampleMemUsage()>0) { + w->writeC(0x67); + w->writeC(0x66); + w->writeC(0x8d); + unsigned char* mem=(unsigned char*)writeC219[i]->getSampleMem(); + size_t memLen=writeC219[i]->getSampleMemUsage(); + w->writeI((memLen+8)|(i*0x80000000)); + w->writeI(writeC219[i]->getSampleMemCapacity()); + w->writeI(0); + for (size_t i=0; iwriteC(mem[i]); + } + } } // initialize streams diff --git a/src/gui/about.cpp b/src/gui/about.cpp index e1ce1b64..0c983479 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -160,6 +160,7 @@ const char* aboutLine[]={ "libsndfile by Erik de Castro Lopo", "Portable File Dialogs by Sam Hocevar", "Native File Dialog by Frogtoss Games", + "PortAudio", "RtMidi by Gary P. Scavone", "FFTW by Matteo Frigo and Steven G. Johnson", "backward-cpp by Google", diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index c5ccb4f3..4371d6fd 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -478,8 +478,6 @@ void FurnaceGUI::drawChanOsc() { needlePos-=phase; } chanOscPitch[ch]=(float)point/32.0f; - - needlePos-=displaySize; for (unsigned short i=0; iisPlaying(); e->walkSong(loopOrder,loopRow,loopEnd); e->stop(); curNibble=false; orderNibble=false; + if (followPattern && wasPlaying) { + nextScroll=-1.0f; + nextAddScroll=0.0f; + cursor.y=e->getRow(); + if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && !selecting) { + selStart=cursor; + selEnd=cursor; + } + updateScroll(cursor.y); + } } void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) { @@ -3617,6 +3628,8 @@ bool FurnaceGUI::loop() { if (prevScrW!=scrW || prevScrH!=scrH) { logV("size change 2: %dx%d (from %dx%d)",scrW,scrH,prevScrW,prevScrH); } + + ImGui::GetIO().InputScale=(float)canvasW/(float)scrW; } wantCaptureKeyboard=ImGui::GetIO().WantTextInput; @@ -4169,70 +4182,76 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } ImGui::Separator(); - if (ImGui::BeginMenu("add chip...")) { - exitDisabledTimer=1; - DivSystem picked=systemPicker(); - if (picked!=DIV_SYSTEM_NULL) { - if (!e->addSystem(picked)) { - showError("cannot add chip! ("+e->getLastError()+")"); - } else { - MARK_MODIFIED; - } - ImGui::CloseCurrentPopup(); - if (e->song.autoSystem) { - autoDetectSystem(); - } - updateWindowTitle(); + if (!settings.classicChipOptions) { + if (ImGui::MenuItem("manage chips")) { + nextWindow=GUI_WINDOW_SYS_MANAGER; } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("configure chip...")) { - exitDisabledTimer=1; - for (int i=0; isong.systemLen; i++) { - if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true,true); - ImGui::TreePop(); - } - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("change chip...")) { - exitDisabledTimer=1; - ImGui::Checkbox("Preserve channel positions",&preserveChanPos); - for (int i=0; isong.systemLen; i++) { - if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - DivSystem picked=systemPicker(); - if (picked!=DIV_SYSTEM_NULL) { - e->changeSystem(i,picked,preserveChanPos); - MARK_MODIFIED; - if (e->song.autoSystem) { - autoDetectSystem(); - } - updateWindowTitle(); - ImGui::CloseCurrentPopup(); - } - ImGui::EndMenu(); - } - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("remove chip...")) { - exitDisabledTimer=1; - ImGui::Checkbox("Preserve channel positions",&preserveChanPos); - for (int i=0; isong.systemLen; i++) { - if (ImGui::MenuItem(fmt::sprintf("%d. %s##_SYSR%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - if (!e->removeSystem(i,preserveChanPos)) { - showError("cannot remove chip! ("+e->getLastError()+")"); + } else { + if (ImGui::BeginMenu("add chip...")) { + exitDisabledTimer=1; + DivSystem picked=systemPicker(); + if (picked!=DIV_SYSTEM_NULL) { + if (!e->addSystem(picked)) { + showError("cannot add chip! ("+e->getLastError()+")"); } else { MARK_MODIFIED; } + ImGui::CloseCurrentPopup(); if (e->song.autoSystem) { autoDetectSystem(); - updateWindowTitle(); + } + updateWindowTitle(); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("configure chip...")) { + exitDisabledTimer=1; + for (int i=0; isong.systemLen; i++) { + if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { + drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true,true); + ImGui::TreePop(); } } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("change chip...")) { + exitDisabledTimer=1; + ImGui::Checkbox("Preserve channel positions",&preserveChanPos); + for (int i=0; isong.systemLen; i++) { + if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { + DivSystem picked=systemPicker(); + if (picked!=DIV_SYSTEM_NULL) { + e->changeSystem(i,picked,preserveChanPos); + MARK_MODIFIED; + if (e->song.autoSystem) { + autoDetectSystem(); + } + updateWindowTitle(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndMenu(); + } + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("remove chip...")) { + exitDisabledTimer=1; + ImGui::Checkbox("Preserve channel positions",&preserveChanPos); + for (int i=0; isong.systemLen; i++) { + if (ImGui::MenuItem(fmt::sprintf("%d. %s##_SYSR%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { + if (!e->removeSystem(i,preserveChanPos)) { + showError("cannot remove chip! ("+e->getLastError()+")"); + } else { + MARK_MODIFIED; + } + if (e->song.autoSystem) { + autoDetectSystem(); + updateWindowTitle(); + } + } + } + ImGui::EndMenu(); } - ImGui::EndMenu(); } ImGui::BeginDisabled(exitDisabledTimer); ImGui::Separator(); @@ -4346,7 +4365,7 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu(settings.capitalMenuBar?"Help":"help")) { if (ImGui::MenuItem("effect list",BIND_FOR(GUI_ACTION_WINDOW_EFFECT_LIST),effectListOpen)) effectListOpen=!effectListOpen; if (ImGui::MenuItem("debug menu",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) debugOpen=!debugOpen; - if (ImGui::MenuItem("inspector",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) inspectorOpen=!inspectorOpen; + if (ImGui::MenuItem("inspector")) inspectorOpen=!inspectorOpen; if (ImGui::MenuItem("panic",BIND_FOR(GUI_ACTION_PANIC))) e->syncReset(); if (ImGui::MenuItem("about...",BIND_FOR(GUI_ACTION_WINDOW_ABOUT))) { aboutOpen=true; @@ -4506,6 +4525,7 @@ bool FurnaceGUI::loop() { MEASURE(compatFlags,drawCompatFlags()); MEASURE(stats,drawStats()); MEASURE(chanOsc,drawChanOsc()); + MEASURE(regView,drawRegView()); } else { globalWinFlags=0; ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0); @@ -6894,6 +6914,8 @@ FurnaceGUI::FurnaceGUI(): displayEditString(false), mobileEdit(false), killGraphics(false), + audioEngineChanged(false), + settingsChanged(false), vgmExportVersion(0x171), vgmExportTrailingTicks(-1), drawHalt(10), diff --git a/src/gui/gui.h b/src/gui/gui.h index 6b22a2d9..8c7837ca 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1334,6 +1334,7 @@ class FurnaceGUI { bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString; bool mobileEdit; bool killGraphics; + bool audioEngineChanged, settingsChanged; bool willExport[DIV_MAX_CHIPS]; int vgmExportVersion; int vgmExportTrailingTicks; @@ -1569,6 +1570,7 @@ class FurnaceGUI { int capitalMenuBar; int centerPopup; int insIconsStyle; + int classicChipOptions; unsigned int maxUndoSteps; String mainFontPath; String headFontPath; @@ -1742,6 +1744,7 @@ class FurnaceGUI { capitalMenuBar(0), centerPopup(1), insIconsStyle(1), + classicChipOptions(0), maxUndoSteps(100), mainFontPath(""), headFontPath(""), diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 4b863d33..c6069477 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -192,12 +192,12 @@ const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={ "QSound ADPCM", "ADPCM-A", "ADPCM-B", - NULL, + "K05 ADPCM", "8-bit PCM", "BRR", "VOX", "8-bit µ-law PCM", - NULL, + "C219 PCM", NULL, NULL, NULL, @@ -588,7 +588,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_SYS_MANAGER", "Chip Manager", 0), D("WINDOW_REGISTER_VIEW", "Register View", 0), D("WINDOW_LOG", "Log Viewer", 0), - D("EFFECT_LIST", "Effect List", 0), + D("WINDOW_EFFECT_LIST", "Effect List", 0), D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0), D("WINDOW_SUBSONGS", "Subsongs", 0), D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index d0029589..00cc903f 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -272,6 +272,10 @@ const char* tedControlBits[3]={ "square", "noise", NULL }; +const char* c219ControlBits[4]={ + "noise", "invert", "surround", NULL +}; + const char* x1_010EnvBits[8]={ "enable", "oneshot", "split L/R", "HinvR", "VinvR", "HinvL", "VinvL", NULL }; @@ -1798,7 +1802,9 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail } #define BUTTON_TO_SET_PROPS(_x) \ + pushToggleColors(_x.macro->speed!=1 || _x.macro->delay); \ ImGui::Button(ICON_FA_ELLIPSIS_H "##IMacroSet"); \ + popToggleColors(); \ if (ImGui::IsItemHovered()) { \ ImGui::SetTooltip("Delay/Step Length"); \ } \ @@ -4510,7 +4516,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20 || ins->type==DIV_INS_K053260 || - ins->type==DIV_INS_C140) { + ins->type==DIV_INS_C140 || + ins->type==DIV_INS_C219) { if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) { String sName; bool wannaOpenSMPopup=false; @@ -5538,7 +5545,7 @@ void FurnaceGUI::drawInsEdit() { volMax=31; } if (ins->type==DIV_INS_ADPCMB || ins->type==DIV_INS_YMZ280B || ins->type==DIV_INS_RF5C68 || - ins->type==DIV_INS_GA20 || ins->type==DIV_INS_C140) { + ins->type==DIV_INS_GA20 || ins->type==DIV_INS_C140 || ins->type==DIV_INS_C219) { volMax=255; } if (ins->type==DIV_INS_QSOUND) { @@ -5584,6 +5591,10 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Group Ctrl"; dutyMax=5; } + if (ins->type==DIV_INS_C219) { + dutyLabel="Control"; + dutyMax=3; + } if (ins->type==DIV_INS_BEEPER || ins->type==DIV_INS_POKEMINI) { dutyLabel="Pulse Width"; dutyMax=255; @@ -5709,6 +5720,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_POKEMINI) waveMax=0; if (ins->type==DIV_INS_TED) waveMax=0; if (ins->type==DIV_INS_C140) waveMax=0; + if (ins->type==DIV_INS_C219) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; @@ -5839,7 +5851,7 @@ void FurnaceGUI::drawInsEdit() { panMin=0; panMax=127; } - if (ins->type==DIV_INS_C140) { + if (ins->type==DIV_INS_C140 || ins->type==DIV_INS_C219) { panMin=0; panMax=255; } @@ -5864,6 +5876,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,msm5232ControlBits)); } else if (ins->type==DIV_INS_ES5506) { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,dutyMin,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,¯oHoverES5506FilterMode)); + } else if (ins->type==DIV_INS_C219) { + macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,120,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,c219ControlBits)); } else { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,dutyMin,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER])); } diff --git a/src/gui/mixer.cpp b/src/gui/mixer.cpp index dfa270ec..c023445a 100644 --- a/src/gui/mixer.cpp +++ b/src/gui/mixer.cpp @@ -105,7 +105,7 @@ bool FurnaceGUI::portSet(String label, unsigned int portSetID, int ins, int outs bool hovered=false; bool active=false; if (visible) { - hovered=ImGui::ItemHoverable(rect,ImGui::GetID(portID.c_str())); + hovered=ImGui::ItemHoverable(rect,ImGui::GetID(portID.c_str()),0); active=(hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)); if (hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 0bce014a..afd96ea5 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -102,7 +102,7 @@ void FurnaceGUI::drawOrderButtons() { int buttonColumns=(settings.orderButtonPos==0)?8:1; int buttonColumn=0; - while (buttonColumns<8 && ((8/buttonColumns)*ImGui::GetFrameHeightWithSpacing())>ImGui::GetContentRegionAvail().y) { + while (buttonColumns<8 && ((int)(8/buttonColumns)*ImGui::GetFrameHeightWithSpacing())>ImGui::GetContentRegionAvail().y) { buttonColumns++; } @@ -260,6 +260,7 @@ void FurnaceGUI::drawOrders() { ImGui::PushFont(patFont); bool tooSmall=((displayChans+1)>((ImGui::GetContentRegionAvail().x)/(ImGui::CalcTextSize("AA").x+2.0*ImGui::GetStyle().ItemInnerSpacing.x))); ImGui::PopFont(); + float yHeight=ImGui::GetContentRegionAvail().y; if (ImGui::BeginTable("OrdersTable",1+displayChans,(tooSmall?ImGuiTableFlags_SizingFixedFit:ImGuiTableFlags_SizingStretchSame)|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY)) { ImGui::PushFont(patFont); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,prevSpacing); @@ -267,7 +268,7 @@ void FurnaceGUI::drawOrders() { float lineHeight=(ImGui::GetTextLineHeight()+4*dpiScale); if (e->isPlaying()) { if (followOrders) { - ImGui::SetScrollY((e->getOrder()+1)*lineHeight-(ImGui::GetContentRegionAvail().y/2)); + ImGui::SetScrollY((e->getOrder()+1)*lineHeight-((yHeight-(tooSmall?ImGui::GetStyle().ScrollbarSize:0.0f))/2.0f)); } } ImGui::TableNextRow(0,lineHeight); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 6c84c8d7..f87b4ae7 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -378,7 +378,7 @@ void FurnaceGUI::drawPattern() { bool inhibitMenu=false; if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) { - cursor.y=oldRow+((pendingStepUpdate)?1:0); + cursor.y=e->isStepping()?e->getRow():oldRow; if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && !selecting) { selStart=cursor; selEnd=cursor; @@ -623,7 +623,7 @@ void FurnaceGUI::drawPattern() { case 0: // classic ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { - bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0); ImU32 col=(hovered || (mobileUI && ImGui::IsMouseDown(ImGuiMouseButton_Left)))?ImGui::GetColorU32(ImGuiCol_HeaderHovered):ImGui::GetColorU32(ImGuiCol_Header); dl->AddRectFilled(rect.Min,rect.Max,col); dl->AddText(ImVec2(minLabelArea.x,rect.Min.y),ImGui::GetColorU32(channelTextColor(i)),chanID); @@ -632,7 +632,7 @@ void FurnaceGUI::drawPattern() { case 1: { // line ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { - bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0); ImU32 fadeCol0=ImGui::GetColorU32(ImVec4( chanHeadBase.x, chanHeadBase.y, @@ -654,7 +654,7 @@ void FurnaceGUI::drawPattern() { case 2: { // round ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { - bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0); ImU32 fadeCol0=ImGui::GetColorU32(ImVec4( chanHeadBase.x, chanHeadBase.y, @@ -690,7 +690,7 @@ void FurnaceGUI::drawPattern() { case 4: { // square border ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { - bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0); ImU32 fadeCol=ImGui::GetColorU32(ImVec4( chanHeadBase.x, chanHeadBase.y, @@ -711,7 +711,7 @@ void FurnaceGUI::drawPattern() { case 5: { // round border ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { - bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0); ImU32 fadeCol=ImGui::GetColorU32(ImVec4( chanHeadBase.x, chanHeadBase.y, diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp index 3c501c45..4a40c33a 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -228,7 +228,7 @@ void FurnaceGUI::drawPiano() { //ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) { bool canInput=false; - if (!pianoReadonly && ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) { + if (!pianoReadonly && ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"),0)) { canInput=true; ImGui::InhibitInertialScroll(); } @@ -442,7 +442,7 @@ void FurnaceGUI::drawPiano() { ImGui::End(); // draw input pad if necessary - if (curWindow==GUI_WINDOW_PATTERN && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && cursor.xFine>0) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) { + if ((curWindow==GUI_WINDOW_PATTERN || !mobileUI) && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && cursor.xFine>0) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) { if (ImGui::Begin("Input Pad",NULL,ImGuiWindowFlags_NoTitleBar)) { ImGui::BeginDisabled(cursor.xFine==0); if (ImGui::BeginTable("InputPad",3,ImGuiTableFlags_Borders)) { diff --git a/src/gui/plot_nolerp.cpp b/src/gui/plot_nolerp.cpp index e6c3989e..eefb4ea1 100644 --- a/src/gui/plot_nolerp.cpp +++ b/src/gui/plot_nolerp.cpp @@ -78,7 +78,7 @@ int PlotNoLerpEx(ImGuiPlotType plot_type, const char* label, float (*values_gett ImGui::ItemSize(total_bb, style.FramePadding.y); if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll)) return -1; - const bool hovered = ImGui::ItemHoverable(frame_bb, id); + const bool hovered = ImGui::ItemHoverable(frame_bb, id, 0); // Determine scale from values if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) @@ -205,7 +205,7 @@ int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx), ImGui::ItemSize(total_bb, style.FramePadding.y); if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll)) return -1; - const bool hovered = ImGui::ItemHoverable(frame_bb, id); + const bool hovered = ImGui::ItemHoverable(frame_bb, id, 0); ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); @@ -315,7 +315,7 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett ImGui::ItemSize(total_bb, style.FramePadding.y); if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll)) return -1; - const bool hovered = ImGui::ItemHoverable(frame_bb, id); + const bool hovered = ImGui::ItemHoverable(frame_bb, id, 0); // Determine scale from values if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 7c423ea4..d6463564 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -2030,6 +2030,11 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_C140, 1.0f, 0, "") } ); + ENTRY( + "Namco NA-1/2", { + CH(DIV_SYSTEM_C219, 1.0f, 0, "") + } + ); ENTRY( "Taito Arcade", { CH(DIV_SYSTEM_YM2610B, 1.0f, 0, "") @@ -2096,7 +2101,10 @@ void FurnaceGUI::initSystemPresets() { ); ENTRY( "Seta 2", { - CH(DIV_SYSTEM_X1_010, 1.0f, 0, "clockSel=1") + CH(DIV_SYSTEM_X1_010, 1.0f, 0, + "clockSel=1\n" + "isBanked=true\n" + ) } ); ENTRY( @@ -2557,6 +2565,11 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_C140, 1.0f, 0, "") } ); + ENTRY( + "Namco C219", { + CH(DIV_SYSTEM_C219, 1.0f, 0, "") + } + ); CATEGORY_END; CATEGORY_BEGIN("Wavetable","chips which use user-specified waveforms to generate sound."); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 02d0aed5..2ca22f14 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -320,6 +320,22 @@ void FurnaceGUI::drawSampleEdit() { if (sample->samples>65535) { SAMPLE_WARN(warnLength,"C140: maximum sample length is 65535"); } + if (dispatch!=NULL) { + MAX_RATE("C140",dispatch->rate); + } + break; + case DIV_SYSTEM_C219: + if (sample->loop) { + if (sample->loopStart&1 || sample->loopEnd&1) { + SAMPLE_WARN(warnLoopPos,"C219: loop must be a multiple of 2"); + } + } + if (sample->samples>131072) { + SAMPLE_WARN(warnLength,"C219: maximum sample length is 131072"); + } + if (dispatch!=NULL) { + MAX_RATE("C219",dispatch->rate); + } break; default: break; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 7e08be4b..1c219d01 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -82,11 +82,13 @@ const char* patFonts[]={ const char* audioBackends[]={ "JACK", - "SDL" + "SDL", + "PortAudio" }; const bool isProAudio[]={ true, + false, false }; @@ -724,17 +726,32 @@ void FurnaceGUI::drawSettings() { if (ImGui::BeginTable("##Output",2)) { ImGui::TableSetupColumn("##Label",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("##Combo",ImGuiTableColumnFlags_WidthStretch); -#ifdef HAVE_JACK +#if defined(HAVE_JACK) || defined(HAVE_PA) ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::AlignTextToFramePadding(); ImGui::Text("Backend"); ImGui::TableNextColumn(); int prevAudioEngine=settings.audioEngine; - if (ImGui::Combo("##Backend",&settings.audioEngine,audioBackends,2)) { + if (ImGui::BeginCombo("##Backend",audioBackends[settings.audioEngine])) { +#ifdef HAVE_JACK + if (ImGui::Selectable("JACK",settings.audioEngine==DIV_AUDIO_JACK)) { + settings.audioEngine=DIV_AUDIO_JACK; + } +#endif + if (ImGui::Selectable("SDL",settings.audioEngine==DIV_AUDIO_SDL)) { + settings.audioEngine=DIV_AUDIO_SDL; + } +#ifdef HAVE_PA + if (ImGui::Selectable("PortAudio",settings.audioEngine==DIV_AUDIO_PORTAUDIO)) { + settings.audioEngine=DIV_AUDIO_PORTAUDIO; + } +#endif if (settings.audioEngine!=prevAudioEngine) { + audioEngineChanged=true; if (!isProAudio[settings.audioEngine]) settings.audioChans=2; } + ImGui::EndCombo(); } #endif @@ -765,17 +782,30 @@ void FurnaceGUI::drawSettings() { ImGui::AlignTextToFramePadding(); ImGui::Text("Device"); ImGui::TableNextColumn(); - String audioDevName=settings.audioDevice.empty()?"":settings.audioDevice; - if (ImGui::BeginCombo("##AudioDevice",audioDevName.c_str())) { - if (ImGui::Selectable("",settings.audioDevice.empty())) { - settings.audioDevice=""; - } - for (String& i: e->getAudioDevices()) { - if (ImGui::Selectable(i.c_str(),i==settings.audioDevice)) { - settings.audioDevice=i; + if (audioEngineChanged) { + ImGui::BeginDisabled(); + if (ImGui::BeginCombo("##AudioDevice","")) { + ImGui::Text("ALERT - TRESPASSER DETECTED"); + if (ImGui::IsItemHovered()) { + showError("you have been arrested for trying to engage with a disabled combo box."); + ImGui::CloseCurrentPopup(); } + ImGui::EndCombo(); + } + ImGui::EndDisabled(); + } else { + String audioDevName=settings.audioDevice.empty()?"":settings.audioDevice; + if (ImGui::BeginCombo("##AudioDevice",audioDevName.c_str())) { + if (ImGui::Selectable("",settings.audioDevice.empty())) { + settings.audioDevice=""; + } + for (String& i: e->getAudioDevices()) { + if (ImGui::Selectable(i.c_str(),i==settings.audioDevice)) { + settings.audioDevice=i; + } + } + ImGui::EndCombo(); } - ImGui::EndCombo(); } ImGui::TableNextRow(); @@ -1231,7 +1261,7 @@ void FurnaceGUI::drawSettings() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::AlignTextToFramePadding(); - ImGui::Text("Arcade/YM2151"); + ImGui::Text("YM2151"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::Combo("##ArcadeCore",&settings.arcadeCore,arcadeCores,2); @@ -1242,7 +1272,7 @@ void FurnaceGUI::drawSettings() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::AlignTextToFramePadding(); - ImGui::Text("Genesis/YM2612"); + ImGui::Text("YM2612"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2); @@ -1376,6 +1406,7 @@ void FurnaceGUI::drawSettings() { KEYBIND_CONFIG_BEGIN("keysGlobal"); UI_KEYBIND_CONFIG(GUI_ACTION_NEW); + UI_KEYBIND_CONFIG(GUI_ACTION_CLEAR); UI_KEYBIND_CONFIG(GUI_ACTION_OPEN); UI_KEYBIND_CONFIG(GUI_ACTION_OPEN_BACKUP); UI_KEYBIND_CONFIG(GUI_ACTION_SAVE); @@ -1401,6 +1432,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_ORDERS); UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_PATTERN); UI_KEYBIND_CONFIG(GUI_ACTION_FULLSCREEN); + UI_KEYBIND_CONFIG(GUI_ACTION_TX81Z_REQUEST); UI_KEYBIND_CONFIG(GUI_ACTION_PANIC); KEYBIND_CONFIG_END; @@ -1409,33 +1441,38 @@ void FurnaceGUI::drawSettings() { if (ImGui::TreeNode("Window activation")) { KEYBIND_CONFIG_BEGIN("keysWindow"); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EDIT_CONTROLS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ORDERS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_FIND); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SETTINGS); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SONG_INFO); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SUBSONGS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SPEED); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ABOUT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SETTINGS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ORDERS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_MIXER); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_DEBUG); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_GROOVES); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHANNELS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PAT_MANAGER); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SYS_MANAGER); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_COMPAT_FLAGS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_NOTES); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EDIT_CONTROLS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PIANO); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_OSCILLOSCOPE); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHAN_OSC); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EFFECT_LIST); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_VOL_METER); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_STATS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_COMPAT_FLAGS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PIANO); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_NOTES); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHANNELS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CLOCK); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_REGISTER_VIEW); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_LOG); - + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_STATS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EFFECT_LIST); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_DEBUG); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ABOUT); UI_KEYBIND_CONFIG(GUI_ACTION_COLLAPSE_WINDOW); UI_KEYBIND_CONFIG(GUI_ACTION_CLOSE_WINDOW); @@ -1603,6 +1640,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_OPEN); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_OPEN_REPLACE); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_SAVE); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_SAVE_DMP); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_MOVE_UP); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_MOVE_DOWN); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DELETE); @@ -1620,7 +1658,10 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_ADD); UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DUPLICATE); UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_OPEN); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_OPEN_REPLACE); UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_SAVE); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_SAVE_DMW); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_SAVE_RAW); UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_MOVE_UP); UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_MOVE_DOWN); UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DELETE); @@ -1637,8 +1678,13 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_ADD); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DUPLICATE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_CREATE_WAVE); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_OPEN); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_OPEN_RAW); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_SAVE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_SAVE_RAW); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_MOVE_UP); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DELETE); @@ -2199,6 +2245,11 @@ void FurnaceGUI::drawSettings() { settings.capitalMenuBar=capitalMenuBarB; } + bool classicChipOptionsB=settings.classicChipOptions; + if (ImGui::Checkbox("Display add/configure/change/remove chip menus in File menu",&classicChipOptionsB)) { + settings.classicChipOptions=classicChipOptionsB; + } + // SUBSECTION ORDERS CONFIG_SUBSECTION("Orders"); // sorry. temporarily disabled until ImGui has a way to add separators in tables arbitrarily. @@ -2998,6 +3049,7 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); if (ImGui::Button("Cancel##SettingsCancel")) { settingsOpen=false; + audioEngineChanged=false; syncSettings(); } ImGui::SameLine(); @@ -3024,6 +3076,13 @@ void FurnaceGUI::syncSettings() { settings.patFontSize=e->getConfInt("patFontSize",18); settings.iconSize=e->getConfInt("iconSize",16); settings.audioEngine=(e->getConfString("audioEngine","SDL")=="SDL")?1:0; + if (e->getConfString("audioEngine","SDL")=="JACK") { + settings.audioEngine=DIV_AUDIO_JACK; + } else if (e->getConfString("audioEngine","SDL")=="PortAudio") { + settings.audioEngine=DIV_AUDIO_PORTAUDIO; + } else { + settings.audioEngine=DIV_AUDIO_SDL; + } settings.audioDevice=e->getConfString("audioDevice",""); settings.audioChans=e->getConfInt("audioChans",2); settings.midiInDevice=e->getConfString("midiInDevice",""); @@ -3191,12 +3250,13 @@ void FurnaceGUI::syncSettings() { settings.capitalMenuBar=e->getConfInt("capitalMenuBar",0); settings.centerPopup=e->getConfInt("centerPopup",1); settings.insIconsStyle=e->getConfInt("insIconsStyle",1); + settings.classicChipOptions=e->getConfInt("classicChipOptions",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.headFontSize,2,96); clampSetting(settings.patFontSize,2,96); clampSetting(settings.iconSize,2,48); - clampSetting(settings.audioEngine,0,1); + clampSetting(settings.audioEngine,0,2); clampSetting(settings.audioQuality,0,1); clampSetting(settings.audioBufSize,32,4096); clampSetting(settings.audioRate,8000,384000); @@ -3337,6 +3397,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.capitalMenuBar,0,1); clampSetting(settings.centerPopup,0,1); clampSetting(settings.insIconsStyle,0,2); + clampSetting(settings.classicChipOptions,0,1); if (settings.exportLoops<0.0) settings.exportLoops=0.0; if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; @@ -3590,6 +3651,7 @@ void FurnaceGUI::commitSettings() { e->setConf("capitalMenuBar",settings.capitalMenuBar); e->setConf("centerPopup",settings.centerPopup); e->setConf("insIconsStyle",settings.insIconsStyle); + e->setConf("classicChipOptions",settings.classicChipOptions); // colors for (int i=0; icreateFontsTexture(); } + + audioEngineChanged=false; } bool FurnaceGUI::importColors(String path) { diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 1609ca7b..f2358312 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1016,6 +1016,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo case DIV_SYSTEM_X1_010: { int clockSel=flags.getInt("clockSel",0); bool stereo=flags.getBool("stereo",false); + bool isBanked=flags.getBool("isBanked",false); ImGui::Text("Clock rate:"); ImGui::Indent(); @@ -1037,10 +1038,15 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } + if (ImGui::Checkbox("Bankswitched (Seta 2)",&isBanked)) { + altered=true; + } + if (altered) { e->lockSave([&]() { flags.set("clockSel",clockSel); flags.set("stereo",stereo); + flags.set("isBanked",isBanked); }); } break; @@ -2099,6 +2105,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo case DIV_SYSTEM_PV1000: case DIV_SYSTEM_VERA: case DIV_SYSTEM_C140: + case DIV_SYSTEM_C219: break; case DIV_SYSTEM_YMU759: supportsCustomRate=false; diff --git a/src/main.cpp b/src/main.cpp index dd0cc2bf..6cc6b803 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -166,6 +166,7 @@ TAParamResult pVersion(String) { printf("- libsndfile by Erik de Castro Lopo and rest of libsndfile team (LGPLv2.1)\n"); printf("- SDL2 by Sam Lantinga (zlib license)\n"); printf("- zlib by Jean-loup Gailly and Mark Adler (zlib license)\n"); + printf("- PortAudio (PortAudio license)\n"); printf("- RtMidi by Gary P. Scavone (RtMidi license)\n"); printf("- backward-cpp by Google (MIT)\n"); printf("- Dear ImGui by Omar Cornut (MIT)\n");