update dear imgui from 1.91.8 to 1.91.9b and switch to docking branch
This commit is contained in:
+261
-89
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.91.8
|
||||
// dear imgui, v1.91b
|
||||
// (widgets code)
|
||||
|
||||
/*
|
||||
@@ -70,6 +70,7 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
|
||||
#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
|
||||
#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used.
|
||||
@@ -80,6 +81,7 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||||
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
|
||||
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
|
||||
@@ -169,7 +171,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
|
||||
// Calculate length
|
||||
const char* text_begin = text;
|
||||
if (text_end == NULL)
|
||||
text_end = text + strlen(text); // FIXME-OPT
|
||||
text_end = text + ImStrlen(text); // FIXME-OPT
|
||||
|
||||
const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
|
||||
const float wrap_pos_x = window->DC.TextWrapPos;
|
||||
@@ -209,7 +211,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
|
||||
int lines_skipped = 0;
|
||||
while (line < text_end && lines_skipped < lines_skippable)
|
||||
{
|
||||
const char* line_end = (const char*)memchr(line, '\n', text_end - line);
|
||||
const char* line_end = (const char*)ImMemchr(line, '\n', text_end - line);
|
||||
if (!line_end)
|
||||
line_end = text_end;
|
||||
if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)
|
||||
@@ -230,7 +232,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
|
||||
if (IsClippedEx(line_rect, 0))
|
||||
break;
|
||||
|
||||
const char* line_end = (const char*)memchr(line, '\n', text_end - line);
|
||||
const char* line_end = (const char*)ImMemchr(line, '\n', text_end - line);
|
||||
if (!line_end)
|
||||
line_end = text_end;
|
||||
text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);
|
||||
@@ -245,7 +247,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
|
||||
int lines_skipped = 0;
|
||||
while (line < text_end)
|
||||
{
|
||||
const char* line_end = (const char*)memchr(line, '\n', text_end - line);
|
||||
const char* line_end = (const char*)ImMemchr(line, '\n', text_end - line);
|
||||
if (!line_end)
|
||||
line_end = text_end;
|
||||
if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)
|
||||
@@ -516,7 +518,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
|
||||
flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_;
|
||||
|
||||
ImGuiWindow* backup_hovered_window = g.HoveredWindow;
|
||||
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window;
|
||||
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree;
|
||||
if (flatten_hovered_children)
|
||||
g.HoveredWindow = window;
|
||||
|
||||
@@ -874,7 +876,8 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
|
||||
return pressed;
|
||||
}
|
||||
|
||||
bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
|
||||
// The Collapse button also functions as a Dock Menu button.
|
||||
bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
@@ -887,16 +890,21 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
|
||||
return pressed;
|
||||
|
||||
// Render
|
||||
//bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed);
|
||||
ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
|
||||
ImU32 text_col = GetColorU32(ImGuiCol_Text);
|
||||
if (hovered || held)
|
||||
window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
|
||||
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);
|
||||
RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
|
||||
|
||||
if (dock_node)
|
||||
RenderArrowDockMenu(window->DrawList, bb.Min, g.FontSize, text_col);
|
||||
else
|
||||
RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
|
||||
|
||||
// Switch to moving the window after mouse is moved beyond the initial drag threshold
|
||||
if (IsItemActive() && IsMouseDragging(0))
|
||||
StartMouseMovingWindow(window);
|
||||
StartMouseMovingWindowOrNode(window, dock_node, true); // Undock from window/collapse menu button
|
||||
|
||||
return pressed;
|
||||
}
|
||||
@@ -913,7 +921,7 @@ ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)
|
||||
const ImRect outer_rect = window->Rect();
|
||||
const ImRect inner_rect = window->InnerRect;
|
||||
const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)
|
||||
IM_ASSERT(scrollbar_size > 0.0f);
|
||||
IM_ASSERT(scrollbar_size >= 0.0f);
|
||||
const float border_size = IM_ROUND(window->WindowBorderSize * 0.5f);
|
||||
const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : 0.0f;
|
||||
if (axis == ImGuiAxis_X)
|
||||
@@ -971,8 +979,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
|
||||
|
||||
// When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab)
|
||||
float alpha = 1.0f;
|
||||
if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f)
|
||||
alpha = ImSaturate((bb_frame_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f));
|
||||
if ((axis == ImGuiAxis_Y) && bb_frame_height < bb_frame_width)
|
||||
alpha = ImSaturate(bb_frame_height / ImMax(bb_frame_width * 2.0f, 1.0f));
|
||||
if (alpha <= 0.0f)
|
||||
return false;
|
||||
|
||||
@@ -989,7 +997,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
|
||||
// But we maintain a minimum size in pixel to allow for the user to still aim inside.
|
||||
IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.
|
||||
const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1);
|
||||
const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v);
|
||||
const float grab_h_minsize = ImMin(bb.GetSize()[axis], style.GrabMinSize);
|
||||
const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), grab_h_minsize, scrollbar_size_v);
|
||||
const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
|
||||
|
||||
// Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
|
||||
@@ -1061,25 +1070,45 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
|
||||
|
||||
// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
|
||||
// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
|
||||
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
|
||||
void ImGui::ImageWithBg(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return;
|
||||
|
||||
const float border_size = (border_col.w > 0.0f) ? 1.0f : 0.0f;
|
||||
const ImVec2 padding(border_size, border_size);
|
||||
const ImVec2 padding(g.Style.ImageBorderSize, g.Style.ImageBorderSize);
|
||||
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);
|
||||
ItemSize(bb);
|
||||
if (!ItemAdd(bb, 0))
|
||||
return;
|
||||
|
||||
// Render
|
||||
if (border_size > 0.0f)
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f, ImDrawFlags_None, border_size);
|
||||
if (g.Style.ImageBorderSize > 0.0f)
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f, ImDrawFlags_None, g.Style.ImageBorderSize);
|
||||
if (bg_col.w > 0.0f)
|
||||
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
|
||||
window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
|
||||
}
|
||||
|
||||
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
|
||||
{
|
||||
ImageWithBg(user_texture_id, image_size, uv0, uv1);
|
||||
}
|
||||
|
||||
// 1.91.9 (February 2025) removed 'tint_col' and 'border_col' parameters, made border size not depend on color value. (#8131, #8238)
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
PushStyleVar(ImGuiStyleVar_ImageBorderSize, (border_col.w > 0.0f) ? ImMax(1.0f, g.Style.ImageBorderSize) : 0.0f); // Preserve legacy behavior where border is always visible when border_col's Alpha is >0.0f
|
||||
PushStyleColor(ImGuiCol_Border, border_col);
|
||||
ImageWithBg(user_texture_id, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);
|
||||
PopStyleColor();
|
||||
PopStyleVar();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@@ -1423,7 +1452,7 @@ bool ImGui::TextLink(const char* label)
|
||||
const ImGuiID id = window->GetID(label);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
|
||||
ImVec2 size = CalcTextSize(label, label_end, true);
|
||||
ImRect bb(pos, pos + size);
|
||||
ItemSize(size, 0.0f);
|
||||
@@ -1640,7 +1669,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
|
||||
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 = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f));
|
||||
const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f));
|
||||
ItemSize(min_size, text_baseline_y);
|
||||
if (!ItemAdd(bb, id))
|
||||
return;
|
||||
@@ -1828,7 +1857,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
|
||||
ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags;
|
||||
ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.HasFlags;
|
||||
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
@@ -1897,7 +1926,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
|
||||
if (!popup_open)
|
||||
return false;
|
||||
|
||||
g.NextWindowData.Flags = backup_next_window_data_flags;
|
||||
g.NextWindowData.HasFlags = backup_next_window_data_flags;
|
||||
return BeginComboPopup(popup_id, bb, flags);
|
||||
}
|
||||
|
||||
@@ -1912,7 +1941,7 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags
|
||||
|
||||
// Set popup size
|
||||
float w = bb.GetWidth();
|
||||
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
|
||||
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint)
|
||||
{
|
||||
g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);
|
||||
}
|
||||
@@ -1926,9 +1955,9 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags
|
||||
else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4;
|
||||
else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20;
|
||||
ImVec2 constraint_min(0.0f, 0.0f), constraint_max(FLT_MAX, FLT_MAX);
|
||||
if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.x <= 0.0f) // Don't apply constraints if user specified a size
|
||||
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.x <= 0.0f) // Don't apply constraints if user specified a size
|
||||
constraint_min.x = w;
|
||||
if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.y <= 0.0f)
|
||||
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.y <= 0.0f)
|
||||
constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);
|
||||
SetNextWindowSizeConstraints(constraint_min, constraint_max);
|
||||
}
|
||||
@@ -2043,7 +2072,7 @@ static const char* Items_SingleStringGetter(void* data, int idx)
|
||||
{
|
||||
if (idx == items_count)
|
||||
break;
|
||||
p += strlen(p) + 1;
|
||||
p += ImStrlen(p) + 1;
|
||||
items_count++;
|
||||
}
|
||||
return *p ? p : NULL;
|
||||
@@ -2060,7 +2089,7 @@ bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(vo
|
||||
preview_value = getter(user_data, *current_item);
|
||||
|
||||
// The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
|
||||
if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint))
|
||||
if (popup_max_height_in_items != -1 && !(g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint))
|
||||
SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
|
||||
|
||||
if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))
|
||||
@@ -2111,7 +2140,7 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa
|
||||
const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
|
||||
while (*p)
|
||||
{
|
||||
p += strlen(p) + 1;
|
||||
p += ImStrlen(p) + 1;
|
||||
items_count++;
|
||||
}
|
||||
bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
|
||||
@@ -2621,7 +2650,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
|
||||
if (!temp_input_is_active)
|
||||
{
|
||||
// Tabbing or CTRL-clicking on Drag turns it into an InputText
|
||||
// Tabbing or CTRL+click on Drag turns it into an InputText
|
||||
const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
|
||||
const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id));
|
||||
const bool make_active = (clicked || double_clicked || g.NavActivateId == id);
|
||||
@@ -3225,7 +3254,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
||||
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
|
||||
if (!temp_input_is_active)
|
||||
{
|
||||
// Tabbing or CTRL-clicking on Slider turns it into an input box
|
||||
// Tabbing or CTRL+click on Slider turns it into an input box
|
||||
const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
|
||||
const bool make_active = (clicked || g.NavActivateId == id);
|
||||
if (make_active && clicked)
|
||||
@@ -3878,7 +3907,7 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char**
|
||||
line_count++;
|
||||
if (s_eol == NULL)
|
||||
{
|
||||
s = s + strlen(s);
|
||||
s = s + ImStrlen(s);
|
||||
break;
|
||||
}
|
||||
s = s_eol + 1;
|
||||
@@ -4166,7 +4195,7 @@ void ImGuiInputTextState::OnCharPressed(unsigned int c)
|
||||
// The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great.
|
||||
char utf8[5];
|
||||
ImTextCharToUtf8(utf8, c);
|
||||
stb_textedit_text(this, Stb, utf8, (int)strlen(utf8));
|
||||
stb_textedit_text(this, Stb, utf8, (int)ImStrlen(utf8));
|
||||
CursorFollow = true;
|
||||
CursorAnimReset();
|
||||
}
|
||||
@@ -4217,7 +4246,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
|
||||
|
||||
// Grow internal buffer if needed
|
||||
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
|
||||
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
|
||||
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text);
|
||||
if (new_text_len + BufTextLen >= BufSize)
|
||||
{
|
||||
if (!is_resizable)
|
||||
@@ -4251,7 +4280,7 @@ void ImGui::PushPasswordFont()
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImFont* in_font = g.Font;
|
||||
ImFont* out_font = &g.InputTextPasswordFont;
|
||||
const ImFontGlyph* glyph = in_font->FindGlyph('*');
|
||||
ImFontGlyph* glyph = in_font->FindGlyph('*');
|
||||
out_font->FontSize = in_font->FontSize;
|
||||
out_font->Scale = in_font->Scale;
|
||||
out_font->Ascent = in_font->Ascent;
|
||||
@@ -4273,7 +4302,13 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im
|
||||
if (c < 0x20)
|
||||
{
|
||||
bool pass = false;
|
||||
pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code)
|
||||
pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code)
|
||||
if (c == '\n' && input_source_is_clipboard && (flags & ImGuiInputTextFlags_Multiline) == 0) // In single line mode, replace \n with a space
|
||||
{
|
||||
c = *p_char = ' ';
|
||||
pass = true;
|
||||
}
|
||||
pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0;
|
||||
pass |= (c == '\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0;
|
||||
if (!pass)
|
||||
return false;
|
||||
@@ -4539,7 +4574,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
const bool init_state = (init_make_active || user_scroll_active);
|
||||
if (init_reload_from_user_buf)
|
||||
{
|
||||
int new_len = (int)strlen(buf);
|
||||
int new_len = (int)ImStrlen(buf);
|
||||
IM_ASSERT(new_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?");
|
||||
state->WantReloadUserBuf = false;
|
||||
InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len);
|
||||
@@ -4561,7 +4596,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
|
||||
// Take a copy of the initial buffer value.
|
||||
// From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode)
|
||||
const int buf_len = (int)strlen(buf);
|
||||
const int buf_len = (int)ImStrlen(buf);
|
||||
IM_ASSERT(buf_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?");
|
||||
state->TextToRevertTo.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->TextToRevertTo.Data, buf, buf_len + 1);
|
||||
@@ -4646,7 +4681,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
|
||||
// Read-only mode always ever read from source buffer. Refresh TextLen when active.
|
||||
if (is_readonly && state != NULL)
|
||||
state->TextLen = (int)strlen(buf);
|
||||
state->TextLen = (int)ImStrlen(buf);
|
||||
//if (is_readonly && state != NULL)
|
||||
// state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation.
|
||||
}
|
||||
@@ -4669,7 +4704,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
|
||||
// Select the buffer to render.
|
||||
const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state;
|
||||
const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
|
||||
bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
|
||||
|
||||
// Password pushes a temporary font with only a fallback glyph
|
||||
if (is_password && !is_displaying_hint)
|
||||
@@ -4803,14 +4838,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
|
||||
const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
|
||||
|
||||
// Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText)
|
||||
// Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: former would be handled by InputText)
|
||||
// Otherwise we could simply assume that we own the keys as we are active.
|
||||
const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat;
|
||||
const bool is_cut = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
|
||||
const bool is_copy = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, 0, id)) && !is_password && (!is_multiline || state->HasSelection());
|
||||
const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat, id)) && !is_readonly;
|
||||
const bool is_undo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable;
|
||||
const bool is_redo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || (is_osx && Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id))) && !is_readonly && is_undoable;
|
||||
const bool is_redo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable;
|
||||
const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0, id);
|
||||
|
||||
// We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
|
||||
@@ -4925,7 +4960,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
if (const char* clipboard = GetClipboardText())
|
||||
{
|
||||
// Filter pasted buffer
|
||||
const int clipboard_len = (int)strlen(clipboard);
|
||||
const int clipboard_len = (int)ImStrlen(clipboard);
|
||||
ImVector<char> clipboard_filtered;
|
||||
clipboard_filtered.reserve(clipboard_len + 1);
|
||||
for (const char* s = clipboard; *s != 0; )
|
||||
@@ -4937,7 +4972,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
continue;
|
||||
char c_utf8[5];
|
||||
ImTextCharToUtf8(c_utf8, c);
|
||||
int out_len = (int)strlen(c_utf8);
|
||||
int out_len = (int)ImStrlen(c_utf8);
|
||||
clipboard_filtered.resize(clipboard_filtered.Size + out_len);
|
||||
memcpy(clipboard_filtered.Data + clipboard_filtered.Size - out_len, c_utf8, out_len);
|
||||
}
|
||||
@@ -5067,7 +5102,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
if (buf_dirty)
|
||||
{
|
||||
// Callback may update buffer and thus set buf_dirty even in read-only mode.
|
||||
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)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
|
||||
InputTextReconcileUndoState(state, state->CallbackTextBackup.Data, state->CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen);
|
||||
state->TextLen = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
|
||||
state->CursorAnimReset();
|
||||
@@ -5151,10 +5186,22 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
const int buf_display_max_length = 2 * 1024 * 1024;
|
||||
const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595
|
||||
const char* buf_display_end = NULL; // We have specialized paths below for setting the length
|
||||
|
||||
// Display hint when contents is empty
|
||||
// At this point we need to handle the possibility that a callback could have modified the underlying buffer (#8368)
|
||||
const bool new_is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
|
||||
if (new_is_displaying_hint != is_displaying_hint)
|
||||
{
|
||||
if (is_password && !is_displaying_hint)
|
||||
PopFont();
|
||||
is_displaying_hint = new_is_displaying_hint;
|
||||
if (is_password && !is_displaying_hint)
|
||||
PushPasswordFont();
|
||||
}
|
||||
if (is_displaying_hint)
|
||||
{
|
||||
buf_display = hint;
|
||||
buf_display_end = hint + strlen(hint);
|
||||
buf_display_end = hint + ImStrlen(hint);
|
||||
}
|
||||
|
||||
// Render text. We currently only render selection when the widget is active or while scrolling.
|
||||
@@ -5187,7 +5234,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
int line_count = 1;
|
||||
if (is_multiline)
|
||||
{
|
||||
for (const char* s = text_begin; (s = (const char*)memchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
|
||||
for (const char* s = text_begin; (s = (const char*)ImMemchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
|
||||
{
|
||||
if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_no = line_count; }
|
||||
if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_no = line_count; }
|
||||
@@ -5265,7 +5312,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
break;
|
||||
if (rect_pos.y < clip_rect.y)
|
||||
{
|
||||
p = (const char*)memchr((void*)p, '\n', text_selected_end - p);
|
||||
p = (const char*)ImMemchr((void*)p, '\n', text_selected_end - p);
|
||||
p = p ? p + 1 : text_selected_end;
|
||||
}
|
||||
else
|
||||
@@ -5306,6 +5353,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
g.PlatformImeData.WantVisible = true;
|
||||
g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
|
||||
g.PlatformImeData.InputLineHeight = g.FontSize;
|
||||
g.PlatformImeViewport = window->Viewport->ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5317,7 +5365,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
else if (!is_displaying_hint && g.ActiveId == id)
|
||||
buf_display_end = buf_display + state->TextLen;
|
||||
else if (!is_displaying_hint)
|
||||
buf_display_end = buf_display + strlen(buf_display);
|
||||
buf_display_end = buf_display + ImStrlen(buf_display);
|
||||
|
||||
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
|
||||
{
|
||||
@@ -5462,7 +5510,7 @@ static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V)
|
||||
|
||||
// Edit colors components (each component in 0.0f..1.0f range).
|
||||
// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
|
||||
// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item.
|
||||
// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL+Click over input fields to edit them and TAB to go to next item.
|
||||
bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
|
||||
{
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
@@ -6166,7 +6214,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
|
||||
if (g.Style.FrameBorderSize > 0.0f)
|
||||
RenderFrameBorder(bb.Min, bb.Max, rounding);
|
||||
else
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color buttons are often in need of some sort of border
|
||||
}
|
||||
|
||||
// Drag and Drop Source
|
||||
@@ -7088,7 +7136,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
||||
|
||||
// Text stays at the submission position. Alignment/clipping extents ignore SpanAllColumns.
|
||||
if (is_visible)
|
||||
RenderTextClipped(pos, ImVec2(window->WorkRect.Max.x, pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb);
|
||||
RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb);
|
||||
|
||||
// Automatically close popups
|
||||
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
|
||||
@@ -7151,7 +7199,7 @@ ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags f
|
||||
|
||||
// Append to buffer
|
||||
const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1;
|
||||
int buffer_len = (int)strlen(data->SearchBuffer);
|
||||
int buffer_len = (int)ImStrlen(data->SearchBuffer);
|
||||
bool select_request = false;
|
||||
for (ImWchar w : g.IO.InputQueueCharacters)
|
||||
{
|
||||
@@ -8634,9 +8682,10 @@ bool ImGui::BeginMenuBar()
|
||||
|
||||
// We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
|
||||
// We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.
|
||||
const float border_top = ImMax(window->WindowBorderSize * 0.5f - window->TitleBarHeight, 0.0f);
|
||||
const float border_top = ImMax(IM_ROUND(window->WindowBorderSize * 0.5f - window->TitleBarHeight), 0.0f);
|
||||
const float border_half = IM_ROUND(window->WindowBorderSize * 0.5f);
|
||||
ImRect bar_rect = window->MenuBarRect();
|
||||
ImRect clip_rect(IM_ROUND(bar_rect.Min.x + window->WindowBorderSize * 0.5f), IM_ROUND(bar_rect.Min.y + border_top), IM_ROUND(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize * 0.5f))), IM_ROUND(bar_rect.Max.y));
|
||||
ImRect clip_rect(ImFloor(bar_rect.Min.x + border_half), ImFloor(bar_rect.Min.y + border_top), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, border_half))), ImFloor(bar_rect.Max.y));
|
||||
clip_rect.ClipWith(window->OuterRectClipped);
|
||||
PushClipRect(clip_rect.Min, clip_rect.Max, false);
|
||||
|
||||
@@ -8712,10 +8761,10 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im
|
||||
IM_ASSERT(dir != ImGuiDir_None);
|
||||
|
||||
ImGuiWindow* bar_window = FindWindowByName(name);
|
||||
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport());
|
||||
if (bar_window == NULL || bar_window->BeginCount == 0)
|
||||
{
|
||||
// Calculate and set window size/position
|
||||
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport());
|
||||
ImRect avail_rect = viewport->GetBuildWorkRect();
|
||||
ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
|
||||
ImVec2 pos = avail_rect.Min;
|
||||
@@ -8733,7 +8782,8 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im
|
||||
viewport->BuildWorkInsetMax[axis] += axis_size;
|
||||
}
|
||||
|
||||
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
|
||||
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
|
||||
SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set.
|
||||
PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint
|
||||
bool is_open = Begin(name, NULL, window_flags);
|
||||
@@ -8747,6 +8797,9 @@ bool ImGui::BeginMainMenuBar()
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
|
||||
|
||||
// Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change
|
||||
SetCurrentViewport(NULL, viewport);
|
||||
|
||||
// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
|
||||
// FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea?
|
||||
// FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.
|
||||
@@ -8808,7 +8861,7 @@ static bool IsRootOfOpenMenuSet()
|
||||
const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size];
|
||||
if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer)
|
||||
return false;
|
||||
return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true);
|
||||
return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true, false);
|
||||
}
|
||||
|
||||
bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
@@ -8834,7 +8887,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
if (g.MenusIdSubmittedThisFrame.contains(id))
|
||||
{
|
||||
if (menu_is_open)
|
||||
menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
|
||||
menu_is_open = BeginPopupMenuEx(id, label, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
|
||||
else
|
||||
g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values
|
||||
return menu_is_open;
|
||||
@@ -8865,7 +8918,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
|
||||
{
|
||||
// Menu inside an horizontal menu bar
|
||||
// Menu inside a horizontal menu bar
|
||||
// Selectable extend their highlight by half ItemSpacing in each direction.
|
||||
// For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
|
||||
popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight);
|
||||
@@ -8996,7 +9049,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
ImGuiLastItemData last_item_in_parent = g.LastItemData;
|
||||
SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
|
||||
PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding
|
||||
menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
|
||||
menu_is_open = BeginPopupMenuEx(id, label, window_flags); // menu_is_open may be 'false' when the popup is completely clipped (e.g. zero size display)
|
||||
PopStyleVar();
|
||||
if (menu_is_open)
|
||||
{
|
||||
@@ -9150,8 +9203,10 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected,
|
||||
// - TabBarCalcMaxTabWidth() [Internal]
|
||||
// - TabBarFindTabById() [Internal]
|
||||
// - TabBarFindTabByOrder() [Internal]
|
||||
// - TabBarFindMostRecentlySelectedTabForActiveWindow() [Internal]
|
||||
// - TabBarGetCurrentTab() [Internal]
|
||||
// - TabBarGetTabName() [Internal]
|
||||
// - TabBarAddTab() [Internal]
|
||||
// - TabBarRemoveTab() [Internal]
|
||||
// - TabBarCloseTab() [Internal]
|
||||
// - TabBarScrollClamp() [Internal]
|
||||
@@ -9273,7 +9328,8 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
|
||||
|
||||
// Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable
|
||||
if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable)))
|
||||
ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
|
||||
if ((flags & ImGuiTabBarFlags_DockNode) == 0) // FIXME: TabBar with DockNode can now be hybrid
|
||||
ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
|
||||
tab_bar->TabsAddedNew = false;
|
||||
|
||||
// Flags
|
||||
@@ -9555,6 +9611,10 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
||||
tab_bar->VisibleTabId = tab_bar->SelectedTabId;
|
||||
tab_bar->VisibleTabWasSubmitted = false;
|
||||
|
||||
// CTRL+TAB can override visible tab temporarily
|
||||
if (g.NavWindowingTarget != NULL && g.NavWindowingTarget->DockNode && g.NavWindowingTarget->DockNode->TabBar == tab_bar)
|
||||
tab_bar->VisibleTabId = scroll_to_tab_id = g.NavWindowingTarget->TabId;
|
||||
|
||||
// Apply request requests
|
||||
if (scroll_to_tab_id != 0)
|
||||
TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections);
|
||||
@@ -9600,11 +9660,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
||||
// Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack.
|
||||
static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window)
|
||||
{
|
||||
IM_ASSERT(docked_window == NULL); // master branch only
|
||||
IM_UNUSED(docked_window);
|
||||
if (tab_bar->Flags & ImGuiTabBarFlags_DockNode)
|
||||
if (docked_window != NULL)
|
||||
{
|
||||
ImGuiID id = ImHashStr(label);
|
||||
IM_UNUSED(tab_bar);
|
||||
IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode);
|
||||
ImGuiID id = docked_window->TabId;
|
||||
KeepAliveID(id);
|
||||
return id;
|
||||
}
|
||||
@@ -9638,6 +9698,20 @@ ImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order)
|
||||
return &tab_bar->Tabs[order];
|
||||
}
|
||||
|
||||
// FIXME: See references to #2304 in TODO.txt
|
||||
ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar)
|
||||
{
|
||||
ImGuiTabItem* most_recently_selected_tab = NULL;
|
||||
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
|
||||
{
|
||||
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
|
||||
if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected)
|
||||
if (tab->Window && tab->Window->WasActive)
|
||||
most_recently_selected_tab = tab;
|
||||
}
|
||||
return most_recently_selected_tab;
|
||||
}
|
||||
|
||||
ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar)
|
||||
{
|
||||
if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size)
|
||||
@@ -9647,12 +9721,35 @@ ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar)
|
||||
|
||||
const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
|
||||
{
|
||||
if (tab->Window)
|
||||
return tab->Window->Name;
|
||||
if (tab->NameOffset == -1)
|
||||
return "N/A";
|
||||
IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size);
|
||||
return tab_bar->TabsNames.Buf.Data + tab->NameOffset;
|
||||
}
|
||||
|
||||
// The purpose of this call is to register tab in advance so we can control their order at the time they appear.
|
||||
// Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function.
|
||||
void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL);
|
||||
IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame)
|
||||
|
||||
if (!window->HasCloseButton)
|
||||
tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation.
|
||||
|
||||
ImGuiTabItem new_tab;
|
||||
new_tab.ID = window->TabId;
|
||||
new_tab.Flags = tab_flags;
|
||||
new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab
|
||||
if (new_tab.LastFrameVisible == -1)
|
||||
new_tab.LastFrameVisible = g.FrameCount - 1;
|
||||
new_tab.Window = window; // Required so tab bar layout can compute the tab width before tab submission
|
||||
tab_bar->Tabs.push_back(new_tab);
|
||||
}
|
||||
|
||||
// The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless.
|
||||
void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)
|
||||
{
|
||||
@@ -10069,16 +10166,19 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0;
|
||||
tab->LastFrameVisible = g.FrameCount;
|
||||
tab->Flags = flags;
|
||||
tab->Window = docked_window;
|
||||
|
||||
// Append name _WITH_ the zero-terminator
|
||||
// (regular tabs are permitted in a DockNode tab bar, but window tabs not permitted in a non-DockNode tab bar)
|
||||
if (docked_window != NULL)
|
||||
{
|
||||
IM_ASSERT(docked_window == NULL); // master branch only
|
||||
IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode);
|
||||
tab->NameOffset = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->NameOffset = (ImS32)tab_bar->TabsNames.size();
|
||||
tab_bar->TabsNames.append(label, label + strlen(label) + 1);
|
||||
tab_bar->TabsNames.append(label, label + ImStrlen(label) + 1);
|
||||
}
|
||||
|
||||
// Update selected tab
|
||||
@@ -10098,7 +10198,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
tab_bar->VisibleTabWasSubmitted = true;
|
||||
|
||||
// On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches
|
||||
if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing)
|
||||
if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing && docked_window == NULL)
|
||||
if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))
|
||||
tab_contents_visible = true;
|
||||
|
||||
@@ -10146,9 +10246,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
}
|
||||
|
||||
// Click to Select a tab
|
||||
// Allow the close button to overlap
|
||||
ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap);
|
||||
if (g.DragDropActive)
|
||||
if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this
|
||||
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
|
||||
bool hovered, held, pressed;
|
||||
if (flags & ImGuiTabItemFlags_Invisible)
|
||||
@@ -10158,21 +10257,74 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
if (pressed && !is_tab_button)
|
||||
TabBarQueueFocus(tab_bar, tab);
|
||||
|
||||
// Drag and drop: re-order tabs
|
||||
if (held && !tab_appearing && IsMouseDragging(0))
|
||||
// Transfer active id window so the active id is not owned by the dock host (as StartMouseMovingWindow()
|
||||
// will only do it on the drag). This allows FocusWindow() to be more conservative in how it clears active id.
|
||||
if (held && docked_window && g.ActiveId == id && g.ActiveIdIsJustActivated)
|
||||
g.ActiveIdWindow = docked_window;
|
||||
|
||||
// Drag and drop a single floating window node moves it
|
||||
ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL;
|
||||
const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1);
|
||||
if (held && single_floating_window_node && IsMouseDragging(0, 0.0f))
|
||||
{
|
||||
if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
|
||||
// Move
|
||||
StartMouseMovingWindow(docked_window);
|
||||
}
|
||||
else if (held && !tab_appearing && IsMouseDragging(0))
|
||||
{
|
||||
// Drag and drop: re-order tabs
|
||||
int drag_dir = 0;
|
||||
float drag_distance_from_edge_x = 0.0f;
|
||||
if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL)))
|
||||
{
|
||||
// While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x
|
||||
if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x)
|
||||
{
|
||||
drag_dir = -1;
|
||||
drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x;
|
||||
TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);
|
||||
}
|
||||
else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x)
|
||||
{
|
||||
drag_dir = +1;
|
||||
drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x;
|
||||
TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract a Dockable window out of it's tab bar
|
||||
const bool can_undock = docked_window != NULL && !(docked_window->Flags & ImGuiWindowFlags_NoMove) && !(node->MergedFlags & ImGuiDockNodeFlags_NoUndocking);
|
||||
if (can_undock)
|
||||
{
|
||||
// We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar
|
||||
bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id);
|
||||
if (!undocking_tab) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift)
|
||||
{
|
||||
float threshold_base = g.FontSize;
|
||||
float threshold_x = (threshold_base * 2.2f);
|
||||
float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f);
|
||||
//GetForegroundDrawList()->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG]
|
||||
|
||||
float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y);
|
||||
if (distance_from_edge_y >= threshold_y)
|
||||
undocking_tab = true;
|
||||
if (drag_distance_from_edge_x > threshold_x)
|
||||
if ((drag_dir < 0 && TabBarGetTabOrder(tab_bar, tab) == 0) || (drag_dir > 0 && TabBarGetTabOrder(tab_bar, tab) == tab_bar->Tabs.Size - 1))
|
||||
undocking_tab = true;
|
||||
}
|
||||
|
||||
if (undocking_tab)
|
||||
{
|
||||
// Undock
|
||||
// FIXME: refactor to share more code with e.g. StartMouseMovingWindow
|
||||
DockContextQueueUndockWindow(&g, docked_window);
|
||||
g.MovingWindow = docked_window;
|
||||
SetActiveID(g.MovingWindow->MoveId, g.MovingWindow);
|
||||
g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min;
|
||||
g.ActiveIdNoClearOnFocusLoss = true;
|
||||
SetActiveIdUsingAllKeyboardKeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -10221,7 +10373,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
|
||||
|
||||
// Render tab label, process close button
|
||||
const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0;
|
||||
const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0;
|
||||
bool just_closed;
|
||||
bool text_clipped;
|
||||
TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped);
|
||||
@@ -10231,6 +10383,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
TabBarCloseTab(tab_bar, tab);
|
||||
}
|
||||
|
||||
// Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent)
|
||||
// That state is copied to window->DockTabItemStatusFlags by our caller.
|
||||
if (docked_window && (hovered || g.HoveredId == close_button_id))
|
||||
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
|
||||
|
||||
// Tooltip
|
||||
// (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok)
|
||||
// (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores)
|
||||
@@ -10266,6 +10423,16 @@ void ImGui::SetTabItemClosed(const char* label)
|
||||
if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
|
||||
tab->WantClose = true; // Will be processed by next call to TabBarLayout()
|
||||
}
|
||||
else if (ImGuiWindow* window = FindWindowByName(label))
|
||||
{
|
||||
if (window->DockIsActive)
|
||||
if (ImGuiDockNode* node = window->DockNode)
|
||||
{
|
||||
ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label, window);
|
||||
TabBarRemoveTab(node->TabBar, tab_id);
|
||||
window->DockTabWantClose = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker)
|
||||
@@ -10280,10 +10447,9 @@ ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsave
|
||||
return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y);
|
||||
}
|
||||
|
||||
ImVec2 ImGui::TabItemCalcSize(ImGuiWindow*)
|
||||
ImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window)
|
||||
{
|
||||
IM_ASSERT(0); // This function exists to facilitate merge with 'docking' branch.
|
||||
return ImVec2(0.0f, 0.0f);
|
||||
return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument));
|
||||
}
|
||||
|
||||
void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)
|
||||
@@ -10355,13 +10521,24 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
|
||||
// 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false
|
||||
bool close_button_pressed = false;
|
||||
bool close_button_visible = false;
|
||||
if (close_button_id != 0)
|
||||
if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton))
|
||||
if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id)
|
||||
close_button_visible = true;
|
||||
bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x);
|
||||
bool is_hovered = g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id; // Any interaction account for this too.
|
||||
|
||||
if (close_button_visible)
|
||||
if (close_button_id != 0)
|
||||
{
|
||||
if (is_contents_visible)
|
||||
close_button_visible = (g.Style.TabCloseButtonMinWidthSelected < 0.0f) ? true : (is_hovered && bb.GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthSelected));
|
||||
else
|
||||
close_button_visible = (g.Style.TabCloseButtonMinWidthUnselected < 0.0f) ? true : (is_hovered && bb.GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthUnselected));
|
||||
}
|
||||
|
||||
// When tabs/document is unsaved, the unsaved marker takes priority over the close button.
|
||||
const bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x) && (!close_button_visible || !is_hovered);
|
||||
if (unsaved_marker_visible)
|
||||
{
|
||||
const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz));
|
||||
RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text));
|
||||
}
|
||||
else if (close_button_visible)
|
||||
{
|
||||
ImGuiLastItemData last_item_backup = g.LastItemData;
|
||||
if (CloseButton(close_button_id, button_pos))
|
||||
@@ -10369,14 +10546,9 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
|
||||
g.LastItemData = last_item_backup;
|
||||
|
||||
// Close with middle mouse button
|
||||
if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2))
|
||||
if (is_hovered && !(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2))
|
||||
close_button_pressed = true;
|
||||
}
|
||||
else if (unsaved_marker_visible)
|
||||
{
|
||||
const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz));
|
||||
RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text));
|
||||
}
|
||||
|
||||
// This is all rather complicated
|
||||
// (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)
|
||||
|
||||
Reference in New Issue
Block a user