|
|
@@ -1,4 +1,4 @@
|
|
|
-// dear imgui, v1.89.3 WIP
|
|
|
+// dear imgui, v1.89.5 WIP
|
|
|
// (widgets code)
|
|
|
|
|
|
/*
|
|
|
@@ -32,12 +32,12 @@ Index of this file:
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
#endif
|
|
|
|
|
|
-#include "imgui.h"
|
|
|
-#ifndef IMGUI_DISABLE
|
|
|
-
|
|
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
|
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
|
#endif
|
|
|
+
|
|
|
+#include "imgui.h"
|
|
|
+#ifndef IMGUI_DISABLE
|
|
|
#include "imgui_internal.h"
|
|
|
|
|
|
// System includes
|
|
|
@@ -498,8 +498,9 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
|
|
|
g.HoveredWindow = window;
|
|
|
|
|
|
#ifdef IMGUI_ENABLE_TEST_ENGINE
|
|
|
+ // Alternate registration spot, for when caller didn't use ItemAdd()
|
|
|
if (id != 0 && g.LastItemData.ID != id)
|
|
|
- IMGUI_TEST_ENGINE_ITEM_ADD(bb, id);
|
|
|
+ IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL);
|
|
|
#endif
|
|
|
|
|
|
bool pressed = false;
|
|
|
@@ -609,10 +610,11 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
|
|
|
bool nav_activated_by_inputs = (g.NavActivatePressedId == id);
|
|
|
if (!nav_activated_by_inputs && (flags & ImGuiButtonFlags_Repeat))
|
|
|
{
|
|
|
- // Avoid pressing both keys from triggering double amount of repeat events
|
|
|
+ // Avoid pressing multiple keys from triggering excessive amount of repeat events
|
|
|
const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space);
|
|
|
- const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_NavGamepadActivate);
|
|
|
- const float t1 = ImMax(key1->DownDuration, key2->DownDuration);
|
|
|
+ const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter);
|
|
|
+ const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate);
|
|
|
+ const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration);
|
|
|
nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
|
|
|
}
|
|
|
if (nav_activated_by_code || nav_activated_by_inputs)
|
|
|
@@ -620,7 +622,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
|
|
|
// Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
|
|
|
pressed = true;
|
|
|
SetActiveID(id, window);
|
|
|
- g.ActiveIdSource = ImGuiInputSource_Nav;
|
|
|
+ g.ActiveIdSource = g.NavInputSource;
|
|
|
if (!(flags & ImGuiButtonFlags_NoNavFocus))
|
|
|
SetFocusID(id, window);
|
|
|
}
|
|
|
@@ -658,7 +660,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
|
|
|
if (!(flags & ImGuiButtonFlags_NoNavFocus))
|
|
|
g.NavDisableHighlight = true;
|
|
|
}
|
|
|
- else if (g.ActiveIdSource == ImGuiInputSource_Nav)
|
|
|
+ else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
|
|
|
{
|
|
|
// When activated using Nav, we hold on the ActiveID until activation button is released
|
|
|
if (g.NavActivateDownId != id)
|
|
|
@@ -1384,6 +1386,7 @@ void ImGui::AlignTextToFramePadding()
|
|
|
}
|
|
|
|
|
|
// Horizontal/vertical separating line
|
|
|
+// FIXME: Surprisingly, this seemingly simple widget is adjacent to MANY different legacy/tricky layout issues.
|
|
|
void ImGui::SeparatorEx(ImGuiSeparatorFlags flags)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
@@ -1393,20 +1396,19 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags)
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected
|
|
|
|
|
|
- float thickness_draw = 1.0f;
|
|
|
- float thickness_layout = 0.0f;
|
|
|
+ const float thickness = 1.0f; // Cannot use g.Style.SeparatorTextSize yet for various reasons.
|
|
|
if (flags & ImGuiSeparatorFlags_Vertical)
|
|
|
{
|
|
|
- // Vertical separator, for menu bars (use current line height). Not exposed because it is misleading and it doesn't have an effect on regular layout.
|
|
|
+ // Vertical separator, for menu bars (use current line height).
|
|
|
float y1 = window->DC.CursorPos.y;
|
|
|
float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y;
|
|
|
- const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness_draw, y2));
|
|
|
- ItemSize(ImVec2(thickness_layout, 0.0f));
|
|
|
+ const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness, y2));
|
|
|
+ ItemSize(ImVec2(thickness, 0.0f));
|
|
|
if (!ItemAdd(bb, 0))
|
|
|
return;
|
|
|
|
|
|
// Draw
|
|
|
- window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
|
|
|
+ window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));
|
|
|
if (g.LogEnabled)
|
|
|
LogText(" |");
|
|
|
}
|
|
|
@@ -1434,13 +1436,14 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags)
|
|
|
|
|
|
// We don't provide our width to the layout so that it doesn't get feed back into AutoFit
|
|
|
// FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell)
|
|
|
- const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness_draw));
|
|
|
- ItemSize(ImVec2(0.0f, thickness_layout));
|
|
|
+ const float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change.
|
|
|
+ const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness));
|
|
|
+ ItemSize(ImVec2(0.0f, thickness_for_layout));
|
|
|
const bool item_visible = ItemAdd(bb, 0);
|
|
|
if (item_visible)
|
|
|
{
|
|
|
// Draw
|
|
|
- window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x, bb.Min.y), GetColorU32(ImGuiCol_Separator));
|
|
|
+ window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));
|
|
|
if (g.LogEnabled)
|
|
|
LogRenderedText(&bb.Min, "--------------------------------\n");
|
|
|
|
|
|
@@ -1466,6 +1469,71 @@ void ImGui::Separator()
|
|
|
SeparatorEx(flags);
|
|
|
}
|
|
|
|
|
|
+void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w)
|
|
|
+{
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiWindow* window = g.CurrentWindow;
|
|
|
+ ImGuiStyle& style = g.Style;
|
|
|
+
|
|
|
+ const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
|
|
+ const ImVec2 pos = window->DC.CursorPos;
|
|
|
+ const ImVec2 padding = style.SeparatorTextPadding;
|
|
|
+
|
|
|
+ const float separator_thickness = style.SeparatorTextBorderSize;
|
|
|
+ const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness));
|
|
|
+ const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y));
|
|
|
+ const float text_baseline_y = ImFloor((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f));
|
|
|
+ ItemSize(min_size, text_baseline_y);
|
|
|
+ if (!ItemAdd(bb, id))
|
|
|
+ return;
|
|
|
+
|
|
|
+ const float sep1_x1 = pos.x;
|
|
|
+ const float sep2_x2 = bb.Max.x;
|
|
|
+ const float seps_y = ImFloor((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f);
|
|
|
+
|
|
|
+ const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f);
|
|
|
+ const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN
|
|
|
+
|
|
|
+ // This allows using SameLine() to position something in the 'extra_w'
|
|
|
+ window->DC.CursorPosPrevLine.x = label_pos.x + label_size.x;
|
|
|
+
|
|
|
+ const ImU32 separator_col = GetColorU32(ImGuiCol_Separator);
|
|
|
+ if (label_size.x > 0.0f)
|
|
|
+ {
|
|
|
+ const float sep1_x2 = label_pos.x - style.ItemSpacing.x;
|
|
|
+ const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x;
|
|
|
+ if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f)
|
|
|
+ window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness);
|
|
|
+ if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f)
|
|
|
+ window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
|
|
|
+ if (g.LogEnabled)
|
|
|
+ LogSetNextTextDecoration("---", NULL);
|
|
|
+ RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (g.LogEnabled)
|
|
|
+ LogText("---");
|
|
|
+ if (separator_thickness > 0.0f)
|
|
|
+ window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ImGui::SeparatorText(const char* label)
|
|
|
+{
|
|
|
+ ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ if (window->SkipItems)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want:
|
|
|
+ // - allow headers to be draggable items (would require a stable ID + a noticeable highlight)
|
|
|
+ // - this high-level entry point to allow formatting? (may require ID separate from formatted string)
|
|
|
+ // - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...'
|
|
|
+ // Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item,
|
|
|
+ // and then we can turn this into a format function.
|
|
|
+ SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f);
|
|
|
+}
|
|
|
+
|
|
|
// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise.
|
|
|
bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col)
|
|
|
{
|
|
|
@@ -2177,7 +2245,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
|
|
|
if (g.IO.KeyShift)
|
|
|
adjust_delta *= 10.0f;
|
|
|
}
|
|
|
- else if (g.ActiveIdSource == ImGuiInputSource_Nav)
|
|
|
+ else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
|
|
|
{
|
|
|
const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;
|
|
|
const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);
|
|
|
@@ -2284,7 +2352,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v
|
|
|
// Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation.
|
|
|
if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])
|
|
|
ClearActiveID();
|
|
|
- else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
|
|
|
+ else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
|
|
|
ClearActiveID();
|
|
|
}
|
|
|
if (g.ActiveId != id)
|
|
|
@@ -2344,18 +2412,18 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
|
|
const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
|
|
|
const bool clicked = hovered && IsMouseClicked(0, id);
|
|
|
const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id));
|
|
|
- const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id);
|
|
|
+ const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id);
|
|
|
if (make_active && (clicked || double_clicked))
|
|
|
SetKeyOwner(ImGuiKey_MouseLeft, id);
|
|
|
if (make_active && temp_input_allowed)
|
|
|
- if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id)
|
|
|
+ if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
|
|
|
temp_input_is_active = true;
|
|
|
|
|
|
// (Optional) simple click (without moving) turns Drag into an InputText
|
|
|
if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active)
|
|
|
if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))
|
|
|
{
|
|
|
- g.NavActivateId = g.NavActivateInputId = id;
|
|
|
+ g.NavActivateId = id;
|
|
|
g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
|
|
|
temp_input_is_active = true;
|
|
|
}
|
|
|
@@ -2396,7 +2464,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
|
|
if (label_size.x > 0.0f)
|
|
|
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
|
|
|
|
|
- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
|
|
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
|
|
|
return value_changed;
|
|
|
}
|
|
|
|
|
|
@@ -2768,7 +2836,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
|
|
|
set_new_value = true;
|
|
|
}
|
|
|
}
|
|
|
- else if (g.ActiveIdSource == ImGuiInputSource_Nav)
|
|
|
+ else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
|
|
|
{
|
|
|
if (g.ActiveIdIsJustActivated)
|
|
|
{
|
|
|
@@ -2951,11 +3019,11 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
|
|
// Tabbing or CTRL-clicking on Slider turns it into an input box
|
|
|
const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
|
|
|
const bool clicked = hovered && IsMouseClicked(0, id);
|
|
|
- const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id);
|
|
|
+ const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id);
|
|
|
if (make_active && clicked)
|
|
|
SetKeyOwner(ImGuiKey_MouseLeft, id);
|
|
|
if (make_active && temp_input_allowed)
|
|
|
- if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)
|
|
|
+ if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
|
|
|
temp_input_is_active = true;
|
|
|
|
|
|
if (make_active && !temp_input_is_active)
|
|
|
@@ -2999,7 +3067,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
|
|
if (label_size.x > 0.0f)
|
|
|
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
|
|
|
|
|
- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
|
|
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
|
|
|
return value_changed;
|
|
|
}
|
|
|
|
|
|
@@ -3113,7 +3181,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
|
|
|
|
|
|
const bool hovered = ItemHoverable(frame_bb, id);
|
|
|
const bool clicked = hovered && IsMouseClicked(0, id);
|
|
|
- if (clicked || g.NavActivateId == id || g.NavActivateInputId == id)
|
|
|
+ if (clicked || g.NavActivateId == id)
|
|
|
{
|
|
|
if (clicked)
|
|
|
SetKeyOwner(ImGuiKey_MouseLeft, id);
|
|
|
@@ -3397,7 +3465,12 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
|
|
flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
|
|
|
|
|
|
bool value_changed = false;
|
|
|
- if (p_step != NULL)
|
|
|
+ if (p_step == NULL)
|
|
|
+ {
|
|
|
+ if (InputText(label, buf, IM_ARRAYSIZE(buf), flags))
|
|
|
+ value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
const float button_size = GetFrameHeight();
|
|
|
|
|
|
@@ -3406,7 +3479,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
|
|
SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
|
|
|
if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view
|
|
|
value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
|
|
|
- IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags);
|
|
|
+ IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
|
|
|
|
|
|
// Step buttons
|
|
|
const ImVec2 backup_frame_padding = style.FramePadding;
|
|
|
@@ -3440,11 +3513,6 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
|
|
PopID();
|
|
|
EndGroup();
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- if (InputText(label, buf, IM_ARRAYSIZE(buf), flags))
|
|
|
- value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
|
|
|
- }
|
|
|
if (value_changed)
|
|
|
MarkItemEdited(g.LastItemData.ID);
|
|
|
|
|
|
@@ -3790,7 +3858,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
|
|
|
return;
|
|
|
|
|
|
// Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!)
|
|
|
- ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiContext& g = *Ctx;
|
|
|
ImGuiInputTextState* edit_state = &g.InputTextState;
|
|
|
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
|
|
|
IM_ASSERT(Buf == edit_state->TextA.Data);
|
|
|
@@ -3894,8 +3962,9 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
|
|
|
// Custom callback filter
|
|
|
if (flags & ImGuiInputTextFlags_CallbackCharFilter)
|
|
|
{
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
ImGuiInputTextCallbackData callback_data;
|
|
|
- memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
|
|
|
+ callback_data.Ctx = &g;
|
|
|
callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
|
|
|
callback_data.EventChar = (ImWchar)c;
|
|
|
callback_data.Flags = flags;
|
|
|
@@ -3945,6 +4014,21 @@ static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* st
|
|
|
p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i);
|
|
|
}
|
|
|
|
|
|
+// As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables)
|
|
|
+// we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714)
|
|
|
+// It would be more desirable that we discourage users from taking advantage of the "user not retaining data" trick,
|
|
|
+// but that more likely be attractive when we do have _NoLiveEdit flag available.
|
|
|
+void ImGui::InputTextDeactivateHook(ImGuiID id)
|
|
|
+{
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiInputTextState* state = &g.InputTextState;
|
|
|
+ if (id == 0 || state->ID != id)
|
|
|
+ return;
|
|
|
+ g.InputTextDeactivatedState.ID = state->ID;
|
|
|
+ g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1);
|
|
|
+ memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->CurLenA + 1);
|
|
|
+}
|
|
|
+
|
|
|
// Edit a string of text
|
|
|
// - 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
|
|
|
@@ -4040,7 +4124,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
ImGuiInputTextState* state = GetInputTextState(id);
|
|
|
|
|
|
const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
|
|
|
- const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard));
|
|
|
+ const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard)));
|
|
|
|
|
|
const bool user_clicked = hovered && io.MouseClicked[0];
|
|
|
const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
|
|
|
@@ -4050,7 +4134,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
|
|
|
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
|
|
|
|
|
|
- const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline);
|
|
|
+ const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); // state != NULL means its our state.
|
|
|
const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing);
|
|
|
const bool init_state = (init_make_active || user_scroll_active);
|
|
|
if ((init_state && g.ActiveId != id) || init_changed_specs)
|
|
|
@@ -4059,6 +4143,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
state = &g.InputTextState;
|
|
|
state->CursorAnimReset();
|
|
|
|
|
|
+ // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714)
|
|
|
+ InputTextDeactivateHook(state->ID);
|
|
|
+
|
|
|
// Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
|
|
|
// From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
|
|
|
const int buf_len = (int)strlen(buf);
|
|
|
@@ -4132,7 +4219,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
if (is_osx)
|
|
|
SetKeyOwner(ImGuiMod_Alt, id);
|
|
|
if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
|
|
|
- SetKeyOwner(ImGuiKey_Tab, id);
|
|
|
+ SetShortcutRouting(ImGuiKey_Tab, id);
|
|
|
}
|
|
|
|
|
|
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
|
|
|
@@ -4262,8 +4349,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
|
|
|
// We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336)
|
|
|
// (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes)
|
|
|
- const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
|
|
|
- if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly)
|
|
|
+ if ((flags & ImGuiInputTextFlags_AllowTabInput) && Shortcut(ImGuiKey_Tab, id) && !is_readonly)
|
|
|
{
|
|
|
unsigned int c = '\t'; // Insert TAB
|
|
|
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
|
|
|
@@ -4272,6 +4358,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
|
|
|
// Process regular text input (before we check for Return because using some IME will effectively send a Return?)
|
|
|
// We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
|
|
|
+ const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
|
|
|
if (io.InputQueueCharacters.Size > 0)
|
|
|
{
|
|
|
if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav)
|
|
|
@@ -4471,6 +4558,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
// Push records into the undo stack so we can CTRL+Z the revert operation itself
|
|
|
apply_new_text = state->InitialTextA.Data;
|
|
|
apply_new_text_length = state->InitialTextA.Size - 1;
|
|
|
+ value_changed = true;
|
|
|
ImVector<ImWchar> w_text;
|
|
|
if (apply_new_text_length > 0)
|
|
|
{
|
|
|
@@ -4508,7 +4596,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
// The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
|
|
|
ImGuiInputTextFlags event_flag = 0;
|
|
|
ImGuiKey event_key = ImGuiKey_None;
|
|
|
- if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressed(ImGuiKey_Tab))
|
|
|
+ if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, id))
|
|
|
{
|
|
|
event_flag = ImGuiInputTextFlags_CallbackCompletion;
|
|
|
event_key = ImGuiKey_Tab;
|
|
|
@@ -4535,7 +4623,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
if (event_flag)
|
|
|
{
|
|
|
ImGuiInputTextCallbackData callback_data;
|
|
|
- memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
|
|
|
+ callback_data.Ctx = &g;
|
|
|
callback_data.EventFlag = event_flag;
|
|
|
callback_data.Flags = flags;
|
|
|
callback_data.UserData = callback_user_data;
|
|
|
@@ -4584,10 +4672,24 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
{
|
|
|
apply_new_text = state->TextA.Data;
|
|
|
apply_new_text_length = state->CurLenA;
|
|
|
+ value_changed = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details)
|
|
|
+ if (g.InputTextDeactivatedState.ID == id)
|
|
|
+ {
|
|
|
+ if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly)
|
|
|
+ {
|
|
|
+ apply_new_text = g.InputTextDeactivatedState.TextA.Data;
|
|
|
+ apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1;
|
|
|
+ value_changed |= (strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0);
|
|
|
+ //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text);
|
|
|
+ }
|
|
|
+ g.InputTextDeactivatedState.ID = 0;
|
|
|
+ }
|
|
|
+
|
|
|
// Copy result to user buffer. This can currently only happen when (g.ActiveId == id)
|
|
|
if (apply_new_text != NULL)
|
|
|
{
|
|
|
@@ -4598,6 +4700,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
if (is_resizable)
|
|
|
{
|
|
|
ImGuiInputTextCallbackData callback_data;
|
|
|
+ callback_data.Ctx = &g;
|
|
|
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
|
|
|
callback_data.Flags = flags;
|
|
|
callback_data.Buf = buf;
|
|
|
@@ -4614,7 +4717,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
|
|
|
// If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.
|
|
|
ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size));
|
|
|
- value_changed = true;
|
|
|
}
|
|
|
|
|
|
// Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
|
|
|
@@ -4864,7 +4966,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited))
|
|
|
MarkItemEdited(id);
|
|
|
|
|
|
- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
|
|
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
|
|
|
if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
|
|
|
return validated;
|
|
|
else
|
|
|
@@ -4879,7 +4981,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
|
|
|
ImStb::StbUndoState* undo_state = &stb_state->undostate;
|
|
|
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
|
|
|
DebugLocateItemOnHover(state->ID);
|
|
|
- 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("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenW, state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
|
|
|
Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
|
|
|
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
|
|
|
@@ -4927,28 +5029,32 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag
|
|
|
return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
|
|
|
}
|
|
|
|
|
|
+static void ColorEditRestoreH(const float* col, float* H)
|
|
|
+{
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ IM_ASSERT(g.ColorEditCurrentID != 0);
|
|
|
+ if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
|
|
|
+ return;
|
|
|
+ *H = g.ColorEditSavedHue;
|
|
|
+}
|
|
|
+
|
|
|
// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation.
|
|
|
// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting.
|
|
|
static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V)
|
|
|
{
|
|
|
- // This check is optional. Suppose we have two color widgets side by side, both widgets display different colors, but both colors have hue and/or saturation undefined.
|
|
|
- // With color check: hue/saturation is preserved in one widget. Editing color in one widget would reset hue/saturation in another one.
|
|
|
- // Without color check: common hue/saturation would be displayed in all widgets that have hue/saturation undefined.
|
|
|
- // g.ColorEditLastColor is stored as ImU32 RGB value: this essentially gives us color equality check with reduced precision.
|
|
|
- // Tiny external color changes would not be detected and this check would still pass. This is OK, since we only restore hue/saturation _only_ if they are undefined,
|
|
|
- // therefore this change flipping hue/saturation from undefined to a very tiny value would still be represented in color picker.
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
- if (g.ColorEditLastColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
|
|
|
+ IM_ASSERT(g.ColorEditCurrentID != 0);
|
|
|
+ if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
|
|
|
return;
|
|
|
|
|
|
// When S == 0, H is undefined.
|
|
|
// When H == 1 it wraps around to 0.
|
|
|
- if (*S == 0.0f || (*H == 0.0f && g.ColorEditLastHue == 1))
|
|
|
- *H = g.ColorEditLastHue;
|
|
|
+ if (*S == 0.0f || (*H == 0.0f && g.ColorEditSavedHue == 1))
|
|
|
+ *H = g.ColorEditSavedHue;
|
|
|
|
|
|
// When V == 0, S is undefined.
|
|
|
if (*V == 0.0f)
|
|
|
- *S = g.ColorEditLastSat;
|
|
|
+ *S = g.ColorEditSavedSat;
|
|
|
}
|
|
|
|
|
|
// Edit colors components (each component in 0.0f..1.0f range).
|
|
|
@@ -4971,6 +5077,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
|
|
|
|
|
BeginGroup();
|
|
|
PushID(label);
|
|
|
+ const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);
|
|
|
+ if (set_current_color_edit_id)
|
|
|
+ g.ColorEditCurrentID = window->IDStack.back();
|
|
|
|
|
|
// If we're not showing any slider there's no point in doing any HSV conversions
|
|
|
const ImGuiColorEditFlags flags_untouched = flags;
|
|
|
@@ -5004,7 +5113,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
|
|
ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
|
|
|
else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV))
|
|
|
{
|
|
|
- // Hue is lost when converting from greyscale rgb (saturation=0). Restore it.
|
|
|
+ // Hue is lost when converting from grayscale rgb (saturation=0). Restore it.
|
|
|
ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
|
|
|
ColorEditRestoreHS(col, &f[0], &f[1], &f[2]);
|
|
|
}
|
|
|
@@ -5143,10 +5252,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
|
|
f[n] = i[n] / 255.0f;
|
|
|
if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB))
|
|
|
{
|
|
|
- g.ColorEditLastHue = f[0];
|
|
|
- g.ColorEditLastSat = f[1];
|
|
|
+ g.ColorEditSavedHue = f[0];
|
|
|
+ g.ColorEditSavedSat = f[1];
|
|
|
ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
|
|
|
- g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0));
|
|
|
+ g.ColorEditSavedID = g.ColorEditCurrentID;
|
|
|
+ g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0));
|
|
|
}
|
|
|
if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV))
|
|
|
ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
|
|
|
@@ -5158,6 +5268,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
|
|
col[3] = f[3];
|
|
|
}
|
|
|
|
|
|
+ if (set_current_color_edit_id)
|
|
|
+ g.ColorEditCurrentID = 0;
|
|
|
PopID();
|
|
|
EndGroup();
|
|
|
|
|
|
@@ -5231,6 +5343,9 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|
|
g.NextItemData.ClearFlags();
|
|
|
|
|
|
PushID(label);
|
|
|
+ const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);
|
|
|
+ if (set_current_color_edit_id)
|
|
|
+ g.ColorEditCurrentID = window->IDStack.back();
|
|
|
BeginGroup();
|
|
|
|
|
|
if (!(flags & ImGuiColorEditFlags_NoSidePreview))
|
|
|
@@ -5279,7 +5394,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|
|
float R = col[0], G = col[1], B = col[2];
|
|
|
if (flags & ImGuiColorEditFlags_InputRGB)
|
|
|
{
|
|
|
- // Hue is lost when converting from greyscale rgb (saturation=0). Restore it.
|
|
|
+ // Hue is lost when converting from grayscale rgb (saturation=0). Restore it.
|
|
|
ColorConvertRGBtoHSV(R, G, B, H, S, V);
|
|
|
ColorEditRestoreHS(col, &H, &S, &V);
|
|
|
}
|
|
|
@@ -5334,10 +5449,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|
|
{
|
|
|
S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));
|
|
|
V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
|
|
|
-
|
|
|
- // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square.
|
|
|
- if (g.ColorEditLastColor == ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
|
|
|
- H = g.ColorEditLastHue;
|
|
|
+ ColorEditRestoreH(col, &H); // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square.
|
|
|
value_changed = value_changed_sv = true;
|
|
|
}
|
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
|
@@ -5412,9 +5524,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|
|
if (flags & ImGuiColorEditFlags_InputRGB)
|
|
|
{
|
|
|
ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]);
|
|
|
- g.ColorEditLastHue = H;
|
|
|
- g.ColorEditLastSat = S;
|
|
|
- g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0));
|
|
|
+ g.ColorEditSavedHue = H;
|
|
|
+ g.ColorEditSavedSat = S;
|
|
|
+ g.ColorEditSavedID = g.ColorEditCurrentID;
|
|
|
+ g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0));
|
|
|
}
|
|
|
else if (flags & ImGuiColorEditFlags_InputHSV)
|
|
|
{
|
|
|
@@ -5578,6 +5691,8 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|
|
if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId
|
|
|
MarkItemEdited(g.LastItemData.ID);
|
|
|
|
|
|
+ if (set_current_color_edit_id)
|
|
|
+ g.ColorEditCurrentID = 0;
|
|
|
PopID();
|
|
|
|
|
|
return value_changed;
|
|
|
@@ -5691,7 +5806,8 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
|
|
|
- BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None);
|
|
|
+ if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None))
|
|
|
+ return;
|
|
|
const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
|
|
|
if (text_end > text)
|
|
|
{
|
|
|
@@ -7675,6 +7791,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
|
{
|
|
|
ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n];
|
|
|
tab->Offset = tab_offset;
|
|
|
+ tab->NameOffset = -1;
|
|
|
tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f);
|
|
|
}
|
|
|
tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f);
|
|
|
@@ -7682,6 +7799,9 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
|
section_tab_index += section->TabCount;
|
|
|
}
|
|
|
|
|
|
+ // Clear name buffers
|
|
|
+ tab_bar->TabsNames.Buf.resize(0);
|
|
|
+
|
|
|
// If we have lost the selected tab, select the next most recently active one
|
|
|
if (found_selected_tab_id == false)
|
|
|
tab_bar->SelectedTabId = 0;
|
|
|
@@ -7713,10 +7833,6 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
|
tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing;
|
|
|
tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing;
|
|
|
|
|
|
- // Clear name buffers
|
|
|
- if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
|
|
|
- tab_bar->TabsNames.Buf.resize(0);
|
|
|
-
|
|
|
// Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame)
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
window->DC.CursorPos = tab_bar->BarRect.Min;
|
|
|
@@ -7774,7 +7890,9 @@ ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar)
|
|
|
|
|
|
const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
|
|
|
{
|
|
|
- IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < tab_bar->TabsNames.Buf.Size);
|
|
|
+ if (tab->NameOffset == -1)
|
|
|
+ return "N/A";
|
|
|
+ IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size);
|
|
|
return tab_bar->TabsNames.Buf.Data + tab->NameOffset;
|
|
|
}
|
|
|
|