Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_metal.mm
#	imgui.cpp
#	imgui.h
features/sdl_renderer3_multiviewports
ocornut ago%!(EXTRA string=3 years)
commit e900ca355e
  1. 4
      backends/imgui_impl_metal.mm
  2. 14
      docs/CHANGELOG.txt
  3. 87
      imgui.cpp
  4. 24
      imgui.h
  5. 53
      imgui_demo.cpp
  6. 9
      imgui_internal.h
  7. 2
      imgui_tables.cpp
  8. 129
      imgui_widgets.cpp
  9. 28
      misc/fonts/binary_to_compressed_c.cpp

@ -14,6 +14,7 @@
// 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: Metal: Added support for multiple windows via the ImGuiPlatformIO interface. // 2022-XX-XX: Metal: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2022-06-01: Metal: Fixed null dereference on exit inside command buffer completion handler.
// 2022-04-27: Misc: Store backend data in a per-context struct, allowing to use this backend with multiple contexts. // 2022-04-27: Misc: Store backend data in a per-context struct, allowing to use this backend with multiple contexts.
// 2022-01-03: Metal: Ignore ImDrawCmd where ElemCount == 0 (very rare but can technically be manufactured by user code). // 2022-01-03: Metal: Ignore ImDrawCmd where ElemCount == 0 (very rare but can technically be manufactured by user code).
// 2021-12-30: Metal: Added Metal C++ support. Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. // 2021-12-30: Metal: Added Metal C++ support. Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file.
@ -306,8 +307,11 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id<MTLCommandBuffer> c
{ {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
if (bd != NULL)
{
[bd->SharedMetalContext.bufferCache addObject:vertexBuffer]; [bd->SharedMetalContext.bufferCache addObject:vertexBuffer];
[bd->SharedMetalContext.bufferCache addObject:indexBuffer]; [bd->SharedMetalContext.bufferCache addObject:indexBuffer];
}
}); });
}]; }];
} }

@ -132,6 +132,9 @@ Other Changes:
or interacting with a game/3D view). or interacting with a game/3D view).
- IO: Fixed input queue trickling of mouse wheel events: multiple wheel events are merged, while - IO: Fixed input queue trickling of mouse wheel events: multiple wheel events are merged, while
a mouse pos followed by a mouse wheel are now trickled. (#4921, #4821) a mouse pos followed by a mouse wheel are now trickled. (#4921, #4821)
- IO: Added io.SetAppAcceptingEvents() to set a master flag for accepting key/mouse/characters
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.
- Windows: Fixed first-time windows appearing in negative coordinates from being initialized - Windows: Fixed first-time windows appearing in negative coordinates from being initialized
with a wrong size. This would most often be noticeable in multi-viewport mode (docking branch) with a wrong size. This would most often be noticeable in multi-viewport mode (docking branch)
when spawning a window in a monitor with negative coordinates. (#5215, #3414) [@DimaKoltun] when spawning a window in a monitor with negative coordinates. (#5215, #3414) [@DimaKoltun]
@ -146,20 +149,29 @@ Other Changes:
- InputScalar: Automatically allow hexadecimal input when format is %X (without extra flag). - InputScalar: Automatically allow hexadecimal input when format is %X (without extra flag).
- InputScalar: Automatically allow scientific input when format is float/double (without extra flag). - InputScalar: Automatically allow scientific input when format is float/double (without extra flag).
- Nav: Fixed nav movement in a scope with only one disabled item from focusing the disabled item. (#5189) - Nav: Fixed nav movement in a scope with only one disabled item from focusing the disabled item. (#5189)
- Nav: Fixed issues with nav request being transferred to another window when calling SetKeyboardFocusHere()
and simultaneous changing window focus. (#4449)
- IsItemHovered(): added ImGuiHoveredFlags_NoNavOverride to disable the behavior where the - IsItemHovered(): added ImGuiHoveredFlags_NoNavOverride to disable the behavior where the
return value is overriden by focus when gamepad/keyboard navigation is active. return value is overriden by focus when gamepad/keyboard navigation is active.
- InputText: Fixed pressing Tab emitting two tabs characters because of dual Keys/Chars events being - InputText: Fixed pressing Tab emitting two tabs characters because of dual Keys/Chars events being
trickled with the new input queue (happened on some backends only). (#2467, #1336) trickled with the new input queue (happened on some backends only). (#2467, #1336)
- InputText: Fixed a one-frame display glitch where pressing Escape to revert after a deletion - InputText: Fixed a one-frame display glitch where pressing Escape to revert after a deletion
would lead to small garbage being displayed for one frame. Curiously a rather old bug! (#3008) would lead to small garbage being displayed for one frame. Curiously a rather old bug! (#3008)
- InputText: Fixed an undo-state corruption issue when editing main buffer before reactivating item. (#4947)
- InputText: Fixed an undo-state corruption issue when editing in-flight buffer in user callback.
(#4947, #4949] [@JoshuaWebb]
- Tables: Fixed incorrect border height used for logic when resizing one of several synchronized - Tables: Fixed incorrect border height used for logic when resizing one of several synchronized
instance of a same table ID, when instances have a different height. (#3955). instance of a same table ID, when instances have a different height. (#3955).
- Tables: Fixed incorrect auto-fit of parent windows when using non-resizable weighted columns. (#5276) - Tables: Fixed incorrect auto-fit of parent windows when using non-resizable weighted columns. (#5276)
- Tables: Fixed drawcall merging of last column. Depending on some unrelated settings (e.g. BorderH)
merging drawcall of the last column didn't always work (regression since 1.87). (#4843, #4844) [@rokups]
- Inputs: Fixed IsMouseClicked() repeat mode rate being half of keyboard repeat rate. - Inputs: Fixed IsMouseClicked() repeat mode rate being half of keyboard repeat rate.
- ColorEdit: Fixed text baseline alignment after a SameLine() after a ColorEdit() with visible label. - ColorEdit: Fixed text baseline alignment after a SameLine() after a ColorEdit() with visible label.
- Menus: Adjusted BeginMenu() closing logic so hovering void or non-MenuItem() in parent window - Menus: Adjusted BeginMenu() closing logic so hovering void or non-MenuItem() in parent window
always lead to menu closure. Fixes using items that are not MenuItem() or BeginItem() at the root always lead to menu closure. Fixes using items that are not MenuItem() or BeginItem() at the root
level of a popup with a child menu opened. level of a popup with a child menu opened.
- Menus: Menus emitted from the main/scrolling layer are not part of the same menuset as menus emitted
from the menu-bar, avoiding accidental hovering from one to the other. (#3496, #4797) [@rokups]
- Stack Tool: Added option to copy item path to clipboard. (#4631) - Stack Tool: Added option to copy item path to clipboard. (#4631)
- Settings: Fixed out-of-bounds read when .ini file on disk is empty. (#5351) [@quantum5] - Settings: Fixed out-of-bounds read when .ini file on disk is empty. (#5351) [@quantum5]
- DrawList: Fixed PathArcTo() emitting terminating vertices too close to arc vertices. (#4993) [@thedmd] - DrawList: Fixed PathArcTo() emitting terminating vertices too close to arc vertices. (#4993) [@thedmd]
@ -170,6 +182,7 @@ Other Changes:
you have a UTF-8 text encoding issue or a font loading issue. [@LaMarche05, @ocornut] you have a UTF-8 text encoding issue or a font loading issue. [@LaMarche05, @ocornut]
- Demo: Add better demo of how to use SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). - Demo: Add better demo of how to use SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard().
- Metrics: Added a "UTF-8 Encoding Viewer" section using the aforementioned DebugTextEncoding() function. - Metrics: Added a "UTF-8 Encoding Viewer" section using the aforementioned DebugTextEncoding() function.
- Metrics: Added "InputText" section to visualize internal state (#4947, #4949).
- Misc: Fixed calling GetID("label") _before_ a widget emitting this item inside a group (such as InputInt()) - Misc: Fixed calling GetID("label") _before_ a widget emitting this item inside a group (such as InputInt())
from causing an assertion when closing the group. (#5181). from causing an assertion when closing the group. (#5181).
- Misc: Fixed IsAnyItemHovered() returning false when using navigation. - Misc: Fixed IsAnyItemHovered() returning false when using navigation.
@ -191,6 +204,7 @@ Other Changes:
- Backends: OSX: Monitor NSKeyUp events to catch missing keyUp for key when user press Cmd + key (#5128) [@thedmd] - Backends: OSX: Monitor NSKeyUp events to catch missing keyUp for key when user press Cmd + key (#5128) [@thedmd]
- Backends: OSX, Metal: Store backend data in a per-context struct, allowing to use these backends with - Backends: OSX, Metal: Store backend data in a per-context struct, allowing to use these backends with
multiple contexts. (#5203, #5221, #4141) [@noisewuwei] multiple contexts. (#5203, #5221, #4141) [@noisewuwei]
- Backends: Metal: Fixed null dereference on exit inside command buffer completion handler. (#5363, #5365) [@warrenm]
- Backends: OpenGL3: Partially revert 1.86 change of using glBufferSubData(): now only done on Windows and - Backends: OpenGL3: Partially revert 1.86 change of using glBufferSubData(): now only done on Windows and
Intel GPU, based on querying glGetString(GL_VENDOR). Essentially we got report of accumulating leaks on Intel Intel GPU, based on querying glGetString(GL_VENDOR). Essentially we got report of accumulating leaks on Intel
with multi-viewports when using simple glBufferData() without orphaning, and report of corruptions on other with multi-viewports when using simple glBufferData() without orphaning, and report of corruptions on other

@ -1205,6 +1205,7 @@ ImGuiIO::ImGuiIO()
for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.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; } for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
AppAcceptingEvents = true;
BackendUsingLegacyKeyArrays = (ImS8)-1; BackendUsingLegacyKeyArrays = (ImS8)-1;
BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong
} }
@ -1217,7 +1218,7 @@ void ImGuiIO::AddInputCharacter(unsigned int c)
{ {
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 (c == 0) if (c == 0 || !AppAcceptingEvents)
return; return;
ImGuiInputEvent e; ImGuiInputEvent e;
@ -1231,7 +1232,7 @@ void ImGuiIO::AddInputCharacter(unsigned int c)
// we should save the high surrogate. // we should save the high surrogate.
void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
{ {
if (c == 0 && InputQueueSurrogate == 0) if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
return; return;
if ((c & 0xFC00) == 0xD800) // High surrogate, must save if ((c & 0xFC00) == 0xD800) // High surrogate, must save
@ -1265,6 +1266,8 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
{ {
if (!AppAcceptingEvents)
return;
while (*utf8_chars != 0) while (*utf8_chars != 0)
{ {
unsigned int c = 0; unsigned int c = 0;
@ -1303,7 +1306,7 @@ void ImGuiIO::ClearInputKeys()
void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
{ {
//if (e->Down) { IMGUI_DEBUG_LOG("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } //if (e->Down) { IMGUI_DEBUG_LOG("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }
if (key == ImGuiKey_None) if (key == ImGuiKey_None || !AppAcceptingEvents)
return; return;
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.");
@ -1344,6 +1347,8 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
{ {
if (!AppAcceptingEvents)
return;
AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f); AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);
} }
@ -1372,11 +1377,19 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native
#endif #endif
} }
// Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
{
AppAcceptingEvents = accepting_events;
}
// Queue a mouse move event // Queue a mouse move event
void ImGuiIO::AddMousePosEvent(float x, float y) void ImGuiIO::AddMousePosEvent(float x, float 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 (!AppAcceptingEvents)
return;
ImGuiInputEvent e; ImGuiInputEvent e;
e.Type = ImGuiInputEventType_MousePos; e.Type = ImGuiInputEventType_MousePos;
@ -1391,6 +1404,8 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
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.");
IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
if (!AppAcceptingEvents)
return;
ImGuiInputEvent e; ImGuiInputEvent e;
e.Type = ImGuiInputEventType_MouseButton; e.Type = ImGuiInputEventType_MouseButton;
@ -1405,7 +1420,7 @@ 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) if ((wheel_x == 0.0f && wheel_y == 0.0f) || !AppAcceptingEvents)
return; return;
ImGuiInputEvent e; ImGuiInputEvent e;
@ -1738,6 +1753,25 @@ int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
} }
#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
{
ImGuiContext& g = *GImGui;
va_list args;
va_start(args, fmt);
int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
*out_buf = g.TempBuffer.Data;
if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
va_end(args);
}
void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
{
ImGuiContext& g = *GImGui;
int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
*out_buf = g.TempBuffer.Data;
if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
}
// CRC32 needs a 1KB lookup table (not cache friendly) // CRC32 needs a 1KB lookup table (not cache friendly)
// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily: // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe. // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
@ -4640,6 +4674,7 @@ void ImGui::Initialize()
viewport->PlatformWindowCreated = true; viewport->PlatformWindowCreated = true;
viewport->Flags = ImGuiViewportFlags_OwnedByApp; viewport->Flags = ImGuiViewportFlags_OwnedByApp;
g.Viewports.push_back(viewport); g.Viewports.push_back(viewport);
g.TempBuffer.resize(1024 * 3 + 1, 0);
g.PlatformIO.Viewports.push_back(g.Viewports[0]); g.PlatformIO.Viewports.push_back(g.Viewports[0]);
#ifdef IMGUI_HAS_DOCK #ifdef IMGUI_HAS_DOCK
@ -5424,15 +5459,16 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
SetNextWindowSize(size); SetNextWindowSize(size);
// Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value. // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
const char* temp_window_name;
if (name) if (name)
ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id); ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
else else
ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id); ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
const float backup_border_size = g.Style.ChildBorderSize; const float backup_border_size = g.Style.ChildBorderSize;
if (!border) if (!border)
g.Style.ChildBorderSize = 0.0f; g.Style.ChildBorderSize = 0.0f;
bool ret = Begin(g.TempBuffer, NULL, flags); bool ret = Begin(temp_window_name, NULL, flags);
g.Style.ChildBorderSize = backup_border_size; g.Style.ChildBorderSize = backup_border_size;
ImGuiWindow* child_window = g.CurrentWindow; ImGuiWindow* child_window = g.CurrentWindow;
@ -6432,6 +6468,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{ {
ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
popup_ref.Window = window; popup_ref.Window = window;
popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
g.BeginPopupStack.push_back(popup_ref); g.BeginPopupStack.push_back(popup_ref);
window->PopupId = popup_ref.PopupId; window->PopupId = popup_ref.PopupId;
} }
@ -7942,7 +7979,10 @@ void ImGui::SetKeyboardFocusHere(int offset)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
IM_ASSERT(offset >= -1); // -1 is allowed but not below IM_ASSERT(offset >= -1); // -1 is allowed but not below
g.NavWindow = window; g.NavWindow = window;
g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; 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, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
if (offset == -1) if (offset == -1)
@ -10190,8 +10230,11 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
// Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text) // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
if (g.NavWindow != window) if (g.NavWindow != window)
g.NavInitRequest = false; {
g.NavWindow = window; g.NavWindow = window;
g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
}
g.NavId = id; g.NavId = id;
g.NavLayer = nav_layer; g.NavLayer = nav_layer;
g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
@ -10438,8 +10481,13 @@ static void ImGui::NavProcessItem()
// Update window-relative bounding box of navigated item // Update window-relative bounding box of navigated item
if (g.NavId == id) if (g.NavId == id)
{
if (g.NavWindow != window)
{ {
g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
}
g.NavLayer = window->DC.NavLayerCurrent; g.NavLayer = window->DC.NavLayerCurrent;
g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
g.NavIdIsAlive = true; g.NavIdIsAlive = true;
@ -10517,10 +10565,11 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM
g.NavMoveScrollFlags = scroll_flags; g.NavMoveScrollFlags = scroll_flags;
g.NavMoveForwardToNextFrame = false; g.NavMoveForwardToNextFrame = false;
g.NavMoveKeyMods = g.IO.KeyMods; g.NavMoveKeyMods = g.IO.KeyMods;
g.NavTabbingCounter = 0;
g.NavMoveResultLocal.Clear(); g.NavMoveResultLocal.Clear();
g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultLocalVisible.Clear();
g.NavMoveResultOther.Clear(); g.NavMoveResultOther.Clear();
g.NavTabbingCounter = 0;
g.NavTabbingResultFirst.Clear();
NavUpdateAnyRequestFlag(); NavUpdateAnyRequestFlag();
} }
@ -10590,7 +10639,7 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (layer == ImGuiNavLayer_Main) if (layer == ImGuiNavLayer_Main)
g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests?
ImGuiWindow* window = g.NavWindow; ImGuiWindow* window = g.NavWindow;
if (window->NavLastIds[layer] != 0) if (window->NavLastIds[layer] != 0)
{ {
@ -11071,7 +11120,6 @@ void ImGui::NavUpdateCreateTabbingRequest()
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; 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, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
g.NavTabbingResultFirst.Clear();
g.NavTabbingCounter = -1; g.NavTabbingCounter = -1;
} }
@ -17954,6 +18002,13 @@ void ImGui::ShowMetricsWindow(bool* p_open)
TreePop(); TreePop();
} }
// Details for InputText
if (TreeNode("InputText"))
{
DebugNodeInputTextState(&g.InputTextState);
TreePop();
}
// Details for Docking // Details for Docking
#ifdef IMGUI_HAS_DOCK #ifdef IMGUI_HAS_DOCK
if (TreeNode("Docking")) if (TreeNode("Docking"))
@ -18840,8 +18895,8 @@ void ImGui::ShowStackToolWindow(bool* p_open)
if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiKey_ModCtrl) && IsKeyPressed(ImGuiKey_C)) if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiKey_ModCtrl) && IsKeyPressed(ImGuiKey_C))
{ {
tool->CopyToClipboardLastTime = (float)g.Time; tool->CopyToClipboardLastTime = (float)g.Time;
char* p = g.TempBuffer; char* p = g.TempBuffer.Data;
char* p_end = p + IM_ARRAYSIZE(g.TempBuffer); char* p_end = p + g.TempBuffer.Size;
for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++) for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++)
{ {
*p++ = '/'; *p++ = '/';
@ -18855,7 +18910,7 @@ void ImGui::ShowStackToolWindow(bool* p_open)
} }
} }
*p = '\0'; *p = '\0';
SetClipboardText(g.TempBuffer); SetClipboardText(g.TempBuffer.Data);
} }
// Display decorated stack // Display decorated stack
@ -18873,8 +18928,8 @@ void ImGui::ShowStackToolWindow(bool* p_open)
TableNextColumn(); TableNextColumn();
Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0); Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
TableNextColumn(); TableNextColumn();
StackToolFormatLevelInfo(tool, n, true, g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer)); StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size);
TextUnformatted(g.TempBuffer); TextUnformatted(g.TempBuffer.Data);
TableNextColumn(); TableNextColumn();
Text("0x%08X", info->ID); Text("0x%08X", info->ID);
if (n == tool->Results.Size - 1) if (n == tool->Results.Size - 1)

@ -65,7 +65,7 @@ Index of this file:
// Version // Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
#define IMGUI_VERSION "1.88 WIP" #define IMGUI_VERSION "1.88 WIP"
#define IMGUI_VERSION_NUM 18724 #define IMGUI_VERSION_NUM 18728
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_TABLE #define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch #define IMGUI_HAS_VIEWPORT // Viewport WIP branch
@ -1402,6 +1402,8 @@ enum ImGuiSortDirection_
ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc. ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc.
}; };
// Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87)
// Keys value >= 512 are named keys (>= 1.87)
enum ImGuiKey_ enum ImGuiKey_
{ {
// Keyboard // Keyboard
@ -1502,10 +1504,10 @@ enum ImGuiKey_
ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys
ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in KeysData[0] ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in io.KeysData[0]. Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET).
#else #else
ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys
ImGuiKey_KeysData_OFFSET = 0 // First key stored in KeysData[0] ImGuiKey_KeysData_OFFSET = 0 // First key stored in io.KeysData[0]. Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET).
#endif #endif
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@ -1524,6 +1526,7 @@ enum ImGuiModFlags_
}; };
// Gamepad/Keyboard navigation // Gamepad/Keyboard navigation
// Since >= 1.87 backends you generally don't need to care about this enum since io.NavInputs[] is setup automatically. This might become private/internal some day.
// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. // Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls.
// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame().
// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets. // Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets.
@ -1576,7 +1579,7 @@ enum ImGuiConfigFlags_
ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // [BETA: Don't use] FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // [BETA: Don't use] FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application.
ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // [BETA: Don't use] FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // [BETA: Don't use] FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress.
// User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core Dear ImGui) // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui)
ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware.
ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse.
}; };
@ -1629,10 +1632,10 @@ enum ImGuiCol_
ImGuiCol_Separator, ImGuiCol_Separator,
ImGuiCol_SeparatorHovered, ImGuiCol_SeparatorHovered,
ImGuiCol_SeparatorActive, ImGuiCol_SeparatorActive,
ImGuiCol_ResizeGrip, ImGuiCol_ResizeGrip, // Resize grip in lower-right and lower-left corners of windows.
ImGuiCol_ResizeGripHovered, ImGuiCol_ResizeGripHovered,
ImGuiCol_ResizeGripActive, ImGuiCol_ResizeGripActive,
ImGuiCol_Tab, ImGuiCol_Tab, // TabItem in a TabBar
ImGuiCol_TabHovered, ImGuiCol_TabHovered,
ImGuiCol_TabActive, ImGuiCol_TabActive,
ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocused,
@ -1649,7 +1652,7 @@ enum ImGuiCol_
ImGuiCol_TableRowBg, // Table row background (even rows) ImGuiCol_TableRowBg, // Table row background (even rows)
ImGuiCol_TableRowBgAlt, // Table row background (odd rows) ImGuiCol_TableRowBgAlt, // Table row background (odd rows)
ImGuiCol_TextSelectedBg, ImGuiCol_TextSelectedBg,
ImGuiCol_DragDropTarget, ImGuiCol_DragDropTarget, // Rectangle highlighting a drop target
ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item
ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB
ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active
@ -1884,6 +1887,7 @@ struct ImVector
inline void resize(int new_size, const T& v) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; } inline void resize(int new_size, const T& v) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; }
inline void shrink(int new_size) { IM_ASSERT(new_size <= Size); Size = new_size; } // Resize a vector to a smaller size, guaranteed not to cause a reallocation inline void shrink(int new_size) { IM_ASSERT(new_size <= Size); Size = new_size; } // Resize a vector to a smaller size, guaranteed not to cause a reallocation
inline void reserve(int new_capacity) { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; } inline void reserve(int new_capacity) { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
inline void reserve_discard(int new_capacity) { if (new_capacity <= Capacity) return; if (Data) IM_FREE(Data); Data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); Capacity = new_capacity; }
// NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden. // NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden.
inline void push_back(const T& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; } inline void push_back(const T& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
@ -2066,9 +2070,10 @@ struct ImGuiIO
IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate
IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string
IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode.
IMGUI_API void SetAppAcceptingEvents(bool accepting_events); // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually
IMGUI_API void ClearInputKeys(); // [Internal] Release all keys IMGUI_API void ClearInputKeys(); // [Internal] Release all keys
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.
//------------------------------------------------------------------ //------------------------------------------------------------------
// Output - Updated by NewFrame() or EndFrame()/Render() // Output - Updated by NewFrame() or EndFrame()/Render()
@ -2137,7 +2142,8 @@ struct ImGuiIO
float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDuration[ImGuiNavInput_COUNT];
float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT];
float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.
bool AppFocusLost; bool AppFocusLost; // Only modify via AddFocusEvent()
bool AppAcceptingEvents; // Only modify via SetAppAcceptingEvents()
ImS8 BackendUsingLegacyKeyArrays; // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[] ImS8 BackendUsingLegacyKeyArrays; // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[]
bool BackendUsingLegacyNavInputArray; // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly bool BackendUsingLegacyNavInputArray; // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly
ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16()

@ -3275,59 +3275,58 @@ static void ShowDemoWindowLayout()
ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f"); ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f");
ImGui::TextWrapped("(Click and drag to scroll)"); ImGui::TextWrapped("(Click and drag to scroll)");
HelpMarker(
"(Left) Using ImGui::PushClipRect():\n"
"Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
"(use this if you want your clipping rectangle to affect interactions)\n\n"
"(Center) Using ImDrawList::PushClipRect():\n"
"Will alter ImDrawList rendering only.\n"
"(use this as a shortcut if you are only using ImDrawList calls)\n\n"
"(Right) Using ImDrawList::AddText() with a fine ClipRect:\n"
"Will alter only this specific ImDrawList::AddText() rendering.\n"
"This is often used internally to avoid altering the clipping rectangle and minimize draw calls.");
for (int n = 0; n < 3; n++) for (int n = 0; n < 3; n++)
{ {
if (n > 0) if (n > 0)
ImGui::SameLine(); ImGui::SameLine();
ImGui::PushID(n);
ImGui::BeginGroup(); // Lock X position
ImGui::InvisibleButton("##empty", size); ImGui::PushID(n);
ImGui::InvisibleButton("##canvas", size);
if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left))
{ {
offset.x += ImGui::GetIO().MouseDelta.x; offset.x += ImGui::GetIO().MouseDelta.x;
offset.y += ImGui::GetIO().MouseDelta.y; offset.y += ImGui::GetIO().MouseDelta.y;
} }
ImGui::PopID();
if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped.
continue;
const ImVec2 p0 = ImGui::GetItemRectMin(); const ImVec2 p0 = ImGui::GetItemRectMin();
const ImVec2 p1 = ImGui::GetItemRectMax(); const ImVec2 p1 = ImGui::GetItemRectMax();
const char* text_str = "Line 1 hello\nLine 2 clip me!"; const char* text_str = "Line 1 hello\nLine 2 clip me!";
const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y); const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);
ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImDrawList* draw_list = ImGui::GetWindowDrawList();
switch (n) switch (n)
{ {
case 0: case 0:
HelpMarker(
"Using ImGui::PushClipRect():\n"
"Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
"(use this if you want your clipping rectangle to affect interactions)");
ImGui::PushClipRect(p0, p1, true); ImGui::PushClipRect(p0, p1, true);
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
draw_list->AddText(text_pos, IM_COL32_WHITE, text_str); draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
ImGui::PopClipRect(); ImGui::PopClipRect();
break; break;
case 1: case 1:
HelpMarker(
"Using ImDrawList::PushClipRect():\n"
"Will alter ImDrawList rendering only.\n"
"(use this as a shortcut if you are only using ImDrawList calls)");
draw_list->PushClipRect(p0, p1, true); draw_list->PushClipRect(p0, p1, true);
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
draw_list->AddText(text_pos, IM_COL32_WHITE, text_str); draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
draw_list->PopClipRect(); draw_list->PopClipRect();
break; break;
case 2: case 2:
HelpMarker(
"Using ImDrawList::AddText() with a fine ClipRect:\n"
"Will alter only this specific ImDrawList::AddText() rendering.\n"
"(this is often used internally to avoid altering the clipping rectangle and minimize draw calls)");
ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert. ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect); draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);
break; break;
} }
ImGui::EndGroup();
ImGui::PopID();
} }
ImGui::TreePop(); ImGui::TreePop();
@ -3620,19 +3619,12 @@ static void ShowDemoWindowPopups()
ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
ImGui::Separator(); ImGui::Separator();
// Note: As a quirk in this very specific example, we want to differentiate the parent of this menu from the
// parent of the various popup menus above. To do so we are encloding the items in a PushID()/PopID() block
// to make them two different menusets. If we don't, opening any popup above and hovering our menu here would
// open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it,
// which is the desired behavior for regular menus.
ImGui::PushID("foo");
ImGui::MenuItem("Menu item", "CTRL+M"); ImGui::MenuItem("Menu item", "CTRL+M");
if (ImGui::BeginMenu("Menu inside a regular window")) if (ImGui::BeginMenu("Menu inside a regular window"))
{ {
ShowExampleMenuFile(); ShowExampleMenuFile();
ImGui::EndMenu(); ImGui::EndMenu();
} }
ImGui::PopID();
ImGui::Separator(); ImGui::Separator();
ImGui::TreePop(); ImGui::TreePop();
} }
@ -4914,7 +4906,7 @@ static void ShowDemoWindowTables()
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f); ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f);
ImGui::TableSetColumnIndex(2); ImGui::TableSetColumnIndex(2);
ImGui::SliderFloat("float2", &dummy_f, 0.0f, 1.0f); ImGui::SliderFloat("##float2", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned
ImGui::PopID(); ImGui::PopID();
} }
ImGui::EndTable(); ImGui::EndTable();
@ -5828,6 +5820,11 @@ static void ShowDemoWindowMisc()
{ 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V } { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
}; };
// Elements rendered manually via ImDrawList API are not clipped automatically.
// While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
ImGui::Dummy(ImVec2(board_max.x - board_min.x, board_max.y - board_min.y));
if (ImGui::IsItemVisible())
{
ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->PushClipRect(board_min, board_max, true); draw_list->PushClipRect(board_min, board_max, true);
for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++) for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
@ -5847,7 +5844,7 @@ static void ShowDemoWindowMisc()
draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding); draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
} }
draw_list->PopClipRect(); draw_list->PopClipRect();
ImGui::Dummy(ImVec2(board_max.x - board_min.x, board_max.y - board_min.y)); }
} }
ImGui::TreePop(); ImGui::TreePop();
} }
@ -7455,7 +7452,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open)
static void ShowExampleAppFullscreen(bool* p_open) static void ShowExampleAppFullscreen(bool* p_open)
{ {
static bool use_work_area = true; static bool use_work_area = true;
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
// We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.) // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)
// Based on your use case you may want one of the other. // Based on your use case you may want one of the other.

@ -343,6 +343,8 @@ static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c =
// 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);
IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3);
IMGUI_API void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) IM_FMTARGS(3);
IMGUI_API void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) IM_FMTLIST(3);
IMGUI_API const char* ImParseFormatFindStart(const char* format); IMGUI_API const char* ImParseFormatFindStart(const char* format);
IMGUI_API const char* ImParseFormatFindEnd(const char* format); IMGUI_API const char* ImParseFormatFindEnd(const char* format);
IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size); IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size);
@ -1048,12 +1050,13 @@ struct ImGuiPopupData
ImGuiID PopupId; // Set on OpenPopup() ImGuiID PopupId; // Set on OpenPopup()
ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
ImGuiWindow* SourceWindow; // Set on OpenPopup() copy of NavWindow at the time of opening the popup ImGuiWindow* SourceWindow; // Set on OpenPopup() copy of NavWindow at the time of opening the popup
int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value
int OpenFrameCount; // Set on OpenPopup() int OpenFrameCount; // Set on OpenPopup()
ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items)
ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)
ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup
ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; } ImGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; }
}; };
enum ImGuiNextWindowDataFlags_ enum ImGuiNextWindowDataFlags_
@ -2002,7 +2005,7 @@ struct ImGuiContext
int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1. int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.
int WantCaptureKeyboardNextFrame; // " int WantCaptureKeyboardNextFrame; // "
int WantTextInputNextFrame; int WantTextInputNextFrame;
char TempBuffer[1024 * 3 + 1]; // Temporary text buffer ImVector<char> TempBuffer; // Temporary text buffer
ImGuiContext(ImFontAtlas* shared_font_atlas) ImGuiContext(ImFontAtlas* shared_font_atlas)
{ {
@ -2160,7 +2163,6 @@ struct ImGuiContext
FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
FramerateSecPerFrameAccum = 0.0f; FramerateSecPerFrameAccum = 0.0f;
WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
memset(TempBuffer, 0, sizeof(TempBuffer));
} }
}; };
@ -3169,6 +3171,7 @@ namespace ImGui
IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);
IMGUI_API void DebugNodeTable(ImGuiTable* table); IMGUI_API void DebugNodeTable(ImGuiTable* table);
IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings); IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings);
IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state);
IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label);
IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings);
IMGUI_API void DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label); IMGUI_API void DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);

@ -2370,7 +2370,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
// Don't attempt to merge if there are multiple draw calls within the column // Don't attempt to merge if there are multiple draw calls within the column
ImDrawChannel* src_channel = &splitter->_Channels[channel_no]; ImDrawChannel* src_channel = &splitter->_Channels[channel_no];
if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback != NULL) // Equivalent of PopUnusedDrawCmd() if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()
src_channel->_CmdBuffer.pop_back(); src_channel->_CmdBuffer.pop_back();
if (src_channel->_CmdBuffer.Size != 1) if (src_channel->_CmdBuffer.Size != 1)
continue; continue;

@ -276,9 +276,9 @@ void ImGui::TextV(const char* fmt, va_list args)
return; return;
// FIXME-OPT: Handle the %s shortcut? // FIXME-OPT: Handle the %s shortcut?
ImGuiContext& g = *GImGui; const char* text, *text_end;
const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); ImFormatStringToTempBufferV(&text, &text_end, fmt, args);
TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
} }
void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
@ -359,8 +359,8 @@ void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const float w = CalcItemWidth(); const float w = CalcItemWidth();
const char* value_text_begin = &g.TempBuffer[0]; const char* value_text_begin, *value_text_end;
const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args);
const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false); const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, NULL, true);
@ -395,8 +395,8 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const char* text_begin = g.TempBuffer; const char* text_begin, *text_end;
const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args);
const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y); // Empty text doesn't add padding const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y); // Empty text doesn't add padding
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
@ -1918,9 +1918,9 @@ static const char* PatchFormatStringFloatToInt(const char* fmt)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
if (fmt_start == fmt && fmt_end[0] == 0) if (fmt_start == fmt && fmt_end[0] == 0)
return "%d"; return "%d";
ImGuiContext& g = *GImGui; const char* tmp_format;
ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision. ImFormatStringToTempBuffer(&tmp_format, NULL, "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision.
return g.TempBuffer; return tmp_format;
#else #else
IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d" IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d"
#endif #endif
@ -3579,6 +3579,7 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f
// - InputTextReindexLines() [Internal] // - InputTextReindexLines() [Internal]
// - InputTextReindexLinesRange() [Internal] // - InputTextReindexLinesRange() [Internal]
// - InputTextEx() [Internal] // - InputTextEx() [Internal]
// - DebugNodeInputTextState() [Internal]
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
@ -3936,6 +3937,41 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
return true; return true;
} }
// Find the shortest single replacement we can make to get the new text from the old text.
// Important: needs to be run before TextW is rewritten with the new characters because calling STB_TEXTEDIT_GETCHAR() at the end.
// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a)
{
ImGuiContext& g = *GImGui;
const ImWchar* old_buf = state->TextW.Data;
const int old_length = state->CurLenW;
const int new_length = ImTextCountCharsFromUtf8(new_buf_a, new_buf_a + new_length_a);
g.TempBuffer.reserve_discard((new_length + 1) * sizeof(ImWchar));
ImWchar* new_buf = (ImWchar*)(void*)g.TempBuffer.Data;
ImTextStrFromUtf8(new_buf, new_length + 1, new_buf_a, new_buf_a + new_length_a);
const int shorter_length = ImMin(old_length, new_length);
int first_diff;
for (first_diff = 0; first_diff < shorter_length; first_diff++)
if (old_buf[first_diff] != new_buf[first_diff])
break;
if (first_diff == old_length && first_diff == new_length)
return;
int old_last_diff = old_length - 1;
int new_last_diff = new_length - 1;
for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)
if (old_buf[old_last_diff] != new_buf[new_last_diff])
break;
const int insert_len = new_last_diff - first_diff + 1;
const int delete_len = old_last_diff - first_diff + 1;
if (insert_len > 0 || delete_len > 0)
if (STB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len))
for (int i = 0; i < delete_len; i++)
p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i);
}
// Edit a string of text // Edit a string of text
// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". // - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match // This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
@ -4056,17 +4092,21 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
memcpy(state->InitialTextA.Data, buf, buf_len + 1); memcpy(state->InitialTextA.Data, buf, buf_len + 1);
// Preserve cursor position and undo/redo stack if we come back to same widget
// FIXME: Since we reworked this on 2022/06, may want to differenciate recycle_cursor vs recycle_undostate?
bool recycle_state = (state->ID == id && !init_changed_specs);
if (recycle_state && (state->CurLenA != buf_len || (state->TextAIsValid && strncmp(state->TextA.Data, buf, buf_len) != 0)))
recycle_state = false;
// Start edition // Start edition
const char* buf_end = NULL; const char* buf_end = NULL;
state->ID = id;
state->TextW.resize(buf_size + 1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string. state->TextW.resize(buf_size + 1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string.
state->TextA.resize(0); state->TextA.resize(0);
state->TextAIsValid = false; // TextA is not valid yet (we will display buf until then) state->TextAIsValid = false; // TextA is not valid yet (we will display buf until then)
state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end); state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end);
state->CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. state->CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
// Preserve cursor position and undo/redo stack if we come back to same widget
// FIXME: For non-readonly widgets we might be able to require that TextAIsValid && TextA == buf ? (untested) and discard undo stack if user buffer has changed.
const bool recycle_state = (state->ID == id && !init_changed_specs);
if (recycle_state) if (recycle_state)
{ {
// Recycle existing cursor/selection/undo stack but clamp position // Recycle existing cursor/selection/undo stack but clamp position
@ -4075,7 +4115,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
} }
else else
{ {
state->ID = id;
state->ScrollX = 0.0f; state->ScrollX = 0.0f;
stb_textedit_initialize_state(&state->Stb, !is_multiline); stb_textedit_initialize_state(&state->Stb, !is_multiline);
} }
@ -4520,8 +4559,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (buf_dirty) if (buf_dirty)
{ {
IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
if (callback_data.BufTextLen > backup_current_text_length && is_resizable) if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); // Worse case scenario resize
state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL); state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL);
state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
state->CursorAnimReset(); state->CursorAnimReset();
@ -4825,6 +4865,40 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
return value_changed; return value_changed;
} }
void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
{
#ifndef IMGUI_DISABLE_METRICS_WINDOW
ImGuiContext& g = *GImGui;
ImStb::STB_TexteditState* stb_state = &state->Stb;
ImStb::StbUndoState* undo_state = &stb_state->undostate;
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, state->CurLenW, stb_state->cursor, stb_state->select_start, stb_state->select_end);
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 15), true)) // Visualize undo state
{
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
for (int n = 0; n < STB_TEXTEDIT_UNDOSTATECOUNT; n++)
{
ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n];
const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' ';
if (undo_rec_type == ' ')
BeginDisabled();
char buf[64] = "";
if (undo_rec_type != ' ' && undo_rec->char_storage != -1)
ImTextStrToUtf8(buf, IM_ARRAYSIZE(buf), undo_state->undo_char + undo_rec->char_storage, undo_state->undo_char + undo_rec->char_storage + undo_rec->insert_length);
Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%s\"",
undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf);
if (undo_rec_type == ' ')
EndDisabled();
}
PopStyleVar();
}
EndChild();
#else
IM_UNUSED(state);
#endif
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. // [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -5806,9 +5880,9 @@ bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char
if (window->SkipItems) if (window->SkipItems)
return false; return false;
ImGuiContext& g = *GImGui; const char* label, *label_end;
const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); ImFormatStringToTempBufferV(&label, &label_end, fmt, args);
return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); return TreeNodeBehavior(window->GetID(str_id), flags, label, label_end);
} }
bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
@ -5817,9 +5891,9 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char
if (window->SkipItems) if (window->SkipItems)
return false; return false;
ImGuiContext& g = *GImGui; const char* label, *label_end;
const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); ImFormatStringToTempBufferV(&label, &label_end, fmt, args);
return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); return TreeNodeBehavior(window->GetID(ptr_id), flags, label, label_end);
} }
bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
@ -6878,14 +6952,19 @@ static bool IsRootOfOpenMenuSet()
if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu))
return false; return false;
// Initially we used 'OpenParentId' to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) based on parent ID. // Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others
// (e.g. inside menu bar vs loose menu items) based on parent ID.
// This would however prevent the use of e.g. PuhsID() user code submitting menus. // This would however prevent the use of e.g. PuhsID() user code submitting menus.
// Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag,
// making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects.
// Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup
// doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu. // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu.
// In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer.
// This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still
// open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart
// it likely won't be a problem anyone runs into.
const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size];
return (/*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); return (window->DC.NavLayerCurrent == upper_popup->ParentNavLayer && upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu));
} }
bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
@ -6900,7 +6979,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None);
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
// The first menu in a hierarchy isn't so hovering doesn't get accross (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. // The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation.
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
if (window->Flags & ImGuiWindowFlags_ChildMenu) if (window->Flags & ImGuiWindowFlags_ChildMenu)
flags |= ImGuiWindowFlags_ChildWindow; flags |= ImGuiWindowFlags_ChildWindow;

@ -66,7 +66,7 @@ int main(int argc, char** argv)
char Encode85Byte(unsigned int x) char Encode85Byte(unsigned int x)
{ {
x = (x % 85) + 35; x = (x % 85) + 35;
return (x >= '\\') ? x + 1 : x; return (char)((x >= '\\') ? x + 1 : x);
} }
bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression, bool use_static) bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression, bool use_static)
@ -263,17 +263,17 @@ static int stb_compress_chunk(stb_uchar *history,
int best = 2, dist=0; int best = 2, dist=0;
if (q+65536 > end) if (q+65536 > end)
match_max = end-q; match_max = (stb_uint)(end-q);
else else
match_max = 65536; match_max = 65536;
#define stb__nc(b,d) ((d) <= window && ((b) > 9 || stb_not_crap(b,d))) #define stb__nc(b,d) ((d) <= window && ((b) > 9 || stb_not_crap((int)(b),(int)(d))))
#define STB__TRY(t,p) /* avoid retrying a match we already tried */ \ #define STB__TRY(t,p) /* avoid retrying a match we already tried */ \
if (p ? dist != q-t : 1) \ if (p ? dist != (int)(q-t) : 1) \
if ((m = stb_matchlen(t, q, match_max)) > best) \ if ((m = stb_matchlen(t, q, match_max)) > best) \
if (stb__nc(m,q-(t))) \ if (stb__nc(m,q-(t))) \
best = m, dist = q - (t) best = m, dist = (int)(q - (t))
// rather than search for all matches, only try 4 candidate locations, // rather than search for all matches, only try 4 candidate locations,
// chosen based on 4 different hash functions of different lengths. // chosen based on 4 different hash functions of different lengths.
@ -299,24 +299,24 @@ static int stb_compress_chunk(stb_uchar *history,
if (best < 3) { // fast path literals if (best < 3) { // fast path literals
++q; ++q;
} else if (best > 2 && best <= 0x80 && dist <= 0x100) { } else if (best > 2 && best <= 0x80 && dist <= 0x100) {
outliterals(lit_start, q-lit_start); lit_start = (q += best); outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
stb_out(0x80 + best-1); stb_out(0x80 + best-1);
stb_out(dist-1); stb_out(dist-1);
} else if (best > 5 && best <= 0x100 && dist <= 0x4000) { } else if (best > 5 && best <= 0x100 && dist <= 0x4000) {
outliterals(lit_start, q-lit_start); lit_start = (q += best); outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
stb_out2(0x4000 + dist-1); stb_out2(0x4000 + dist-1);
stb_out(best-1); stb_out(best-1);
} else if (best > 7 && best <= 0x100 && dist <= 0x80000) { } else if (best > 7 && best <= 0x100 && dist <= 0x80000) {
outliterals(lit_start, q-lit_start); lit_start = (q += best); outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
stb_out3(0x180000 + dist-1); stb_out3(0x180000 + dist-1);
stb_out(best-1); stb_out(best-1);
} else if (best > 8 && best <= 0x10000 && dist <= 0x80000) { } else if (best > 8 && best <= 0x10000 && dist <= 0x80000) {
outliterals(lit_start, q-lit_start); lit_start = (q += best); outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
stb_out3(0x100000 + dist-1); stb_out3(0x100000 + dist-1);
stb_out2(best-1); stb_out2(best-1);
} else if (best > 9 && dist <= 0x1000000) { } else if (best > 9 && dist <= 0x1000000) {
if (best > 65536) best = 65536; if (best > 65536) best = 65536;
outliterals(lit_start, q-lit_start); lit_start = (q += best); outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
if (best <= 0x100) { if (best <= 0x100) {
stb_out(0x06); stb_out(0x06);
stb_out3(dist-1); stb_out3(dist-1);
@ -336,10 +336,10 @@ static int stb_compress_chunk(stb_uchar *history,
q = start+length; q = start+length;
// the literals are everything from lit_start to q // the literals are everything from lit_start to q
*pending_literals = (q - lit_start); *pending_literals = (int)(q - lit_start);
stb__running_adler = stb_adler32(stb__running_adler, start, q - start); stb__running_adler = stb_adler32(stb__running_adler, start, (stb_uint)(q - start));
return q - start; return (int)(q - start);
} }
static int stb_compress_inner(stb_uchar *input, stb_uint length) static int stb_compress_inner(stb_uchar *input, stb_uint length)
@ -384,5 +384,5 @@ stb_uint stb_compress(stb_uchar *out, stb_uchar *input, stb_uint length)
stb_compress_inner(input, length); stb_compress_inner(input, length);
return stb__out - out; return (stb_uint)(stb__out - out);
} }

Loading…
Cancel
Save