Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_opengl3.cpp
#	backends/imgui_impl_win32.cpp
features/sdl_renderer3_multiviewports
ocornut ago%!(EXTRA string=3 years)
commit d91211f9f2
  1. 3
      .gitignore
  2. 1
      backends/imgui_impl_glut.cpp
  3. 10
      backends/imgui_impl_win32.cpp
  4. 17
      docs/CHANGELOG.txt
  5. 1
      examples/example_glfw_opengl3/main.cpp
  6. 1
      examples/example_glut_opengl2/main.cpp
  7. 2
      examples/example_sdl_sdlrenderer/main.cpp
  8. 10
      examples/example_win32_directx10/main.cpp
  9. 10
      examples/example_win32_directx11/main.cpp
  10. 10
      examples/example_win32_directx12/main.cpp
  11. 10
      examples/example_win32_directx9/main.cpp
  12. 112
      imgui.cpp
  13. 11
      imgui.h
  14. 9
      imgui_demo.cpp
  15. 75
      imgui_draw.cpp
  16. 23
      imgui_internal.h
  17. 39
      imgui_widgets.cpp

3
.gitignore vendored

@ -45,9 +45,12 @@ examples/example_emscripten_wgpu/web/*
cmake-build-* cmake-build-*
## Unix executables from our example Makefiles ## Unix executables from our example Makefiles
examples/example_glfw_metal/example_glfw_metal
examples/example_glfw_opengl2/example_glfw_opengl2 examples/example_glfw_opengl2/example_glfw_opengl2
examples/example_glfw_opengl3/example_glfw_opengl3 examples/example_glfw_opengl3/example_glfw_opengl3
examples/example_glut_opengl2/example_glut_opengl2 examples/example_glut_opengl2/example_glut_opengl2
examples/example_null/example_null examples/example_null/example_null
examples/example_sdl_metal/example_sdl_metal
examples/example_sdl_opengl2/example_sdl_opengl2 examples/example_sdl_opengl2/example_sdl_opengl2
examples/example_sdl_opengl3/example_sdl_opengl3 examples/example_sdl_opengl3/example_sdl_opengl3
examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer

@ -31,6 +31,7 @@
#include "imgui.h" #include "imgui.h"
#include "imgui_impl_glut.h" #include "imgui_impl_glut.h"
#define GL_SILENCE_DEPRECATION
#ifdef __APPLE__ #ifdef __APPLE__
#include <GLUT/glut.h> #include <GLUT/glut.h>
#else #else

@ -36,6 +36,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode).
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). // 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
@ -691,9 +692,18 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
io.AddFocusEvent(msg == WM_SETFOCUS); io.AddFocusEvent(msg == WM_SETFOCUS);
return 0; return 0;
case WM_CHAR: case WM_CHAR:
if (::IsWindowUnicode(hwnd))
{
// You can also use ToAscii()+GetKeyboardState() to retrieve characters. // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
if (wParam > 0 && wParam < 0x10000) if (wParam > 0 && wParam < 0x10000)
io.AddInputCharacterUTF16((unsigned short)wParam); io.AddInputCharacterUTF16((unsigned short)wParam);
}
else
{
wchar_t wch = 0;
::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1);
io.AddInputCharacter(wch);
}
return 0; return 0;
case WM_SETCURSOR: case WM_SETCURSOR:
// This is required to restore cursor when transitioning from e.g resize borders to client area. // This is required to restore cursor when transitioning from e.g resize borders to client area.

@ -161,6 +161,8 @@ Breaking changes:
Without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it. Without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
(This incorrect pattern has been mentioned or suggested in: #4510, #3355, #1760, #1490, #4152, #150, (This incorrect pattern has been mentioned or suggested in: #4510, #3355, #1760, #1490, #4152, #150,
threads have been amended to refer to this issue). threads have been amended to refer to this issue).
- Removed support for 1.42-era IMGUI_DISABLE_INCLUDE_IMCONFIG_H / IMGUI_INCLUDE_IMCONFIG_H. (#255)
They only made sense before we could use IMGUI_USER_CONFIG.
Other Changes: Other Changes:
@ -177,6 +179,7 @@ Other Changes:
- InputText: added support for shift+click style selection. (#5619) [@procedural] - InputText: added support for shift+click style selection. (#5619) [@procedural]
- InputText: clarified that callbacks cannot modify buffer when using the ReadOnly flag. - InputText: clarified that callbacks cannot modify buffer when using the ReadOnly flag.
- InputText: fixed minor one-frame selection glitch when reverting with Escape. - InputText: fixed minor one-frame selection glitch when reverting with Escape.
- ColorEdit3: fixed id collision leading to an assertion. (#5707)
- IsItemHovered: Added ImGuiHoveredFlags_DelayNormal and ImGuiHoveredFlags_DelayShort flags, - IsItemHovered: Added ImGuiHoveredFlags_DelayNormal and ImGuiHoveredFlags_DelayShort flags,
allowing to introduce a shared delay for tooltip idioms. The delays are respectively 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.HoverDelayFast (default to 0.10f). (#1485)
@ -194,14 +197,24 @@ Other Changes:
- IO: Added ImGuiMod_Shortcut which is ImGuiMod_Super on Mac and ImGuiMod_Ctrl otherwise. (#456) - IO: Added ImGuiMod_Shortcut which is ImGuiMod_Super on Mac and ImGuiMod_Ctrl otherwise. (#456)
- IO: Added ImGuiKey_MouseXXX aliases for mouse buttons/wheel so all operations done on ImGuiKey - IO: Added ImGuiKey_MouseXXX aliases for mouse buttons/wheel so all operations done on ImGuiKey
can apply to mouse data as well. (#4921) can apply to mouse data as well. (#4921)
- IO: Filter duplicate input events during the AddXXX() calls. (#5599, #4921)
- Menus: Fixed incorrect sub-menu parent association when opening a menu by closing another. - Menus: Fixed incorrect sub-menu parent association when opening a menu by closing another.
Among other things, it would accidentally break part of the closing heuristic logic when moving Among other things, it would accidentally break part of the closing heuristic logic when moving
towards a sub-menu. (#2517, #5614). [@rokups] towards a sub-menu. (#2517, #5614). [@rokups]
- Menus: Fixed gaps in closing logic which would make child-menu erroneously close when crossing - Menus: Fixed gaps in closing logic which would make child-menu erroneously close when crossing
the gap between a menu item inside a window and a child-menu in a secondary viewport. (#5614) the gap between a menu item inside a window and a child-menu in a secondary viewport. (#5614)
- Menus, Nav: Fixed keyboard/gamepad navigation occasionally erroneously landing on menu-item
in parent window when the parent is not a popup. (#5730)
- Menus, Nav: Fixed not being able to close a menu with Left arrow when parent is not a popup. (#5730)
- Menus, Nav: Fixed using left/right navigation when appending to an existing menu (multiple
BeginMenu() call with same names). (#1207)
- Nav: Fixed moving/resizing window with gamepad or keyboard when running at very high framerate. - Nav: Fixed moving/resizing window with gamepad or keyboard when running at very high framerate.
- Nav: Pressing Space/GamepadFaceDown on a repeating button uses the same repeating rate as a mouse hold. - Nav: Pressing Space/GamepadFaceDown on a repeating button uses the same repeating rate as a mouse hold.
- Nav: Fixed an issue opening a menu with Right key from a non-menu window. - Nav: Fixed an issue opening a menu with Right key from a non-menu window.
- Text: Fixed wrapped-text not doing a fast-forward on lines above the clipping region,
which would result in an abnormal number of vertices created (was slower and more likely to
asserts with 16-bits ImDrawVtx). (#5720)
- Fonts: Added GetGlyphRangesGreek() helper for Greek & Coptic glyph range. (#5676, #5727) [@azonenberg]
- Platform IME: [Windows] Removed call to ImmAssociateContextEx() leading to freeze on some setups. - Platform IME: [Windows] Removed call to ImmAssociateContextEx() leading to freeze on some setups.
(#2589, #5535, #5264, #4972) (#2589, #5535, #5264, #4972)
- Misc: ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers. (#4921) - Misc: ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers. (#4921)
@ -213,10 +226,14 @@ Other Changes:
- Docs: Fixed various typos in comments and documentations. (#5649, #5675, #5679) [@tocic, @lessigsx] - Docs: Fixed various typos in comments and documentations. (#5649, #5675, #5679) [@tocic, @lessigsx]
- Demo: Improved "Constrained-resizing window" example, more clearly showcase aspect-ratio. (#5627) - Demo: Improved "Constrained-resizing window" example, more clearly showcase aspect-ratio. (#5627)
- Demo: Added more explicit "Center window" mode to "Overlay example". (#5618) - Demo: Added more explicit "Center window" mode to "Overlay example". (#5618)
- Demo: Fixed Log & Console from losing scrolling position with Auto-Scroll when child is clipped. (#5721)
- Examples: Added all SDL examples to default VS solution. - Examples: Added all SDL examples to default VS solution.
- Examples: Win32: Always use RegisterClassW() to ensure windows are Unicode. (#5725)
- Backends: GLFW: Honor GLFW_CURSOR_DISABLED by not setting mouse position. (#5625) [@scorpion-26] - Backends: GLFW: Honor GLFW_CURSOR_DISABLED by not setting mouse position. (#5625) [@scorpion-26]
- Backends: SDL: Disable SDL 2.0.22 new "auto capture" which prevents drag and drop across windows - Backends: SDL: Disable SDL 2.0.22 new "auto capture" which prevents drag and drop across windows
(e.g. for multi-viewport support) and don't capture mouse when drag and dropping. (#5710) (e.g. for multi-viewport support) and don't capture mouse when drag and dropping. (#5710)
- Backends: Win32: Convert WM_CHAR values with MultiByteToWideChar() when window class was
registered as MBCS (not Unicode). (#5725, #1807, #471, #2815, #1060) [@or75, @ocornut]
- Backends: Metal: Use __bridge for ARC based systems. (#5403) [@stack] - Backends: Metal: Use __bridge for ARC based systems. (#5403) [@stack]
- Backends: Metal: Add dispatch synchronization. (#5447) [@luigifcruz] - Backends: Metal: Add dispatch synchronization. (#5447) [@luigifcruz]
- Backends: Metal: Update deprecated property 'sampleCount'->'rasterSampleCount'. (#5603) [@dcvz] - Backends: Metal: Update deprecated property 'sampleCount'->'rasterSampleCount'. (#5603) [@dcvz]

@ -7,6 +7,7 @@
#include "imgui_impl_glfw.h" #include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h" #include "imgui_impl_opengl3.h"
#include <stdio.h> #include <stdio.h>
#define GL_SILENCE_DEPRECATION
#if defined(IMGUI_IMPL_OPENGL_ES2) #if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#endif #endif

@ -17,6 +17,7 @@
#include "imgui.h" #include "imgui.h"
#include "imgui_impl_glut.h" #include "imgui_impl_glut.h"
#include "imgui_impl_opengl2.h" #include "imgui_impl_opengl2.h"
#define GL_SILENCE_DEPRECATION
#ifdef __APPLE__ #ifdef __APPLE__
#include <GLUT/glut.h> #include <GLUT/glut.h>
#else #else

@ -38,7 +38,7 @@ int main(int, char**)
if (renderer == NULL) if (renderer == NULL)
{ {
SDL_Log("Error creating SDL_Renderer!"); SDL_Log("Error creating SDL_Renderer!");
return false; return 0;
} }
//SDL_RendererInfo info; //SDL_RendererInfo info;
//SDL_GetRendererInfo(renderer, &info); //SDL_GetRendererInfo(renderer, &info);

@ -26,15 +26,15 @@ int main(int, char**)
{ {
// Create application window // Create application window
//ImGui_ImplWin32_EnableDpiAwareness(); //ImGui_ImplWin32_EnableDpiAwareness();
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"ImGui Example", NULL };
::RegisterClassEx(&wc); ::RegisterClassExW(&wc);
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX10 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL); HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX10 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
// Initialize Direct3D // Initialize Direct3D
if (!CreateDeviceD3D(hwnd)) if (!CreateDeviceD3D(hwnd))
{ {
CleanupDeviceD3D(); CleanupDeviceD3D();
::UnregisterClass(wc.lpszClassName, wc.hInstance); ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 1; return 1;
} }
@ -173,7 +173,7 @@ int main(int, char**)
CleanupDeviceD3D(); CleanupDeviceD3D();
::DestroyWindow(hwnd); ::DestroyWindow(hwnd);
::UnregisterClass(wc.lpszClassName, wc.hInstance); ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 0; return 0;
} }

@ -26,15 +26,15 @@ int main(int, char**)
{ {
// Create application window // Create application window
//ImGui_ImplWin32_EnableDpiAwareness(); //ImGui_ImplWin32_EnableDpiAwareness();
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"ImGui Example", NULL };
::RegisterClassEx(&wc); ::RegisterClassExW(&wc);
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX11 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL); HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
// Initialize Direct3D // Initialize Direct3D
if (!CreateDeviceD3D(hwnd)) if (!CreateDeviceD3D(hwnd))
{ {
CleanupDeviceD3D(); CleanupDeviceD3D();
::UnregisterClass(wc.lpszClassName, wc.hInstance); ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 1; return 1;
} }
@ -179,7 +179,7 @@ int main(int, char**)
CleanupDeviceD3D(); CleanupDeviceD3D();
::DestroyWindow(hwnd); ::DestroyWindow(hwnd);
::UnregisterClass(wc.lpszClassName, wc.hInstance); ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 0; return 0;
} }

@ -61,15 +61,15 @@ int main(int, char**)
{ {
// Create application window // Create application window
//ImGui_ImplWin32_EnableDpiAwareness(); //ImGui_ImplWin32_EnableDpiAwareness();
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"ImGui Example", NULL };
::RegisterClassEx(&wc); ::RegisterClassExW(&wc);
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX12 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL); HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX12 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
// Initialize Direct3D // Initialize Direct3D
if (!CreateDeviceD3D(hwnd)) if (!CreateDeviceD3D(hwnd))
{ {
CleanupDeviceD3D(); CleanupDeviceD3D();
::UnregisterClass(wc.lpszClassName, wc.hInstance); ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 1; return 1;
} }
@ -242,7 +242,7 @@ int main(int, char**)
CleanupDeviceD3D(); CleanupDeviceD3D();
::DestroyWindow(hwnd); ::DestroyWindow(hwnd);
::UnregisterClass(wc.lpszClassName, wc.hInstance); ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 0; return 0;
} }

@ -24,15 +24,15 @@ int main(int, char**)
{ {
// Create application window // Create application window
//ImGui_ImplWin32_EnableDpiAwareness(); //ImGui_ImplWin32_EnableDpiAwareness();
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"ImGui Example", NULL };
::RegisterClassEx(&wc); ::RegisterClassExW(&wc);
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX9 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL); HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX9 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
// Initialize Direct3D // Initialize Direct3D
if (!CreateDeviceD3D(hwnd)) if (!CreateDeviceD3D(hwnd))
{ {
CleanupDeviceD3D(); CleanupDeviceD3D();
::UnregisterClass(wc.lpszClassName, wc.hInstance); ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 1; return 1;
} }
@ -181,7 +181,7 @@ int main(int, char**)
CleanupDeviceD3D(); CleanupDeviceD3D();
::DestroyWindow(hwnd); ::DestroyWindow(hwnd);
::UnregisterClass(wc.lpszClassName, wc.hInstance); ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 0; return 0;
} }

@ -1318,11 +1318,13 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
} }
} }
// FIXME: Perhaps we could clear queued events as well?
void ImGuiIO::ClearInputCharacters() void ImGuiIO::ClearInputCharacters()
{ {
InputQueueCharacters.resize(0); InputQueueCharacters.resize(0);
} }
// FIXME: Perhaps we could clear queued events as well?
void ImGuiIO::ClearInputKeys() void ImGuiIO::ClearInputKeys()
{ {
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
@ -1338,6 +1340,23 @@ void ImGuiIO::ClearInputKeys()
KeyMods = ImGuiMod_None; KeyMods = ImGuiMod_None;
} }
static ImGuiInputEvent* FindLatestInputEvent(ImGuiInputEventType type, int arg = -1)
{
ImGuiContext& g = *GImGui;
for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
{
ImGuiInputEvent* e = &g.InputEventsQueue[n];
if (e->Type != type)
continue;
if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
continue;
if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
continue;
return e;
}
return NULL;
}
// Queue a new key down/up event. // Queue a new key down/up event.
// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) // - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
// - bool down: Is the key down? use false to signify a key release. // - bool down: Is the key down? use false to signify a key release.
@ -1363,17 +1382,13 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
if (ImGui::IsGamepadKey(key)) if (ImGui::IsGamepadKey(key))
BackendUsingLegacyNavInputArray = false; BackendUsingLegacyNavInputArray = false;
// Partial filter of duplicates (not strictly needed, but makes data neater in particular for key mods and gamepad values which are most commonly spmamed) // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
ImGuiKeyData* key_data = ImGui::GetKeyData(key); const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_Key, (int)key);
if (key_data->Down == down && key_data->AnalogValue == analog_value) const ImGuiKeyData* key_data = ImGui::GetKeyData(key);
{ const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;
bool found = false; const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;
for (int n = g.InputEventsQueue.Size - 1; n >= 0 && !found; n--) if (latest_key_down == down && latest_key_analog == analog_value)
if (g.InputEventsQueue[n].Type == ImGuiInputEventType_Key && g.InputEventsQueue[n].Key.Key == key)
found = true;
if (!found)
return; return;
}
// Add event // Add event
ImGuiInputEvent e; ImGuiInputEvent e;
@ -1431,11 +1446,20 @@ void ImGuiIO::AddMousePosEvent(float x, float y)
if (!AppAcceptingEvents) if (!AppAcceptingEvents)
return; return;
// Apply same flooring as UpdateMouseInputs()
ImVec2 pos((x > -FLT_MAX) ? ImFloorSigned(x) : x, (y > -FLT_MAX) ? ImFloorSigned(y) : y);
// Filter duplicate
const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_MousePos);
const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
if (latest_pos.x == pos.x && latest_pos.y == pos.y)
return;
ImGuiInputEvent e; ImGuiInputEvent e;
e.Type = ImGuiInputEventType_MousePos; e.Type = ImGuiInputEventType_MousePos;
e.Source = ImGuiInputSource_Mouse; e.Source = ImGuiInputSource_Mouse;
e.MousePos.PosX = x; e.MousePos.PosX = pos.x;
e.MousePos.PosY = y; e.MousePos.PosY = pos.y;
g.InputEventsQueue.push_back(e); g.InputEventsQueue.push_back(e);
} }
@ -1447,6 +1471,12 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
if (!AppAcceptingEvents) if (!AppAcceptingEvents)
return; return;
// Filter duplicate
const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_MouseButton, (int)mouse_button);
const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
if (latest_button_down == down)
return;
ImGuiInputEvent e; ImGuiInputEvent e;
e.Type = ImGuiInputEventType_MouseButton; e.Type = ImGuiInputEventType_MouseButton;
e.Source = ImGuiInputSource_Mouse; e.Source = ImGuiInputSource_Mouse;
@ -1460,7 +1490,9 @@ void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(&g.IO == this && "Can only add events to current context."); IM_ASSERT(&g.IO == this && "Can only add events to current context.");
if ((wheel_x == 0.0f && wheel_y == 0.0f) || !AppAcceptingEvents)
// Filter duplicate (unlike most events, wheel values are relative and easy to filter)
if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
return; return;
ImGuiInputEvent e; ImGuiInputEvent e;
@ -1489,6 +1521,12 @@ void ImGuiIO::AddFocusEvent(bool focused)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(&g.IO == this && "Can only add events to current context."); IM_ASSERT(&g.IO == this && "Can only add events to current context.");
// Filter duplicate
const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_Focus);
const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
if (latest_focused == focused)
return;
ImGuiInputEvent e; ImGuiInputEvent e;
e.Type = ImGuiInputEventType_Focus; e.Type = ImGuiInputEventType_Focus;
e.AppFocused.Focused = focused; e.AppFocused.Focused = focused;
@ -3654,6 +3692,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
return false; return false;
IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy)) == 0); // Flags not supported by this function 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
// Test if we are hovering the right window (our window could be behind another window) // Test if we are hovering the right window (our window could be behind another window)
// [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
// [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable
@ -3720,7 +3759,10 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
return false; return false;
if (!IsMouseHoveringRect(bb.Min, bb.Max)) if (!IsMouseHoveringRect(bb.Min, bb.Max))
return false; return false;
if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
// 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; g.HoveredIdDisabled = true;
return false; return false;
@ -3732,7 +3774,6 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
SetHoveredID(id); SetHoveredID(id);
// When disabled we'll return false but still set HoveredId // When disabled we'll return false but still set HoveredId
ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
if (item_flags & ImGuiItemFlags_Disabled) if (item_flags & ImGuiItemFlags_Disabled)
{ {
// Release active id if turning disabled // Release active id if turning disabled
@ -4389,6 +4430,7 @@ static void StartLockWheelingWindow(ImGuiWindow* window)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (g.WheelingWindow == window) if (g.WheelingWindow == window)
return; return;
IMGUI_DEBUG_LOG_IO("StartLockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
g.WheelingWindow = window; g.WheelingWindow = window;
g.WheelingWindowRefMousePos = g.IO.MousePos; g.WheelingWindowRefMousePos = g.IO.MousePos;
g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER; g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
@ -4406,6 +4448,7 @@ void ImGui::UpdateMouseWheel()
g.WheelingWindowTimer = 0.0f; g.WheelingWindowTimer = 0.0f;
if (g.WheelingWindowTimer <= 0.0f) if (g.WheelingWindowTimer <= 0.0f)
{ {
IMGUI_DEBUG_LOG_IO("UpdateMouseWheel() release WheelingWindow lock \"%s\"\n", g.WheelingWindow->Name);
g.WheelingWindow = NULL; g.WheelingWindow = NULL;
g.WheelingWindowTimer = 0.0f; g.WheelingWindowTimer = 0.0f;
} }
@ -4772,9 +4815,10 @@ void ImGui::NewFrame()
{ {
ImGuiWindow* window = g.Windows[i]; ImGuiWindow* window = g.Windows[i];
window->WasActive = window->Active; window->WasActive = window->Active;
window->BeginCount = 0;
window->Active = false; window->Active = false;
window->WriteAccessed = false; window->WriteAccessed = false;
window->BeginCountPreviousFrame = window->BeginCount;
window->BeginCount = 0;
// Garbage collect transient buffers of recently unused windows // Garbage collect transient buffers of recently unused windows
if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
@ -8410,6 +8454,8 @@ const char* ImGui::GetKeyName(ImGuiKey key)
#endif #endif
if (key == ImGuiKey_None) if (key == ImGuiKey_None)
return "None"; return "None";
if (key & ImGuiMod_Mask_)
key = ConvertSingleModFlagToKey(key);
if (!IsNamedKey(key)) if (!IsNamedKey(key))
return "Unknown"; return "Unknown";
@ -8679,7 +8725,7 @@ static const char* GetInputSourceName(ImGuiInputSource source)
static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f, %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("%s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f, %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; }
if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; }
if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.1f, %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.1f, %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; }
if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; }
@ -8711,38 +8757,25 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
{ {
ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
if (e->Type == ImGuiInputEventType_MousePos) if (e->Type == ImGuiInputEventType_MousePos)
{
ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
if (IsMousePosValid(&event_pos))
event_pos = ImVec2(ImFloorSigned(event_pos.x), ImFloorSigned(event_pos.y)); // Apply same flooring as UpdateMouseInputs()
e->IgnoredAsSame = (io.MousePos.x == event_pos.x && io.MousePos.y == event_pos.y);
if (!e->IgnoredAsSame)
{ {
// Trickling Rule: Stop processing queued events if we already handled a mouse button change // Trickling Rule: Stop processing queued events if we already handled a mouse button change
ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted)) if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
break; break;
io.MousePos = event_pos; io.MousePos = event_pos;
mouse_moved = true; mouse_moved = true;
} }
}
else if (e->Type == ImGuiInputEventType_MouseButton) else if (e->Type == ImGuiInputEventType_MouseButton)
{ {
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
const ImGuiMouseButton button = e->MouseButton.Button; const ImGuiMouseButton button = e->MouseButton.Button;
IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
e->IgnoredAsSame = (io.MouseDown[button] == e->MouseButton.Down);
if (!e->IgnoredAsSame)
{
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled)) if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
break; break;
io.MouseDown[button] = e->MouseButton.Down; io.MouseDown[button] = e->MouseButton.Down;
mouse_button_changed |= (1 << button); mouse_button_changed |= (1 << button);
} }
}
else if (e->Type == ImGuiInputEventType_MouseWheel) else if (e->Type == ImGuiInputEventType_MouseWheel)
{
e->IgnoredAsSame = (e->MouseWheel.WheelX == 0.0f && e->MouseWheel.WheelY == 0.0f);
if (!e->IgnoredAsSame)
{ {
// Trickling Rule: Stop processing queued events if we got multiple action on the event // Trickling Rule: Stop processing queued events if we got multiple action on the event
if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0)) if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
@ -8751,21 +8784,17 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
io.MouseWheel += e->MouseWheel.WheelY; io.MouseWheel += e->MouseWheel.WheelY;
mouse_wheeled = true; mouse_wheeled = true;
} }
}
else if (e->Type == ImGuiInputEventType_MouseViewport) else if (e->Type == ImGuiInputEventType_MouseViewport)
{ {
io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID; io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID;
} }
else if (e->Type == ImGuiInputEventType_Key) else if (e->Type == ImGuiInputEventType_Key)
{ {
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
ImGuiKey key = e->Key.Key; ImGuiKey key = e->Key.Key;
IM_ASSERT(key != ImGuiKey_None); IM_ASSERT(key != ImGuiKey_None);
ImGuiKeyData* key_data = GetKeyData(key); ImGuiKeyData* key_data = GetKeyData(key);
const int key_data_index = (int)(key_data - g.IO.KeysData); const int key_data_index = (int)(key_data - g.IO.KeysData);
e->IgnoredAsSame = (key_data->Down == e->Key.Down && key_data->AnalogValue == e->Key.AnalogValue);
if (!e->IgnoredAsSame)
{
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || text_inputted || mouse_button_changed != 0)) if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || text_inputted || mouse_button_changed != 0))
break; break;
key_data->Down = e->Key.Down; key_data->Down = e->Key.Down;
@ -8789,7 +8818,6 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down; io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down;
#endif #endif
} }
}
else if (e->Type == ImGuiInputEventType_Text) else if (e->Type == ImGuiInputEventType_Text)
{ {
// Trickling Rule: Stop processing queued events if keys/mouse have been interacted with // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
@ -8802,11 +8830,9 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
} }
else if (e->Type == ImGuiInputEventType_Focus) else if (e->Type == ImGuiInputEventType_Focus)
{ {
// We intentionally overwrite this and process lower, in order to give a chance // We intentionally overwrite this and process in NewFrame(), in order to give a chance
// to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame. // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
const bool focus_lost = !e->AppFocused.Focused; const bool focus_lost = !e->AppFocused.Focused;
e->IgnoredAsSame = (io.AppFocusLost == focus_lost);
if (!e->IgnoredAsSame)
io.AppFocusLost = focus_lost; io.AppFocusLost = focus_lost;
} }
else else
@ -8824,7 +8850,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
#ifndef IMGUI_DISABLE_DEBUG_TOOLS #ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO)) if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
for (int n = 0; n < g.InputEventsQueue.Size; n++) for (int n = 0; n < g.InputEventsQueue.Size; n++)
DebugPrintInputEvent(n < event_n ? (g.InputEventsQueue[n].IgnoredAsSame ? "Processed (Same)" : "Processed") : "Remaining", &g.InputEventsQueue[n]); DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);
#endif #endif
// Remaining events will be processed on the next frame // Remaining events will be processed on the next frame
@ -11511,7 +11537,7 @@ static void ImGui::NavUpdateCancelRequest()
SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect));
NavRestoreHighlightAfterMove(); NavRestoreHighlightAfterMove();
} }
else if (g.OpenPopupStack.Size > 0 && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
{ {
// Close open popup/menu // Close open popup/menu
ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);

@ -23,7 +23,7 @@
// Library Version // Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345') // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345')
#define IMGUI_VERSION "1.89 WIP" #define IMGUI_VERSION "1.89 WIP"
#define IMGUI_VERSION_NUM 18823 #define IMGUI_VERSION_NUM 18828
#define IMGUI_HAS_TABLE #define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch #define IMGUI_HAS_VIEWPORT // Viewport WIP branch
#define IMGUI_HAS_DOCK // Docking WIP branch #define IMGUI_HAS_DOCK // Docking WIP branch
@ -50,13 +50,12 @@ Index of this file:
#pragma once #pragma once
// Configuration file with compile-time options (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system') // Configuration file with compile-time options
// (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system')
#ifdef IMGUI_USER_CONFIG #ifdef IMGUI_USER_CONFIG
#include IMGUI_USER_CONFIG #include IMGUI_USER_CONFIG
#endif #endif
#if !defined(IMGUI_DISABLE_INCLUDE_IMCONFIG_H) || defined(IMGUI_INCLUDE_IMCONFIG_H)
#include "imconfig.h" #include "imconfig.h"
#endif
#ifndef IMGUI_DISABLE #ifndef IMGUI_DISABLE
@ -989,6 +988,7 @@ namespace ImGui
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Flags for ImGui::Begin() // Flags for ImGui::Begin()
// (Those are per-window flags. There are shared flags in ImGuiIO: io.ConfigWindowsResizeFromEdges and io.ConfigWindowsMoveFromTitleBarOnly)
enum ImGuiWindowFlags_ enum ImGuiWindowFlags_
{ {
ImGuiWindowFlags_None = 0, ImGuiWindowFlags_None = 0,
@ -1029,6 +1029,7 @@ enum ImGuiWindowFlags_
}; };
// Flags for ImGui::InputText() // Flags for ImGui::InputText()
// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive)
enum ImGuiInputTextFlags_ enum ImGuiInputTextFlags_
{ {
ImGuiInputTextFlags_None = 0, ImGuiInputTextFlags_None = 0,
@ -1754,6 +1755,7 @@ enum ImGuiColorEditFlags_
// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
// We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigDragClickToInputText)
enum ImGuiSliderFlags_ enum ImGuiSliderFlags_
{ {
ImGuiSliderFlags_None = 0, ImGuiSliderFlags_None = 0,
@ -2892,6 +2894,7 @@ struct ImFontAtlas
// NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details. // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details.
// NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data. // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data.
IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin
IMGUI_API const ImWchar* GetGlyphRangesGreek(); // Default + Greek and Coptic
IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters
IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs
IMGUI_API const ImWchar* GetGlyphRangesChineseFull(); // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs IMGUI_API const ImWchar* GetGlyphRangesChineseFull(); // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs

@ -6758,7 +6758,8 @@ struct ExampleAppConsole
// Reserve enough left-over height for 1 separator + 1 input text // Reserve enough left-over height for 1 separator + 1 input text
const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar); if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar))
{
if (ImGui::BeginPopupContextWindow()) if (ImGui::BeginPopupContextWindow())
{ {
if (ImGui::Selectable("Clear")) ClearLog(); if (ImGui::Selectable("Clear")) ClearLog();
@ -6818,6 +6819,7 @@ struct ExampleAppConsole
ScrollToBottom = false; ScrollToBottom = false;
ImGui::PopStyleVar(); ImGui::PopStyleVar();
}
ImGui::EndChild(); ImGui::EndChild();
ImGui::Separator(); ImGui::Separator();
@ -7065,8 +7067,9 @@ struct ExampleAppLog
Filter.Draw("Filter", -100.0f); Filter.Draw("Filter", -100.0f);
ImGui::Separator(); ImGui::Separator();
ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
if (ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar))
{
if (clear) if (clear)
Clear(); Clear();
if (copy) if (copy)
@ -7121,7 +7124,7 @@ struct ExampleAppLog
if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f); ImGui::SetScrollHereY(1.0f);
}
ImGui::EndChild(); ImGui::EndChild();
ImGui::End(); ImGui::End();
} }

@ -1303,6 +1303,7 @@ void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, cons
ImVec2 p1 = _Path.back(); ImVec2 p1 = _Path.back();
if (num_segments == 0) if (num_segments == 0)
{ {
IM_ASSERT(_Data->CurveTessellationTol > 0.0f);
PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated
} }
else else
@ -1318,6 +1319,7 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
ImVec2 p1 = _Path.back(); ImVec2 p1 = _Path.back();
if (num_segments == 0) if (num_segments == 0)
{ {
IM_ASSERT(_Data->CurveTessellationTol > 0.0f);
PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated
} }
else else
@ -2827,6 +2829,17 @@ const ImWchar* ImFontAtlas::GetGlyphRangesDefault()
return &ranges[0]; return &ranges[0];
} }
const ImWchar* ImFontAtlas::GetGlyphRangesGreek()
{
static const ImWchar ranges[] =
{
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x0370, 0x03FF, // Greek and Coptic
0,
};
return &ranges[0];
}
const ImWchar* ImFontAtlas::GetGlyphRangesKorean() const ImWchar* ImFontAtlas::GetGlyphRangesKorean()
{ {
static const ImWchar ranges[] = static const ImWchar ranges[] =
@ -3339,11 +3352,21 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
return &Glyphs.Data[i]; return &Glyphs.Data[i];
} }
const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const // Wrapping skips upcoming blanks
static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)
{ {
while (text < text_end && ImCharIsBlankA(*text))
text++;
if (*text == '\n')
text++;
return text;
}
// Simple word-wrapping for English, not full-featured. Please submit failing cases! // Simple word-wrapping for English, not full-featured. Please submit failing cases!
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const
{
// For references, possible wrap point marked with ^ // For references, possible wrap point marked with ^
// "aaa bbb, ccc,ddd. eee fff. ggg!" // "aaa bbb, ccc,ddd. eee fff. ggg!"
// ^ ^ ^ ^ ^__ ^ ^ // ^ ^ ^ ^ ^__ ^ ^
@ -3355,7 +3378,6 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
// Cut words that cannot possibly fit within one line. // Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
float line_width = 0.0f; float line_width = 0.0f;
float word_width = 0.0f; float word_width = 0.0f;
float blank_width = 0.0f; float blank_width = 0.0f;
@ -3435,6 +3457,10 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
s = next_s; s = next_s;
} }
// Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
// +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
if (s == text && text < text_end)
return s + 1;
return s; return s;
} }
@ -3459,11 +3485,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
{ {
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol) if (!word_wrap_eol)
{
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width); word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width);
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
}
if (s >= word_wrap_eol) if (s >= word_wrap_eol)
{ {
@ -3472,13 +3494,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
text_size.y += line_height; text_size.y += line_height;
line_width = 0.0f; line_width = 0.0f;
word_wrap_eol = NULL; word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
// Wrapping skips upcoming blanks
while (s < text_end)
{
const char c = *s;
if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
}
continue; continue;
} }
} }
@ -3563,15 +3579,25 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
const float scale = size / FontSize; const float scale = size / FontSize;
const float line_height = FontSize * scale; const float line_height = FontSize * scale;
const bool word_wrap_enabled = (wrap_width > 0.0f); const bool word_wrap_enabled = (wrap_width > 0.0f);
const char* word_wrap_eol = NULL;
// Fast-forward to first visible line // Fast-forward to first visible line
const char* s = text_begin; const char* s = text_begin;
if (y + line_height < clip_rect.y && !word_wrap_enabled) if (y + line_height < clip_rect.y)
while (y + line_height < clip_rect.y && s < text_end) while (y + line_height < clip_rect.y && s < text_end)
{ {
s = (const char*)memchr(s, '\n', text_end - s); const char* line_end = (const char*)memchr(s, '\n', text_end - s);
s = s ? s + 1 : text_end; if (word_wrap_enabled)
{
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA().
// If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both.
// However it is still better than nothing performing the fast-forward!
s = CalcWordWrapPositionA(scale, s, line_end, wrap_width);
s = CalcWordWrapNextLineStartA(s, text_end);
}
else
{
s = line_end ? line_end + 1 : text_end;
}
y += line_height; y += line_height;
} }
@ -3603,6 +3629,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
const ImU32 col_untinted = col | ~IM_COL32_A_MASK; const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
const char* word_wrap_eol = NULL;
while (s < text_end) while (s < text_end)
{ {
@ -3610,24 +3637,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
{ {
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol) if (!word_wrap_eol)
{
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x)); word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x));
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
}
if (s >= word_wrap_eol) if (s >= word_wrap_eol)
{ {
x = start_x; x = start_x;
y += line_height; y += line_height;
word_wrap_eol = NULL; word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
// Wrapping skips upcoming blanks
while (s < text_end)
{
const char c = *s;
if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
}
continue; continue;
} }
} }

@ -154,8 +154,8 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for IsKeyPressedEx() typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for IsKeyPressedEx()
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags
typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns()
typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight()
typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests
@ -344,8 +344,10 @@ IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* bu
IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
IMGUI_API void ImStrTrimBlanks(char* str); IMGUI_API void ImStrTrimBlanks(char* str);
IMGUI_API const char* ImStrSkipBlank(const char* str); IMGUI_API const char* ImStrSkipBlank(const char* str);
IM_MSVC_RUNTIME_CHECKS_OFF
static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
IM_MSVC_RUNTIME_CHECKS_RESTORE
// Helpers: Formatting // Helpers: Formatting
IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3);
@ -766,7 +768,10 @@ struct ImDrawDataBuilder
// [SECTION] Widgets support: flags, enums, data structures // [SECTION] Widgets support: flags, enums, data structures
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). // Flags used by upcoming items
// - input: PushItemFlag() manipulates g.CurrentItemFlags, ItemAdd() calls may add extra flags.
// - output: stored in g.LastItemData.InFlags
// Current window shared by all windows.
// This is going to be exposed in imgui.h when stabilized enough. // This is going to be exposed in imgui.h when stabilized enough.
enum ImGuiItemFlags_ enum ImGuiItemFlags_
{ {
@ -780,12 +785,14 @@ enum ImGuiItemFlags_
ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window
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_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_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable()
// Controlled by widget code // Controlled by widget code
ImGuiItemFlags_Inputable = 1 << 8, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. ImGuiItemFlags_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.
}; };
// Storage for LastItem data // Status flags for an already submitted item
// - output: stored in g.LastItemData.StatusFlags
enum ImGuiItemStatusFlags_ enum ImGuiItemStatusFlags_
{ {
ImGuiItemStatusFlags_None = 0, ImGuiItemStatusFlags_None = 0,
@ -1033,7 +1040,7 @@ struct IMGUI_API ImGuiInputTextState
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
bool Edited; // edited this frame bool Edited; // edited this frame
ImGuiInputTextFlags Flags; // copy of InputText() flags ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } ImGuiInputTextState() { memset(this, 0, sizeof(*this)); }
void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); }
@ -1259,7 +1266,6 @@ struct ImGuiInputEvent
ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text
ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus
}; };
bool IgnoredAsSame;
bool AddedByTestEngine; bool AddedByTestEngine;
ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } ImGuiInputEvent() { memset(this, 0, sizeof(*this)); }
@ -2316,6 +2322,7 @@ struct IMGUI_API ImGuiWindow
bool HasCloseButton; // Set when the window has a close button (p_open != NULL) bool HasCloseButton; // Set when the window has a close button (p_open != NULL)
signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3)
short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
short BeginCountPreviousFrame; // Number of Begin() during the previous frame
short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0.
short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.
short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused.
@ -2886,7 +2893,7 @@ namespace ImGui
IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API ImVec2 GetContentRegionMaxAbs();
IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess);
// Parameter stacks // Parameter stacks (shared)
IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled);
IMGUI_API void PopItemFlag(); IMGUI_API void PopItemFlag();

@ -4598,9 +4598,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
apply_new_text_length = state->CurLenA; apply_new_text_length = state->CurLenA;
} }
} }
// Clear temporary user storage
state->Flags = ImGuiInputTextFlags_None;
} }
// Copy result to user buffer. This can currently only happen when (g.ActiveId == id) // Copy result to user buffer. This can currently only happen when (g.ActiveId == id)
@ -5122,6 +5119,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
if (BeginPopup("picker")) if (BeginPopup("picker"))
{
if (g.CurrentWindow->BeginCount == 1)
{ {
picker_active_window = g.CurrentWindow; picker_active_window = g.CurrentWindow;
if (label != label_display_end) if (label != label_display_end)
@ -5133,6 +5132,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
}
EndPopup(); EndPopup();
} }
} }
@ -5195,7 +5195,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
g.LastItemData.ID = g.ActiveId; g.LastItemData.ID = g.ActiveId;
if (value_changed) if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId
MarkItemEdited(g.LastItemData.ID); MarkItemEdited(g.LastItemData.ID);
return value_changed; return value_changed;
@ -5583,7 +5583,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)
value_changed = false; value_changed = false;
if (value_changed) if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId
MarkItemEdited(g.LastItemData.ID); MarkItemEdited(g.LastItemData.ID);
PopID(); PopID();
@ -7031,10 +7031,10 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label, NULL, true);
// Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)
// This is only done for items for the menu set and not the full parent window.
const bool menuset_is_open = IsRootOfOpenMenuSet(); const bool menuset_is_open = IsRootOfOpenMenuSet();
ImGuiWindow* backed_nav_window = g.NavWindow;
if (menuset_is_open) if (menuset_is_open)
g.NavWindow = window; PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
@ -7083,7 +7083,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
const bool hovered = (g.HoveredId == id) && enabled && !g.NavDisableMouseHover; const bool hovered = (g.HoveredId == id) && enabled && !g.NavDisableMouseHover;
if (menuset_is_open) if (menuset_is_open)
g.NavWindow = backed_nav_window; PopItemFlag();
bool want_open = false; bool want_open = false;
bool want_close = false; bool want_close = false;
@ -7169,7 +7169,8 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
if (menu_is_open) if (menu_is_open)
{ {
SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. // FIXME: This technically breaks functions relying on LastItemData, somehow nobody complained yet. Should backup/restore LastItemData.
SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding
menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
PopStyleVar(); PopStyleVar();
@ -7189,16 +7190,16 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
void ImGui::EndMenu() void ImGui::EndMenu()
{ {
// Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). // Nav: When a left move request our menu failed, close ourselves.
// A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
// However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
// FIXME: This doesn't work if the parent BeginMenu() is not on a menu.
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginMenu()/EndMenu() calls
if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window) ImGuiWindow* parent_window = window->ParentWindow; // Should always be != NULL is we passed assert.
if (window->BeginCount == window->BeginCountPreviousFrame)
if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet())
if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical)
{ {
ClosePopupToLevel(g.BeginPopupStack.Size, true); ClosePopupToLevel(g.BeginPopupStack.Size - 1, true);
NavMoveRequestCancel(); NavMoveRequestCancel();
} }
@ -7216,10 +7217,10 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label, NULL, true);
// See BeginMenuEx() for comments about this.
const bool menuset_is_open = IsRootOfOpenMenuSet(); const bool menuset_is_open = IsRootOfOpenMenuSet();
ImGuiWindow* backed_nav_window = g.NavWindow;
if (menuset_is_open) if (menuset_is_open)
g.NavWindow = window; PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
@ -7271,7 +7272,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
EndDisabled(); EndDisabled();
PopID(); PopID();
if (menuset_is_open) if (menuset_is_open)
g.NavWindow = backed_nav_window; PopItemFlag();
return pressed; return pressed;
} }

Loading…
Cancel
Save